Merge branch 'develop' into bug/JAL-2399textColour bug/JAL-2399textColour
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 30 Mar 2017 11:20:53 +0000 (12:20 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 30 Mar 2017 11:20:53 +0000 (12:20 +0100)
179 files changed:
examples/exampleFeatures.txt
help/help.jhm
help/helpTOC.xml
help/html/features/preferences.html
help/html/webServices/urllinks.html
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/MCview/AppletPDBCanvas.java
src/MCview/PDBCanvas.java
src/MCview/PDBChain.java
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
src/jalview/analysis/AAFrequency.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/PCA.java
src/jalview/api/AlignViewportI.java
src/jalview/api/FeatureRenderer.java
src/jalview/api/SequenceRenderer.java
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/appletgui/AnnotationColourChooser.java
src/jalview/appletgui/AnnotationLabels.java
src/jalview/appletgui/AnnotationPanel.java
src/jalview/appletgui/AppletJmolBinding.java
src/jalview/appletgui/ExtJmol.java
src/jalview/appletgui/FeatureRenderer.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/IdCanvas.java
src/jalview/appletgui/IdPanel.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/appletgui/ScalePanel.java
src/jalview/appletgui/SeqCanvas.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/SequenceRenderer.java
src/jalview/bin/Cache.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentAnnotation.java
src/jalview/datamodel/AlignmentI.java
src/jalview/datamodel/BinarySequence.java
src/jalview/datamodel/ColumnSelection.java
src/jalview/datamodel/HiddenSequences.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/datamodel/SequenceI.java
src/jalview/ext/android/ContainerHelpers.java
src/jalview/ext/android/SparseDoubleArray.java [new file with mode: 0644]
src/jalview/ext/ensembl/EnsemblSeqProxy.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolCommands.java
src/jalview/ext/rbvi/chimera/AtomSpecModel.java [new file with mode: 0644]
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
src/jalview/ext/rbvi/chimera/ChimeraListener.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/ext/varna/VarnaCommands.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/AnnotationColourChooser.java
src/jalview/gui/AnnotationColumnChooser.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/AnnotationRowFilter.java
src/jalview/gui/AppJmol.java
src/jalview/gui/AppJmolBinding.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/ColourMenuHelper.java
src/jalview/gui/DasSourceBrowser.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/IdPanel.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/Jalview2XML_V1.java
src/jalview/gui/JalviewChimeraBindingModel.java
src/jalview/gui/OverviewPanel.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/gui/ScalePanel.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SequenceRenderer.java
src/jalview/gui/StructureViewerBase.java
src/jalview/gui/TreeCanvas.java
src/jalview/gui/UserDefinedColours.java
src/jalview/gui/WsPreferences.java
src/jalview/io/JSONFile.java
src/jalview/io/PDBFeatureSettings.java
src/jalview/javascript/MouseOverStructureListener.java
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GSequenceLink.java
src/jalview/jbgui/GStructureViewer.java
src/jalview/jbgui/GUserDefinedColours.java
src/jalview/math/Matrix.java
src/jalview/math/MatrixI.java [new file with mode: 0644]
src/jalview/math/SparseMatrix.java [new file with mode: 0644]
src/jalview/renderer/AnnotationRenderer.java
src/jalview/renderer/ResidueShader.java
src/jalview/renderer/seqfeatures/FeatureColourFinder.java [new file with mode: 0644]
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/schemes/AnnotationColourGradient.java
src/jalview/schemes/Blosum62ColourScheme.java
src/jalview/schemes/ColourSchemeLoader.java [new file with mode: 0644]
src/jalview/schemes/ColourSchemes.java
src/jalview/schemes/PIDColourScheme.java
src/jalview/schemes/ResidueProperties.java
src/jalview/schemes/ScoreMatrix.java
src/jalview/schemes/UserColourScheme.java
src/jalview/structure/AtomSpec.java
src/jalview/structure/StructureMapping.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/structures/models/AAStructureBindingModel.java
src/jalview/urls/CustomUrlProvider.java [new file with mode: 0644]
src/jalview/urls/IdOrgSettings.java [new file with mode: 0644]
src/jalview/urls/IdentifiersUrlProvider.java [new file with mode: 0644]
src/jalview/urls/UrlLinkDisplay.java [new file with mode: 0644]
src/jalview/urls/UrlLinkTableModel.java [new file with mode: 0644]
src/jalview/urls/UrlProvider.java [new file with mode: 0644]
src/jalview/urls/UrlProviderImpl.java [new file with mode: 0644]
src/jalview/urls/api/UrlProviderFactoryI.java [new file with mode: 0644]
src/jalview/urls/api/UrlProviderI.java [new file with mode: 0644]
src/jalview/urls/applet/AppletUrlProviderFactory.java [new file with mode: 0644]
src/jalview/urls/desktop/DesktopUrlProviderFactory.java [new file with mode: 0644]
src/jalview/util/RangeComparator.java [new file with mode: 0644]
src/jalview/util/UrlConstants.java
src/jalview/util/UrlLink.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/OverviewDimensions.java [new file with mode: 0644]
src/jalview/viewmodel/PCAModel.java
src/jalview/viewmodel/ViewportProperties.java [new file with mode: 0644]
src/jalview/viewmodel/ViewportRanges.java [new file with mode: 0644]
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/workers/ConsensusThread.java
src/jalview/workers/ConservationThread.java
src/jalview/ws/DasSequenceFeatureFetcher.java
src/jalview/ws/sifts/SiftsClient.java
src/jalview/ws/utils/UrlDownloadClient.java [new file with mode: 0644]
test/jalview/datamodel/AlignmentTest.java
test/jalview/datamodel/ColumnSelectionTest.java
test/jalview/datamodel/HiddenSequencesTest.java
test/jalview/datamodel/SequenceGroupTest.java
test/jalview/ext/android/SparseDoubleArrayTest.java [new file with mode: 0644]
test/jalview/ext/jmol/JmolCommandsTest.java
test/jalview/ext/jmol/JmolViewerTest.java
test/jalview/ext/rbvi/chimera/4zho.pdb [new file with mode: 0644]
test/jalview/ext/rbvi/chimera/4zho.xml.gz [new file with mode: 0644]
test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java [new file with mode: 0644]
test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java
test/jalview/ext/rbvi/chimera/JalviewChimeraView.java
test/jalview/ext/rbvi/chimera/testProps.jvprops
test/jalview/gui/AlignViewportTest.java
test/jalview/gui/AlignmentPanelTest.java [new file with mode: 0644]
test/jalview/gui/AnnotationChooserTest.java
test/jalview/gui/AnnotationRowFilterTest.java [new file with mode: 0644]
test/jalview/gui/SequenceRendererTest.java
test/jalview/math/MatrixTest.java
test/jalview/math/SparseMatrixTest.java [new file with mode: 0644]
test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java [new file with mode: 0644]
test/jalview/schemes/AnnotationColourGradientTest.java [new file with mode: 0644]
test/jalview/schemes/Blosum62ColourSchemeTest.java
test/jalview/schemes/ColourSchemesTest.java
test/jalview/schemes/PIDColourSchemeTest.java
test/jalview/schemes/ScoreMatrixPrinter.java
test/jalview/schemes/ScoreMatrixTest.java [new file with mode: 0644]
test/jalview/structure/AtomSpecTest.java [new file with mode: 0644]
test/jalview/structure/StructureMappingTest.java [new file with mode: 0644]
test/jalview/structures/models/AAStructureBindingModelTest.java
test/jalview/urls/AppletUrlProviderFactoryTest.java [new file with mode: 0644]
test/jalview/urls/CustomUrlProviderTest.java [new file with mode: 0644]
test/jalview/urls/DesktopUrlProviderFactoryTest.java [new file with mode: 0644]
test/jalview/urls/IdentifiersUrlProviderTest.java [new file with mode: 0644]
test/jalview/urls/UrlLinkDisplayTest.java [new file with mode: 0644]
test/jalview/urls/UrlLinkTableModelTest.java [new file with mode: 0644]
test/jalview/urls/UrlProviderTest.java [new file with mode: 0644]
test/jalview/util/UrlLinkTest.java
test/jalview/viewmodel/OverviewDimensionsTest.java [new file with mode: 0644]
test/jalview/viewmodel/ViewportRangesTest.java [new file with mode: 0644]
test/jalview/ws/seqfetcher/DasSequenceFetcher.java
test/jalview/ws/sifts/SiftsClientTest.java
test/jalview/ws/utils/UrlDownloadClientTest.java [new file with mode: 0644]

index 2de9817..83dc4b1 100755 (executable)
@@ -1,5 +1,5 @@
 ST-TURN-IIL    blue|255,0,255|absolute|20.0|95.0|below|66.0
-GAMMA-TURN-CLASSIC     red|0,255,255|20.0|95.0|below|66.0
+GAMMA-TURN-CLASSIC     lightGray|0,255,255|20.0|95.0|below|66.0
 BETA-TURN-IR   9a6a94
 BETA-TURN-IL   d6a6ca
 BETA-BULGE     1dc451
index f69ed00..984c2d1 100755 (executable)
    
    <mapID target="uniprotfetcher" url="html/features/uniprotsequencefetcher.html" />
    
+   <mapID target="urllinks" url="html/webServices/urllinks.html" />
+   <mapID target="linksprefs" url="html/features/Preferences.html#links" />
+
    <mapID target="backIcon" url="icons/back.png" />
    <mapID target="forwardIcon" url="icons/forward.png" />
    <mapID target="homeIcon" url="icons/Home.png" />
index 54abd53..482ccdf 100755 (executable)
                        <tocitem text="Chimera Viewer" target="chimera" />                      
                </tocitem>
                <tocitem text="Viewing RNA structures" target="varna" expand="false"/>
+               <tocitem text="Opening URLs from Jalview" target="urllinks" expand="true">
+                   <tocitem text="Configuring URL Links" target="urllinkspref" />
+               </tocitem>
                <tocitem text="VAMSAS Data Exchange" target="vamsas">
                        <!-- what can Jalview share with other apps -->
                        <!-- what other apps exist -->
index 6a8c86c..da045ba 100755 (executable)
       and displaying structure information.
     </li>
     <li>The <a href="#connections"><strong>&quot;Connections&quot;</strong>
-        Preferences</a> tab allows you to change the links made from Jalview
-      to your default web browser.
+        Preferences</a> tab allows you to configure Jalview's internet
+      settings and specify your default web browser.
+    </li>
+    <li>The <a href="#links"><strong>&quot;Links&quot;</strong>
+        Preferences</a> tab shows the currently configured <em>URL
+        Links</em> shown in the <strong>Link</strong> submenu in the Sequence
+      ID popup menu.
     </li>
     <li>The <a href="#output"><strong>&quot;Output&quot;</strong>
         Preferences</a> tab contains settings affecting the export of
         Preferences tab</strong></a>
   </p>
   <p>
-    <em>URL Link From Sequence ID</em><br> These definitions are
-    used to generate URLs from a sequence's ID or database cross
-    references. Read more about <a
-      href="../webServices/urllinks.html#urllinks">configuring
-      URL links here</a>.
-  </p>
-  <p>
     <em>Default Browser (Unix)</em><br> Its difficult in Java to
     detect the default web browser for Unix users. If Jalview can't find
     your default web browser, enter the name or full path to your web
       statement</a> for more information.
   </p>
   <p>
+    <a name="links"><strong>The &quot;Links&quot; Preferences
+        tab</strong></a>
+  </p>
+  <p>
+    This panel shows a table, and two sections - <em>Edit</em> and <em>Filter</em>.
+    The table shows the available URL link definitions (consisting of a
+    database, Name, and URL template string), a checkbox <em>In
+      Menu</em> which indicates if the link is enabled, and <em>Double
+      Click</em> which marks the link that will be opened if a sequence's ID
+    is double clicked. The table can be sorted by clicking on the column headers.
+  </p>
+  <p><em>Edit Links</em><br /> This section contains three buttons,
+    <em>New</em>, <em>Edit</em> and <em>Delete</em>, which allow you to
+    create, modify and remove user-defined URL links from the Sequence
+    ID's links submenu.
+  </p>
+  <p>
+    <em>Filter</em><br /> The <em>Filter text</em> box allows you to
+    quickly show rows in the table containing a particular text string.
+    The <em>Custom only</em> button limits the entries in the table to
+    just those you have configured yourself <em>via</em> the <em>Edit
+      Links</em> buttons. Press <em>Show all</em> to clear any filters.
+  <p>
+    <a href="../webServices/urllinks.html#urllinks">Read more about configuring
+      URL links.</a>
+  </p>
+  <p>
     <a name="output"><strong>Output Preferences tab</strong></a>
   </p>
   <p>
     and PDB file association (if available). The Jalview id/start-end
     option is ignored if Modeller output is selected.
   <p>
-    <a name="editing"><strong>Editing Preferences tab</strong></a>
+    <a name="editing"><strong>e&quot;Editinge&quot; Preferences tab</strong></a>
   </p>
   <p>There are currently three options available which can be
     selected / deselected.</p>
index 088a539..da5d7dd 100644 (file)
 </head>
 <body>
   <p>
-  <p>
     <strong>Opening URLs from Jalview</strong><br> Both the applet
     and the desktop application are able to open URLs as 'popups' in
-    your web browser. <br> Double-clicking on the ID of a sequence
-    will open the first URL that can be generated from its sequence ID.
+    your web browser.</p>
+    <p> Double-clicking on the ID of a sequence
+    will open whichever URL is selected for 'popups' in the <strong>&quot;Links&quot;</strong> tab of the <a
+    href="../features/preferences.html#links">Jalview desktop
+    preferences</a>.
     This is by default the EMBL-EBI site, but you can easily configure your own <a
       href="#urllinks">sequence URL links</a>.
   </p>
   <p>
-    Other links for a sequence either derived from any other configured
+    Other links for a sequence, either derived from any other configured
     URL links, or imported from the sequence's annotation, are accessed
     by right clicking to open the sequence pop-up menu, and selecting
     from the <em>Links</em> submenu.
   </p>
   <p>
     <strong><a name="urllinks">Configuring URL Links</a></strong> <br>URL
-    links are defined in the &quot;Connections&quot; tab of the <a
-    href="../features/preferences.html">Jalview desktop
+    links are defined in the &quot;Links&quot; tab of the <a
+    href="../features/preferences.html#links">Jalview desktop
     preferences</a>, or specified as <a
     href="http://www.jalview.org/examples/appletParameters.html#parameters">applet
-    parameters</a>. <br> By default the item &quot;EMBL-EBI Search&quot; is added
-    to this link menu. This link will show a web page in your default
-    browser with the selected sequence id as part of the URL.<br>
-    In the preferences dialog box, click <strong>new</strong> to add a
-    new link, and <strong>edit</strong> to modify an existing link, or <strong>delete</strong>
-    to remove it.<br> You can name the link, this will be displayed
-    on a new menu item under the &quot;Link&quot; menu when you right
-    click on a sequence id. <br> The URL string must contain a
-    token that can be replaced with a sequence ID or DB accession ID. The simplest token is
-    &quot;$SEQUENCE_ID$&quot;, which will be replaced by the chosen
-    sequence id when you click on it. 
+    parameters</a>.</p>
+  <p>
+    <em>Default Link Settings</em><br /> The &quot;EMBL-EBI Search&quot;
+    link is the default link shown in the &quot;Link&quot; submenu, and
+    opened when double-clicking on a sequence ID. When clicked, this
+    link will show a web page in your default browser with the selected
+    sequence ID as part of the URL.
+  </p>
+  <p>
+    <em>Adding additional links</em><br /> You can configure your own
+    links via the Jalview <a href="../features/preferences.html#links"><strong>Preferences</strong></a>
+    dialog. Jalview also provides persistent URLs for many common
+    bioinformatics databases. These links are downloaded by Jalview from
+    the <em>identifiers.org</em> website, and the names and URLs are not
+    user editable.
   </p>
   <p>
-    eg.<br> UniRef100 =
-    http://www.ebi.uniprot.org/uniprot-srv/uniRefView.do?proteinAc=$SEQUENCE_ID$&amp;library=uniref100<br>
-    Swissprot = http://www.expasy.org/uniprot/$SEQUENCE_ID$ <br> <br>
+    <em>Creating your own URL link</em> URL links are specified as a
+    template containing special tokens that Jalview will replace with
+    the Sequence ID or Database Accession of the sequence when you
+    double click on its ID or open it's <strong>Link</strong> submenu.
+    Link URL templates must contain at least one token. 
+  </p>
+    <em>eg.</em><pre> UniRef100 =
+    http://www.ebi.uniprot.org/uniprot-srv/uniRefView.do?proteinAc=$SEQUENCE_ID$&amp;library=uniref100<br/>
+    Swissprot = http://www.expasy.org/uniprot/$SEQUENCE_ID$ <br> </pre>
+  <p>
     Links will also be made for any database cross references associated
     with the sequence where the database name exactly matches a URL link
     name. In this case, the $DB_ACCESSION$ string will be replaced with
   <p>
     <strong>Regular Expression Substitution</strong><br> A url may
     contain a string of the form $SEQUENCE_ID=/<em>regular
-    expression</em>/=$ or $DB_ACCESSION=/<em>regular expression</em>/=$. 
-    In this case, the regular expression will be
-    applied to the full sequence ID or DB accession ID string and the resulting match will
+      expression</em>/=$ or $DB_ACCESSION=/<em>regular expression</em>/=$. In
+    this case, the regular expression will be applied to the full
+    sequence ID or DB accession ID string and the resulting match will
     be inserted into the URL. Groups of parentheses can be used to
     specify which regions of the regular expression will be used to
     generate the URL:
+  
   <ul>
     <li>Each top level parenthesis will yield a URL containing the
       text matched within that parenthesis.</li>
     <li>Regions matching sub-parentheses within a top-level
       parenthesis will be concatenated to form the text inserted into
       the URL for the top-level parenthesis.</li>
-    <em>Please Note:
-      <ul>
-        <li>The regular expressions supported by Jalview are those
-          provided by the <a href="http://www.javaregex.com">Stevesoft
-            javaregex package</a>.
-        </li>
-        <li>Some characters must be escaped when specifying them as
-          a match within a regular expression.</li>
-      </ul> <br> Many Thanks to Bernd Brandt of the Free University of
-      Amsterdam for testing this new regular-expression expansion
-      feature!
-    </em>
-    <em>
   </ul>
-  </p>
-  </p>
+  <em>Please Note:</em>
+    <ul>
+      <li>The regular expressions supported by Jalview are those
+        provided by the <a href="http://www.javaregex.com">Stevesoft
+          javaregex package</a>.
+      </li>
+      <li>Some characters must be escaped when specifying them as a
+        match within a regular expression.</li>
+    </ul> <br> Many Thanks to Bernd Brandt of the Free University of
+    Amsterdam for testing the regular-expression expansion feature!
 </body>
 </html>
index 8c3c84f..7db822c 100644 (file)
@@ -138,7 +138,8 @@ action.view_flanking_regions = Show flanking regions
 label.view_flanking_regions = Show sequence data either side of the subsequences involved in this alignment
 label.structures_manager = Structures Manager
 label.nickname = Nickname:
-label.url = URL:
+label.url = URL
+label.url\: = URL:
 label.input_file_url = Enter URL or Input File
 label.select_feature = Select feature
 label.name = Name
@@ -413,7 +414,6 @@ label.couldnt_import_as_vamsas_session = Couldn't import {0} as a new vamsas ses
 label.vamsas_document_import_failed = Vamsas Document Import Failed
 label.couldnt_locate = Couldn't locate {0}
 label.url_not_found = URL not found
-label.no_link_selected = No link selected
 label.new_sequence_url_link = New sequence URL link
 label.cannot_edit_annotations_in_wrapped_view = Cannot edit annotations in wrapped view
 label.wrapped_view_no_edit = Wrapped view - no edit
@@ -515,7 +515,7 @@ label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences =
 label.standard_databases = Standard Databases
 label.fetch_embl_uniprot = Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources
 label.reset_min_max_colours_to_defaults = Reset min and max colours to defaults from user preferences.
-label.align_structures_using_linked_alignment_views = Align structures using {0} linked alignment views
+label.align_structures_using_linked_alignment_views = Superpose structures using {0} selected alignment view(s)
 label.connect_to_session = Connect to session {0}
 label.threshold_feature_display_by_score = Threshold the feature display by score.
 label.threshold_feature_no_threshold = No Threshold
@@ -620,6 +620,8 @@ label.web_services = Web Services
 label.right_click_to_edit_currently_selected_parameter = Right click to edit currently selected parameter.
 label.let_jmol_manage_structure_colours = Let Jmol manage structure colours
 label.let_chimera_manage_structure_colours = Let Chimera manage structure colours
+label.fetch_chimera_attributes = Fetch Chimera attributes
+label.fetch_chimera_attributes_tip = Copy Chimera attribute to Jalview feature
 label.marks_leaves_tree_not_associated_with_sequence = Marks leaves of tree not associated with a sequence
 label.index_web_services_menu_by_host_site = Index web services in menu by the host site
 label.option_want_informed_web_service_URL_cannot_be_accessed_jalview_when_starts_up = Check this option if you want to be informed<br>when a web service URL cannot be accessed by Jalview<br>when it starts up
@@ -712,15 +714,21 @@ label.link_name = Link Name
 label.pdb_file = PDB file
 label.colour_with_jmol = Colour with Jmol
 label.colour_with_chimera = Colour with Chimera
-label.align_structures = Align Structures
+label.superpose_structures = Superpose Structures
+error.superposition_failed = Superposition failed: {0}
+label.insufficient_residues = Not enough aligned residues ({0}) to perform superposition
 label.jmol = Jmol
 label.chimera = Chimera
+label.create_chimera_attributes = Write Jalview features
+label.create_chimera_attributes_tip = Set Chimera residue attributes for visible features
+label.attributes_set = {0} attribute values set on Chimera
 label.sort_alignment_by_tree = Sort Alignment By Tree
 label.mark_unlinked_leaves = Mark Unlinked Leaves
 label.associate_leaves_with = Associate Leaves With
 label.save_colour_scheme_with_unique_name_added_to_colour_menu = Save your colour scheme with a unique name and it will be added to the Colour menu
 label.case_sensitive = Case Sensitive
-label.lower_case_colour = Lower Case Colour
+label.lower_case_colour = Colour All Lower Case
+label.lower_case_tip = Chosen colour applies to all lower case symbols
 label.index_by_host = Index by Host
 label.index_by_type = Index by Type
 label.enable_jabaws_services = Enable JABAWS Services
@@ -1269,4 +1277,21 @@ 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
+exception.url_cannot_have_miriam_id = {0} is a MIRIAM id and cannot be used as a custom url name
+exception.url_cannot_have_duplicate_id = {0} cannot be used as a label for more than one line
+label.filter = Filter text:
+action.customfilter = Custom only
+action.showall = Show All
+label.insert = Insert:
+action.seq_id = $SEQUENCE_ID$
+action.db_acc = $DB_ACCESSION$
+label.primary = Double Click
+label.inmenu = In Menu
+label.id = ID
+label.database = Database
+label.urltooltip = Only one url, which must use a sequence id, can be selected for the 'On Click' option
+label.edit_sequence_url_link = Edit sequence URL link
+warn.name_cannot_be_duplicate = User-defined URL names must be unique and cannot be MIRIAM ids
+label.invalid_name = Invalid Name !
 label.output_seq_details = Output Sequence Details to list all database references
+label.urllinks = Links
index 64a3654..2e405c8 100644 (file)
@@ -135,7 +135,8 @@ action.view_flanking_regions = Mostrar flancos
 label.view_flanking_regions = Mostrar los datos de la secuencia a ambos lados de las subsecuencias implicadas en este alineamiento
 label.structures_manager = Administrar estructuras
 label.nickname = Sobrenombre:
-label.url = URL: 
+label.url\: = URL:
+label.url = URL 
 label.input_file_url = Introducir URL en el fichero de entrada
 label.select_feature = Seleccionar característica
 label.name = Nombre
@@ -380,7 +381,6 @@ label.couldnt_import_as_vamsas_session = No se pudo importar {0} como una nueva
 label.vamsas_document_import_failed =  Fallo en la importación del documento Vamsas
 label.couldnt_locate = No se pudo localizar {0}
 label.url_not_found = URL no encontrada
-label.no_link_selected = Enlace no seleccionado
 label.new_sequence_url_link = Enlace a una nueva secuencia URL
 label.cannot_edit_annotations_in_wrapped_view = No se pueden editar anotaciones en vista envolvente
 label.wrapped_view_no_edit = Vista envolvente - no editar
@@ -476,7 +476,7 @@ label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences =
 label.standard_databases = Bases de datos estándar
 label.fetch_embl_uniprot = Recuperar de EMBL/EMBLCDS o Uniprot/PDB y de cualquier fuente DAS seleccionada
 label.reset_min_max_colours_to_defaults = Reiniciar los colores min y max colours a los valores por defecto establecidos en las preferencias de usuario
-label.align_structures_using_linked_alignment_views = Alinear las estructuras utlizando las {0} vistas de alineamiento enlazadas
+label.align_structures_using_linked_alignment_views = Alinear las estructuras utilizando las {0} vista(s) de alineamiento enlazada(s)
 label.connect_to_session = Conectar a la sesión {0}
 label.threshold_feature_display_by_score = Filtrar la característica mostrada por puntuación.
 label.threshold_feature_no_threshold = Sin umbral
@@ -665,7 +665,8 @@ label.mark_unlinked_leaves = Marcar las hojas como no enlazadas
 label.associate_leaves_with = Asociar hojas con
 label.save_colour_scheme_with_unique_name_added_to_colour_menu = Guarde el esquema cromáticos con un nombre Ãºnico y se añadirá al menú de colores
 label.case_sensitive = Sensible a mayúsculas
-label.lower_case_colour = Color para las minúsculas
+label.lower_case_colour = Colorear todas las minúsculas
+label.lower_case_tip = El color elegido se aplicará a todas las minúsculas
 label.index_by_host = Indizar por host
 label.index_by_type = Indizar por tipo
 label.enable_jabaws_services = Habilitar servicios JABAWS
@@ -1269,4 +1270,21 @@ label.SEQUENCE_ID_no_longer_used = $SEQUENCE_ID$ no se utiliza m
 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
+exception.url_cannot_have_miriam_id = {0} es una id MIRIAM y no puede ser usada como nombre url personalizado
+exception.url_cannot_have_duplicate_id = {0} no puede ser usada como etiqueta en más de un enlace
+label.filter = Filtrar texto:
+action.customfilter = Sólo personalizado
+action.showall = Mostrar todo
+label.insert = Insertar:
+action.seq_id = $SEQUENCE_ID$
+action.db_acc = $DB_ACCESSION$
+label.primary = Doble clic
+label.inmenu = En Menú
+label.id = ID
+label.database = Base de datos
+label.urltooltip = Sólo una url, que debe usar una id de secuencia, puede ser seleccionada en la opción 'On Click'
+label.edit_sequence_url_link = Editar link de secuencia URL
+warn.name_cannot_be_duplicate = Los nombres URL definidos por el usuario deben ser Ãºnicos y no pueden ser ids de MIRIAM
+label.invalid_name = Nombre inválido !
 label.output_seq_details = Seleccionar Detalles de la secuencia para ver todas
+label.urllinks = Enlaces
index aac796c..3ae0650 100644 (file)
@@ -28,6 +28,7 @@ import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.structure.AtomSpec;
 import jalview.structure.StructureListener;
 import jalview.structure.StructureMapping;
@@ -577,6 +578,8 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
       showFeatures = true;
     }
 
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
+
     PDBChain chain;
     if (bysequence && pdb != null)
     {
@@ -604,25 +607,16 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
                 if (pos > 0)
                 {
                   pos = sequence[s].findIndex(pos);
-                  tmp.startCol = sr.getResidueBoxColour(sequence[s], pos);
-                  if (showFeatures)
-                  {
-                    tmp.startCol = fr.findFeatureColour(tmp.startCol,
-                            sequence[s], pos);
-                  }
+                  tmp.startCol = sr.getResidueColour(sequence[s], pos,
+                          finder);
                 }
                 pos = mapping[m].getSeqPos(tmp.at2.resNumber) - 1;
                 if (pos > 0)
                 {
                   pos = sequence[s].findIndex(pos);
-                  tmp.endCol = sr.getResidueBoxColour(sequence[s], pos);
-                  if (showFeatures)
-                  {
-                    tmp.endCol = fr.findFeatureColour(tmp.endCol,
-                            sequence[s], pos);
-                  }
+                  tmp.endCol = sr
+                          .getResidueColour(sequence[s], pos, finder);
                 }
-
               }
             }
           }
index 292de91..08bca8c 100644 (file)
@@ -28,6 +28,7 @@ import jalview.gui.FeatureRenderer;
 import jalview.gui.SequenceRenderer;
 import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.structure.AtomSpec;
 import jalview.structure.StructureListener;
 import jalview.structure.StructureMapping;
@@ -546,6 +547,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
       showFeatures = true;
     }
 
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
     PDBChain chain;
     if (bysequence && pdb != null)
     {
@@ -573,23 +575,15 @@ public class PDBCanvas extends JPanel implements MouseListener,
                 if (pos > 0)
                 {
                   pos = sequence[s].findIndex(pos);
-                  tmp.startCol = sr.getResidueBoxColour(sequence[s], pos);
-                  if (showFeatures)
-                  {
-                    tmp.startCol = fr.findFeatureColour(tmp.startCol,
-                            sequence[s], pos);
-                  }
+                  tmp.startCol = sr.getResidueColour(sequence[s], pos,
+                          finder);
                 }
                 pos = mapping[m].getSeqPos(tmp.at2.resNumber) - 1;
                 if (pos > 0)
                 {
                   pos = sequence[s].findIndex(pos);
-                  tmp.endCol = sr.getResidueBoxColour(sequence[s], pos);
-                  if (showFeatures)
-                  {
-                    tmp.endCol = fr.findFeatureColour(tmp.endCol,
-                            sequence[s], pos);
-                  }
+                  tmp.endCol = sr
+                          .getResidueColour(sequence[s], pos, finder);
                 }
 
               }
index c40cdda..ba93046 100755 (executable)
@@ -39,6 +39,8 @@ import java.util.Vector;
 
 public class PDBChain
 {
+  public static final String RESNUM_FEATURE = "RESNUM";
+
   /**
    * SequenceFeature group for PDB File features added to sequences
    */
@@ -367,7 +369,7 @@ public class PDBChain
         Residue tmpres = residues.lastElement();
         Atom tmpat = tmpres.atoms.get(0);
         // Make A new SequenceFeature for the current residue numbering
-        SequenceFeature sf = new SequenceFeature("RESNUM", tmpat.resName
+        SequenceFeature sf = new SequenceFeature(RESNUM_FEATURE, tmpat.resName
                 + ":" + tmpat.resNumIns + " " + pdbid + id, "", offset
                 + count, offset + count, pdbid);
         resFeatures.addElement(sf);
index 439d479..85ae718 100644 (file)
@@ -423,6 +423,11 @@ public class ChimeraManager
             "list selection level residue", true);
     if (chimeraReply != null)
     {
+      /*
+       * expect 0, 1 or more lines of the format
+       * residue id #0:43.A type GLY
+       * where we are only interested in the atomspec #0.43.A
+       */
       for (String inputLine : chimeraReply)
       {
         String[] inputLineParts = inputLine.split("\\s+");
index 17874e6..ee16f94 100755 (executable)
  */
 package jalview.analysis;
 
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
@@ -37,10 +41,6 @@ import jalview.util.Format;
 import jalview.util.MappingUtils;
 import jalview.util.QuickSort;
 
-import java.util.Arrays;
-import java.util.Hashtable;
-import java.util.List;
-
 /**
  * Takes in a vector or array of sequences and column start and column end and
  * returns a new Hashtable[] of size maxSeqLength, if Hashtable not supplied.
@@ -67,8 +67,8 @@ public class AAFrequency
     }
   }
 
-  public static final ProfilesI calculate(List<SequenceI> list,
-          int start, int end)
+  public static final ProfilesI calculate(List<SequenceI> list, int start,
+          int end)
   {
     return calculate(list, start, end, false);
   }
@@ -289,6 +289,56 @@ public class AAFrequency
   }
 
   /**
+   * Derive the gap count annotation row.
+   * 
+   * @param gaprow
+   *          the annotation row to add annotations to
+   * @param profiles
+   *          the source consensus data
+   * @param startCol
+   *          start column (inclusive)
+   * @param endCol
+   *          end column (exclusive)
+   */
+  public static void completeGapAnnot(AlignmentAnnotation gaprow,
+          ProfilesI profiles, int startCol, int endCol, long nseq)
+  {
+    if (gaprow == null || gaprow.annotations == null
+            || gaprow.annotations.length < endCol)
+    {
+      /*
+       * called with a bad alignment annotation row 
+       * wait for it to be initialised properly
+       */
+      return;
+    }
+    // always set ranges again
+    gaprow.graphMax = nseq;
+    gaprow.graphMin = 0;
+    for (int i = startCol; i < endCol; i++)
+    {
+      ProfileI profile = profiles.get(i);
+      if (profile == null)
+      {
+        /*
+         * happens if sequences calculated over were 
+         * shorter than alignment width
+         */
+        gaprow.annotations[i] = null;
+        return;
+      }
+
+      final int gapped = profile.getNonGapped();
+
+      String description = String.valueOf(gapped);
+
+      gaprow.annotations[i] = new Annotation(description, description,
+              '\0',
+              gapped);
+    }
+  }
+
+  /**
    * Returns a tooltip showing either
    * <ul>
    * <li>the full profile (percentages of all residues present), if
@@ -357,8 +407,7 @@ public class AAFrequency
    *          calculations
    * @return
    */
-  public static int[] extractProfile(ProfileI profile,
-          boolean ignoreGaps)
+  public static int[] extractProfile(ProfileI profile, boolean ignoreGaps)
   {
     int[] rtnval = new int[64];
     ResidueCount counts = profile.getCounts();
index ea96b3b..232cb5d 100644 (file)
@@ -42,6 +42,7 @@ import jalview.util.Comparison;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
+import jalview.util.RangeComparator;
 import jalview.util.StringUtils;
 
 import java.io.UnsupportedEncodingException;
@@ -2265,14 +2266,7 @@ public class AlignmentUtils
      * ranges are assembled in order. Other cases should not use this method,
      * but instead construct an explicit mapping for CDS (e.g. EMBL parsing).
      */
-    Collections.sort(result, new Comparator<int[]>()
-    {
-      @Override
-      public int compare(int[] o1, int[] o2)
-      {
-        return Integer.compare(o1[0], o2[0]);
-      }
-    });
+    Collections.sort(result, new RangeComparator(true));
     return result;
   }
 
index eaea7bf..9babaee 100755 (executable)
@@ -20,9 +20,7 @@
  */
 package jalview.analysis;
 
-import jalview.datamodel.BinarySequence;
-import jalview.datamodel.BinarySequence.InvalidSequenceTypeException;
-import jalview.math.Matrix;
+import jalview.math.MatrixI;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.ScoreMatrix;
 
@@ -30,23 +28,22 @@ import java.io.PrintStream;
 
 /**
  * Performs Principal Component Analysis on given sequences
- * 
- * @author $author$
- * @version $Revision$
  */
 public class PCA implements Runnable
 {
-  Matrix m;
-
-  Matrix symm;
+  boolean jvCalcMode = true;
 
-  Matrix m2;
+  MatrixI symm;
 
   double[] eigenvalue;
 
-  Matrix eigenvector;
+  MatrixI eigenvector;
 
-  StringBuffer details = new StringBuffer();
+  StringBuilder details = new StringBuilder(1024);
+
+  private String[] seqs;
+
+  private ScoreMatrix scoreMatrix;
 
   /**
    * Creates a new PCA object. By default, uses blosum62 matrix to generate
@@ -77,87 +74,23 @@ public class PCA implements Runnable
 
   public PCA(String[] s, boolean nucleotides, String s_m)
   {
+    this.seqs = s;
 
-    BinarySequence[] bs = new BinarySequence[s.length];
-    int ii = 0;
-
-    while ((ii < s.length) && (s[ii] != null))
-    {
-      bs[ii] = new BinarySequence(s[ii], nucleotides);
-      bs[ii].encode();
-      ii++;
-    }
-
-    BinarySequence[] bs2 = new BinarySequence[s.length];
-    ii = 0;
-    ScoreMatrix smtrx = null;
+    scoreMatrix = null;
     String sm = s_m;
     if (sm != null)
     {
-      smtrx = ResidueProperties.getScoreMatrix(sm);
+      scoreMatrix = ResidueProperties.getScoreMatrix(sm);
     }
-    if (smtrx == null)
+    if (scoreMatrix == null)
     {
       // either we were given a non-existent score matrix or a scoremodel that
       // isn't based on a pairwise symbol score matrix
-      smtrx = ResidueProperties.getScoreMatrix(sm = (nucleotides ? "DNA"
-              : "BLOSUM62"));
+      scoreMatrix = ResidueProperties
+              .getScoreMatrix(sm = (nucleotides ? "DNA" : "BLOSUM62"));
     }
     details.append("PCA calculation using " + sm
             + " sequence similarity matrix\n========\n\n");
-    while ((ii < s.length) && (s[ii] != null))
-    {
-      bs2[ii] = new BinarySequence(s[ii], nucleotides);
-      if (smtrx != null)
-      {
-        try
-        {
-          bs2[ii].matrixEncode(smtrx);
-        } catch (InvalidSequenceTypeException x)
-        {
-          details.append("Unexpected mismatch of sequence type and score matrix. Calculation will not be valid!\n\n");
-        }
-      }
-      ii++;
-    }
-
-    // System.out.println("Created binary encoding");
-    // printMemory(rt);
-    int count = 0;
-
-    while ((count < bs.length) && (bs[count] != null))
-    {
-      count++;
-    }
-
-    double[][] seqmat = new double[count][bs[0].getDBinary().length];
-    double[][] seqmat2 = new double[count][bs2[0].getDBinary().length];
-    int i = 0;
-
-    while (i < count)
-    {
-      seqmat[i] = bs[i].getDBinary();
-      seqmat2[i] = bs2[i].getDBinary();
-      i++;
-    }
-
-    // System.out.println("Created array");
-    // printMemory(rt);
-    // System.out.println(" --- Original matrix ---- ");
-    m = new Matrix(seqmat);
-    m2 = new Matrix(seqmat2);
-
-  }
-
-  /**
-   * Returns the matrix used in PCA calculation
-   * 
-   * @return java.math.Matrix object
-   */
-
-  public Matrix getM()
-  {
-    return m;
   }
 
   /**
@@ -170,7 +103,7 @@ public class PCA implements Runnable
    */
   public double getEigenvalue(int i)
   {
-    return eigenvector.d[i];
+    return eigenvector.getD()[i];
   }
 
   /**
@@ -189,9 +122,9 @@ public class PCA implements Runnable
    */
   public float[][] getComponents(int l, int n, int mm, float factor)
   {
-    float[][] out = new float[m.rows][3];
+    float[][] out = new float[getHeight()][3];
 
-    for (int i = 0; i < m.rows; i++)
+    for (int i = 0; i < getHeight(); i++)
     {
       out[i][0] = (float) component(i, l) * factor;
       out[i][1] = (float) component(i, n) * factor;
@@ -212,9 +145,9 @@ public class PCA implements Runnable
   public double[] component(int n)
   {
     // n = index of eigenvector
-    double[] out = new double[m.rows];
+    double[] out = new double[getHeight()];
 
-    for (int i = 0; i < m.rows; i++)
+    for (int i = 0; i < out.length; i++)
     {
       out[i] = component(i, n);
     }
@@ -236,12 +169,12 @@ public class PCA implements Runnable
   {
     double out = 0.0;
 
-    for (int i = 0; i < symm.cols; i++)
+    for (int i = 0; i < symm.width(); i++)
     {
-      out += (symm.value[row][i] * eigenvector.value[i][n]);
+      out += (symm.getValue(row, i) * eigenvector.getValue(i, n));
     }
 
-    return out / eigenvector.d[n];
+    return out / eigenvector.getD()[n];
   }
 
   public String getDetails()
@@ -270,25 +203,17 @@ public class PCA implements Runnable
       }
     };
 
+    // long now = System.currentTimeMillis();
     try
     {
       details.append("PCA Calculation Mode is "
               + (jvCalcMode ? "Jalview variant" : "Original SeqSpace")
               + "\n");
-      Matrix mt = m.transpose();
 
-      details.append(" --- OrigT * Orig ---- \n");
-      if (!jvCalcMode)
-      {
-        eigenvector = mt.preMultiply(m); // standard seqspace comparison matrix
-      }
-      else
-      {
-        eigenvector = mt.preMultiply(m2); // jalview variation on seqsmace
-                                          // method
-      }
+      eigenvector = scoreMatrix.computePairwiseScores(seqs);
 
-      eigenvector.print(ps);
+      details.append(" --- OrigT * Orig ---- \n");
+      eigenvector.print(ps, "%8.2f");
 
       symm = eigenvector.copy();
 
@@ -296,10 +221,10 @@ public class PCA implements Runnable
 
       details.append(" ---Tridiag transform matrix ---\n");
       details.append(" --- D vector ---\n");
-      eigenvector.printD(ps);
+      eigenvector.printD(ps, "%15.4e");
       ps.println();
       details.append("--- E vector ---\n");
-      eigenvector.printE(ps);
+      eigenvector.printE(ps, "%15.4e");
       ps.println();
 
       // Now produce the diagonalization matrix
@@ -313,9 +238,9 @@ public class PCA implements Runnable
     }
 
     details.append(" --- New diagonalization matrix ---\n");
-    eigenvector.print(ps);
+    eigenvector.print(ps, "%8.2f");
     details.append(" --- Eigenvalues ---\n");
-    eigenvector.printD(ps);
+    eigenvector.printD(ps, "%15.4e");
     ps.println();
     /*
      * for (int seq=0;seq<symm.rows;seq++) { ps.print("\"Seq"+seq+"\""); for
@@ -323,12 +248,24 @@ public class PCA implements Runnable
      * 
      * ps.print(","+component(seq, ev)); } ps.println(); }
      */
+    // System.out.println(("PCA.run() took "
+    // + (System.currentTimeMillis() - now) + "ms"));
   }
 
-  boolean jvCalcMode = true;
-
   public void setJvCalcMode(boolean calcMode)
   {
     this.jvCalcMode = calcMode;
   }
+
+  /**
+   * Answers the N dimensions of the NxN PCA matrix. This is the number of
+   * sequences involved in the pairwise score calculation.
+   * 
+   * @return
+   */
+  public int getHeight()
+  {
+    // TODO can any of seqs[] be null?
+    return seqs.length;
+  }
 }
index 2802684..8b07340 100644 (file)
@@ -33,6 +33,7 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ColourSchemeI;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.Color;
 import java.util.Hashtable;
@@ -46,7 +47,13 @@ import java.util.Map;
 public interface AlignViewportI extends ViewStyleI
 {
 
-  int getEndRes();
+  /**
+   * Get the ranges object containing details of the start and end sequences and
+   * residues
+   * 
+   * @return
+   */
+  public ViewportRanges getRanges();
 
   /**
    * calculate the height for visible annotation, revalidating bounds where
@@ -119,6 +126,13 @@ public interface AlignViewportI extends ViewStyleI
   AlignmentAnnotation getAlignmentConsensusAnnotation();
 
   /**
+   * get the container for alignment gap annotation
+   * 
+   * @return
+   */
+  AlignmentAnnotation getAlignmentGapAnnotation();
+
+  /**
    * get the container for cDNA complement consensus annotation
    * 
    * @return
index f54231e..7123b8c 100644 (file)
@@ -24,6 +24,7 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 
 import java.awt.Color;
+import java.awt.Graphics;
 import java.util.List;
 import java.util.Map;
 
@@ -37,18 +38,32 @@ public interface FeatureRenderer
 {
 
   /**
-   * compute the perceived colour for a given column position in sequenceI,
-   * taking transparency and feature visibility into account.
+   * Computes the feature colour for a given sequence and column position,
+   * taking into account sequence feature locations, feature colour schemes,
+   * render ordering, feature and feature group visibility, and transparency.
+   * <p>
+   * The graphics argument should be provided if transparency is applied
+   * (getTransparency() < 1). With feature transparency, visible features are
+   * written to the graphics context and the composite colour may be read off
+   * from it. In this case, the returned feature colour is not the composite
+   * colour but that of the last feature drawn.
+   * <p>
+   * If no transparency applies, then the graphics argument may be null, and the
+   * returned colour is the one that would be drawn for the feature.
+   * <p>
+   * Returns null if there is no visible feature at the position.
+   * <p>
+   * This is provided to support rendering of feature colours other than on the
+   * sequence alignment, including by structure viewers and the overview window.
+   * Note this method takes no account of whether the sequence or column is
+   * hidden.
    * 
-   * @param col
-   *          - background colour (due to alignment/group shading schemes, etc).
-   * @param sequenceI
-   *          - sequence providing features
-   * @param r
-   *          - column position
+   * @param sequence
+   * @param column
+   * @param g
    * @return
    */
-  Color findFeatureColour(Color col, SequenceI sequenceI, int r);
+  Color findFeatureColour(SequenceI sequence, int column, Graphics g);
 
   /**
    * trigger the feature discovery process for a newly created feature renderer.
@@ -170,4 +185,19 @@ public interface FeatureRenderer
    */
   void setVisible(String featureType);
 
+  /**
+   * Sets the transparency value, between 0 (full transparency) and 1 (no
+   * transparency)
+   * 
+   * @param value
+   */
+  void setTransparency(float value);
+
+  /**
+   * Returns the transparency value, between 0 (full transparency) and 1 (no
+   * transparency)
+   * 
+   * @return
+   */
+  float getTransparency();
 }
index d708902..54f7fb6 100644 (file)
 package jalview.api;
 
 import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 
 import java.awt.Color;
 
 public interface SequenceRenderer
 {
 
-  Color getResidueBoxColour(SequenceI sequenceI, int r);
-
-  Color getResidueColour(SequenceI seq, int position, FeatureRenderer fr);
+  Color getResidueColour(SequenceI seq, int position,
+          FeatureColourFinder finder);
 
 }
index 4ae0ed2..8fd317a 100644 (file)
@@ -210,7 +210,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
   Menu menu1 = new Menu();
 
   public APopupMenu(AlignmentPanel apanel, final SequenceI seq,
-          Vector<String> links)
+          List<String> links)
   {
     // /////////////////////////////////////////////////////////
     // If this is activated from the sequence panel, the user may want to
@@ -243,6 +243,24 @@ public class APopupMenu extends java.awt.PopupMenu implements
     SequenceGroup sg = ap.av.getSelectionGroup();
     if (sg != null && sg.getSize() > 0)
     {
+      if (sg.isNucleotide())
+      {
+        conservationColour.setEnabled(false);
+        clustalColour.setEnabled(false);
+        BLOSUM62Colour.setEnabled(false);
+        zappoColour.setEnabled(false);
+        taylorColour.setEnabled(false);
+        hydrophobicityColour.setEnabled(false);
+        helixColour.setEnabled(false);
+        strandColour.setEnabled(false);
+        turnColour.setEnabled(false);
+        buriedColour.setEnabled(false);
+      }
+      else
+      {
+        purinePyrimidineColour.setEnabled(false);
+        nucleotideColour.setEnabled(false);
+      }
       editGroupName.setLabel(MessageManager.formatMessage(
               "label.name_param", new Object[] { sg.getName() }));
       showText.setState(sg.getDisplayText());
index 5cabda3..e51131b 100644 (file)
@@ -75,6 +75,7 @@ import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.BorderLayout;
 import java.awt.Canvas;
@@ -284,6 +285,16 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
     if (viewport.getAlignment().isNucleotide())
     {
+      conservationMenuItem.setEnabled(false);
+      clustalColour.setEnabled(false);
+      BLOSUM62Colour.setEnabled(false);
+      zappoColour.setEnabled(false);
+      taylorColour.setEnabled(false);
+      hydrophobicityColour.setEnabled(false);
+      helixColour.setEnabled(false);
+      strandColour.setEnabled(false);
+      turnColour.setEnabled(false);
+      buriedColour.setEnabled(false);
       viewport.updateStrucConsensus(alignPanel);
       if (viewport.getAlignment().hasRNAStructure())
       {
@@ -410,6 +421,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   @Override
   public void keyPressed(KeyEvent evt)
   {
+    ViewportRanges ranges = viewport.getRanges();
+
     if (viewport.cursorMode
             && ((evt.getKeyCode() >= KeyEvent.VK_0 && evt.getKeyCode() <= KeyEvent.VK_9) || (evt
                     .getKeyCode() >= KeyEvent.VK_NUMPAD0 && evt
@@ -561,8 +574,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
               new String[] { (viewport.cursorMode ? "on" : "off") }));
       if (viewport.cursorMode)
       {
-        alignPanel.seqPanel.seqCanvas.cursorX = viewport.startRes;
-        alignPanel.seqPanel.seqCanvas.cursorY = viewport.startSeq;
+        alignPanel.seqPanel.seqCanvas.cursorX = ranges.getStartRes();
+        alignPanel.seqPanel.seqCanvas.cursorY = ranges.getStartSeq();
       }
       break;
 
@@ -588,8 +601,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       }
       else
       {
-        alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
-                - viewport.endSeq + viewport.startSeq);
+        alignPanel.setScrollValues(ranges.getStartRes(),
+                2 * ranges.getStartSeq() - ranges.getEndSeq());
       }
       break;
 
@@ -600,8 +613,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       }
       else
       {
-        alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
-                + viewport.endSeq - viewport.startSeq);
+        alignPanel
+                .setScrollValues(ranges.getStartRes(), ranges.getEndSeq());
       }
       break;
 
@@ -2053,7 +2066,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
             seqs, 0, viewport.getAlignment().getWidth(),
             viewport.getAlignment()));
 
-    viewport.setEndSeq(viewport.getAlignment().getHeight());
+    viewport.getRanges().setEndSeq(viewport.getAlignment().getHeight());
     viewport.getAlignment().getWidth();
     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
             .getSequences());
@@ -2289,6 +2302,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   void trimAlignment(boolean trimLeft)
   {
+    AlignmentI al = viewport.getAlignment();
+    ViewportRanges ranges = viewport.getRanges();
     ColumnSelection colSel = viewport.getColumnSelection();
     int column;
 
@@ -2311,20 +2326,20 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       }
       else
       {
-        seqs = viewport.getAlignment().getSequencesArray();
+        seqs = al.getSequencesArray();
       }
 
       TrimRegionCommand trimRegion;
       if (trimLeft)
       {
         trimRegion = new TrimRegionCommand("Remove Left", true, seqs,
-                column, viewport.getAlignment());
-        viewport.setStartRes(0);
+                column, al);
+        ranges.setStartRes(0);
       }
       else
       {
         trimRegion = new TrimRegionCommand("Remove Right", false, seqs,
-                column, viewport.getAlignment());
+                column, al);
       }
 
       statusBar.setText(MessageManager.formatMessage(
@@ -2333,23 +2348,25 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
                       .toString() }));
       addHistoryItem(trimRegion);
 
-      for (SequenceGroup sg : viewport.getAlignment().getGroups())
+      for (SequenceGroup sg : al.getGroups())
       {
         if ((trimLeft && !sg.adjustForRemoveLeft(column))
                 || (!trimLeft && !sg.adjustForRemoveRight(column)))
         {
-          viewport.getAlignment().deleteGroup(sg);
+          al.deleteGroup(sg);
         }
       }
 
-      viewport.firePropertyChange("alignment", null, viewport
-              .getAlignment().getSequences());
+      viewport.firePropertyChange("alignment", null, al.getSequences());
     }
   }
 
   public void removeGappedColumnMenuItem_actionPerformed()
   {
-    int start = 0, end = viewport.getAlignment().getWidth() - 1;
+    AlignmentI al = viewport.getAlignment();
+    ViewportRanges ranges = viewport.getRanges();
+    int start = 0;
+    int end = ranges.getAbsoluteAlignmentWidth() - 1;
 
     SequenceI[] seqs;
     if (viewport.getSelectionGroup() != null)
@@ -2377,22 +2394,24 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
     // This is to maintain viewport position on first residue
     // of first sequence
-    SequenceI seq = viewport.getAlignment().getSequenceAt(0);
-    int startRes = seq.findPosition(viewport.startRes);
+    SequenceI seq = al.getSequenceAt(0);
+    int startRes = seq.findPosition(ranges.getStartRes());
     // ShiftList shifts;
     // viewport.getAlignment().removeGaps(shifts=new ShiftList());
     // edit.alColumnChanges=shifts.getInverse();
     // if (viewport.hasHiddenColumns)
     // viewport.getColumnSelection().compensateForEdits(shifts);
-    viewport.setStartRes(seq.findIndex(startRes) - 1);
-    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
-            .getSequences());
+    ranges.setStartRes(seq.findIndex(startRes) - 1);
+    viewport.firePropertyChange("alignment", null, al.getSequences());
 
   }
 
   public void removeAllGapsMenuItem_actionPerformed()
   {
-    int start = 0, end = viewport.getAlignment().getWidth() - 1;
+    AlignmentI al = viewport.getAlignment();
+    ViewportRanges ranges = viewport.getRanges();
+    int start = 0;
+    int end = ranges.getAbsoluteAlignmentWidth() - 1;
 
     SequenceI[] seqs;
     if (viewport.getSelectionGroup() != null)
@@ -2409,16 +2428,15 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
     // This is to maintain viewport position on first residue
     // of first sequence
-    SequenceI seq = viewport.getAlignment().getSequenceAt(0);
-    int startRes = seq.findPosition(viewport.startRes);
+    SequenceI seq = al.getSequenceAt(0);
+    int startRes = seq.findPosition(ranges.getStartRes());
 
     addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
-            viewport.getAlignment()));
+            al));
 
-    viewport.setStartRes(seq.findIndex(startRes) - 1);
+    ranges.setStartRes(seq.findIndex(startRes) - 1);
 
-    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
-            .getSequences());
+    viewport.firePropertyChange("alignment", null, al.getSequences());
 
   }
 
index fc087c6..065c503 100644 (file)
@@ -35,16 +35,16 @@ import jalview.datamodel.SequenceI;
 import jalview.renderer.ResidueShader;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.schemes.UserColourScheme;
-import jalview.structure.CommandListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.Font;
 
 public class AlignViewport extends AlignmentViewport implements
-        SelectionSource, VamsasSource, CommandListener
+        SelectionSource
 {
   boolean cursorMode = false;
 
@@ -75,12 +75,10 @@ public class AlignViewport extends AlignmentViewport implements
     calculator = new jalview.workers.AlignCalcManager();
     this.applet = applet;
     alignment = al;
+    ranges = new ViewportRanges(this.alignment);
     // we always pad gaps
     this.setPadGaps(true);
-    this.startRes = 0;
-    this.endRes = al.getWidth() - 1;
-    this.startSeq = 0;
-    this.endSeq = al.getHeight() - 1;
+
     if (applet != null)
     {
       // get the width and height scaling factors if they were specified
@@ -299,7 +297,7 @@ public class AlignViewport extends AlignmentViewport implements
 
   public void resetSeqLimits(int height)
   {
-    setEndSeq(height / getCharHeight());
+    ranges.setEndSeq(height / getCharHeight());
   }
 
   public void setCurrentTree(NJTree tree)
index e97c347..3ae0394 100644 (file)
@@ -28,6 +28,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceI;
 import jalview.structure.StructureSelectionManager;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -65,6 +66,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
 
   AnnotationLabels alabels;
 
+  ViewportRanges vpRanges;
+
   // this value is set false when selection area being dragged
   boolean fastPaint = true;
 
@@ -73,6 +76,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
   {
     alignFrame = null;
     av = null;
+    vpRanges = null;
     seqPanel = null;
     seqPanelHolder = null;
     sequenceHolderPanel = null;
@@ -96,6 +100,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
 
     alignFrame = af;
     this.av = av;
+    vpRanges = av.getRanges();
     seqPanel = new SeqPanel(av, this);
     idPanel = new IdPanel(av, this);
     scalePanel = new ScalePanel(av, this);
@@ -126,7 +131,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       @Override
       public void componentResized(ComponentEvent evt)
       {
-        setScrollValues(av.getStartRes(), av.getStartSeq());
+        setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq());
         if (getSize().height > 0
                 && annotationPanelHolder.getSize().height > 0)
         {
@@ -383,7 +388,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
        */
       if (centre)
       {
-        int offset = (av.getEndRes() - av.getStartRes() + 1) / 2 - 1;
+        int offset = (vpRanges.getEndRes() - vpRanges.getStartRes() + 1) / 2 - 1;
         start = Math.max(start - offset, 0);
         end = Math.min(end + offset, seq.getEnd() - 1);
       }
@@ -468,33 +473,34 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       // setScrollValues(start, seqIndex);
       // }
       // logic copied from jalview.gui.AlignmentPanel:
-      if ((startv = av.getStartRes()) >= start)
+      if ((startv = vpRanges.getStartRes()) >= start)
       {
         /*
          * Scroll left to make start of search results visible
          */
         setScrollValues(start - 1, seqIndex);
       }
-      else if ((endv = av.getEndRes()) <= end)
+      else if ((endv = vpRanges.getEndRes()) <= end)
       {
         /*
          * Scroll right to make end of search results visible
          */
         setScrollValues(startv + 1 + end - endv, seqIndex);
       }
-      else if ((starts = av.getStartSeq()) > seqIndex)
+      else if ((starts = vpRanges.getStartSeq()) > seqIndex)
       {
         /*
          * Scroll up to make start of search results visible
          */
-        setScrollValues(av.getStartRes(), seqIndex);
+        setScrollValues(vpRanges.getStartRes(), seqIndex);
       }
-      else if ((ends = av.getEndSeq()) <= seqIndex)
+      else if ((ends = vpRanges.getEndSeq()) <= seqIndex)
       {
         /*
          * Scroll down to make end of search results visible
          */
-        setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1);
+        setScrollValues(vpRanges.getStartRes(), starts + seqIndex - ends
+                + 1);
       }
       /*
        * Else results are already visible - no need to scroll
@@ -516,10 +522,11 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
   {
     int cwidth = seqPanel.seqCanvas
             .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width);
-    if (res <= av.getStartRes() || res >= (av.getStartRes() + cwidth))
+    if (res <= vpRanges.getStartRes()
+            || res >= (vpRanges.getStartRes() + cwidth))
     {
       vscroll.setValue(res / cwidth);
-      av.startRes = vscroll.getValue() * cwidth;
+      vpRanges.setStartRes(vscroll.getValue() * cwidth);
     }
   }
 
@@ -632,8 +639,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
 
   public void setWrapAlignment(boolean wrap)
   {
-    av.startSeq = 0;
-    av.startRes = 0;
+    vpRanges.setStartSeq(0);
+    vpRanges.setStartRes(0);
     scalePanelHolder.setVisible(!wrap);
 
     hscroll.setVisible(!wrap);
@@ -724,7 +731,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     {
       x = 0;
     }
-    ;
+
 
     hextent = seqPanel.seqCanvas.getSize().width / av.getCharWidth();
     vextent = seqPanel.seqCanvas.getSize().height / av.getCharHeight();
@@ -762,17 +769,10 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       x = 0;
     }
 
-    av.setStartSeq(y);
-
-    int endSeq = y + vextent;
-    if (endSeq > av.getAlignment().getHeight())
-    {
-      endSeq = av.getAlignment().getHeight();
-    }
-
-    av.setEndSeq(endSeq);
-    av.setStartRes(x);
-    av.setEndRes((x + (seqPanel.seqCanvas.getSize().width / av
+    vpRanges.setStartSeq(y);
+    vpRanges.setEndSeq(y + vextent);
+    vpRanges.setStartRes(x);
+    vpRanges.setEndRes((x + (seqPanel.seqCanvas.getSize().width / av
             .getCharWidth())) - 1);
 
     hscroll.setValues(x, hextent, 0, width);
@@ -789,8 +789,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
   @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
-    int oldX = av.getStartRes();
-    int oldY = av.getStartSeq();
+    int oldX = vpRanges.getStartRes();
+    int oldY = vpRanges.getStartSeq();
 
     if (evt == null || evt.getSource() == apvscroll)
     {
@@ -804,8 +804,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     if (evt == null || evt.getSource() == hscroll)
     {
       int x = hscroll.getValue();
-      av.setStartRes(x);
-      av.setEndRes(x + seqPanel.seqCanvas.getSize().width
+      vpRanges.setStartRes(x);
+      vpRanges.setEndRes(x + seqPanel.seqCanvas.getSize().width
               / av.getCharWidth() - 1);
     }
 
@@ -816,14 +816,14 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       {
         int rowSize = seqPanel.seqCanvas
                 .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width);
-        av.setStartRes(vscroll.getValue() * rowSize);
-        av.setEndRes((vscroll.getValue() + 1) * rowSize);
+        vpRanges.setStartRes(vscroll.getValue() * rowSize);
+        vpRanges.setEndRes((vscroll.getValue() + 1) * rowSize);
       }
       else
       {
-        av.setStartSeq(offy);
-        av.setEndSeq(offy + seqPanel.seqCanvas.getSize().height
-                / av.getCharHeight());
+        vpRanges.setStartSeq(offy);
+        vpRanges.setEndSeq(offy + seqPanel.seqCanvas.getSize().height
+                / av.getCharHeight() - 1);
       }
     }
 
@@ -832,8 +832,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       overviewPanel.setBoxPosition();
     }
 
-    int scrollX = av.startRes - oldX;
-    int scrollY = av.startSeq - oldY;
+    int scrollX = vpRanges.getStartRes() - oldX;
+    int scrollY = vpRanges.getStartSeq() - oldY;
 
     if (av.getWrapAlignment() || !fastPaint || av.MAC)
     {
@@ -843,13 +843,13 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     {
       // Make sure we're not trying to draw a panel
       // larger than the visible window
-      if (scrollX > av.endRes - av.startRes)
+      if (scrollX > vpRanges.getEndRes() - vpRanges.getStartRes())
       {
-        scrollX = av.endRes - av.startRes;
+        scrollX = vpRanges.getEndRes() - vpRanges.getStartRes();
       }
-      else if (scrollX < av.startRes - av.endRes)
+      else if (scrollX < vpRanges.getStartRes() - vpRanges.getEndRes())
       {
-        scrollX = av.startRes - av.endRes;
+        scrollX = vpRanges.getStartRes() - vpRanges.getEndRes();
       }
 
       idPanel.idCanvas.fastPaint(scrollY);
@@ -858,7 +858,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       scalePanel.repaint();
       if (av.isShowAnnotation())
       {
-        annotationPanel.fastPaint(av.getStartRes() - oldX);
+        annotationPanel.fastPaint(vpRanges.getStartRes() - oldX);
       }
     }
     sendViewPosition();
@@ -955,8 +955,9 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
   private void sendViewPosition()
   {
     StructureSelectionManager.getStructureSelectionManager(av.applet)
-            .sendViewPosition(this, av.startRes, av.endRes, av.startSeq,
-                    av.endSeq);
+            .sendViewPosition(this, vpRanges.getStartRes(),
+                    vpRanges.getEndRes(), vpRanges.getStartSeq(),
+                    vpRanges.getEndSeq());
   }
 
   /**
@@ -1024,7 +1025,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     }
     else
     {
-      setScrollValues(av.getStartRes(), av.getStartSeq());
+      setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq());
     }
 
     seqPanel.seqCanvas.repaint();
index 487b75c..f516bc9 100644 (file)
@@ -46,7 +46,8 @@ import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
-import java.util.Hashtable;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Vector;
 
 public class AnnotationColourChooser extends Panel implements
@@ -60,9 +61,15 @@ public class AnnotationColourChooser extends Panel implements
 
   ColourSchemeI oldcs;
 
-  Hashtable oldgroupColours;
+  Map<SequenceGroup, ColourSchemeI> oldgroupColours;
 
-  jalview.datamodel.AlignmentAnnotation currentAnnotation;
+  /*
+   * map from annotation to its menu item display label
+   * - so we know which item to pre-select on restore
+   */
+  private Map<AlignmentAnnotation, String> annotationLabels;
+
+  AlignmentAnnotation currentAnnotation;
 
   boolean adjusting = false;
 
@@ -78,17 +85,10 @@ public class AnnotationColourChooser extends Panel implements
     oldcs = av.getGlobalColourScheme();
     if (av.getAlignment().getGroups() != null)
     {
-      oldgroupColours = new Hashtable();
+      oldgroupColours = new HashMap<SequenceGroup, ColourSchemeI>();
       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
       {
-        if (sg.getColourScheme() != null)
-        {
-          oldgroupColours.put(sg, sg.getColourScheme());
-        }
-        else
-        {
-          oldgroupColours.put(sg, "null");
-        }
+        oldgroupColours.put(sg, sg.getColourScheme());
       }
     }
     this.av = av;
@@ -119,24 +119,7 @@ public class AnnotationColourChooser extends Panel implements
       // seqAssociated.setState(acg.isSeqAssociated());
     }
 
-    Vector<String> list = new Vector<String>();
-    int index = 1;
-    for (int i = 0; i < anns.length; i++)
-    {
-      String label = anns[i].label;
-      if (anns[i].sequenceRef != null)
-      {
-        label = label + "_" + anns[i].sequenceRef.getName();
-      }
-      if (!list.contains(label))
-      {
-        list.addElement(label);
-      }
-      else
-      {
-        list.addElement(label + "_" + (index++));
-      }
-    }
+    Vector<String> list = getAnnotationItems();
 
     for (int i = 0; i < list.size(); i++)
     {
@@ -153,7 +136,8 @@ public class AnnotationColourChooser extends Panel implements
     if (oldcs instanceof AnnotationColourGradient)
     {
       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
-      annotations.select(acg.getAnnotation());
+      String label = annotationLabels.get(acg.getAnnotation());
+      annotations.select(label);
       switch (acg.getAboveThreshold())
       {
       case AnnotationColourGradient.NO_THRESHOLD:
@@ -170,7 +154,7 @@ public class AnnotationColourChooser extends Panel implements
                 MessageManager
                         .getString("error.implementation_error_dont_know_threshold_annotationcolourgradient"));
       }
-      thresholdIsMin.setState(acg.thresholdIsMinMax);
+      thresholdIsMin.setState(acg.isThresholdIsMinMax());
       thresholdValue.setText("" + acg.getAnnotationThreshold());
     }
 
@@ -186,6 +170,51 @@ public class AnnotationColourChooser extends Panel implements
     validate();
   }
 
+  /**
+   * Builds and returns a list of menu items (display text) for choice of
+   * annotation. Also builds a map between annotations and their display labels.
+   * 
+   * @return
+   */
+  protected Vector<String> getAnnotationItems()
+  {
+    // TODO remove duplication with gui.AnnotationRowFilter
+    // TODO add 'per sequence only' option / parameter
+
+    annotationLabels = new HashMap<AlignmentAnnotation, String>();
+    Vector<String> list = new Vector<String>();
+    AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
+    if (anns == null)
+    {
+      return list;
+    }
+    int index = 1;
+    for (int i = 0; i < anns.length; i++)
+    {
+      String label = anns[i].label;
+      if (anns[i].sequenceRef != null)
+      {
+        /*
+         * be helpful and include sequence id in label for
+         * sequence-associated annotation (JAL-2236)
+         */
+        label = label + "_" + anns[i].sequenceRef.getName();
+      }
+      if (!list.contains(label))
+      {
+        list.addElement(label);
+        annotationLabels.put(anns[i], label);
+      }
+      else
+      {
+        label = label + "_" + (index++);
+        list.addElement(label);
+        annotationLabels.put(anns[i], label);
+      }
+    }
+    return list;
+  }
+
   private void setDefaultMinMax()
   {
     minColour.setBackground(av.applet.getDefaultColourParameter(
@@ -501,7 +530,7 @@ public class AnnotationColourChooser extends Panel implements
       acg.setPredefinedColours(true);
     }
 
-    acg.thresholdIsMinMax = thresholdIsMin.getState();
+    acg.setThresholdIsMinMax(thresholdIsMin.getState());
 
     av.setGlobalColourScheme(acg);
 
@@ -510,7 +539,6 @@ public class AnnotationColourChooser extends Panel implements
     {
       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
       {
-
         if (sg.getColourScheme() == null)
         {
           continue;
@@ -527,7 +555,6 @@ public class AnnotationColourChooser extends Panel implements
                   currentAnnotation, minColour.getBackground(), maxColour
                           .getBackground(), aboveThreshold));
         }
-
       }
     }
 
@@ -543,20 +570,10 @@ public class AnnotationColourChooser extends Panel implements
     {
       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
       {
-        Object cs = oldgroupColours.get(sg);
-        if (cs instanceof ColourSchemeI)
-        {
-          sg.setColourScheme((ColourSchemeI) cs);
-        }
-        else
-        {
-          // probably the "null" string we set it to if it was null originally.
-          sg.setColourScheme(null);
-        }
+        sg.setColourScheme(oldgroupColours.get(sg));
       }
     }
     ap.paintAlignment(true);
-
   }
 
   @Override
index b28ccc7..ad74b25 100755 (executable)
@@ -339,7 +339,8 @@ public class AnnotationLabels extends Panel implements ActionListener,
                 av.calcPanelHeight());
         f.height += dif;
         ap.seqPanelHolder.setPreferredSize(f);
-        ap.setScrollValues(av.getStartRes(), av.getStartSeq());
+        ap.setScrollValues(av.getRanges().getStartRes(), av.getRanges()
+                .getStartSeq());
         ap.validate();
         // ap.paintAlignment(true);
         ap.addNotify();
index 6012c1a..0ec7adf 100755 (executable)
@@ -462,7 +462,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
       }
     }
 
-    int column = evt.getX() / av.getCharWidth() + av.getStartRes();
+    int column = evt.getX() / av.getCharWidth()
+            + av.getRanges().getStartRes();
 
     if (av.hasHiddenColumns())
     {
@@ -618,7 +619,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
 
     gg.setColor(Color.white);
     gg.fillRect(0, 0, getSize().width, getSize().height);
-    drawComponent(gg, av.startRes, av.endRes + 1);
+    drawComponent(gg, av.getRanges().getStartRes(), av.getRanges()
+            .getEndRes() + 1);
 
     g.drawImage(image, 0, 0, this);
   }
@@ -635,7 +637,7 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
 
     gg.copyArea(0, 0, imgWidth, getSize().height,
             -horizontal * av.getCharWidth(), 0);
-    int sr = av.startRes, er = av.endRes + 1, transX = 0;
+    int sr = av.getRanges().getStartRes(), er = av.getRanges().getEndRes() + 1, transX = 0;
 
     if (horizontal > 0) // scrollbar pulled right, image to the left
     {
index f938cad..9b8a235 100644 (file)
@@ -54,21 +54,7 @@ class AppletJmolBinding extends JalviewJmolBinding
   public jalview.api.FeatureRenderer getFeatureRenderer(
           AlignmentViewPanel alignment)
   {
-    AlignmentPanel ap = (AlignmentPanel) alignment;
-    if (appletJmolBinding.ap.av.isShowSequenceFeatures())
-    {
-      if (appletJmolBinding.fr == null)
-      {
-        appletJmolBinding.fr = new jalview.appletgui.FeatureRenderer(
-                appletJmolBinding.ap.av);
-      }
-
-      appletJmolBinding.fr
-              .transferSettings(appletJmolBinding.ap.seqPanel.seqCanvas
-                      .getFeatureRenderer());
-    }
-
-    return appletJmolBinding.fr;
+    return appletJmolBinding.ap.getFeatureRenderer();
   }
 
   @Override
index 189fe88..b369318 100644 (file)
@@ -82,10 +82,10 @@ public class ExtJmol extends JalviewJmolBinding
   @Override
   public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
   {
-    AlignmentPanel ap = (AlignmentPanel) alignment;
-    if (ap.av.isShowSequenceFeatures())
+    AlignmentPanel alignPanel = (AlignmentPanel) alignment;
+    if (alignPanel.av.isShowSequenceFeatures())
     {
-      return ap.getFeatureRenderer();
+      return alignPanel.getFeatureRenderer();
     }
     else
     {
index 67ca8e9..b88a1dc 100644 (file)
@@ -377,9 +377,6 @@ public class FeatureRenderer extends
 
     if (dialog.accept)
     {
-      // This ensures that the last sequence
-      // is refreshed and new features are rendered
-      lastSeq = null;
       lastFeatureAdded = name.getText().trim();
       lastFeatureGroupAdded = source.getText().trim();
       lastDescriptionAdded = description.getText().replace('\n', ' ');
index 2c454a4..1b9fbf9 100755 (executable)
@@ -696,8 +696,7 @@ public class FeatureSettings extends Panel implements ItemListener,
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
     fr.setTransparency((100 - transparency.getValue()) / 100f);
-    ap.seqPanel.seqCanvas.repaint();
-
+    ap.paintAlignment(true);
   }
 
   class MyCheckbox extends Checkbox
index d72e91f..abcbd70 100755 (executable)
@@ -21,6 +21,7 @@
 package jalview.appletgui;
 
 import jalview.datamodel.SequenceI;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.Color;
 import java.awt.Font;
@@ -103,28 +104,32 @@ public class IdCanvas extends Panel
       return;
     }
 
+    ViewportRanges ranges = av.getRanges();
+
     gg.copyArea(0, 0, getSize().width, imgHeight, 0,
             -vertical * av.getCharHeight());
 
-    int ss = av.startSeq, es = av.endSeq, transY = 0;
+    int ss = ranges.getStartSeq(), es = ranges.getEndSeq(), transY = 0;
     if (vertical > 0) // scroll down
     {
       ss = es - vertical;
-      if (ss < av.startSeq) // ie scrolling too fast, more than a page at a time
+      if (ss < ranges.getStartSeq()) // ie scrolling too fast, more than a page
+                                     // at a
+                                 // time
       {
-        ss = av.startSeq;
+        ss = ranges.getStartSeq();
       }
       else
       {
-        transY = imgHeight - vertical * av.getCharHeight();
+        transY = imgHeight - ((vertical + 1) * av.getCharHeight());
       }
     }
     else if (vertical < 0)
     {
       es = ss - vertical;
-      if (es > av.endSeq)
+      if (es > ranges.getEndSeq())
       {
-        es = av.endSeq;
+        es = ranges.getEndSeq();
       }
     }
 
@@ -180,7 +185,7 @@ public class IdCanvas extends Panel
     gg.setFont(italic);
 
     gg.fillRect(0, 0, getSize().width, getSize().height);
-    drawIds(av.startSeq, av.endSeq);
+    drawIds(av.getRanges().getStartSeq(), av.getRanges().getEndSeq());
     g.drawImage(image, 0, 0, this);
   }
 
@@ -233,9 +238,10 @@ public class IdCanvas extends Panel
 
       int cHeight = alheight * avcharHeight + hgap + annotationHeight;
 
-      int rowSize = av.getEndRes() - av.getStartRes();
+      int rowSize = av.getRanges().getEndRes()
+              - av.getRanges().getStartRes();
       // Draw the rest of the panels
-      for (int ypos = hgap, row = av.startRes; (ypos <= getSize().height)
+      for (int ypos = hgap, row = av.getRanges().getStartRes(); (ypos <= getSize().height)
               && (row < maxwidth); ypos += cHeight, row += rowSize)
       {
         for (int i = starty; i < alheight; i++)
@@ -263,7 +269,7 @@ public class IdCanvas extends Panel
     {
       // Now draw the id strings
       SequenceI seq;
-      for (int i = starty; i < endy; i++)
+      for (int i = starty; i <= endy; i++)
       {
 
         seq = av.getAlignment().getSequenceAt(i);
index 182f20e..e47c50a 100755 (executable)
  */
 package jalview.appletgui;
 
-import static jalview.util.UrlConstants.EMBLEBI_STRING;
-import static jalview.util.UrlConstants.SRS_STRING;
-
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
-import jalview.util.UrlLink;
+import jalview.urls.api.UrlProviderFactoryI;
+import jalview.urls.api.UrlProviderI;
+import jalview.urls.applet.AppletUrlProviderFactory;
 import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.BorderLayout;
@@ -36,8 +35,8 @@ import java.awt.event.InputEvent;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Vector;
 
 public class IdPanel extends Panel implements MouseListener,
         MouseMotionListener
@@ -55,7 +54,7 @@ public class IdPanel extends Panel implements MouseListener,
 
   boolean mouseDragging = false;
 
-  java.util.Vector links = new java.util.Vector();
+  UrlProviderI urlProvider = null;
 
   public IdPanel(AlignViewport av, AlignmentPanel parent)
   {
@@ -69,6 +68,9 @@ public class IdPanel extends Panel implements MouseListener,
 
     String label, url;
     // TODO: add in group link parameter
+
+    // make a list of label,url pairs
+    HashMap<String, String> urlList = new HashMap<String, String>();
     if (av.applet != null)
     {
       for (int i = 1; i < 10; i++)
@@ -76,26 +78,22 @@ public class IdPanel extends Panel implements MouseListener,
         label = av.applet.getParameter("linkLabel_" + i);
         url = av.applet.getParameter("linkURL_" + i);
 
-        if (label != null && url != null)
+        // only add non-null parameters
+        if (label != null)
         {
-          links.addElement(label + "|" + url);
+          urlList.put(label, url);
         }
-
       }
-    }
-    {
-      // upgrade old SRS link
-      int srsPos = links.indexOf(SRS_STRING);
-      if (srsPos > -1)
+
+      if (!urlList.isEmpty())
       {
-        links.setElementAt(EMBLEBI_STRING, srsPos);
+        // set default as first entry in list
+        String defaultUrl = av.applet.getParameter("linkLabel_1");
+        UrlProviderFactoryI factory = new AppletUrlProviderFactory(
+                defaultUrl, urlList);
+        urlProvider = factory.createUrlProvider();
       }
     }
-    if (links.size() < 1)
-    {
-      links = new java.util.Vector();
-      links.addElement(EMBLEBI_STRING);
-    }
   }
 
   Tooltip tooltip;
@@ -217,7 +215,7 @@ public class IdPanel extends Panel implements MouseListener,
       return;
     }
 
-    // DEFAULT LINK IS FIRST IN THE LINK LIST
+    // get the sequence details
     int seq = alignPanel.seqPanel.findSeq(e);
     SequenceI sq = av.getAlignment().getSequenceAt(seq);
     if (sq == null)
@@ -226,53 +224,11 @@ public class IdPanel extends Panel implements MouseListener,
     }
     String id = sq.getName();
 
-    String target = null;
-    String url = null;
-    int i = 0;
-    while (url == null && i < links.size())
-    {
-      // DEFAULT LINK IS FIRST IN THE LINK LIST
-      // BUT IF ITS A REGEX AND DOES NOT MATCH THE NEXT ONE WILL BE TRIED
-      url = links.elementAt(i++).toString();
-      jalview.util.UrlLink urlLink = null;
-      try
-      {
-        urlLink = new UrlLink(url);
-        target = urlLink.getTarget();
-      } catch (Exception foo)
-      {
-        System.err.println("Exception for URLLink '" + url + "'");
-        foo.printStackTrace();
-        url = null;
-        continue;
-      }
-
-      if (urlLink.usesDBAccession())
-      {
-        // this URL requires an accession id, not the name of a sequence
-        url = null;
-        continue;
-      }
-
-      if (!urlLink.isValid())
-      {
-        System.err.println(urlLink.getInvalidMessage());
-        url = null;
-        continue;
-      }
-
-      String urls[] = urlLink.makeUrls(id, true);
-      if (urls == null || urls[0] == null || urls[0].length() < 1)
-      {
-        url = null;
-        continue;
-      }
-      // just take first URL made from regex
-      url = urls[1];
-    }
+    // get the default url with the sequence details filled in
+    String url = urlProvider.getPrimaryUrl(id);
+    String target = urlProvider.getPrimaryTarget(id);
     try
     {
-
       alignPanel.alignFrame.showURL(url, target);
     } catch (Exception ex)
     {
@@ -297,13 +253,13 @@ public class IdPanel extends Panel implements MouseListener,
       return;
     }
 
-    if (mouseDragging && e.getY() < 0 && av.getStartSeq() > 0)
+    if (mouseDragging && e.getY() < 0 && av.getRanges().getStartSeq() > 0)
     {
       scrollThread = new ScrollThread(true);
     }
 
     if (mouseDragging && e.getY() >= getSize().height
-            && av.getAlignment().getHeight() > av.getEndSeq())
+            && av.getAlignment().getHeight() > av.getRanges().getEndSeq())
     {
       scrollThread = new ScrollThread(false);
     }
@@ -331,11 +287,8 @@ public class IdPanel extends Panel implements MouseListener,
 
       // build a new links menu based on the current links + any non-positional
       // features
-      Vector nlinks = new Vector();
-      for (int l = 0, lSize = links.size(); l < lSize; l++)
-      {
-        nlinks.addElement(links.elementAt(l));
-      }
+      List<String> nlinks = urlProvider.getLinksForMenu();
+
       SequenceFeature sf[] = sq == null ? null : sq.getSequenceFeatures();
       for (int sl = 0; sf != null && sl < sf.length; sl++)
       {
@@ -345,7 +298,7 @@ public class IdPanel extends Panel implements MouseListener,
           {
             for (int l = 0, lSize = sf[sl].links.size(); l < lSize; l++)
             {
-              nlinks.addElement(sf[sl].links.elementAt(l));
+              nlinks.add(sf[sl].links.elementAt(l));
             }
           }
         }
@@ -445,9 +398,10 @@ public class IdPanel extends Panel implements MouseListener,
     int index = av.getAlignment().findIndex(list.get(0));
 
     // do we need to scroll the panel?
-    if (av.getStartSeq() > index || av.getEndSeq() < index)
+    if (av.getRanges().getStartSeq() > index
+            || av.getRanges().getEndSeq() < index)
     {
-      alignPanel.setScrollValues(av.getStartRes(), index);
+      alignPanel.setScrollValues(av.getRanges().getStartRes(), index);
     }
   }
 
@@ -478,10 +432,10 @@ public class IdPanel extends Panel implements MouseListener,
         if (alignPanel.scrollUp(up))
         {
           // scroll was ok, so add new sequence to selection
-          int seq = av.getStartSeq();
+          int seq = av.getRanges().getStartSeq();
           if (!up)
           {
-            seq = av.getEndSeq();
+            seq = av.getRanges().getEndSeq();
           }
 
           if (seq < lastid)
index 9b2be4c..3ef2936 100755 (executable)
@@ -20,7 +20,9 @@
  */
 package jalview.appletgui;
 
-import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
+import jalview.viewmodel.OverviewDimensions;
 
 import java.awt.Color;
 import java.awt.Dimension;
@@ -37,38 +39,34 @@ import java.awt.event.MouseMotionListener;
 public class OverviewPanel extends Panel implements Runnable,
         MouseMotionListener, MouseListener
 {
-  Image miniMe;
+  private OverviewDimensions od;
 
-  Image offscreen;
+  private Image miniMe;
 
-  AlignViewport av;
+  private Image offscreen;
 
-  AlignmentPanel ap;
+  private AlignViewport av;
 
-  float scalew = 1f;
+  private AlignmentPanel ap;
 
-  float scaleh = 1f;
+  private boolean resizing = false;
 
-  public int width, sequencesHeight;
-
-  int graphHeight = 20;
-
-  int boxX = -1, boxY = -1, boxWidth = -1, boxHeight = -1;
-
-  boolean resizing = false;
+  // This is set true if the user resizes whilst
+  // the overview is being calculated
+  private boolean resizeAgain = false;
 
   // Can set different properties in this seqCanvas than
   // main visible SeqCanvas
-  SequenceRenderer sr;
+  private SequenceRenderer sr;
 
-  FeatureRenderer fr;
+  private FeatureRenderer fr;
 
-  Frame nullFrame;
+  private Frame nullFrame;
 
-  public OverviewPanel(AlignmentPanel ap)
+  public OverviewPanel(AlignmentPanel alPanel)
   {
-    this.av = ap.av;
-    this.ap = ap;
+    this.av = alPanel.av;
+    this.ap = alPanel;
     setLayout(null);
     nullFrame = new Frame();
     nullFrame.addNotify();
@@ -79,45 +77,18 @@ public class OverviewPanel extends Panel implements Runnable,
     sr.forOverview = true;
     fr = new FeatureRenderer(av);
 
-    // scale the initial size of overviewpanel to shape of alignment
-    float initialScale = (float) av.getAlignment().getWidth()
-            / (float) av.getAlignment().getHeight();
-
-    if (av.getSequenceConsensusHash() == null)
-    {
-      graphHeight = 0;
-    }
-
-    if (av.getAlignment().getWidth() > av.getAlignment().getHeight())
-    {
-      // wider
-      width = 400;
-      sequencesHeight = (int) (400f / initialScale);
-      if (sequencesHeight < 40)
-      {
-        sequencesHeight = 40;
-      }
-    }
-    else
-    {
-      // taller
-      width = (int) (400f * initialScale);
-      sequencesHeight = 300;
-      if (width < 120)
-      {
-        width = 120;
-      }
-    }
+    od = new OverviewDimensions(av.getRanges(),
+            (av.isShowAnnotation() && av.getSequenceConsensusHash() != null));
 
-    setSize(new Dimension(width, sequencesHeight + graphHeight));
+    setSize(new Dimension(od.getWidth(), od.getHeight()));
     addComponentListener(new ComponentAdapter()
     {
 
       @Override
       public void componentResized(ComponentEvent evt)
       {
-        if (getSize().width != width
-                || getSize().height != sequencesHeight + graphHeight)
+        if ((getWidth() != od.getWidth())
+                || (getHeight() != (od.getHeight())))
         {
           updateOverviewImage();
         }
@@ -155,79 +126,32 @@ public class OverviewPanel extends Panel implements Runnable,
   @Override
   public void mousePressed(MouseEvent evt)
   {
-    boxX = evt.getX();
-    boxY = evt.getY();
-    checkValid();
+    mouseAction(evt);
   }
 
   @Override
   public void mouseReleased(MouseEvent evt)
   {
-    boxX = evt.getX();
-    boxY = evt.getY();
-    checkValid();
+    mouseAction(evt);
   }
 
   @Override
   public void mouseDragged(MouseEvent evt)
   {
-    boxX = evt.getX();
-    boxY = evt.getY();
-    checkValid();
+    mouseAction(evt);
   }
 
-  void checkValid()
+  private void mouseAction(MouseEvent evt)
   {
-    if (boxY < 0)
-    {
-      boxY = 0;
-    }
-
-    if (boxY > (sequencesHeight - boxHeight))
-    {
-      boxY = sequencesHeight - boxHeight + 1;
-    }
-
-    if (boxX < 0)
-    {
-      boxX = 0;
-    }
-
-    if (boxX > (width - boxWidth))
-    {
-      if (av.hasHiddenColumns())
-      {
-        // Try smallest possible box
-        boxWidth = (int) ((av.endRes - av.startRes + 1) * av.getCharWidth() * scalew);
-      }
-      boxX = width - boxWidth;
-    }
-
-    int col = (int) (boxX / scalew / av.getCharWidth());
-    int row = (int) (boxY / scaleh / av.getCharHeight());
-
-    if (av.hasHiddenColumns())
-    {
-      if (!av.getColumnSelection().isVisible(col))
-      {
-        return;
-      }
-
-      col = av.getColumnSelection().findColumnPosition(col);
-    }
-
-    if (av.hasHiddenRows())
-    {
-      row = av.getAlignment().getHiddenSequences()
-              .findIndexWithoutHiddenSeqs(row);
-    }
-
-    ap.setScrollValues(col, row);
+    od.updateViewportFromMouse(evt.getX(), evt.getY(), av.getAlignment()
+            .getHiddenSequences(), av.getColumnSelection(), av
+            .getRanges());
+    ap.setScrollValues(od.getScrollCol(), od.getScrollRow());
     ap.paintAlignment(false);
   }
 
   /**
-   * DOCUMENT ME!
+   * Updates the overview image when the related alignment panel is updated
    */
   public void updateOverviewImage()
   {
@@ -246,27 +170,20 @@ public class OverviewPanel extends Panel implements Runnable,
 
     if ((getSize().width > 0) && (getSize().height > 0))
     {
-      width = getSize().width;
-      sequencesHeight = getSize().height - graphHeight;
+      od.setWidth(getSize().width);
+      od.setHeight(getSize().height);
     }
-    setSize(new Dimension(width, sequencesHeight + graphHeight));
+    setSize(new Dimension(od.getWidth(), od.getHeight()));
 
     Thread thread = new Thread(this);
     thread.start();
     repaint();
   }
 
-  // This is set true if the user resizes whilst
-  // the overview is being calculated
-  boolean resizeAgain = false;
-
   @Override
   public void run()
   {
     miniMe = null;
-    int alwidth = av.getAlignment().getWidth();
-    int alheight = av.getAlignment().getHeight()
-            + av.getAlignment().getHiddenSequences().getSize();
 
     if (av.isShowSequenceFeatures())
     {
@@ -275,135 +192,37 @@ public class OverviewPanel extends Panel implements Runnable,
 
     if (getSize().width > 0 && getSize().height > 0)
     {
-      width = getSize().width;
-      sequencesHeight = getSize().height - graphHeight;
+      od.setWidth(getSize().width);
+      od.setHeight(getSize().height);
     }
 
-    setSize(new Dimension(width, sequencesHeight + graphHeight));
-
-    int fullsizeWidth = alwidth * av.getCharWidth();
-    int fullsizeHeight = alheight * av.getCharHeight();
+    setSize(new Dimension(od.getWidth(), od.getHeight()));
 
-    scalew = (float) width / (float) fullsizeWidth;
-    scaleh = (float) sequencesHeight / (float) fullsizeHeight;
-
-    miniMe = nullFrame.createImage(width, sequencesHeight + graphHeight);
-    offscreen = nullFrame.createImage(width, sequencesHeight + graphHeight);
+    miniMe = nullFrame.createImage(od.getWidth(), od.getHeight());
+    offscreen = nullFrame.createImage(od.getWidth(), od.getHeight());
 
     Graphics mg = miniMe.getGraphics();
-    float sampleCol = (float) alwidth / (float) width;
-    float sampleRow = (float) alheight / (float) sequencesHeight;
-
-    int lastcol = 0, lastrow = 0;
-    int xstart = 0, ystart = 0;
-    Color color = Color.yellow;
-    int row, col, sameRow = 0, sameCol = 0;
-    jalview.datamodel.SequenceI seq;
-    final boolean hasHiddenRows = av.hasHiddenRows(), hasHiddenCols = av
-            .hasHiddenColumns();
-    boolean hiddenRow = false;
-    AlignmentI alignment = av.getAlignment();
-    for (row = 0; row <= sequencesHeight; row++)
-    {
-      if (resizeAgain)
-      {
-        break;
-      }
-      if ((int) (row * sampleRow) == lastrow)
-      {
-        sameRow++;
-        continue;
-      }
-
-      hiddenRow = false;
-      if (hasHiddenRows)
-      {
-        seq = alignment.getHiddenSequences().getHiddenSequence(lastrow);
-        if (seq == null)
-        {
-          int index = alignment.getHiddenSequences()
-                  .findIndexWithoutHiddenSeqs(lastrow);
-
-          seq = alignment.getSequenceAt(index);
-        }
-        else
-        {
-          hiddenRow = true;
-        }
-      }
-      else
-      {
-        seq = alignment.getSequenceAt(lastrow);
-      }
-
-      for (col = 0; col < width; col++)
-      {
-        if ((int) (col * sampleCol) == lastcol
-                && (int) (row * sampleRow) == lastrow)
-        {
-          sameCol++;
-          continue;
-        }
-
-        lastcol = (int) (col * sampleCol);
-
-        if (seq.getLength() > lastcol)
-        {
-          color = sr.getResidueBoxColour(seq, lastcol);
-
-          if (av.isShowSequenceFeatures())
-          {
-            color = fr.findFeatureColour(color, seq, lastcol);
-          }
-        }
-        else
-        {
-          color = Color.white; // White
-        }
-
-        if (hiddenRow
-                || (hasHiddenCols && !av.getColumnSelection().isVisible(
-                        lastcol)))
-        {
-          color = color.darker().darker();
-        }
 
-        mg.setColor(color);
-        if (sameCol == 1 && sameRow == 1)
-        {
-          mg.drawLine(xstart, ystart, xstart, ystart);
-        }
-        else
-        {
-          mg.fillRect(xstart, ystart, sameCol, sameRow);
-        }
+    int alwidth = av.getAlignment().getWidth();
+    int alheight = av.getAlignment().getAbsoluteHeight();
+    float sampleCol = alwidth / (float) od.getWidth();
+    float sampleRow = alheight / (float) od.getSequencesHeight();
 
-        xstart = col;
-        sameCol = 1;
-      }
-      lastrow = (int) (row * sampleRow);
-      ystart = row;
-      sameRow = 1;
-    }
+    buildImage(sampleRow, sampleCol, mg);
 
-    if (av.getAlignmentConservationAnnotation() != null)
+    // check for conservation annotation to make sure overview works for DNA too
+    if (av.isShowAnnotation()
+            && (av.getAlignmentConservationAnnotation() != null))
     {
-      for (col = 0; col < width; col++)
+      for (int col = 0; col < od.getWidth() && !resizeAgain; col++)
       {
-        if (resizeAgain)
-        {
-          break;
-        }
-        lastcol = (int) (col * sampleCol);
-        {
-          mg.translate(col, sequencesHeight);
-          ap.annotationPanel.renderer.drawGraph(mg,
-                  av.getAlignmentConservationAnnotation(),
-                  av.getAlignmentConservationAnnotation().annotations,
-                  (int) (sampleCol) + 1, graphHeight,
-                  (int) (col * sampleCol), (int) (col * sampleCol) + 1);
-          mg.translate(-col, -sequencesHeight);
-        }
+        mg.translate(col, od.getSequencesHeight());
+        ap.annotationPanel.renderer.drawGraph(mg,
+                av.getAlignmentConservationAnnotation(),
+                av.getAlignmentConservationAnnotation().annotations,
+                (int) (sampleCol) + 1, od.getGraphHeight(),
+                (int) (col * sampleCol), (int) (col * sampleCol) + 1);
+        mg.translate(-col, -od.getSequencesHeight());
       }
     }
     System.gc();
@@ -419,52 +238,104 @@ public class OverviewPanel extends Panel implements Runnable,
     }
   }
 
-  public void setBoxPosition()
+  /*
+   * Build the overview panel image
+   */
+  private void buildImage(float sampleRow, float sampleCol, Graphics mg)
   {
-    int fullsizeWidth = av.getAlignment().getWidth() * av.getCharWidth();
-    int fullsizeHeight = (av.getAlignment().getHeight() + av.getAlignment()
-            .getHiddenSequences().getSize())
-            * av.getCharHeight();
-
-    int startRes = av.getStartRes();
-    int endRes = av.getEndRes();
+    int lastcol = 0;
+    int lastrow = 0;
+    int xstart = 0;
+    int ystart = 0;
+    Color color = Color.yellow;
+    int sameRow = 0;
+    int sameCol = 0;
 
-    if (av.hasHiddenColumns())
-    {
-      startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);
-      endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);
-    }
+    SequenceI seq = null;
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
 
-    int startSeq = av.startSeq;
-    int endSeq = av.endSeq;
+    final boolean hasHiddenCols = av.hasHiddenColumns();
+    boolean hiddenRow = false;
 
-    if (av.hasHiddenRows())
+    for (int row = 0; row <= od.getSequencesHeight() && !resizeAgain; row++)
     {
-      startSeq = av.getAlignment().getHiddenSequences()
-              .adjustForHiddenSeqs(startSeq);
-
-      endSeq = av.getAlignment().getHiddenSequences()
-              .adjustForHiddenSeqs(endSeq);
+      if ((int) (row * sampleRow) == lastrow)
+      {
+        sameRow++;
+      }
+      else
+      {
+        // get the sequence which would be at alignment index 'lastrow' if no
+        // columns were hidden, and determine whether it is hidden or not
+        hiddenRow = av.getAlignment().isHidden(lastrow);
+        seq = av.getAlignment().getSequenceAtAbsoluteIndex(lastrow);
 
+        for (int col = 0; col < od.getWidth(); col++)
+        {
+          if ((int) (col * sampleCol) == lastcol
+                  && (int) (row * sampleRow) == lastrow)
+          {
+            sameCol++;
+          }
+          else
+          {
+            lastcol = (int) (col * sampleCol);
+
+            color = getColumnColourFromSequence(seq, hiddenRow,
+                    hasHiddenCols, lastcol, finder);
+
+            mg.setColor(color);
+            if (sameCol == 1 && sameRow == 1)
+            {
+              mg.drawLine(xstart, ystart, xstart, ystart);
+            }
+            else
+            {
+              mg.fillRect(xstart, ystart, sameCol, sameRow);
+            }
+
+            xstart = col;
+            sameCol = 1;
+          }
+        }
+        lastrow = (int) (row * sampleRow);
+        ystart = row;
+        sameRow = 1;
+      }
     }
+  }
 
-    scalew = (float) width / (float) fullsizeWidth;
-    scaleh = (float) sequencesHeight / (float) fullsizeHeight;
-
-    boxX = (int) (startRes * av.getCharWidth() * scalew);
-    boxY = (int) (startSeq * av.getCharHeight() * scaleh);
-
-    if (av.hasHiddenColumns())
+  /*
+   * Find the colour of a sequence at a specified column position
+   */
+  private Color getColumnColourFromSequence(
+          jalview.datamodel.SequenceI seq, boolean hiddenRow,
+          boolean hasHiddenCols, int lastcol, FeatureColourFinder finder)
+  {
+    Color color = Color.white;
+    if (seq.getLength() > lastcol)
     {
-      boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
+      color = sr.getResidueColour(seq, lastcol, finder);
     }
-    else
+
+    if (hiddenRow
+            || (hasHiddenCols && !av.getColumnSelection()
+                    .isVisible(lastcol)))
     {
-      boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
+      color = color.darker().darker();
     }
+    return color;
+  }
 
-    boxHeight = (int) ((endSeq - startSeq) * av.getCharHeight() * scaleh);
-
+  /**
+   * Update the overview panel box when the associated alignment panel is
+   * changed
+   * 
+   */
+  public void setBoxPosition()
+  {
+    od.setBoxPosition(av.getAlignment()
+            .getHiddenSequences(), av.getColumnSelection(), av.getRanges());
     repaint();
   }
 
@@ -482,8 +353,7 @@ public class OverviewPanel extends Panel implements Runnable,
     {
       og.drawImage(miniMe, 0, 0, this);
       og.setColor(Color.red);
-      og.drawRect(boxX, boxY, boxWidth, boxHeight);
-      og.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
+      od.drawBox(og);
       g.drawImage(offscreen, 0, 0, this);
     }
   }
index ed07b63..15d82a5 100755 (executable)
@@ -76,7 +76,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
   @Override
   public void mousePressed(MouseEvent evt)
   {
-    int x = (evt.getX() / av.getCharWidth()) + av.getStartRes();
+    int x = (evt.getX() / av.getCharWidth()) + av.getRanges().getStartRes();
     final int res;
 
     if (av.hasHiddenColumns())
@@ -229,7 +229,8 @@ public class ScalePanel extends Panel implements MouseMotionListener,
   {
     mouseDragging = false;
 
-    int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
+    int res = (evt.getX() / av.getCharWidth())
+            + av.getRanges().getStartRes();
 
     if (res > av.getAlignment().getWidth())
     {
@@ -276,7 +277,8 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     mouseDragging = true;
     ColumnSelection cs = av.getColumnSelection();
 
-    int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
+    int res = (evt.getX() / av.getCharWidth())
+            + av.getRanges().getStartRes();
     res = Math.max(0, res);
     res = cs.adjustForHiddenColumns(res);
     res = Math.min(res, av.getAlignment().getWidth() - 1);
@@ -324,7 +326,8 @@ public class ScalePanel extends Panel implements MouseMotionListener,
       return;
     }
 
-    int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
+    int res = (evt.getX() / av.getCharWidth())
+            + av.getRanges().getStartRes();
 
     res = av.getColumnSelection().adjustForHiddenColumns(res);
 
@@ -350,7 +353,8 @@ public class ScalePanel extends Panel implements MouseMotionListener,
   @Override
   public void paint(Graphics g)
   {
-    drawScale(g, av.getStartRes(), av.getEndRes(), getSize().width,
+    drawScale(g, av.getRanges().getStartRes(), av.getRanges().getEndRes(),
+            getSize().width,
             getSize().height);
   }
 
index 5d6bb07..ed8a46d 100755 (executable)
@@ -27,6 +27,7 @@ import jalview.datamodel.SequenceI;
 import jalview.renderer.ScaleRenderer;
 import jalview.renderer.ScaleRenderer.ScaleMark;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.Color;
 import java.awt.FontMetrics;
@@ -211,17 +212,19 @@ public class SeqCanvas extends Panel
       return;
     }
 
+    ViewportRanges ranges = av.getRanges();
+
     updateViewport();
 
     // Its possible on certain browsers that the call to fastpaint
     // is faster than it can paint, so this check here catches
     // this possibility
-    if (lastsr + horizontal != av.startRes)
+    if (lastsr + horizontal != ranges.getStartRes())
     {
-      horizontal = av.startRes - lastsr;
+      horizontal = ranges.getStartRes() - lastsr;
     }
 
-    lastsr = av.startRes;
+    lastsr = ranges.getStartRes();
 
     fastPaint = true;
     gg.copyArea(horizontal * avcharWidth, vertical * avcharHeight, imgWidth
@@ -229,7 +232,9 @@ public class SeqCanvas extends Panel
             imgHeight - vertical * avcharHeight, -horizontal * avcharWidth,
             -vertical * avcharHeight);
 
-    int sr = av.startRes, er = av.endRes, ss = av.startSeq, es = av.endSeq, transX = 0, transY = 0;
+    int sr = ranges.getStartRes(), er = ranges.getEndRes(), ss = ranges
+            .getStartSeq(), es = ranges
+            .getEndSeq(), transX = 0, transY = 0;
 
     if (horizontal > 0) // scrollbar pulled right, image to the left
     {
@@ -244,21 +249,23 @@ public class SeqCanvas extends Panel
     else if (vertical > 0) // scroll down
     {
       ss = es - vertical;
-      if (ss < av.startSeq) // ie scrolling too fast, more than a page at a time
+      if (ss < ranges.getStartSeq()) // ie scrolling too fast, more than a page
+                                     // at a
+                                 // time
       {
-        ss = av.startSeq;
+        ss = ranges.getStartSeq();
       }
       else
       {
-        transY = imgHeight - vertical * avcharHeight;
+        transY = imgHeight - ((vertical + 1) * avcharHeight);
       }
     }
     else if (vertical < 0)
     {
       es = ss - vertical;
-      if (es > av.endSeq)
+      if (es > ranges.getEndSeq())
       {
-        es = av.endSeq;
+        es = ranges.getEndSeq();
       }
     }
 
@@ -329,13 +336,16 @@ public class SeqCanvas extends Panel
     gg.setColor(Color.white);
     gg.fillRect(0, 0, imgWidth, imgHeight);
 
+    ViewportRanges ranges = av.getRanges();
+
     if (av.getWrapAlignment())
     {
-      drawWrappedPanel(gg, imgWidth, imgHeight, av.startRes);
+      drawWrappedPanel(gg, imgWidth, imgHeight, ranges.getStartRes());
     }
     else
     {
-      drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
+      drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
+              ranges.getStartSeq(), ranges.getEndSeq(), 0);
     }
 
     g.drawImage(img, 0, 0, this);
@@ -421,7 +431,7 @@ public class SeqCanvas extends Panel
 
     av.setWrappedWidth(cWidth);
 
-    av.endRes = av.startRes + cWidth;
+    av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth);
 
     int endx;
     int ypos = hgap;
@@ -610,7 +620,7 @@ public class SeqCanvas extends Panel
 
     // / First draw the sequences
     // ///////////////////////////
-    for (int i = startSeq; i < endSeq; i++)
+    for (int i = startSeq; i <= endSeq; i++)
     {
       nextSeq = av.getAlignment().getSequenceAt(i);
 
@@ -625,7 +635,7 @@ public class SeqCanvas extends Panel
       if (av.isShowSequenceFeatures())
       {
         fr.drawSequence(g, nextSeq, startRes, endRes, offset
-                + ((i - startSeq) * avcharHeight));
+                + ((i - startSeq) * avcharHeight), false);
       }
 
       // / Highlight search Results once all sequences have been drawn
@@ -694,7 +704,7 @@ public class SeqCanvas extends Panel
         int bottom = -1;
         int alHeight = av.getAlignment().getHeight() - 1;
 
-        for (i = startSeq; i < endSeq; i++)
+        for (i = startSeq; i <= endSeq; i++)
         {
           sx = (group.getStartRes() - startRes) * avcharWidth;
           sy = offset + ((i - startSeq) * avcharHeight);
index 8cfd2dc..0e12703 100644 (file)
@@ -41,6 +41,7 @@ import jalview.structure.VamsasSource;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.BorderLayout;
 import java.awt.Font;
@@ -226,16 +227,17 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     }
     else
     {
-      while (seqCanvas.cursorY < av.startSeq)
+      ViewportRanges ranges = av.getRanges();
+      while (seqCanvas.cursorY < ranges.getStartSeq())
       {
         ap.scrollUp(true);
       }
-      while (seqCanvas.cursorY + 1 > av.endSeq)
+      while (seqCanvas.cursorY + 1 > ranges.getEndSeq())
       {
         ap.scrollUp(false);
       }
       while (seqCanvas.cursorX < av.getColumnSelection()
-              .adjustForHiddenColumns(av.startRes))
+              .adjustForHiddenColumns(ranges.getStartRes()))
       {
 
         if (!ap.scrollRight(false))
@@ -244,7 +246,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
       }
       while (seqCanvas.cursorX > av.getColumnSelection()
-              .adjustForHiddenColumns(av.endRes))
+              .adjustForHiddenColumns(ranges.getEndRes()))
       {
         if (!ap.scrollRight(true))
         {
@@ -624,14 +626,14 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       }
 
       wrappedBlock = y / cHeight;
-      wrappedBlock += av.getStartRes() / cwidth;
+      wrappedBlock += av.getRanges().getStartRes() / cwidth;
 
       res = wrappedBlock * cwidth + x / av.getCharWidth();
 
     }
     else
     {
-      res = (x / av.getCharWidth()) + av.getStartRes();
+      res = (x / av.getCharWidth()) + av.getRanges().getStartRes();
     }
 
     if (av.hasHiddenColumns())
@@ -681,7 +683,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     }
     else
     {
-      seq = Math.min((y / av.getCharHeight()) + av.getStartSeq(), av
+      seq = Math.min((y / av.getCharHeight())
+              + av.getRanges().getStartSeq(),
+              av
               .getAlignment().getHeight() - 1);
       if (seq < 0)
       {
@@ -1423,34 +1427,15 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
     stretchGroup = av.getSelectionGroup();
 
-    if (stretchGroup == null)
+    if (stretchGroup == null || !stretchGroup.contains(sequence, res))
     {
       stretchGroup = av.getAlignment().findGroup(sequence, res);
-      av.setSelectionGroup(stretchGroup);
-    }
-
-    if (stretchGroup == null
-            || !stretchGroup.getSequences(null).contains(sequence)
-            || stretchGroup.getStartRes() > res
-            || stretchGroup.getEndRes() < res)
-    {
-      stretchGroup = null;
-
-      SequenceGroup[] allGroups = av.getAlignment().findAllGroups(sequence);
-
-      if (allGroups != null)
+      if (stretchGroup != null)
       {
-        for (int i = 0; i < allGroups.length; i++)
-        {
-          if (allGroups[i].getStartRes() <= res
-                  && allGroups[i].getEndRes() >= res)
-          {
-            stretchGroup = allGroups[i];
-            break;
-          }
-        }
+        // only update the current selection if the popup menu has a group to
+        // focus on
+        av.setSelectionGroup(stretchGroup);
       }
-      av.setSelectionGroup(stretchGroup);
     }
 
     // DETECT RIGHT MOUSE BUTTON IN AWT
@@ -1662,8 +1647,10 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       oldSeq = -1;
     }
 
-    if (res > av.endRes || res < av.startRes || y < av.startSeq
-            || y > av.endSeq)
+    if (res > av.getRanges().getEndRes()
+            || res < av.getRanges().getStartRes()
+            || y < av.getRanges().getStartSeq()
+            || y > av.getRanges().getEndSeq())
     {
       mouseExited(evt);
     }
@@ -1761,13 +1748,15 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         if (evt != null)
         {
 
-          if (mouseDragging && evt.getY() < 0 && av.getStartSeq() > 0)
+          if (mouseDragging && evt.getY() < 0
+                  && av.getRanges().getStartSeq() > 0)
           {
             running = ap.scrollUp(true);
           }
 
           if (mouseDragging && evt.getY() >= getSize().height
-                  && av.getAlignment().getHeight() > av.getEndSeq())
+                  && av.getAlignment().getHeight() > av.getRanges()
+                          .getEndSeq())
           {
             running = ap.scrollUp(false);
           }
@@ -1909,8 +1898,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   public void scrollTo(int row, int column)
   {
 
-    row = row < 0 ? ap.av.startSeq : row;
-    column = column < 0 ? ap.av.startRes : column;
+    row = row < 0 ? ap.av.getRanges().getStartSeq() : row;
+    column = column < 0 ? ap.av.getRanges().getStartRes() : column;
     ap.scrollTo(column, column, row, true, true);
   }
 
@@ -1922,8 +1911,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   public void scrollToRow(int row)
   {
 
-    row = row < 0 ? ap.av.startSeq : row;
-    ap.scrollTo(ap.av.startRes, ap.av.startRes, row, true, true);
+    row = row < 0 ? ap.av.getRanges().getStartSeq() : row;
+    ap.scrollTo(ap.av.getRanges().getStartRes(), ap.av.getRanges()
+            .getStartRes(), row, true, true);
   }
 
   /**
@@ -1934,8 +1924,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   public void scrollToColumn(int column)
   {
 
-    column = column < 0 ? ap.av.startRes : column;
-    ap.scrollTo(column, column, ap.av.startSeq, true, true);
+    column = column < 0 ? ap.av.getRanges().getStartRes() : column;
+    ap.scrollTo(column, column, ap.av.getRanges().getStartSeq(), true, true);
   }
 
   /**
index 86d1f98..78ed4a3 100755 (executable)
  */
 package jalview.appletgui;
 
-import jalview.api.FeatureRenderer;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.ResidueShaderI;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 
 import java.awt.Color;
 import java.awt.Font;
@@ -69,8 +69,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     this.renderGaps = renderGaps;
   }
 
-  @Override
-  public Color getResidueBoxColour(SequenceI seq, int i)
+  protected Color getResidueBoxColour(SequenceI seq, int i)
   {
     allGroups = av.getAlignment().findAllGroups(seq);
 
@@ -96,20 +95,20 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
    * 
    * @param seq
    * @param position
-   * @param fr
+   * @param finder
    * @return
    */
   @Override
   public Color getResidueColour(final SequenceI seq, int position,
-          FeatureRenderer fr)
+          FeatureColourFinder finder)
   {
     // TODO replace 8 or so code duplications with calls to this method
     // (refactored as needed)
     Color col = getResidueBoxColour(seq, position);
 
-    if (fr != null)
+    if (finder != null)
     {
-      col = fr.findFeatureColour(col, seq, position);
+      col = finder.findFeatureColour(col, seq, position);
     }
     return col;
   }
index 9363c23..da3cb92 100755 (executable)
@@ -22,9 +22,11 @@ package jalview.bin;
 
 import jalview.datamodel.PDBEntry;
 import jalview.gui.UserDefinedColours;
+import jalview.schemes.ColourSchemeLoader;
 import jalview.schemes.ColourSchemes;
 import jalview.schemes.UserColourScheme;
 import jalview.structure.StructureImportSettings;
+import jalview.urls.IdOrgSettings;
 import jalview.util.ColorUtils;
 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
 import jalview.ws.dbsources.das.datamodel.DasSourceRegistry;
@@ -127,6 +129,10 @@ import org.apache.log4j.SimpleLayout;
  * <li>SORT_ALIGNMENT (No sort|Id|Pairwise Identity)</li>
  * <li>SEQUENCE_LINKS list of name|URL pairs for opening a url with
  * $SEQUENCE_ID$</li>
+ * <li>STORED_LINKS list of name|url pairs which user has entered but are not
+ * currently used
+ * <li>DEFAULT_LINK name of single url to be used when user double clicks a
+ * sequence id (must be in SEQUENCE_LINKS or STORED_LINKS)
  * <li>GROUP_LINKS list of name|URL[|&lt;separator&gt;] tuples - see
  * jalview.utils.GroupURLLink for more info</li>
  * <li>DAS_REGISTRY_URL the registry to query</li>
@@ -184,6 +190,8 @@ import org.apache.log4j.SimpleLayout;
  * <li>STRUCTURE_DISPLAY choose from JMOL (default) or CHIMERA for 3D structure
  * display</li>
  * <li>CHIMERA_PATH specify full path to Chimera program (if non-standard)</li>
+ * <li>ID_ORG_HOSTURL location of jalview service providing identifiers.org urls
+ * </li>
  * 
  * </ul>
  * Deprecated settings:
@@ -225,6 +233,9 @@ public class Cache
 
   public static final String DAS_ACTIVE_SOURCE = "DAS_ACTIVE_SOURCE";
 
+  /**
+   * Sifts settings
+   */
   public static final String DEFAULT_SIFTS_DOWNLOAD_DIR = System
           .getProperty("user.home")
           + File.separatorChar
@@ -235,6 +246,12 @@ public class Cache
   private final static String DEFAULT_FAIL_SAFE_PID_THRESHOLD = "30";
 
   /**
+   * Identifiers.org download settings
+   */
+  private static final String ID_ORG_FILE = System.getProperty("user.home")
+          + File.separatorChar + ".identifiers.org.ids.json";
+
+  /**
    * Allowed values are PDB or mmCIF
    */
   private final static String PDB_DOWNLOAD_FORMAT = PDBEntry.Type.MMCIF
@@ -445,6 +462,10 @@ public class Cache
             "sifts_cache_threshold_in_days",
             DEFAULT_CACHE_THRESHOLD_IN_DAYS));
 
+    IdOrgSettings.setUrl(getDefault("ID_ORG_HOSTURL",
+            "http://www.jalview.org/services/identifiers"));
+    IdOrgSettings.setDownloadLocation(ID_ORG_FILE);
+
     System.out
             .println("Jalview Version: " + codeVersion + codeInstallation);
 
@@ -1017,7 +1038,7 @@ public class Cache
       String file = st.nextToken();
       try
       {
-        UserColourScheme ucs = ColourSchemes.loadColourScheme(file);
+        UserColourScheme ucs = ColourSchemeLoader.loadColourScheme(file);
         if (ucs != null)
         {
           if (coloursFound.length() > 0)
index a6f2bf4..41488ea 100755 (executable)
@@ -185,14 +185,7 @@ public class Alignment implements AlignmentI
     return AlignmentUtils.getSequencesByName(this);
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param i
-   *          DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
+
   @Override
   public SequenceI getSequenceAt(int i)
   {
@@ -206,6 +199,28 @@ public class Alignment implements AlignmentI
     return null;
   }
 
+  @Override
+  public SequenceI getSequenceAtAbsoluteIndex(int i)
+  {
+    SequenceI seq = null;
+    if (getHiddenSequences().getSize() > 0)
+    {
+      seq = getHiddenSequences().getHiddenSequence(i);
+      if (seq == null)
+      {
+        // didn't find the sequence in the hidden sequences, get it from the
+        // alignment
+        int index = getHiddenSequences().findIndexWithoutHiddenSeqs(i);
+        seq = getSequenceAt(index);
+      }
+    }
+    else
+    {
+      seq = getSequenceAt(i);
+    }
+    return seq;
+  }
+
   /**
    * Adds a sequence to the alignment. Recalculates maxLength and size. Note
    * this currently does not recalculate whether or not the alignment is
@@ -320,30 +335,21 @@ public class Alignment implements AlignmentI
     }
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param s
-   *          DOCUMENT ME!
-   */
   @Override
   public void deleteSequence(SequenceI s)
   {
-    deleteSequence(findIndex(s));
+    synchronized (sequences)
+    {
+      deleteSequence(findIndex(s));
+    }
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param i
-   *          DOCUMENT ME!
-   */
   @Override
   public void deleteSequence(int i)
   {
-    if (i > -1 && i < getHeight())
+    synchronized (sequences)
     {
-      synchronized (sequences)
+      if (i > -1 && i < getHeight())
       {
         sequences.remove(i);
         hiddenSequences.adjustHeightSequenceDeleted(i);
@@ -351,6 +357,18 @@ public class Alignment implements AlignmentI
     }
   }
 
+  @Override
+  public void deleteHiddenSequence(int i)
+  {
+    synchronized (sequences)
+    {
+      if (i > -1 && i < getHeight())
+      {
+        sequences.remove(i);
+      }
+    }
+  }
+
   /*
    * (non-Javadoc)
    * 
@@ -668,22 +686,19 @@ public class Alignment implements AlignmentI
     return -1;
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
+
   @Override
   public int getHeight()
   {
     return sequences.size();
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
+  @Override
+  public int getAbsoluteHeight()
+  {
+    return sequences.size() + getHiddenSequences().getSize();
+  }
+
   @Override
   public int getWidth()
   {
@@ -769,6 +784,12 @@ public class Alignment implements AlignmentI
     return true;
   }
 
+  @Override
+  public boolean isHidden(int alignmentIndex)
+  {
+    return (getHiddenSequences().getHiddenSequence(alignmentIndex) != null);
+  }
+
   /**
    * Delete all annotations, including auto-calculated if the flag is set true.
    * Returns true if at least one annotation was deleted, else false.
index 05688cb..6117baf 100755 (executable)
  */
 package jalview.datamodel;
 
-import jalview.analysis.Rna;
-import jalview.analysis.SecStrConsensus.SimpleBP;
-import jalview.analysis.WUSSParseException;
-
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -32,6 +28,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import jalview.analysis.Rna;
+import jalview.analysis.SecStrConsensus.SimpleBP;
+import jalview.analysis.WUSSParseException;
+
 /**
  * DOCUMENT ME!
  * 
@@ -170,10 +170,14 @@ public class AlignmentAnnotation
    */
   private Map<Integer, Annotation> sequenceMapping;
 
-  /** DOCUMENT ME!! */
+  /**
+   * lower range for quantitative data
+   */
   public float graphMin;
 
-  /** DOCUMENT ME!! */
+  /**
+   * Upper range for quantitative data
+   */
   public float graphMax;
 
   /**
@@ -863,6 +867,10 @@ public class AlignmentAnnotation
   @Override
   public String toString()
   {
+    if (annotations == null)
+    {
+      return "";
+    }
     StringBuilder buffer = new StringBuilder(256);
 
     for (int i = 0; i < annotations.length; i++)
index 752235b..2abb1f8 100755 (executable)
@@ -31,13 +31,22 @@ import java.util.Set;
 public interface AlignmentI extends AnnotatedCollectionI
 {
   /**
-   * Calculates the number of sequences in an alignment
+   * Calculates the number of sequences in an alignment, excluding hidden
+   * sequences
    * 
    * @return Number of sequences in alignment
    */
   int getHeight();
 
   /**
+   * Calculates the number of sequences in an alignment, including hidden
+   * sequences
+   * 
+   * @return Number of sequences in alignment
+   */
+  int getAbsoluteHeight();
+
+  /**
    * 
    * Calculates the maximum width of the alignment, including gaps.
    * 
@@ -65,6 +74,15 @@ public interface AlignmentI extends AnnotatedCollectionI
   boolean isAligned(boolean includeHidden);
 
   /**
+   * Answers if the sequence at alignmentIndex is hidden
+   * 
+   * @param alignmentIndex
+   *          the index to check
+   * @return true if the sequence is hidden
+   */
+  boolean isHidden(int alignmentIndex);
+
+  /**
    * Gets sequences as a Synchronized collection
    * 
    * @return All sequences in alignment.
@@ -90,6 +108,17 @@ public interface AlignmentI extends AnnotatedCollectionI
   SequenceI getSequenceAt(int i);
 
   /**
+   * Find a specific sequence in this alignment.
+   * 
+   * @param i
+   *          Index of required sequence in full alignment, i.e. if all columns
+   *          were visible
+   * 
+   * @return SequenceI at given index.
+   */
+  SequenceI getSequenceAtAbsoluteIndex(int i);
+
+  /**
    * Returns a map of lists of sequences keyed by sequence name.
    * 
    * @return
@@ -118,7 +147,9 @@ public interface AlignmentI extends AnnotatedCollectionI
   SequenceI replaceSequenceAt(int i, SequenceI seq);
 
   /**
-   * Deletes a sequence from the alignment
+   * Deletes a sequence from the alignment. Updates hidden sequences to account
+   * for the removed sequence. Do NOT use this method to delete sequences which
+   * are just hidden.
    * 
    * @param s
    *          Sequence to be deleted.
@@ -126,7 +157,9 @@ public interface AlignmentI extends AnnotatedCollectionI
   void deleteSequence(SequenceI s);
 
   /**
-   * Deletes a sequence from the alignment.
+   * Deletes a sequence from the alignment. Updates hidden sequences to account
+   * for the removed sequence. Do NOT use this method to delete sequences which
+   * are just hidden.
    * 
    * @param i
    *          Index of sequence to be deleted.
@@ -134,6 +167,14 @@ public interface AlignmentI extends AnnotatedCollectionI
   void deleteSequence(int i);
 
   /**
+   * Deletes a sequence in the alignment which has been hidden.
+   * 
+   * @param i
+   *          Index of sequence to be deleted
+   */
+  void deleteHiddenSequence(int i);
+
+  /**
    * Finds sequence in alignment using sequence name as query.
    * 
    * @param name
@@ -157,10 +198,11 @@ public interface AlignmentI extends AnnotatedCollectionI
 
   /**
    * Returns the first group (in the order in which groups were added) that
-   * includes the given sequence and aligned position (base 0), or null if none
-   * found
+   * includes the given sequence instance and aligned position (base 0), or null
+   * if none found
    * 
    * @param seq
+   *          - must be contained in the alignment (not a dataset sequence)
    * @param position
    * 
    * @return
@@ -544,4 +586,5 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @return
    */
   public int[] getVisibleStartAndEndIndex(List<int[]> hiddenCols);
+
 }
index ed57dce..62ee974 100755 (executable)
@@ -69,13 +69,9 @@ public class BinarySequence extends Sequence
   {
     int nores = (isNa) ? ResidueProperties.maxNucleotideIndex
             : ResidueProperties.maxProteinIndex;
-    // Set all matrix to 0
+
     dbinary = new double[getSequence().length * nores];
 
-    for (int i = 0; i < dbinary.length; i++)
-    {
-      dbinary[i] = 0.0;
-    }
     return nores;
   }
 
@@ -134,14 +130,8 @@ public class BinarySequence extends Sequence
 
   private void matrixEncode(final int[] aaIndex, final int[][] matrix)
   {
-    // Set all matrix to 0
-    // dbinary = new double[getSequence().length * 21];
-
     int nores = initMatrixGetNoRes();
 
-    // for (int i = 0; i < dbinary.length; i++) {
-    // dbinary[i] = 0.0;
-    // }
     for (int i = 0, iSize = getSequence().length; i < iSize; i++)
     {
       int aanum = nores - 1;
index 98a7fe2..97bc5a3 100644 (file)
@@ -690,8 +690,8 @@ public class ColumnSelection
    * left-most visible column will always be returned.
    * 
    * @param hiddenColumn
-   *          int
-   * @return int
+   *          the column index in the full alignment including hidden columns
+   * @return the position of the column in the visible alignment
    */
   public int findColumnPosition(int hiddenColumn)
   {
@@ -708,15 +708,89 @@ public class ColumnSelection
           result -= region[1] + 1 - region[0];
         }
       } while ((hiddenColumn > region[1]) && (index < hiddenColumns.size()));
-      if (hiddenColumn > region[0] && hiddenColumn < region[1])
-      {
-        return region[0] + hiddenColumn - result;
+
+      if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
+       {
+         // Here the hidden column is within a region, so
+         // we want to return the position of region[0]-1, adjusted for any
+         // earlier hidden columns.
+         // Calculate the difference between the actual hidden col position
+         // and region[0]-1, and then subtract from result to convert result from
+         // the adjusted hiddenColumn value to the adjusted region[0]-1 value
+
+        // However, if the region begins at 0 we cannot return region[0]-1
+        // just return 0
+        if (region[0] == 0)
+        {
+          return 0;
+        }
+        else
+        {
+          return result - (hiddenColumn - region[0] + 1);
+        }
       }
     }
     return result; // return the shifted position after removing hidden columns.
   }
 
   /**
+   * Find the visible column which is a given visible number of columns to the
+   * left of another visible column. i.e. for a startColumn x, the column which
+   * is distance 1 away will be column x-1.
+   * 
+   * @param visibleDistance
+   *          the number of visible columns to offset by
+   * @param startColumn
+   *          the column to start from
+   * @return the position of the column in the visible alignment
+   */
+  public int subtractVisibleColumns(int visibleDistance, int startColumn)
+  {
+    int distance = visibleDistance;
+
+    // in case startColumn is in a hidden region, move it to the left
+    int start = adjustForHiddenColumns(findColumnPosition(startColumn));
+
+    // get index of hidden region to left of start
+    int index = getHiddenIndexLeft(start);
+    if (index == -1)
+    {
+      // no hidden regions to left of startColumn
+      return start - distance;
+    }
+
+    // walk backwards through the alignment subtracting the counts of visible
+    // columns from distance
+    int[] region;
+    int gap = 0;
+    int nextstart = start;
+
+    while ((index > -1) && (distance - gap > 0))
+    {
+      // subtract the gap to right of region from distance
+      distance -= gap;
+      start = nextstart;
+
+      // calculate the next gap
+      region = hiddenColumns.get(index);
+      gap = start - region[1];
+
+      // set start to just to left of current region
+      nextstart = region[0] - 1;
+      index--;
+    }
+
+    if (distance - gap > 0)
+    {
+      // fell out of loop because there are no more hidden regions
+      distance -= gap;
+      return nextstart - distance;
+    }
+    return start - distance;
+
+  }
+
+  /**
    * Use this method to determine where the next hiddenRegion starts
    * 
    * @param hiddenRegion
@@ -805,6 +879,35 @@ public class ColumnSelection
 
   }
 
+  /**
+   * This method returns the index of the hidden region to the left of a column
+   * position. If the column is in a hidden region it returns the index of the
+   * region to the left. If there is no hidden region to the left it returns -1.
+   * 
+   * @param pos
+   *          int
+   */
+  private int getHiddenIndexLeft(int pos)
+  {
+    if (hiddenColumns != null)
+    {
+      int index = hiddenColumns.size() - 1;
+      do
+      {
+        int[] region = hiddenColumns.elementAt(index);
+        if (pos > region[1])
+        {
+          return index;
+        }
+
+        index--;
+      } while (index > -1);
+    }
+
+    return -1;
+
+  }
+
   public void hideSelectedColumns()
   {
     synchronized (selection)
index 9e2cf72..6950c28 100755 (executable)
@@ -154,8 +154,8 @@ public class HiddenSequences
       hiddenSequences = new SequenceI[alignment.getHeight()];
     }
 
-    int alignmentIndex = alignment.findIndex(sequence);
-    alignmentIndex = adjustForHiddenSeqs(alignmentIndex);
+    int absAlignmentIndex = alignment.findIndex(sequence);
+    int alignmentIndex = adjustForHiddenSeqs(absAlignmentIndex);
 
     if (hiddenSequences[alignmentIndex] != null)
     {
@@ -164,7 +164,7 @@ public class HiddenSequences
 
     hiddenSequences[alignmentIndex] = sequence;
 
-    alignment.deleteSequence(sequence);
+    alignment.deleteHiddenSequence(absAlignmentIndex);
   }
 
   public List<SequenceI> showAll(
@@ -246,6 +246,12 @@ public class HiddenSequences
     return hiddenSequences == null ? null : hiddenSequences[alignmentIndex];
   }
 
+  /**
+   * Convert absolute alignment index to visible alignment index
+   * 
+   * @param alignmentIndex
+   * @return
+   */
   public int findIndexWithoutHiddenSeqs(int alignmentIndex)
   {
     if (hiddenSequences == null)
@@ -254,8 +260,14 @@ public class HiddenSequences
     }
     int index = 0;
     int hiddenSeqs = 0;
+    int diff = 0;
     if (hiddenSequences.length <= alignmentIndex)
     {
+      // if the alignmentIndex runs past the end of hidden sequences
+      // and therefore actually past the end of the alignment
+      // store the difference to add back on at the end, so that behaviour
+      // is consistent with hidden columns behaviour (used by overview panel)
+      diff = alignmentIndex - hiddenSequences.length + 1;
       alignmentIndex = hiddenSequences.length - 1;
     }
 
@@ -268,9 +280,50 @@ public class HiddenSequences
       index++;
     }
 
-    return (alignmentIndex - hiddenSeqs);
+    return (alignmentIndex - hiddenSeqs + diff);
+  }
+
+  /**
+   * Find the visible row which is a given visible number of rows above another
+   * visible row. i.e. for a startRow x, the row which is distance 1 away will
+   * be row x-1.
+   * 
+   * @param visibleDistance
+   *          the number of visible rows to offset by
+   * @param startRow
+   *          the row to start from
+   * @return the position of the row in the visible alignment
+   */
+  public int subtractVisibleRows(int visibleDistance, int startRow)
+  {
+    // walk upwards through the alignment
+    // count all the non-null sequences until we have visibleDistance counted
+    // then return the next visible sequence
+    if (hiddenSequences == null)
+    {
+      return startRow - visibleDistance;
+    }
+
+    int index = startRow;
+    int count = 0;
+    while ((index > -1) && (count < visibleDistance))
+    {
+      if (hiddenSequences[index] == null)
+      {
+        // count visible sequences
+        count++;
+      }
+      index--;
+    }
+    return index;
   }
 
+  /**
+   * Convert alignment index from visible alignment to absolute alignment
+   * 
+   * @param alignmentIndex
+   * @return
+   */
   public int adjustForHiddenSeqs(int alignmentIndex)
   {
     if (hiddenSequences == null)
index c8b94ce..b0faf21 100755 (executable)
@@ -314,12 +314,11 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   @Override
-  public synchronized void addSequenceFeature(SequenceFeature sf)
+  public synchronized boolean addSequenceFeature(SequenceFeature sf)
   {
     if (sequenceFeatures == null && datasetSequence != null)
     {
-      datasetSequence.addSequenceFeature(sf);
-      return;
+      return datasetSequence.addSequenceFeature(sf);
     }
     if (sequenceFeatures == null)
     {
@@ -330,7 +329,7 @@ public class Sequence extends ASequence implements SequenceI
     {
       if (sequenceFeatures[i].equals(sf))
       {
-        return;
+        return false;
       }
     }
 
@@ -339,6 +338,7 @@ public class Sequence extends ASequence implements SequenceI
     temp[sequenceFeatures.length] = sf;
 
     sequenceFeatures = temp;
+    return true;
   }
 
   @Override
index a1579c5..e37c55e 100755 (executable)
@@ -1347,13 +1347,25 @@ public class SequenceGroup implements AnnotatedCollectionI
   private AnnotatedCollectionI context;
 
   /**
-   * set the alignment or group context for this group
+   * Sets the alignment or group context for this group
    * 
-   * @param context
+   * @param ctx
+   * @throws IllegalArgumentException
+   *           if setting the context would result in a circular reference chain
    */
-  public void setContext(AnnotatedCollectionI context)
+  public void setContext(AnnotatedCollectionI ctx)
   {
-    this.context = context;
+    AnnotatedCollectionI ref = ctx;
+    while (ref != null)
+    {
+      if (ref == this)
+      {
+        throw new IllegalArgumentException(
+                "Circular reference in SequenceGroup.context");
+      }
+      ref = ref.getContext();
+    }
+    this.context = ctx;
   }
 
   /*
@@ -1399,4 +1411,24 @@ public class SequenceGroup implements AnnotatedCollectionI
     }
     return false;
   }
+
+  /**
+   * @param seq
+   * @return true if seq is a member of the group
+   */
+
+  public boolean contains(SequenceI seq1)
+  {
+    return sequences.contains(seq1);
+  }
+
+  /**
+   * @param seq
+   * @param apos
+   * @return true if startRes<=apos and endRes>=apos and seq is in the group
+   */
+  public boolean contains(SequenceI seq, int apos)
+  {
+    return (startRes <= apos && endRes >= apos) && sequences.contains(seq);
+  }
 }
index aec68ab..92f797f 100755 (executable)
@@ -337,7 +337,14 @@ public interface SequenceI extends ASequenceI
    */
   public void addDBRef(DBRefEntry entry);
 
-  public void addSequenceFeature(SequenceFeature sf);
+  /**
+   * Adds the given sequence feature and returns true, or returns false if it is
+   * already present on the sequence
+   * 
+   * @param sf
+   * @return
+   */
+  public boolean addSequenceFeature(SequenceFeature sf);
 
   public void deleteFeature(SequenceFeature sf);
 
index 4033dcc..26bd142 100644 (file)
@@ -19,8 +19,10 @@ package jalview.ext.android;
 /*
  * 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
+ * Change Log:
+ * Sep 2016: Method binarySearch(short[] array, int size, short value) added to support
  * SparseShortArray.
+ * Jan 2017: EMPTY_DOUBLES added
  */
 class ContainerHelpers
 {
@@ -28,6 +30,8 @@ class ContainerHelpers
 
   static final int[] EMPTY_INTS = new int[0];
 
+  static final double[] EMPTY_DOUBLES = new double[0];
+
   static final long[] EMPTY_LONGS = new long[0];
 
   static final Object[] EMPTY_OBJECTS = new Object[0];
diff --git a/src/jalview/ext/android/SparseDoubleArray.java b/src/jalview/ext/android/SparseDoubleArray.java
new file mode 100644 (file)
index 0000000..eaf059c
--- /dev/null
@@ -0,0 +1,443 @@
+package jalview.ext.android;
+
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * SparseDoubleArray map integers to doubles. Unlike a normal array of integers,
+ * there can be gaps in the indices. It is intended to be more memory efficient
+ * than using a HashMap to map Integer to Double, both because it avoids
+ * auto-boxing keys and values and its data structure doesn't rely on an extra
+ * entry object for each mapping.
+ *
+ * <p>
+ * Note that this container keeps its mappings in an array data structure, using
+ * a binary search to find keys. The implementation is not intended to be
+ * appropriate for data structures that may contain large numbers of items. It
+ * is generally slower than a traditional HashMap, since lookups require a
+ * binary search and adds and removes require inserting and deleting entries in
+ * the array. For containers holding up to hundreds of items, the performance
+ * difference is not significant, less than 50%.
+ * </p>
+ *
+ * <p>
+ * It is possible to iterate over the items in this container using
+ * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
+ * <code>keyAt(int)</code> with ascending values of the index will return the
+ * keys in ascending order, or the values corresponding to the keys in ascending
+ * order in the case of <code>valueAt(int)<code>.
+ * </p>
+ */
+
+/*
+ * Change log:
+ * Jan 2017 cloned from SparseIntArray for Jalview to support SparseMatrix
+ * - SparseDoubleArray(double[]) constructor added
+ * - multiply() added for more efficient multiply (or divide) of a value
+ */
+public class SparseDoubleArray implements Cloneable
+{
+  private int[] mKeys;
+
+  private double[] mValues;
+
+  private int mSize;
+
+  /**
+   * Creates a new SparseDoubleArray containing no mappings.
+   */
+  public SparseDoubleArray()
+  {
+    this(10);
+  }
+
+  /**
+   * Creates a new SparseDoubleArray containing no mappings that will not
+   * require any additional memory allocation to store the specified number of
+   * mappings. If you supply an initial capacity of 0, the sparse array will be
+   * initialized with a light-weight representation not requiring any additional
+   * array allocations.
+   */
+  public SparseDoubleArray(int initialCapacity)
+  {
+    if (initialCapacity == 0)
+    {
+      mKeys = ContainerHelpers.EMPTY_INTS;
+      mValues = ContainerHelpers.EMPTY_DOUBLES;
+    }
+    else
+    {
+      initialCapacity = idealDoubleArraySize(initialCapacity);
+      mKeys = new int[initialCapacity];
+      mValues = new double[initialCapacity];
+    }
+    mSize = 0;
+  }
+
+  /**
+   * Constructor given an array of double values; stores the non-zero values
+   * 
+   * @param row
+   */
+  public SparseDoubleArray(double[] row)
+  {
+    this();
+    for (int i = 0; i < row.length; i++)
+    {
+      if (row[i] != 0d)
+      {
+        put(i, row[i]);
+      }
+    }
+  }
+
+  @Override
+  public SparseDoubleArray clone()
+  {
+    SparseDoubleArray clone = null;
+    try
+    {
+      clone = (SparseDoubleArray) super.clone();
+      clone.mKeys = mKeys.clone();
+      clone.mValues = mValues.clone();
+    } catch (CloneNotSupportedException cnse)
+    {
+      /* ignore */
+    }
+    return clone;
+  }
+
+  /**
+   * Gets the value mapped from the specified key, or <code>0</code> if no such
+   * mapping has been made.
+   */
+  public double get(int key)
+  {
+    return get(key, 0d);
+  }
+
+  /**
+   * Gets the int mapped from the specified key, or the specified value if no
+   * such mapping has been made.
+   */
+  public double get(int key, double valueIfKeyNotFound)
+  {
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+    if (i < 0)
+    {
+      return valueIfKeyNotFound;
+    }
+    else
+    {
+      return mValues[i];
+    }
+  }
+
+  /**
+   * Removes the mapping from the specified key, if there was any.
+   */
+  public void delete(int key)
+  {
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+    if (i >= 0)
+    {
+      removeAt(i);
+    }
+  }
+
+  /**
+   * Removes the mapping at the given index.
+   */
+  public void removeAt(int index)
+  {
+    System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+    System.arraycopy(mValues, index + 1, mValues, index, mSize
+            - (index + 1));
+    mSize--;
+  }
+
+  /**
+   * Adds a mapping from the specified key to the specified value, replacing the
+   * previous mapping from the specified key if there was one.
+   */
+  public void put(int key, double value)
+  {
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+    if (i >= 0)
+    {
+      mValues[i] = value;
+    }
+    else
+    {
+      i = ~i;
+      if (mSize >= mKeys.length)
+      {
+        int n = idealDoubleArraySize(mSize + 1);
+        int[] nkeys = new int[n];
+        double[] nvalues = new double[n];
+        // Log.e("SparseDoubleArray", "grow " + mKeys.length + " to " + n);
+        System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+        System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+        mKeys = nkeys;
+        mValues = nvalues;
+      }
+      if (mSize - i != 0)
+      {
+        // Log.e("SparseDoubleArray", "move " + (mSize - i));
+        System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+        System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+      }
+      mKeys[i] = key;
+      mValues[i] = value;
+      mSize++;
+    }
+  }
+
+  /**
+   * Returns the number of key-value mappings that this SparseDoubleArray
+   * currently stores.
+   */
+  public int size()
+  {
+    return mSize;
+  }
+
+  /**
+   * Given an index in the range <code>0...size()-1</code>, returns the key from
+   * the <code>index</code>th key-value mapping that this SparseDoubleArray
+   * stores.
+   *
+   * <p>
+   * The keys corresponding to indices in ascending order are guaranteed to be
+   * in ascending order, e.g., <code>keyAt(0)</code> will return the smallest
+   * key and <code>keyAt(size()-1)</code> will return the largest key.
+   * </p>
+   */
+  public int keyAt(int index)
+  {
+    return mKeys[index];
+  }
+
+  /**
+   * Given an index in the range <code>0...size()-1</code>, returns the value
+   * from the <code>index</code>th key-value mapping that this SparseDoubleArray
+   * stores.
+   *
+   * <p>
+   * The values corresponding to indices in ascending order are guaranteed to be
+   * associated with keys in ascending order, e.g., <code>valueAt(0)</code> will
+   * return the value associated with the smallest key and
+   * <code>valueAt(size()-1)</code> will return the value associated with the
+   * largest key.
+   * </p>
+   */
+  public double valueAt(int index)
+  {
+    return mValues[index];
+  }
+
+  /**
+   * Returns the index for which {@link #keyAt} would return the specified key,
+   * or a negative number if the specified key is not mapped.
+   */
+  public int indexOfKey(int key)
+  {
+    return ContainerHelpers.binarySearch(mKeys, mSize, key);
+  }
+
+  /**
+   * Returns an index for which {@link #valueAt} would return the specified key,
+   * or a negative number if no keys map to the specified value. Beware that
+   * this is a linear search, unlike lookups by key, and that multiple keys can
+   * map to the same value and this will find only one of them.
+   */
+  public int indexOfValue(double value)
+  {
+    for (int i = 0; i < mSize; i++)
+    {
+      if (mValues[i] == value)
+      {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * Removes all key-value mappings from this SparseDoubleArray.
+   */
+  public void clear()
+  {
+    mSize = 0;
+  }
+
+  /**
+   * Puts a key/value pair into the array, optimizing for the case where the key
+   * is greater than all existing keys in the array.
+   */
+  public void append(int key, double value)
+  {
+    if (mSize != 0 && key <= mKeys[mSize - 1])
+    {
+      put(key, value);
+      return;
+    }
+    int pos = mSize;
+    if (pos >= mKeys.length)
+    {
+      int n = idealDoubleArraySize(pos + 1);
+      int[] nkeys = new int[n];
+      double[] nvalues = new double[n];
+      // Log.e("SparseDoubleArray", "grow " + mKeys.length + " to " + n);
+      System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+      System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+      mKeys = nkeys;
+      mValues = nvalues;
+    }
+    mKeys[pos] = key;
+    mValues[pos] = value;
+    mSize = pos + 1;
+  }
+
+  /**
+   * Created by analogy with
+   * com.android.internal.util.ArrayUtils#idealLongArraySize
+   * 
+   * @param i
+   * @return
+   */
+  public static int idealDoubleArraySize(int need)
+  {
+    return idealByteArraySize(need * 8) / 8;
+  }
+
+  /**
+   * Inlined here by copying from com.android.internal.util.ArrayUtils
+   * 
+   * @param i
+   * @return
+   */
+  public static int idealByteArraySize(int need)
+  {
+    for (int i = 4; i < 32; i++)
+    {
+      if (need <= (1 << i) - 12)
+      {
+        return (1 << i) - 12;
+      }
+    }
+
+    return need;
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * <p>
+   * This implementation composes a string by iterating over its mappings.
+   */
+  @Override
+  public String toString()
+  {
+    if (size() <= 0)
+    {
+      return "{}";
+    }
+    StringBuilder buffer = new StringBuilder(mSize * 28);
+    buffer.append('{');
+    for (int i = 0; i < mSize; i++)
+    {
+      if (i > 0)
+      {
+        buffer.append(", ");
+      }
+      int key = keyAt(i);
+      buffer.append(key);
+      buffer.append('=');
+      double value = valueAt(i);
+      buffer.append(value);
+    }
+    buffer.append('}');
+    return buffer.toString();
+  }
+
+  /**
+   * Method (copied from put) added for Jalview to efficiently increment a key's
+   * value if present, else add it with the given value. This avoids a double
+   * binary search (once to get the value, again to put the updated value).
+   * 
+   * @param key
+   * @oparam toAdd
+   * @return the new value for the key
+   */
+  public double add(int key, double toAdd)
+  {
+    double newValue = toAdd;
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+    if (i >= 0)
+    {
+      mValues[i] += toAdd;
+      newValue = mValues[i];
+    }
+    else
+    {
+      i = ~i;
+      if (mSize >= mKeys.length)
+      {
+        int n = idealDoubleArraySize(mSize + 1);
+        int[] nkeys = new int[n];
+        double[] nvalues = new double[n];
+        System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+        System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+        mKeys = nkeys;
+        mValues = nvalues;
+      }
+      if (mSize - i != 0)
+      {
+        System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+        System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+      }
+      mKeys[i] = key;
+      mValues[i] = toAdd;
+      mSize++;
+    }
+    return newValue;
+  }
+
+  /**
+   * Method added for Jalview to efficiently multiply a key's value if present,
+   * else do nothing. This avoids a double binary search (once to get the value,
+   * again to put the updated value).
+   * 
+   * @param key
+   * @oparam toAdd
+   * @return the new value for the key
+   */
+  public double divide(int key, double divisor)
+  {
+    double newValue = 0d;
+    if (divisor == 0d)
+    {
+      return newValue;
+    }
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+    if (i >= 0)
+    {
+      mValues[i] /= divisor;
+      newValue = mValues[i];
+    }
+    return newValue;
+  }
+}
index 732bc0f..233707b 100644 (file)
@@ -38,6 +38,7 @@ import jalview.io.gff.SequenceOntologyI;
 import jalview.util.Comparison;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
+import jalview.util.RangeComparator;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -110,26 +111,6 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
   }
 
   /**
-   * A comparator to sort ranges into ascending start position order
-   */
-  private class RangeSorter implements Comparator<int[]>
-  {
-    boolean forwards;
-
-    RangeSorter(boolean forward)
-    {
-      forwards = forward;
-    }
-
-    @Override
-    public int compare(int[] o1, int[] o2)
-    {
-      return (forwards ? 1 : -1) * Integer.compare(o1[0], o2[0]);
-    }
-
-  }
-
-  /**
    * Default constructor (to use rest.ensembl.org)
    */
   public EnsemblSeqProxy()
@@ -626,7 +607,7 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
      * a final sort is needed since Ensembl returns CDS sorted within source
      * (havana / ensembl_havana)
      */
-    Collections.sort(regions, new RangeSorter(direction == 1));
+    Collections.sort(regions, new RangeComparator(direction == 1));
 
     List<int[]> to = Arrays.asList(new int[] { start,
         start + mappedLength - 1 });
index bf80831..94df99a 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.ext.jmol;
 
+import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
 import jalview.datamodel.AlignmentI;
@@ -34,6 +35,7 @@ import jalview.structure.AtomSpec;
 import jalview.structure.StructureMappingcommandSet;
 import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.MessageManager;
 
 import java.awt.Color;
 import java.awt.Container;
@@ -43,6 +45,7 @@ import java.io.File;
 import java.net.URL;
 import java.security.AccessControlException;
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -227,21 +230,10 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   }
 
   /**
-   * Construct and send a command to align structures against a reference
-   * structure, based on one or more sequence alignments
-   * 
-   * @param _alignment
-   *          an array of alignments to process
-   * @param _refStructure
-   *          an array of corresponding reference structures (index into pdb
-   *          file array); if a negative value is passed, the first PDB file
-   *          mapped to an alignment sequence is used as the reference for
-   *          superposition
-   * @param _hiddenCols
-   *          an array of corresponding hidden columns for each alignment
+   * {@inheritDoc}
    */
   @Override
-  public void superposeStructures(AlignmentI[] _alignment,
+  public String superposeStructures(AlignmentI[] _alignment,
           int[] _refStructure, ColumnSelection[] _hiddenCols)
   {
     while (viewer.isScriptExecuting())
@@ -261,7 +253,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     String[] files = getPdbFile();
     if (!waitForFileLoad(files))
     {
-      return;
+      return null;
     }
 
     StringBuilder selectioncom = new StringBuilder(256);
@@ -277,6 +269,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       nSeconds = " " + (2.0 / files.length) + " ";
       // if (nSeconds).substring(0,5)+" ";
     }
+
     // see JAL-1345 - should really automatically turn off the animation for
     // large numbers of structures, but Jmol doesn't seem to allow that.
     // nSeconds = " ";
@@ -302,14 +295,16 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       }
 
       /*
-       * 'matched' array will hold 'true' for visible alignment columns where
+       * 'matched' bit j will be set for visible alignment columns j where
        * all sequences have a residue with a mapping to the PDB structure
        */
-      // TODO could use a BitSet for matched
-      boolean matched[] = new boolean[alignment.getWidth()];
-      for (int m = 0; m < matched.length; m++)
+      BitSet matched = new BitSet();
+      for (int m = 0; m < alignment.getWidth(); m++)
       {
-        matched[m] = (hiddenCols != null) ? hiddenCols.isVisible(m) : true;
+        if (hiddenCols == null || hiddenCols.isVisible(m))
+        {
+          matched.set(m);
+        }
       }
 
       SuperposeData[] structures = new SuperposeData[files.length];
@@ -334,17 +329,12 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       }
 
       String[] selcom = new String[files.length];
-      int nmatched = 0;
-      for (boolean b : matched)
-      {
-        if (b)
-        {
-          nmatched++;
-        }
-      }
+      int nmatched = matched.cardinality();
       if (nmatched < 4)
       {
-        // TODO: bail out here because superposition illdefined?
+        return (MessageManager.formatMessage(
+"label.insufficient_residues",
+                nmatched));
       }
 
       /*
@@ -359,35 +349,35 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
           boolean run = false;
           StringBuilder molsel = new StringBuilder();
           molsel.append("{");
-          for (int r = 0; r < matched.length; r++)
+
+          int nextColumnMatch = matched.nextSetBit(0);
+          while (nextColumnMatch != -1)
           {
-            if (matched[r])
+            int pdbResNo = structures[pdbfnum].pdbResNo[nextColumnMatch];
+            if (lpos != pdbResNo - 1)
             {
-              int pdbResNo = structures[pdbfnum].pdbResNo[r];
-              if (lpos != pdbResNo - 1)
+              // discontinuity
+              if (lpos != -1)
               {
-                // discontinuity
-                if (lpos != -1)
-                {
-                  molsel.append(lpos);
-                  molsel.append(chainCd);
-                  molsel.append("|");
-                }
-                run = false;
+                molsel.append(lpos);
+                molsel.append(chainCd);
+                molsel.append("|");
               }
-              else
+              run = false;
+            }
+            else
+            {
+              // continuous run - and lpos >-1
+              if (!run)
               {
-                // continuous run - and lpos >-1
-                if (!run)
-                {
-                  // at the beginning, so add dash
-                  molsel.append(lpos);
-                  molsel.append("-");
-                }
-                run = true;
+                // at the beginning, so add dash
+                molsel.append(lpos);
+                molsel.append("-");
               }
-              lpos = pdbResNo;
+              run = true;
             }
+            lpos = pdbResNo;
+            nextColumnMatch = matched.nextSetBit(nextColumnMatch + 1);
           }
           /*
            * add final selection phrase
@@ -473,6 +463,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
               + selectioncom.toString() + "); cartoons; ");
       // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
     }
+
+    return null;
   }
 
   public void evalStateCommand(String command)
@@ -507,17 +499,15 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   /**
    * @param files
    * @param sr
-   * @param fr
-   * @param alignment
+   * @param viewPanel
    * @return
    */
   @Override
   protected StructureMappingcommandSet[] getColourBySequenceCommands(
-          String[] files, SequenceRenderer sr, FeatureRenderer fr,
-          AlignmentI alignment)
+          String[] files, SequenceRenderer sr, AlignmentViewPanel viewPanel)
   {
     return JmolCommands.getColourBySequenceCommand(getSsm(), files,
-            getSequence(), sr, fr, alignment);
+            getSequence(), sr, viewPanel);
   }
 
   /**
index d5676c5..4212749 100644 (file)
  */
 package jalview.ext.jmol;
 
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureMappingcommandSet;
 import jalview.structure.StructureSelectionManager;
 
 import java.awt.Color;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Routines for generating Jmol commands for Jalview/Jmol binding another
@@ -50,11 +55,15 @@ public class JmolCommands
    */
   public static StructureMappingcommandSet[] getColourBySequenceCommand(
           StructureSelectionManager ssm, String[] files,
-          SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
-          AlignmentI alignment)
+          SequenceI[][] sequence, SequenceRenderer sr,
+          AlignmentViewPanel viewPanel)
   {
-
-    ArrayList<StructureMappingcommandSet> cset = new ArrayList<StructureMappingcommandSet>();
+    FeatureRenderer fr = viewPanel.getFeatureRenderer();
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
+    AlignViewportI viewport = viewPanel.getAlignViewport();
+    ColumnSelection cs = viewport.getColumnSelection();
+    AlignmentI al = viewport.getAlignment();
+    List<StructureMappingcommandSet> cset = new ArrayList<StructureMappingcommandSet>();
 
     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
     {
@@ -74,9 +83,9 @@ public class JmolCommands
         for (int sp, m = 0; m < mapping.length; m++)
         {
           if (mapping[m].getSequence() == sequence[pdbfnum][s]
-                  && (sp = alignment.findIndex(sequence[pdbfnum][s])) > -1)
+                  && (sp = al.findIndex(sequence[pdbfnum][s])) > -1)
           {
-            SequenceI asp = alignment.getSequenceAt(sp);
+            SequenceI asp = al.getSequenceAt(sp);
             for (int r = 0; r < asp.getLength(); r++)
             {
               // no mapping to gaps in sequence
@@ -93,12 +102,18 @@ public class JmolCommands
 
               lastPos = pos;
 
-              Color col = sr.getResidueBoxColour(sequence[pdbfnum][s], r);
+              Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
+                      finder);
 
-              if (fr != null)
+              /*
+               * shade hidden regions darker
+               */
+              if (!cs.isVisible(r))
               {
-                col = fr.findFeatureColour(col, sequence[pdbfnum][s], r);
+                // col = ColorUtils.darkerThan(col);
+                col = Color.GRAY;
               }
+
               String newSelcom = (mapping[m].getChain() != " " ? ":"
                       + mapping[m].getChain() : "")
                       + "/"
diff --git a/src/jalview/ext/rbvi/chimera/AtomSpecModel.java b/src/jalview/ext/rbvi/chimera/AtomSpecModel.java
new file mode 100644 (file)
index 0000000..d62cc3c
--- /dev/null
@@ -0,0 +1,180 @@
+package jalview.ext.rbvi.chimera;
+
+import jalview.util.RangeComparator;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A class to model a Chimera atomspec pattern, for example
+ * 
+ * <pre>
+ * #0:15.A,28.A,54.A,63.A,70-72.A,83-84.A,97-98.A|#1:2.A,6.A,11.A,13-14.A,70.A,82.A,96-97.A
+ * </pre>
+ * 
+ * where
+ * <ul>
+ * <li>#0 is a model number</li>
+ * <li>15 or 70-72 is a residue number, or range of residue numbers</li>
+ * <li>.A is a chain identifier</li>
+ * <li>residue ranges are separated by comma</li>
+ * <li>atomspecs for distinct models are separated by | (or)</li>
+ * </ul>
+ * 
+ * <pre>
+ * @see http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
+ * </pre>
+ */
+public class AtomSpecModel
+{
+  private Map<Integer, Map<String, List<int[]>>> atomSpec;
+
+  /**
+   * Constructor
+   */
+  public AtomSpecModel()
+  {
+    atomSpec = new TreeMap<Integer, Map<String, List<int[]>>>();
+  }
+
+  /**
+   * Adds one contiguous range to this atom spec
+   * 
+   * @param model
+   * @param startPos
+   * @param endPos
+   * @param chain
+   */
+  public void addRange(int model, int startPos, int endPos, String chain)
+  {
+    /*
+     * Get/initialize map of data for the colour and model
+     */
+    Map<String, List<int[]>> modelData = atomSpec.get(model);
+    if (modelData == null)
+    {
+      atomSpec.put(model, modelData = new TreeMap<String, List<int[]>>());
+    }
+
+    /*
+     * Get/initialize map of data for colour, model and chain
+     */
+    List<int[]> chainData = modelData.get(chain);
+    if (chainData == null)
+    {
+      chainData = new ArrayList<int[]>();
+      modelData.put(chain, chainData);
+    }
+
+    /*
+     * Add the start/end positions
+     */
+    chainData.add(new int[] { startPos, endPos });
+    // TODO add intelligently, using a RangeList class
+  }
+
+  /**
+   * Returns the range(s) formatted as a Chimera atomspec
+   * 
+   * @return
+   */
+  public String getAtomSpec()
+  {
+    StringBuilder sb = new StringBuilder(128);
+    boolean firstModel = true;
+    for (Integer model : atomSpec.keySet())
+    {
+      if (!firstModel)
+      {
+        sb.append("|");
+      }
+      firstModel = false;
+      sb.append("#").append(model).append(":");
+
+      boolean firstPositionForModel = true;
+      final Map<String, List<int[]>> modelData = atomSpec.get(model);
+
+      for (String chain : modelData.keySet())
+      {
+        chain = chain.trim();
+
+        List<int[]> rangeList = modelData.get(chain);
+
+        /*
+         * sort ranges into ascending start position order
+         */
+        Collections.sort(rangeList, new RangeComparator(true));
+
+        int start = rangeList.isEmpty() ? 0 : rangeList.get(0)[0];
+        int end = rangeList.isEmpty() ? 0 : rangeList.get(0)[1];
+
+        Iterator<int[]> iterator = rangeList.iterator();
+        while (iterator.hasNext())
+        {
+          int[] range = iterator.next();
+          if (range[0] <= end + 1)
+          {
+            /*
+             * range overlaps or is contiguous with the last one
+             * - so just extend the end position, and carry on
+             * (unless this is the last in the list)
+             */
+            end = Math.max(end, range[1]);
+          }
+          else
+          {
+            /*
+             * we have a break so append the last range
+             */
+            appendRange(sb, start, end, chain, firstPositionForModel);
+            firstPositionForModel = false;
+            start = range[0];
+            end = range[1];
+          }
+        }
+
+        /*
+         * and append the last range
+         */
+        if (!rangeList.isEmpty())
+        {
+          appendRange(sb, start, end, chain, firstPositionForModel);
+          firstPositionForModel = false;
+        }
+      }
+    }
+    return sb.toString();
+  }
+
+  /**
+   * @param sb
+   * @param start
+   * @param end
+   * @param chain
+   * @param firstPositionForModel
+   */
+  protected void appendRange(StringBuilder sb, int start, int end,
+          String chain, boolean firstPositionForModel)
+  {
+    if (!firstPositionForModel)
+    {
+      sb.append(",");
+    }
+    if (end == start)
+    {
+      sb.append(start);
+    }
+    else
+    {
+      sb.append(start).append("-").append(end);
+    }
+    if (chain.length() > 0)
+    {
+      sb.append(".").append(chain);
+    }
+  }
+}
index 8f6b2f1..1d8b944 100644 (file)
  */
 package jalview.ext.rbvi.chimera;
 
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureMappingcommandSet;
 import jalview.structure.StructureSelectionManager;
@@ -32,11 +37,10 @@ import jalview.util.Comparison;
 
 import java.awt.Color;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
 
 /**
  * Routines for generating Chimera commands for Jalview/Chimera binding
@@ -47,26 +51,32 @@ import java.util.TreeMap;
 public class ChimeraCommands
 {
 
+  public static final String NAMESPACE_PREFIX = "jv_";
+
   /**
-   * utility to construct the commands to colour chains by the given alignment
-   * for passing to Chimera
-   * 
-   * @returns Object[] { Object[] { <model being coloured>,
+   * Constructs Chimera commands to colour residues as per the Jalview alignment
    * 
+   * @param ssm
+   * @param files
+   * @param sequence
+   * @param sr
+   * @param fr
+   * @param viewPanel
+   * @return
    */
   public static StructureMappingcommandSet[] getColourBySequenceCommand(
           StructureSelectionManager ssm, String[] files,
-          SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
-          AlignmentI alignment)
+          SequenceI[][] sequence, SequenceRenderer sr,
+          AlignmentViewPanel viewPanel)
   {
-    Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> colourMap = buildColoursMap(
-            ssm, files, sequence, sr, fr, alignment);
+    Map<Object, AtomSpecModel> colourMap = buildColoursMap(ssm, files,
+            sequence, sr, viewPanel);
 
     List<String> colourCommands = buildColourCommands(colourMap);
 
     StructureMappingcommandSet cs = new StructureMappingcommandSet(
             ChimeraCommands.class, null,
-            colourCommands.toArray(new String[0]));
+            colourCommands.toArray(new String[colourCommands.size()]));
 
     return new StructureMappingcommandSet[] { cs };
   }
@@ -76,18 +86,18 @@ public class ChimeraCommands
    * 'color' commands (one per distinct colour used). The format of each command
    * is
    * 
-   * <blockquote> color colorname #modelnumber:range.chain e.g. color #00ff00
-   * #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,...
-   * 
-   * @see http 
-   *      ://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec
-   *      .html </pre>
+   * <pre>
+   * <blockquote> 
+   * color colorname #modelnumber:range.chain 
+   * e.g. color #00ff00 #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,...
+   * </blockquote>
+   * </pre>
    * 
    * @param colourMap
    * @return
    */
   protected static List<String> buildColourCommands(
-          Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> colourMap)
+          Map<Object, AtomSpecModel> colourMap)
   {
     /*
      * This version concatenates all commands into a single String (semi-colon
@@ -97,8 +107,9 @@ public class ChimeraCommands
     List<String> commands = new ArrayList<String>();
     StringBuilder sb = new StringBuilder(256);
     boolean firstColour = true;
-    for (Color colour : colourMap.keySet())
+    for (Object key : colourMap.keySet())
     {
+      Color colour = (Color) key;
       String colourCode = ColorUtils.toTkCode(colour);
       if (!firstColour)
       {
@@ -106,55 +117,68 @@ public class ChimeraCommands
       }
       sb.append("color ").append(colourCode).append(" ");
       firstColour = false;
-      boolean firstModelForColour = true;
-      final Map<Integer, Map<String, List<int[]>>> colourData = colourMap
-              .get(colour);
-      for (Integer model : colourData.keySet())
+      final AtomSpecModel colourData = colourMap.get(colour);
+      sb.append(colourData.getAtomSpec());
+    }
+    commands.add(sb.toString());
+    return commands;
+  }
+
+  /**
+   * Traverses a map of { modelNumber, {chain, {list of from-to ranges} } } and
+   * builds a Chimera format atom spec
+   * 
+   * @param modelAndChainRanges
+   */
+  protected static String getAtomSpec(
+          Map<Integer, Map<String, List<int[]>>> modelAndChainRanges)
+  {
+    StringBuilder sb = new StringBuilder(128);
+    boolean firstModelForColour = true;
+    for (Integer model : modelAndChainRanges.keySet())
+    {
+      boolean firstPositionForModel = true;
+      if (!firstModelForColour)
       {
-        boolean firstPositionForModel = true;
-        if (!firstModelForColour)
-        {
-          sb.append("|");
-        }
-        firstModelForColour = false;
-        sb.append("#").append(model).append(":");
+        sb.append("|");
+      }
+      firstModelForColour = false;
+      sb.append("#").append(model).append(":");
 
-        final Map<String, List<int[]>> modelData = colourData.get(model);
-        for (String chain : modelData.keySet())
+      final Map<String, List<int[]>> modelData = modelAndChainRanges
+              .get(model);
+      for (String chain : modelData.keySet())
+      {
+        boolean hasChain = !"".equals(chain.trim());
+        for (int[] range : modelData.get(chain))
         {
-          boolean hasChain = !"".equals(chain.trim());
-          for (int[] range : modelData.get(chain))
+          if (!firstPositionForModel)
           {
-            if (!firstPositionForModel)
-            {
-              sb.append(",");
-            }
-            if (range[0] == range[1])
-            {
-              sb.append(range[0]);
-            }
-            else
-            {
-              sb.append(range[0]).append("-").append(range[1]);
-            }
-            if (hasChain)
-            {
-              sb.append(".").append(chain);
-            }
-            firstPositionForModel = false;
+            sb.append(",");
           }
+          if (range[0] == range[1])
+          {
+            sb.append(range[0]);
+          }
+          else
+          {
+            sb.append(range[0]).append("-").append(range[1]);
+          }
+          if (hasChain)
+          {
+            sb.append(".").append(chain);
+          }
+          firstPositionForModel = false;
         }
       }
     }
-    commands.add(sb.toString());
-    return commands;
+    return sb.toString();
   }
 
   /**
    * <pre>
-   * Build a data structure which maps contiguous subsequences for each colour. 
-   * This generates a data structure from which we can easily generate the 
-   * Chimera command for colour by sequence.
+   * Build a data structure which records contiguous subsequences for each colour. 
+   * From this we can easily generate the Chimera command for colour by sequence.
    * Color
    *     Model number
    *         Chain
@@ -162,13 +186,19 @@ public class ChimeraCommands
    * Ordering is by order of addition (for colours and positions), natural ordering (for models and chains)
    * </pre>
    */
-  protected static Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> buildColoursMap(
+  protected static Map<Object, AtomSpecModel> buildColoursMap(
           StructureSelectionManager ssm, String[] files,
-          SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
-          AlignmentI alignment)
+          SequenceI[][] sequence, SequenceRenderer sr,
+          AlignmentViewPanel viewPanel)
   {
-    Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> colourMap = new LinkedHashMap<Color, SortedMap<Integer, Map<String, List<int[]>>>>();
+    FeatureRenderer fr = viewPanel.getFeatureRenderer();
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
+    AlignViewportI viewport = viewPanel.getAlignViewport();
+    ColumnSelection cs = viewport.getColumnSelection();
+    AlignmentI al = viewport.getAlignment();
+    Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<Object, AtomSpecModel>();
     Color lastColour = null;
+
     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
     {
       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
@@ -186,9 +216,9 @@ public class ChimeraCommands
         {
           final SequenceI seq = sequence[pdbfnum][s];
           if (mapping[m].getSequence() == seq
-                  && (sp = alignment.findIndex(seq)) > -1)
+                  && (sp = al.findIndex(seq)) > -1)
           {
-            SequenceI asp = alignment.getSequenceAt(sp);
+            SequenceI asp = al.getSequenceAt(sp);
             for (int r = 0; r < asp.getLength(); r++)
             {
               // no mapping to gaps in sequence
@@ -203,7 +233,17 @@ public class ChimeraCommands
                 continue;
               }
 
-              Color colour = sr.getResidueColour(seq, r, fr);
+              Color colour = sr.getResidueColour(seq, r, finder);
+
+              /*
+               * darker colour for hidden regions
+               */
+              if (!cs.isVisible(r))
+              {
+                // colour = ColorUtils.darkerThan(colour);
+                colour = Color.GRAY;
+              }
+
               final String chain = mapping[m].getChain();
 
               /*
@@ -230,8 +270,8 @@ public class ChimeraCommands
             // final colour range
             if (lastColour != null)
             {
-              addColourRange(colourMap, lastColour, pdbfnum, startPos,
-                      lastPos, lastChain);
+              addColourRange(colourMap, lastColour, pdbfnum, startPos, lastPos,
+                      lastChain);
             }
             // break;
           }
@@ -244,51 +284,266 @@ public class ChimeraCommands
   /**
    * Helper method to add one contiguous colour range to the colour map.
    * 
-   * @param colourMap
-   * @param colour
+   * @param map
+   * @param key
    * @param model
    * @param startPos
    * @param endPos
    * @param chain
    */
-  protected static void addColourRange(
-          Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> colourMap,
-          Color colour, int model, int startPos, int endPos, String chain)
+  protected static void addColourRange(Map<Object, AtomSpecModel> map,
+          Object key, int model, int startPos, int endPos, String chain)
   {
     /*
      * Get/initialize map of data for the colour
      */
-    SortedMap<Integer, Map<String, List<int[]>>> colourData = colourMap
-            .get(colour);
-    if (colourData == null)
+    AtomSpecModel atomSpec = map.get(key);
+    if (atomSpec == null)
     {
-      colourMap
-              .put(colour,
-                      colourData = new TreeMap<Integer, Map<String, List<int[]>>>());
+      atomSpec = new AtomSpecModel();
+      map.put(key, atomSpec);
     }
 
-    /*
-     * Get/initialize map of data for the colour and model
-     */
-    Map<String, List<int[]>> modelData = colourData.get(model);
-    if (modelData == null)
+    atomSpec.addRange(model, startPos, endPos, chain);
+  }
+
+  /**
+   * Constructs and returns Chimera commands to set attributes on residues
+   * corresponding to features in Jalview. Attribute names are the Jalview
+   * feature type, with a "jv_" prefix.
+   * 
+   * @param ssm
+   * @param files
+   * @param seqs
+   * @param viewPanel
+   * @return
+   */
+  public static StructureMappingcommandSet getSetAttributeCommandsForFeatures(
+          StructureSelectionManager ssm, String[] files,
+          SequenceI[][] seqs, AlignmentViewPanel viewPanel)
+  {
+    Map<String, Map<Object, AtomSpecModel>> featureMap = buildFeaturesMap(
+            ssm, files, seqs, viewPanel);
+
+    List<String> commands = buildSetAttributeCommands(featureMap);
+
+    StructureMappingcommandSet cs = new StructureMappingcommandSet(
+            ChimeraCommands.class, null,
+            commands.toArray(new String[commands.size()]));
+
+    return cs;
+  }
+
+  /**
+   * <pre>
+   * Helper method to build a map of 
+   *   { featureType, { feature value, AtomSpecModel } }
+   * </pre>
+   * 
+   * @param ssm
+   * @param files
+   * @param seqs
+   * @param viewPanel
+   * @return
+   */
+  protected static Map<String, Map<Object, AtomSpecModel>> buildFeaturesMap(
+          StructureSelectionManager ssm, String[] files,
+          SequenceI[][] seqs, AlignmentViewPanel viewPanel)
+  {
+    Map<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<String, Map<Object, AtomSpecModel>>();
+
+    FeatureRenderer fr = viewPanel.getFeatureRenderer();
+    if (fr == null)
     {
-      colourData.put(model, modelData = new TreeMap<String, List<int[]>>());
+      return theMap;
     }
 
-    /*
-     * Get/initialize map of data for colour, model and chain
-     */
-    List<int[]> chainData = modelData.get(chain);
-    if (chainData == null)
+    List<String> visibleFeatures = fr.getDisplayedFeatureTypes();
+    if (visibleFeatures.isEmpty())
     {
-      modelData.put(chain, chainData = new ArrayList<int[]>());
+      return theMap;
     }
 
+    AlignmentI alignment = viewPanel.getAlignment();
+    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
+    {
+      StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
+
+      if (mapping == null || mapping.length < 1)
+      {
+        continue;
+      }
+
+      for (int seqNo = 0; seqNo < seqs[pdbfnum].length; seqNo++)
+      {
+        for (int m = 0; m < mapping.length; m++)
+        {
+          final SequenceI seq = seqs[pdbfnum][seqNo];
+          int sp = alignment.findIndex(seq);
+          if (mapping[m].getSequence() == seq && sp > -1)
+          {
+            /*
+             * found a sequence with a mapping to a structure;
+             * now scan its features
+             */
+            SequenceI asp = alignment.getSequenceAt(sp);
+
+            scanSequenceFeatures(visibleFeatures, mapping[m], asp, theMap,
+                    pdbfnum);
+          }
+        }
+      }
+    }
+    return theMap;
+  }
+
+  /**
+   * Inspect features on the sequence; for each feature that is visible,
+   * determine its mapped ranges in the structure (if any) according to the
+   * given mapping, and add them to the map
+   * 
+   * @param visibleFeatures
+   * @param mapping
+   * @param seq
+   * @param theMap
+   * @param modelNumber
+   */
+  protected static void scanSequenceFeatures(List<String> visibleFeatures,
+          StructureMapping mapping, SequenceI seq,
+          Map<String, Map<Object, AtomSpecModel>> theMap, int modelNumber)
+  {
+    SequenceFeature[] sfs = seq.getSequenceFeatures();
+    if (sfs == null)
+    {
+      return;
+    }
+
+    for (SequenceFeature sf : sfs)
+    {
+      String type = sf.getType();
+
+      /*
+       * Only copy visible features, don't copy any which originated
+       * from Chimera, and suppress uninteresting ones (e.g. RESNUM)
+       */
+      boolean isFromViewer = JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
+              .equals(sf.getFeatureGroup());
+      if (isFromViewer || !visibleFeatures.contains(type))
+      {
+        continue;
+      }
+      List<int[]> mappedRanges = mapping.getPDBResNumRanges(sf.getBegin(),
+              sf.getEnd());
+
+      if (!mappedRanges.isEmpty())
+      {
+        String value = sf.getDescription();
+        if (value == null || value.length() == 0)
+        {
+          value = type;
+        }
+        float score = sf.getScore();
+        if (score != 0f && !Float.isNaN(score))
+        {
+          value = Float.toString(score);
+        }
+        Map<Object, AtomSpecModel> featureValues = theMap.get(type);
+        if (featureValues == null)
+        {
+          featureValues = new HashMap<Object, AtomSpecModel>();
+          theMap.put(type, featureValues);
+        }
+        for (int[] range : mappedRanges)
+        {
+          addColourRange(featureValues, value, modelNumber, range[0], range[1],
+                  mapping.getChain());
+        }
+      }
+    }
+  }
+
+  /**
+   * Traverse the map of features/values/models/chains/positions to construct a
+   * list of 'setattr' commands (one per distinct feature type and value).
+   * <p>
+   * The format of each command is
+   * 
+   * <pre>
+   * <blockquote> setattr r <featureName> " " #modelnumber:range.chain 
+   * e.g. setattr r jv:chain <value> #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,...
+   * </blockquote>
+   * </pre>
+   * 
+   * @param featureMap
+   * @return
+   */
+  protected static List<String> buildSetAttributeCommands(
+          Map<String, Map<Object, AtomSpecModel>> featureMap)
+  {
+    List<String> commands = new ArrayList<String>();
+    for (String featureType : featureMap.keySet())
+    {
+      String attributeName = makeAttributeName(featureType);
+
+      /*
+       * clear down existing attributes for this feature
+       */
+      // 'problem' - sets attribute to None on all residues - overkill?
+      // commands.add("~setattr r " + attributeName + " :*");
+
+      Map<Object, AtomSpecModel> values = featureMap.get(featureType);
+      for (Object value : values.keySet())
+      {
+        /*
+         * for each distinct value recorded for this feature type,
+         * add a command to set the attribute on the mapped residues
+         * Put values in single quotes, encoding any embedded single quotes
+         */
+        StringBuilder sb = new StringBuilder(128);
+        String featureValue = value.toString();
+        featureValue = featureValue.replaceAll("\\'", "&#39;");
+        sb.append("setattr r ").append(attributeName).append(" '")
+                .append(featureValue).append("' ");
+        sb.append(values.get(value).getAtomSpec());
+        commands.add(sb.toString());
+      }
+    }
+
+    return commands;
+  }
+
+  /**
+   * Makes a prefixed and valid Chimera attribute name. A jv_ prefix is applied
+   * for a 'Jalview' namespace, and any non-alphanumeric character is converted
+   * to an underscore.
+   * 
+   * @param featureType
+   * @return <pre>
+   * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/setattr.html
+   * </pre>
+   */
+  protected static String makeAttributeName(String featureType)
+  {
+    StringBuilder sb = new StringBuilder();
+    if (featureType != null)
+    {
+      for (char c : featureType.toCharArray())
+      {
+        sb.append(Character.isLetterOrDigit(c) ? c : '_');
+      }
+    }
+    String attName = NAMESPACE_PREFIX + sb.toString();
+
     /*
-     * Add the start/end positions
+     * Chimera treats an attribute name ending in 'color' as colour-valued;
+     * Jalview doesn't, so prevent this by appending an underscore
      */
-    chainData.add(new int[] { startPos, endPos });
+    if (attName.toUpperCase().endsWith("COLOR"))
+    {
+      attName += "_";
+    }
+
+    return attName;
   }
 
 }
index ba5e332..507ddbc 100644 (file)
@@ -129,7 +129,7 @@ public class ChimeraListener extends AbstractRequestHandler implements
   }
 
   /**
-   * Handler a ModelChanged notification from Chimera
+   * Handle a ModelChanged notification from Chimera
    * 
    * @param substring
    */
index b05c168..fad3137 100644 (file)
 package jalview.ext.rbvi.chimera;
 
 import jalview.api.AlignmentViewPanel;
-import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.httpserver.AbstractRequestHandler;
 import jalview.io.DataSourceType;
@@ -40,8 +42,14 @@ import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.MessageManager;
 
 import java.awt.Color;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
 import java.net.BindException;
 import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
 import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -54,6 +62,8 @@ import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
 
 public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 {
+  public static final String CHIMERA_FEATURE_GROUP = "Chimera";
+
   // Chimera clause to exclude alternate locations in atom selection
   private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9";
 
@@ -98,14 +108,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    */
   private Map<String, List<ChimeraModel>> chimeraMaps = new LinkedHashMap<String, List<ChimeraModel>>();
 
-  /*
-   * the default or current model displayed if the model cannot be identified
-   * from the selection message
-   */
-  private int frameNo = 0;
-
-  private String lastCommand;
-
   String lastHighlightCommand;
 
   /*
@@ -171,11 +173,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       {
         getSsm().addStructureViewerListener(this);
         // ssm.addSelectionListener(this);
-        FeatureRenderer fr = getFeatureRenderer(null);
-        if (fr != null)
-        {
-          fr.featuresAdded();
-        }
         refreshGUI();
       }
       return true;
@@ -304,7 +301,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       chimeraListener.shutdown();
       chimeraListener = null;
     }
-    lastCommand = null;
     viewer = null;
 
     if (chimeraMonitor != null)
@@ -339,21 +335,10 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   }
 
   /**
-   * Construct and send a command to align structures against a reference
-   * structure, based on one or more sequence alignments
-   * 
-   * @param _alignment
-   *          an array of alignments to process
-   * @param _refStructure
-   *          an array of corresponding reference structures (index into pdb
-   *          file array); if a negative value is passed, the first PDB file
-   *          mapped to an alignment sequence is used as the reference for
-   *          superposition
-   * @param _hiddenCols
-   *          an array of corresponding hidden columns for each alignment
+   * {@inheritDoc}
    */
   @Override
-  public void superposeStructures(AlignmentI[] _alignment,
+  public String superposeStructures(AlignmentI[] _alignment,
           int[] _refStructure, ColumnSelection[] _hiddenCols)
   {
     StringBuilder allComs = new StringBuilder(128);
@@ -361,7 +346,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
     if (!waitForFileLoad(files))
     {
-      return;
+      return null;
     }
 
     refreshPdbEntries();
@@ -380,13 +365,16 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       }
 
       /*
-       * 'matched' array will hold 'true' for visible alignment columns where
+       * 'matched' bit i will be set for visible alignment columns i where
        * all sequences have a residue with a mapping to the PDB structure
        */
-      boolean matched[] = new boolean[alignment.getWidth()];
-      for (int m = 0; m < matched.length; m++)
+      BitSet matched = new BitSet();
+      for (int m = 0; m < alignment.getWidth(); m++)
       {
-        matched[m] = (hiddenCols != null) ? hiddenCols.isVisible(m) : true;
+        if (hiddenCols == null || hiddenCols.isVisible(m))
+        {
+          matched.set(m);
+        }
       }
 
       SuperposeData[] structures = new SuperposeData[files.length];
@@ -410,17 +398,11 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
         refStructure = candidateRefStructure;
       }
 
-      int nmatched = 0;
-      for (boolean b : matched)
-      {
-        if (b)
-        {
-          nmatched++;
-        }
-      }
+      int nmatched = matched.cardinality();
       if (nmatched < 4)
       {
-        // TODO: bail out here because superposition illdefined?
+        return MessageManager.formatMessage("label.insufficient_residues",
+                nmatched);
       }
 
       /*
@@ -433,41 +415,41 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
         int lpos = -1;
         boolean run = false;
         StringBuilder molsel = new StringBuilder();
-        for (int r = 0; r < matched.length; r++)
+
+        int nextColumnMatch = matched.nextSetBit(0);
+        while (nextColumnMatch != -1)
         {
-          if (matched[r])
+          int pdbResNum = structures[pdbfnum].pdbResNo[nextColumnMatch];
+          if (lpos != pdbResNum - 1)
           {
-            int pdbResNum = structures[pdbfnum].pdbResNo[r];
-            if (lpos != pdbResNum - 1)
+            /*
+             * discontiguous - append last residue now
+             */
+            if (lpos != -1)
             {
-              /*
-               * discontiguous - append last residue now
-               */
-              if (lpos != -1)
-              {
-                molsel.append(String.valueOf(lpos));
-                molsel.append(chainCd);
-                molsel.append(",");
-              }
-              run = false;
+              molsel.append(String.valueOf(lpos));
+              molsel.append(chainCd);
+              molsel.append(",");
             }
-            else
+            run = false;
+          }
+          else
+          {
+            /*
+             * extending a contiguous run
+             */
+            if (!run)
             {
               /*
-               * extending a contiguous run
+               * start the range selection
                */
-              if (!run)
-              {
-                /*
-                 * start the range selection
-                 */
-                molsel.append(String.valueOf(lpos));
-                molsel.append("-");
-              }
-              run = true;
+              molsel.append(String.valueOf(lpos));
+              molsel.append("-");
             }
-            lpos = pdbResNum;
+            run = true;
           }
+          lpos = pdbResNum;
+          nextColumnMatch = matched.nextSetBit(nextColumnMatch + 1);
         }
 
         /*
@@ -543,6 +525,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
                 .append(";" + command.toString());
       }
     }
+
+    String error = null;
     if (selectioncom.length() > 0)
     {
       // TODO: visually distinguish regions that were superposed
@@ -556,9 +540,17 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       }
       allComs.append("; ~display all; chain @CA|P; ribbon ")
               .append(selectioncom.toString()).append("; focus");
-      sendChimeraCommand(allComs.toString(), false);
+      List<String> chimeraReplies = sendChimeraCommand(allComs.toString(),
+              true);
+      for (String reply : chimeraReplies)
+      {
+        if (reply.toLowerCase().contains("unequal numbers of atoms"))
+        {
+          error = reply;
+        }
+      }
     }
-
+    return error;
   }
 
   /**
@@ -631,31 +623,42 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   }
 
   /**
-   * Send a command to Chimera, and optionally log any responses.
+   * Send a command to Chimera, and optionally log and return any responses.
+   * <p>
+   * Does nothing, and returns null, if the command is the same as the last one
+   * sent [why?].
    * 
    * @param command
-   * @param logResponse
+   * @param getResponse
    */
-  public void sendChimeraCommand(final String command, boolean logResponse)
+  public List<String> sendChimeraCommand(final String command,
+          boolean getResponse)
   {
     if (viewer == null)
     {
       // ? thread running after viewer shut down
-      return;
+      return null;
     }
+    List<String> reply = null;
     viewerCommandHistory(false);
-    if (lastCommand == null || !lastCommand.equals(command))
+    if (true /*lastCommand == null || !lastCommand.equals(command)*/)
     {
       // trim command or it may never find a match in the replyLog!!
       List<String> lastReply = viewer.sendChimeraCommand(command.trim(),
-              logResponse);
-      if (logResponse && debug)
+              getResponse);
+      if (getResponse)
       {
-        log("Response from command ('" + command + "') was:\n" + lastReply);
+        reply = lastReply;
+        if (debug)
+        {
+          log("Response from command ('" + command + "') was:\n"
+                  + lastReply);
+        }
       }
     }
     viewerCommandHistory(true);
-    lastCommand = command;
+
+    return reply;
   }
 
   /**
@@ -689,17 +692,15 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   /**
    * @param files
    * @param sr
-   * @param fr
-   * @param alignment
+   * @param viewPanel
    * @return
    */
   @Override
   protected StructureMappingcommandSet[] getColourBySequenceCommands(
-          String[] files, SequenceRenderer sr, FeatureRenderer fr,
-          AlignmentI alignment)
+          String[] files, SequenceRenderer sr, AlignmentViewPanel viewPanel)
   {
     return ChimeraCommands.getColourBySequenceCommand(getSsm(), files,
-            getSequence(), sr, fr, alignment);
+            getSequence(), sr, viewPanel);
   }
 
   /**
@@ -742,6 +743,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    */
   private int _modelFileNameMap[];
 
+
   // ////////////////////////////////
   // /StructureListener
   @Override
@@ -837,59 +839,64 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
      * Parse model number, residue and chain for each selected position,
      * formatted as #0:123.A or #1.2:87.B (#model.submodel:residue.chain)
      */
+    List<AtomSpec> atomSpecs = convertStructureResiduesToAlignment(selection);
+
+    /*
+     * Broadcast the selection (which may be empty, if the user just cleared all
+     * selections)
+     */
+    getSsm().mouseOverStructure(atomSpecs);
+  }
+
+  /**
+   * Converts a list of Chimera atomspecs to a list of AtomSpec representing the
+   * corresponding residues (if any) in Jalview
+   * 
+   * @param structureSelection
+   * @return
+   */
+  protected List<AtomSpec> convertStructureResiduesToAlignment(
+          List<String> structureSelection)
+  {
     List<AtomSpec> atomSpecs = new ArrayList<AtomSpec>();
-    for (String atomSpec : selection)
+    for (String atomSpec : structureSelection)
     {
-      int colonPos = atomSpec.indexOf(":");
-      if (colonPos == -1)
-      {
-        continue; // malformed
-      }
-
-      int hashPos = atomSpec.indexOf("#");
-      String modelSubmodel = atomSpec.substring(hashPos + 1, colonPos);
-      int dotPos = modelSubmodel.indexOf(".");
-      int modelId = 0;
       try
       {
-        modelId = Integer.valueOf(dotPos == -1 ? modelSubmodel
-                : modelSubmodel.substring(0, dotPos));
-      } catch (NumberFormatException e)
+        AtomSpec spec = AtomSpec.fromChimeraAtomspec(atomSpec);
+        String pdbfilename = getPdbFileForModel(spec.getModelNumber());
+        spec.setPdbFile(pdbfilename);
+        atomSpecs.add(spec);
+      } catch (IllegalArgumentException e)
       {
-        // ignore, default to model 0
+        System.err.println("Failed to parse atomspec: " + atomSpec);
       }
+    }
+    return atomSpecs;
+  }
 
-      String residueChain = atomSpec.substring(colonPos + 1);
-      dotPos = residueChain.indexOf(".");
-      int pdbResNum = Integer.parseInt(dotPos == -1 ? residueChain
-              : residueChain.substring(0, dotPos));
-
-      String chainId = dotPos == -1 ? "" : residueChain
-              .substring(dotPos + 1);
-
-      /*
-       * Work out the pdbfilename from the model number
-       */
-      String pdbfilename = modelFileNames[frameNo];
-      findfileloop: for (String pdbfile : this.chimeraMaps.keySet())
+  /**
+   * @param modelId
+   * @return
+   */
+  protected String getPdbFileForModel(int modelId)
+  {
+    /*
+     * Work out the pdbfilename from the model number
+     */
+    String pdbfilename = modelFileNames[0];
+    findfileloop: for (String pdbfile : this.chimeraMaps.keySet())
+    {
+      for (ChimeraModel cm : chimeraMaps.get(pdbfile))
       {
-        for (ChimeraModel cm : chimeraMaps.get(pdbfile))
+        if (cm.getModelNumber() == modelId)
         {
-          if (cm.getModelNumber() == modelId)
-          {
-            pdbfilename = pdbfile;
-            break findfileloop;
-          }
+          pdbfilename = pdbfile;
+          break findfileloop;
         }
       }
-      atomSpecs.add(new AtomSpec(pdbfilename, chainId, pdbResNum, 0));
     }
-
-    /*
-     * Broadcast the selection (which may be empty, if the user just cleared all
-     * selections)
-     */
-    getSsm().mouseOverStructure(atomSpecs);
+    return pdbfilename;
   }
 
   private void log(String message)
@@ -1036,6 +1043,18 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   }
 
   /**
+   * Returns a list of chains mapped in this viewer. Note this list is not
+   * currently scoped per structure.
+   * 
+   * @return
+   */
+  @Override
+  public List<String> getChainNames()
+  {
+    return chainNames;
+  }
+
+  /**
    * Send a 'focus' command to Chimera to recentre the visible display
    */
   public void focusView()
@@ -1071,13 +1090,207 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     }
   }
 
+  /**
+   * Constructs and send commands to Chimera to set attributes on residues for
+   * features visible in Jalview
+   * 
+   * @param avp
+   * @return
+   */
+  public int sendFeaturesToViewer(AlignmentViewPanel avp)
+  {
+    // TODO refactor as required to pull up to an interface
+    AlignmentI alignment = avp.getAlignment();
 
-  @Override
-  public List<String> getChainNames()
+    String[] files = getPdbFile();
+    if (files == null)
+    {
+      return 0;
+    }
+
+    StructureMappingcommandSet commandSet = ChimeraCommands
+            .getSetAttributeCommandsForFeatures(getSsm(), files,
+                    getSequence(), avp);
+    String[] commands = commandSet.commands;
+    if (commands.length > 10)
+    {
+      sendCommandsByFile(commands);
+    }
+    else
+    {
+      for (String command : commands)
+      {
+        sendAsynchronousCommand(command, null);
+      }
+    }
+    return commands.length;
+  }
+
+  /**
+   * Write commands to a temporary file, and send a command to Chimera to open
+   * the file as a commands script. For use when sending a large number of
+   * separate commands would overload the REST interface mechanism.
+   * 
+   * @param commands
+   */
+  protected void sendCommandsByFile(String[] commands)
   {
-    return chainNames;
+    try
+    {
+      File tmp = File.createTempFile("chim", ".com");
+      tmp.deleteOnExit();
+      PrintWriter out = new PrintWriter(new FileOutputStream(tmp));
+      for (String command : commands)
+      {
+        out.println(command);
+      }
+      out.flush();
+      out.close();
+      String path = tmp.getAbsolutePath();
+      sendAsynchronousCommand("open cmd:" + path, null);
+    } catch (IOException e)
+    {
+      System.err
+              .println("Sending commands to Chimera via file failed with "
+                      + e.getMessage());
+    }
   }
 
+  /**
+   * Get Chimera residues which have the named attribute, find the mapped
+   * positions in the Jalview sequence(s), and set as sequence features
+   * 
+   * @param attName
+   * @param alignmentPanel
+   */
+  public void copyStructureAttributesToFeatures(String attName,
+          AlignmentViewPanel alignmentPanel)
+  {
+    // todo pull up to AAStructureBindingModel (and interface?)
+
+    /*
+     * ask Chimera to list residues with the attribute, reporting its value
+     */
+    // this alternative command
+    // list residues spec ':*/attName' attr attName
+    // doesn't report 'None' values (which is good), but
+    // fails for 'average.bfactor' (which is bad):
+
+    String cmd = "list residues attr '" + attName + "'";
+    List<String> residues = sendChimeraCommand(cmd, true);
+
+    boolean featureAdded = createFeaturesForAttributes(attName, residues);
+    if (featureAdded)
+    {
+      alignmentPanel.getFeatureRenderer().featuresAdded();
+    }
+  }
+
+  /**
+   * Create features in Jalview for the given attribute name and structure
+   * residues.
+   * 
+   * <pre>
+   * The residue list should be 0, 1 or more reply lines of the format: 
+   *     residue id #0:5.A isHelix -155.000836316 index 5 
+   * or 
+   *     residue id #0:6.A isHelix None
+   * </pre>
+   * 
+   * @param attName
+   * @param residues
+   * @return
+   */
+  protected boolean createFeaturesForAttributes(String attName,
+          List<String> residues)
+  {
+    boolean featureAdded = false;
+    String featureGroup = getViewerFeatureGroup();
+
+    for (String residue : residues)
+    {
+      AtomSpec spec = null;
+      String[] tokens = residue.split(" ");
+      if (tokens.length < 5)
+      {
+        continue;
+      }
+      String atomSpec = tokens[2];
+      String attValue = tokens[4];
+
+      /*
+       * ignore 'None' (e.g. for phi) or 'False' (e.g. for isHelix)
+       */
+      if ("None".equalsIgnoreCase(attValue)
+              || "False".equalsIgnoreCase(attValue))
+      {
+        continue;
+      }
+
+      try
+      {
+        spec = AtomSpec.fromChimeraAtomspec(atomSpec);
+      } catch (IllegalArgumentException e)
+      {
+        System.err.println("Problem parsing atomspec " + atomSpec);
+        continue;
+      }
+
+      String chainId = spec.getChain();
+      String description = attValue;
+      float score = Float.NaN;
+      try
+      {
+        score = Float.valueOf(attValue);
+        description = chainId;
+      } catch (NumberFormatException e)
+      {
+        // was not a float value
+      }
+
+      String pdbFile = getPdbFileForModel(spec.getModelNumber());
+      spec.setPdbFile(pdbFile);
+
+      List<AtomSpec> atoms = Collections.singletonList(spec);
+
+      /*
+       * locate the mapped position in the alignment (if any)
+       */
+      SearchResultsI sr = getSsm()
+              .findAlignmentPositionsForStructurePositions(atoms);
+
+      /*
+       * expect one matched alignment position, or none 
+       * (if the structure position is not mapped)
+       */
+      for (SearchResultMatchI m : sr.getResults())
+      {
+        SequenceI seq = m.getSequence();
+        int start = m.getStart();
+        int end = m.getEnd();
+        SequenceFeature sf = new SequenceFeature(attName, description,
+                start, end, score, featureGroup);
+        // todo: should SequenceFeature have an explicit property for chain?
+        // note: repeating the action shouldn't duplicate features
+        featureAdded |= seq.addSequenceFeature(sf);
+      }
+    }
+    return featureAdded;
+  }
+
+  /**
+   * Answers the feature group name to apply to features created in Jalview from
+   * Chimera attributes
+   * 
+   * @return
+   */
+  protected String getViewerFeatureGroup()
+  {
+    // todo pull up to interface
+    return CHIMERA_FEATURE_GROUP;
+  }
+
+
   public Hashtable<String, String> getChainFile()
   {
     return chainFile;
index a41e10e..d65f1d5 100644 (file)
  */
 package jalview.ext.varna;
 
-import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
 
@@ -47,7 +47,8 @@ public class VarnaCommands
    */
   public static String[] getColourBySequenceCommand(
           StructureSelectionManager ssm, String[] files,
-          SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
+          SequenceI[][] sequence, SequenceRenderer sr,
+          FeatureColourFinder finder,
           AlignmentI alignment)
   {
     ArrayList<String> str = new ArrayList<String>();
@@ -58,7 +59,9 @@ public class VarnaCommands
       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
 
       if (mapping == null || mapping.length < 1)
+      {
         continue;
+      }
 
       int lastPos = -1;
       for (int s = 0; s < sequence[pdbfnum].length; s++)
@@ -79,14 +82,15 @@ public class VarnaCommands
               int pos = mapping[m].getPDBResNum(asp.findPosition(r));
 
               if (pos < 1 || pos == lastPos)
+              {
                 continue;
+              }
 
               lastPos = pos;
 
-              Color col = sr.getResidueBoxColour(sequence[pdbfnum][s], r);
+              Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
+                      finder);
 
-              if (fr != null)
-                col = fr.findFeatureColour(col, sequence[pdbfnum][s], r);
               String newSelcom = (mapping[m].getChain() != " " ? ":"
                       + mapping[m].getChain() : "")
                       + "/"
index b5fc817..ab1ac0e 100644 (file)
@@ -86,6 +86,7 @@ import jalview.schemes.ResidueProperties;
 import jalview.schemes.TCoffeeColourScheme;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 import jalview.ws.DBRefFetcher;
 import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
 import jalview.ws.jws1.Discoverer;
@@ -160,6 +161,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   AlignViewport viewport;
 
+  ViewportRanges vpRanges;
+
   public AlignViewControllerI avc;
 
   List<AlignmentPanel> alignPanels = new ArrayList<AlignmentPanel>();
@@ -331,6 +334,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       progressBar = new ProgressBar(this.statusPanel, this.statusBar);
     }
 
+    vpRanges = viewport.getRanges();
     avc = new jalview.controller.AlignViewController(this, viewport,
             alignPanel);
     if (viewport.getAlignmentConservationAnnotation() == null)
@@ -640,8 +644,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                   new String[] { (viewport.cursorMode ? "on" : "off") }));
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().seqCanvas.cursorX = viewport.startRes;
-            alignPanel.getSeqPanel().seqCanvas.cursorY = viewport.startSeq;
+            alignPanel.getSeqPanel().seqCanvas.cursorX = vpRanges
+                    .getStartRes();
+            alignPanel.getSeqPanel().seqCanvas.cursorY = vpRanges
+                    .getStartSeq();
           }
           alignPanel.getSeqPanel().seqCanvas.repaint();
           break;
@@ -679,8 +685,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
-                    - viewport.endSeq + viewport.startSeq);
+            alignPanel.setScrollValues(vpRanges.getStartRes(),
+                    2 * vpRanges.getStartSeq() - vpRanges.getEndSeq());
           }
           break;
         case KeyEvent.VK_PAGE_DOWN:
@@ -690,8 +696,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
-                    + viewport.endSeq - viewport.startSeq);
+            alignPanel.setScrollValues(vpRanges.getStartRes(),
+                    vpRanges.getEndSeq());
           }
           break;
         }
@@ -2141,7 +2147,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
 
         // propagate alignment changed.
-        viewport.setEndSeq(alignment.getHeight());
+        vpRanges.setEndSeq(alignment.getHeight());
         if (annotationAdded)
         {
           // Duplicate sequence annotation in all views.
@@ -2545,7 +2551,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         trimRegion = new TrimRegionCommand("Remove Left", true, seqs,
                 column, viewport.getAlignment());
-        viewport.setStartRes(0);
+        vpRanges.setStartRes(0);
       }
       else
       {
@@ -2612,13 +2618,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // This is to maintain viewport position on first residue
     // of first sequence
     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
-    int startRes = seq.findPosition(viewport.startRes);
+    int startRes = seq.findPosition(vpRanges.getStartRes());
     // ShiftList shifts;
     // viewport.getAlignment().removeGaps(shifts=new ShiftList());
     // edit.alColumnChanges=shifts.getInverse();
     // if (viewport.hasHiddenColumns)
     // viewport.getColumnSelection().compensateForEdits(shifts);
-    viewport.setStartRes(seq.findIndex(startRes) - 1);
+    vpRanges.setStartRes(seq.findIndex(startRes) - 1);
     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
             .getSequences());
 
@@ -2651,12 +2657,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // This is to maintain viewport position on first residue
     // of first sequence
     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
-    int startRes = seq.findPosition(viewport.startRes);
+    int startRes = seq.findPosition(vpRanges.getStartRes());
 
     addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
             viewport.getAlignment()));
 
-    viewport.setStartRes(seq.findIndex(startRes) - 1);
+    vpRanges.setStartRes(seq.findIndex(startRes) - 1);
 
     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
             .getSequences());
index 69f155b..602e3a1 100644 (file)
@@ -44,13 +44,14 @@ import jalview.datamodel.SequenceI;
 import jalview.renderer.ResidueShader;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.ResidueColourScheme;
 import jalview.schemes.UserColourScheme;
-import jalview.structure.CommandListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 import jalview.ws.params.AutoCalcSetting;
 
 import java.awt.Container;
@@ -71,7 +72,7 @@ import javax.swing.JInternalFrame;
  * @version $Revision: 1.141 $
  */
 public class AlignViewport extends AlignmentViewport implements
-        SelectionSource, CommandListener
+        SelectionSource
 {
   Font font;
 
@@ -237,10 +238,7 @@ public class AlignViewport extends AlignmentViewport implements
 
   void init()
   {
-    this.startRes = 0;
-    this.endRes = alignment.getWidth() - 1;
-    this.startSeq = 0;
-    this.endSeq = alignment.getHeight() - 1;
+    ranges = new ViewportRanges(this.alignment);
     applyViewProperties();
 
     String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
@@ -285,29 +283,27 @@ public class AlignViewport extends AlignmentViewport implements
     initAutoAnnotation();
     String colourProperty = alignment.isNucleotide() ? Preferences.DEFAULT_COLOUR_NUC
             : Preferences.DEFAULT_COLOUR_PROT;
-    String propertyValue = Cache.getProperty(colourProperty);
-    if (propertyValue == null)
+    String schemeName = Cache.getProperty(colourProperty);
+    if (schemeName == null)
     {
-      // fall back on this property for backwards compatibility
-      propertyValue = Cache.getProperty(Preferences.DEFAULT_COLOUR);
+      // only DEFAULT_COLOUR available in Jalview before 2.9
+      schemeName = Cache.getDefault(Preferences.DEFAULT_COLOUR,
+              ResidueColourScheme.NONE);
     }
-    if (propertyValue != null)
-    {
-      ColourSchemeI colourScheme = ColourSchemeProperty.getColourScheme(
-              alignment, propertyValue);
-      residueShading = new ResidueShader(colourScheme);
+    ColourSchemeI colourScheme = ColourSchemeProperty.getColourScheme(
+            alignment, schemeName);
+    residueShading = new ResidueShader(colourScheme);
 
-      if (colourScheme instanceof UserColourScheme)
-      {
-        residueShading = new ResidueShader(
-                UserDefinedColours.loadDefaultColours());
-        residueShading.setThreshold(0, isIgnoreGapsConsensus());
-      }
+    if (colourScheme instanceof UserColourScheme)
+    {
+      residueShading = new ResidueShader(
+              UserDefinedColours.loadDefaultColours());
+      residueShading.setThreshold(0, isIgnoreGapsConsensus());
+    }
 
-      if (residueShading != null)
-      {
-        residueShading.setConsensus(hconsensus);
-      }
+    if (residueShading != null)
+    {
+      residueShading.setConsensus(hconsensus);
     }
   }
 
@@ -385,11 +381,6 @@ public class AlignViewport extends AlignmentViewport implements
     super.setViewStyle(settingsForView);
     setFont(new Font(viewStyle.getFontName(), viewStyle.getFontStyle(),
             viewStyle.getFontSize()), false);
-    if (residueShading != null)
-    {
-      residueShading.setConservationApplied(settingsForView
-              .isConservationColourSelected());
-    }
   }
 
   /**
@@ -861,7 +852,7 @@ public class AlignViewport extends AlignmentViewport implements
       }
     }
 
-    setEndSeq(getAlignment().getHeight());
+    ranges.setEndSeq(getAlignment().getHeight());
     firePropertyChange("alignment", null, getAlignment().getSequences());
   }
 
index e61b042..ac137b9 100644 (file)
@@ -35,6 +35,7 @@ import jalview.schemes.ResidueProperties;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -69,6 +70,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
 {
   public AlignViewport av;
 
+  ViewportRanges vpRanges;
+
   OverviewPanel overviewPanel;
 
   private SeqPanel seqPanel;
@@ -91,9 +94,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
   // this value is set false when selection area being dragged
   boolean fastPaint = true;
 
-  int hextent = 0;
+  private int hextent = 0;
 
-  int vextent = 0;
+  private int vextent = 0;
 
   /*
    * Flag set while scrolling to follow complementary cDNA/protein scroll. When
@@ -113,6 +116,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     alignFrame = af;
     this.av = av;
+    vpRanges = av.getRanges();
     setSeqPanel(new SeqPanel(av, this));
     setIdPanel(new IdPanel(av, this));
 
@@ -377,7 +381,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
        */
       if (centre)
       {
-        int offset = (av.getEndRes() - av.getStartRes() + 1) / 2 - 1;
+        int offset = (vpRanges.getEndRes() - vpRanges.getStartRes() + 1) / 2 - 1;
         start = Math.max(start - offset, 0);
         end = end + offset - 1;
       }
@@ -413,7 +417,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       // + av.getStartSeq() + ", ends=" + av.getEndSeq());
       if (!av.getWrapAlignment())
       {
-        if ((startv = av.getStartRes()) >= start)
+        if ((startv = vpRanges.getStartRes()) >= start)
         {
           /*
            * Scroll left to make start of search results visible
@@ -421,7 +425,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
           // setScrollValues(start - 1, seqIndex); // plus one residue
           setScrollValues(start, seqIndex);
         }
-        else if ((endv = av.getEndRes()) <= end)
+        else if ((endv = vpRanges.getEndRes()) <= end)
         {
           /*
            * Scroll right to make end of search results visible
@@ -429,19 +433,20 @@ public class AlignmentPanel extends GAlignmentPanel implements
           // setScrollValues(startv + 1 + end - endv, seqIndex); // plus one
           setScrollValues(startv + end - endv, seqIndex);
         }
-        else if ((starts = av.getStartSeq()) > seqIndex)
+        else if ((starts = vpRanges.getStartSeq()) > seqIndex)
         {
           /*
            * Scroll up to make start of search results visible
            */
-          setScrollValues(av.getStartRes(), seqIndex);
+          setScrollValues(vpRanges.getStartRes(), seqIndex);
         }
-        else if ((ends = av.getEndSeq()) <= seqIndex)
+        else if ((ends = vpRanges.getEndSeq()) <= seqIndex)
         {
           /*
            * Scroll down to make end of search results visible
            */
-          setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1);
+          setScrollValues(vpRanges.getStartRes(), starts + seqIndex - ends
+                  + 1);
         }
         /*
          * Else results are already visible - no need to scroll
@@ -464,10 +469,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     int cwidth = getSeqPanel().seqCanvas
             .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
-    if (res < av.getStartRes() || res >= (av.getStartRes() + cwidth))
+    if (res < vpRanges.getStartRes()
+            || res >= (vpRanges.getStartRes() + cwidth))
     {
       vscroll.setValue((res / cwidth));
-      av.startRes = vscroll.getValue() * cwidth;
+      vpRanges.setStartRes(vscroll.getValue() * cwidth);
     }
 
   }
@@ -591,7 +597,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     fontChanged();
     setAnnotationVisible(av.isShowAnnotation());
     boolean wrap = av.getWrapAlignment();
-    av.startSeq = 0;
+    vpRanges.setStartSeq(0);
     scalePanelHolder.setVisible(!wrap);
     hscroll.setVisible(!wrap);
     idwidthAdjuster.setVisible(!wrap);
@@ -688,7 +694,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   public void setScrollValues(int x, int y)
   {
-    // System.err.println("Scroll " + this.av.viewName + " to " + x + "," + y);
     if (av == null || av.getAlignment() == null)
     {
       return;
@@ -698,12 +703,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     if (av.hasHiddenColumns())
     {
+      // reset the width to exclude hidden columns
       width = av.getColumnSelection().findColumnPosition(width);
     }
 
-    av.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av
-            .getCharWidth())) - 1);
-
     hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
     vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
 
@@ -737,6 +740,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
       x = 0;
     }
 
+    // update endRes after x has (possibly) been adjusted
+    vpRanges.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av
+            .getCharWidth())) - 1);
+
     /*
      * each scroll adjustment triggers adjustmentValueChanged, which resets the
      * 'do not scroll complement' flag; ensure it is the same for both
@@ -757,14 +764,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
   @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
-    int oldX = av.getStartRes();
-    int oldY = av.getStartSeq();
+    int oldX = vpRanges.getStartRes();
+    int oldY = vpRanges.getStartSeq();
 
     if (evt.getSource() == hscroll)
     {
       int x = hscroll.getValue();
-      av.setStartRes(x);
-      av.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av
+      vpRanges.setStartRes(x);
+      vpRanges.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av
               .getCharWidth())) - 1);
     }
 
@@ -778,8 +785,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
         {
           int rowSize = getSeqPanel().seqCanvas
                   .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
-          av.setStartRes(offy * rowSize);
-          av.setEndRes((offy + 1) * rowSize);
+          vpRanges.setStartRes(offy * rowSize);
+          vpRanges.setEndRes((offy + 1) * rowSize);
         }
         else
         {
@@ -791,16 +798,18 @@ public class AlignmentPanel extends GAlignmentPanel implements
             @Override
             public void run()
             {
-              setScrollValues(av.getStartRes(), av.getStartSeq());
+              setScrollValues(vpRanges.getStartRes(),
+                      vpRanges.getStartSeq());
             }
           });
         }
       }
       else
       {
-        av.setStartSeq(offy);
-        av.setEndSeq(offy
-                + (getSeqPanel().seqCanvas.getHeight() / av.getCharHeight()));
+        vpRanges.setStartSeq(offy);
+        vpRanges.setEndSeq(offy
+                + (getSeqPanel().seqCanvas.getHeight() / av.getCharHeight())
+                - 1);
       }
     }
 
@@ -809,8 +818,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
       overviewPanel.setBoxPosition();
     }
 
-    int scrollX = av.startRes - oldX;
-    int scrollY = av.startSeq - oldY;
+    int scrollX = vpRanges.getStartRes() - oldX;
+    int scrollY = vpRanges.getStartSeq() - oldY;
 
     if (av.getWrapAlignment() || !fastPaint)
     {
@@ -820,13 +829,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
     {
       // Make sure we're not trying to draw a panel
       // larger than the visible window
-      if (scrollX > av.endRes - av.startRes)
+      if (scrollX > vpRanges.getEndRes() - vpRanges.getStartRes())
       {
-        scrollX = av.endRes - av.startRes;
+        scrollX = vpRanges.getEndRes() - vpRanges.getStartRes();
       }
-      else if (scrollX < av.startRes - av.endRes)
+      else if (scrollX < vpRanges.getStartRes() - vpRanges.getEndRes())
       {
-        scrollX = av.startRes - av.endRes;
+        scrollX = vpRanges.getStartRes() - vpRanges.getEndRes();
       }
 
       if (scrollX != 0 || scrollY != 0)
@@ -926,7 +935,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
     else
     {
-      setScrollValues(av.getStartRes(), av.getStartSeq());
+      setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq());
     }
   }
 
index f6352d7..8500888 100644 (file)
@@ -21,6 +21,8 @@
 package jalview.gui;
 
 import jalview.bin.Cache;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.GraphLine;
 import jalview.datamodel.SequenceGroup;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.ColourSchemeI;
@@ -35,9 +37,11 @@ import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.util.Hashtable;
+import java.util.Vector;
 
 import javax.swing.BorderFactory;
 import javax.swing.JButton;
+import javax.swing.JCheckBox;
 import javax.swing.JColorChooser;
 import javax.swing.JComboBox;
 import javax.swing.JInternalFrame;
@@ -49,27 +53,21 @@ import net.miginfocom.swing.MigLayout;
 @SuppressWarnings("serial")
 public class AnnotationColourChooser extends AnnotationRowFilter
 {
+  private static final int ONETHOUSAND = 1000;
 
-  ColourSchemeI oldcs;
+  private ColourSchemeI oldcs;
 
-  Hashtable<SequenceGroup, ColourSchemeI> oldgroupColours;
+  private JButton defColours;
 
-  /**
-   * enabled if the user is dragging the slider - try to keep updates to a
-   * minimun
-   */
+  private Hashtable<SequenceGroup, ColourSchemeI> oldgroupColours;
 
-  JComboBox<String> annotations;
+  private JCheckBox useOriginalColours = new JCheckBox();
 
-  JButton defColours = new JButton();
+  private JPanel minColour = new JPanel();
 
-  JPanel jPanel1 = new JPanel();
+  private JPanel maxColour = new JPanel();
 
-  JPanel jPanel2 = new JPanel();
-
-  BorderLayout borderLayout1 = new BorderLayout();
-
-  private JComboBox<String> threshold = new JComboBox<String>();
+  private JCheckBox thresholdIsMin = new JCheckBox();
 
   public AnnotationColourChooser(AlignViewport av, final AlignmentPanel ap)
   {
@@ -108,7 +106,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     if (oldcs instanceof AnnotationColourGradient)
     {
       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
-      currentColours.setSelected(acg.isPredefinedColours()
+      useOriginalColours.setSelected(acg.isPredefinedColours()
               || acg.getBaseColour() != null);
       if (!acg.isPredefinedColours() && acg.getBaseColour() == null)
       {
@@ -118,15 +116,17 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       seqAssociated.setSelected(acg.isSeqAssociated());
 
     }
-    annotations = new JComboBox<String>(
-            getAnnotationItems(seqAssociated.isSelected()));
+    Vector<String> annotItems = getAnnotationItems(seqAssociated
+            .isSelected());
+    annotations = new JComboBox<String>(annotItems);
 
     populateThresholdComboBox(threshold);
 
     if (oldcs instanceof AnnotationColourGradient)
     {
       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
-      annotations.setSelectedItem(acg.getAnnotation());
+      String label = getAnnotationMenuLabel(acg.getAnnotation());
+      annotations.setSelectedItem(label);
       switch (acg.getAboveThreshold())
       {
       case AnnotationColourGradient.NO_THRESHOLD:
@@ -143,16 +143,11 @@ public class AnnotationColourChooser extends AnnotationRowFilter
                 MessageManager
                         .getString("error.implementation_error_dont_know_about_threshold_setting"));
       }
-      thresholdIsMin.setSelected(acg.thresholdIsMinMax);
+      thresholdIsMin.setSelected(acg.isThresholdIsMinMax());
       thresholdValue.setText("" + acg.getAnnotationThreshold());
     }
 
-    try
-    {
-      jbInit();
-    } catch (Exception ex)
-    {
-    }
+    jbInit();
     adjusting = false;
 
     updateView();
@@ -160,19 +155,11 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     frame.pack();
   }
 
-  public AnnotationColourChooser()
+  @Override
+  protected void jbInit()
   {
-    try
-    {
-      jbInit();
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
-    }
-  }
+    super.jbInit();
 
-  private void jbInit() throws Exception
-  {
     minColour.setFont(JvSwingUtils.getLabelFont());
     minColour.setBorder(BorderFactory.createEtchedBorder());
     minColour.setPreferredSize(new Dimension(40, 20));
@@ -203,26 +190,8 @@ public class AnnotationColourChooser extends AnnotationRowFilter
         }
       }
     });
-    ok.setOpaque(false);
-    ok.setText(MessageManager.getString("action.ok"));
-    ok.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        ok_actionPerformed();
-      }
-    });
-    cancel.setOpaque(false);
-    cancel.setText(MessageManager.getString("action.cancel"));
-    cancel.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        cancel_actionPerformed();
-      }
-    });
+
+    defColours = new JButton();
     defColours.setOpaque(false);
     defColours.setText(MessageManager.getString("action.set_defaults"));
     defColours.setToolTipText(MessageManager
@@ -237,48 +206,16 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       }
     });
 
-    annotations.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        annotations_actionPerformed();
-      }
-    });
-    getThreshold().addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        threshold_actionPerformed();
-      }
-    });
-    thresholdValue.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        thresholdValue_actionPerformed();
-      }
-    });
-    slider.setPaintLabels(false);
-    slider.setPaintTicks(true);
-    slider.setBackground(Color.white);
-    slider.setEnabled(false);
-    slider.setOpaque(false);
-    slider.setPreferredSize(new Dimension(100, 32));
-    thresholdValue.setEnabled(false);
-    thresholdValue.setColumns(7);
-    currentColours.setFont(JvSwingUtils.getLabelFont());
-    currentColours.setOpaque(false);
-    currentColours.setText(MessageManager
+    useOriginalColours.setFont(JvSwingUtils.getLabelFont());
+    useOriginalColours.setOpaque(false);
+    useOriginalColours.setText(MessageManager
             .getString("label.use_original_colours"));
-    currentColours.addActionListener(new ActionListener()
+    useOriginalColours.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        currentColours_actionPerformed();
+        originalColours_actionPerformed();
       }
     });
     thresholdIsMin.setBackground(Color.white);
@@ -307,7 +244,9 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       }
     });
 
-    this.setLayout(borderLayout1);
+    this.setLayout(new BorderLayout());
+    JPanel jPanel1 = new JPanel();
+    JPanel jPanel2 = new JPanel();
     jPanel2.setLayout(new MigLayout("", "[left][center][right]", "[][][]"));
     jPanel1.setBackground(Color.white);
     jPanel2.setBackground(Color.white);
@@ -316,7 +255,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     jPanel1.add(cancel);
     jPanel2.add(annotations, "grow, wrap");
     jPanel2.add(seqAssociated);
-    jPanel2.add(currentColours);
+    jPanel2.add(useOriginalColours);
     JPanel colpanel = new JPanel(new FlowLayout());
     colpanel.setBackground(Color.white);
     colpanel.add(minColour);
@@ -391,7 +330,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
   {
     if (slider.isEnabled())
     {
-      if (currentColours.isSelected()
+      if (useOriginalColours.isSelected()
               && !(av.getGlobalColourScheme() instanceof AnnotationColourGradient))
       {
         updateView();
@@ -403,24 +342,16 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     }
   }
 
-  public JComboBox<String> getThreshold()
+  public void originalColours_actionPerformed()
   {
-    return threshold;
-  }
-
-  public void setThreshold(JComboBox<String> threshold)
-  {
-    this.threshold = threshold;
-  }
-
-  public void currentColours_actionPerformed()
-  {
-    if (currentColours.isSelected())
+    boolean selected = useOriginalColours.isSelected();
+    if (selected)
     {
       reset();
     }
-    maxColour.setEnabled(!currentColours.isSelected());
-    minColour.setEnabled(!currentColours.isSelected());
+    maxColour.setEnabled(!selected);
+    minColour.setEnabled(!selected);
+    thresholdIsMin.setEnabled(!selected);
     updateView();
   }
 
@@ -441,7 +372,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
 
     slider.setEnabled(true);
     thresholdValue.setEnabled(true);
-    thresholdIsMin.setEnabled(true);
+    thresholdIsMin.setEnabled(!useOriginalColours.isSelected());
 
     if (selectedThresholdItem == AnnotationColourGradient.NO_THRESHOLD)
     {
@@ -455,7 +386,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     {
       getCurrentAnnotation()
               .setThreshold(
-                      new jalview.datamodel.GraphLine(
+                      new GraphLine(
                               (getCurrentAnnotation().graphMax - getCurrentAnnotation().graphMin) / 2f,
                               "Threshold", Color.black));
     }
@@ -463,19 +394,19 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD)
     {
       adjusting = true;
-      float range = getCurrentAnnotation().graphMax * 1000
-              - getCurrentAnnotation().graphMin * 1000;
+      float range = getCurrentAnnotation().graphMax * ONETHOUSAND
+              - getCurrentAnnotation().graphMin * ONETHOUSAND;
 
-      slider.setMinimum((int) (getCurrentAnnotation().graphMin * 1000));
-      slider.setMaximum((int) (getCurrentAnnotation().graphMax * 1000));
-      slider.setValue((int) (getCurrentAnnotation().threshold.value * 1000));
+      slider.setMinimum((int) (getCurrentAnnotation().graphMin * ONETHOUSAND));
+      slider.setMaximum((int) (getCurrentAnnotation().graphMax * ONETHOUSAND));
+      slider.setValue((int) (getCurrentAnnotation().threshold.value * ONETHOUSAND));
       thresholdValue.setText(getCurrentAnnotation().threshold.value + "");
       slider.setMajorTickSpacing((int) (range / 10f));
       slider.setEnabled(true);
       thresholdValue.setEnabled(true);
       adjusting = false;
     }
-    colorAlignmContaining(getCurrentAnnotation(), selectedThresholdItem);
+    colorAlignmentContaining(getCurrentAnnotation(), selectedThresholdItem);
 
     ap.alignmentChanged();
     // ensure all associated views (overviews, structures, etc) are notified of
@@ -483,4 +414,60 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     ap.paintAlignment(true);
   }
 
+  protected boolean colorAlignmentContaining(AlignmentAnnotation currentAnn, int selectedThresholdOption)
+  {
+  
+    AnnotationColourGradient acg = null;
+    if (useOriginalColours.isSelected())
+    {
+      acg = new AnnotationColourGradient(currentAnn,
+              av.getGlobalColourScheme(), selectedThresholdOption);
+    }
+    else
+    {
+      acg = new AnnotationColourGradient(currentAnn,
+              minColour.getBackground(), maxColour.getBackground(),
+              selectedThresholdOption);
+    }
+    acg.setSeqAssociated(seqAssociated.isSelected());
+  
+    if (currentAnn.graphMin == 0f && currentAnn.graphMax == 0f)
+    {
+      acg.setPredefinedColours(true);
+    }
+  
+    acg.setThresholdIsMinMax(thresholdIsMin.isSelected());
+  
+    av.setGlobalColourScheme(acg);
+  
+    if (av.getAlignment().getGroups() != null)
+    {
+  
+      for (SequenceGroup sg : ap.av.getAlignment().getGroups())
+      {
+        if (sg.cs == null)
+        {
+          continue;
+        }
+  
+        if (useOriginalColours.isSelected())
+        {
+          sg.setColourScheme(new AnnotationColourGradient(currentAnn, sg
+                  .getColourScheme(), selectedThresholdOption));
+          ((AnnotationColourGradient) sg.cs).setSeqAssociated(seqAssociated
+                  .isSelected());
+        }
+        else
+        {
+          sg.setColourScheme(new AnnotationColourGradient(currentAnn,
+                  minColour.getBackground(), maxColour.getBackground(),
+                  selectedThresholdOption));
+          ((AnnotationColourGradient) sg.cs).setSeqAssociated(seqAssociated
+                  .isSelected());
+        }
+      }
+    }
+    return false;
+  }
+
 }
index 1290d70..637eb30 100644 (file)
@@ -30,7 +30,6 @@ import jalview.viewmodel.annotationfilter.AnnotationFilterParameter;
 import java.awt.BorderLayout;
 import java.awt.CardLayout;
 import java.awt.Color;
-import java.awt.Dimension;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
@@ -55,28 +54,10 @@ import net.miginfocom.swing.MigLayout;
 public class AnnotationColumnChooser extends AnnotationRowFilter implements
         ItemListener
 {
-
-  private JComboBox<String> annotations;
-
-  private JPanel actionPanel = new JPanel();
-
-  private JPanel thresholdPanel = new JPanel();
-
   private JPanel switchableViewsPanel = new JPanel(new CardLayout());
 
-  private CardLayout switchableViewsLayout = (CardLayout) (switchableViewsPanel
-          .getLayout());
-
-  private JPanel noGraphFilterView = new JPanel();
-
-  private JPanel graphFilterView = new JPanel();
-
   private JPanel annotationComboBoxPanel = new JPanel();
 
-  private BorderLayout borderLayout1 = new BorderLayout();
-
-  private JComboBox<String> threshold = new JComboBox<String>();
-
   private StructureFilterPanel gStructureFilterPanel;
 
   private StructureFilterPanel ngStructureFilterPanel;
@@ -107,17 +88,6 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
 
   private ColumnSelection oldColumnSelection;
 
-  public AnnotationColumnChooser()
-  {
-    try
-    {
-      jbInit();
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
-    }
-  }
-
   public AnnotationColumnChooser(AlignViewport av, final AlignmentPanel ap)
   {
     super(av, ap);
@@ -169,72 +139,27 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     frame.pack();
   }
 
-  private void jbInit() throws Exception
+  @Override
+  protected void jbInit()
   {
-    ok.setOpaque(false);
-    ok.setText(MessageManager.getString("action.ok"));
-    ok.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        ok_actionPerformed();
-      }
-    });
-
-    cancel.setOpaque(false);
-    cancel.setText(MessageManager.getString("action.cancel"));
-    cancel.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        cancel_actionPerformed();
-      }
-    });
-
-    annotations.addItemListener(this);
-    annotations.setToolTipText(MessageManager
-            .getString("info.select_annotation_row"));
-    threshold.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        threshold_actionPerformed();
-      }
-    });
-
-    thresholdValue.setEnabled(false);
-    thresholdValue.setColumns(7);
-    thresholdValue.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        thresholdValue_actionPerformed();
-      }
-    });
-
-    slider.setPaintLabels(false);
-    slider.setPaintTicks(true);
-    slider.setBackground(Color.white);
-    slider.setEnabled(false);
-    slider.setOpaque(false);
-    slider.setPreferredSize(new Dimension(100, 32));
+    super.jbInit();
 
+    JPanel thresholdPanel = new JPanel();
     thresholdPanel.setBorder(new TitledBorder(MessageManager
             .getString("label.threshold_filter")));
     thresholdPanel.setBackground(Color.white);
     thresholdPanel.setFont(JvSwingUtils.getLabelFont());
     thresholdPanel.setLayout(new MigLayout("", "[left][right]", "[][]"));
 
+    JPanel actionPanel = new JPanel();
     actionPanel.setBackground(Color.white);
     actionPanel.setFont(JvSwingUtils.getLabelFont());
 
+    JPanel graphFilterView = new JPanel();
     graphFilterView.setLayout(new MigLayout("", "[left][right]", "[][]"));
     graphFilterView.setBackground(Color.white);
 
+    JPanel noGraphFilterView = new JPanel();
     noGraphFilterView.setLayout(new MigLayout("", "[left][right]", "[][]"));
     noGraphFilterView.setBackground(Color.white);
 
@@ -270,7 +195,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     switchableViewsPanel.add(graphFilterView,
             AnnotationColumnChooser.GRAPH_VIEW);
 
-    this.setLayout(borderLayout1);
+    this.setLayout(new BorderLayout());
     this.add(annotationComboBoxPanel, java.awt.BorderLayout.PAGE_START);
     this.add(switchableViewsPanel, java.awt.BorderLayout.CENTER);
     this.add(actionPanel, java.awt.BorderLayout.SOUTH);
@@ -280,7 +205,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     this.validate();
   }
 
-  public void updateThresholdPanelToolTip()
+  protected void updateThresholdPanelToolTip()
   {
     thresholdValue.setToolTipText("");
     slider.setToolTipText("");
@@ -297,7 +222,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
   }
 
   @Override
-  public void reset()
+  protected void reset()
   {
     if (this.getOldColumnSelection() != null)
     {
@@ -338,26 +263,6 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     }
   }
 
-  public JComboBox<String> getThreshold()
-  {
-    return threshold;
-  }
-
-  public void setThreshold(JComboBox<String> threshold)
-  {
-    this.threshold = threshold;
-  }
-
-  public JComboBox<String> getAnnotations()
-  {
-    return annotations;
-  }
-
-  public void setAnnotations(JComboBox<String> annotations)
-  {
-    this.annotations = annotations;
-  }
-
   @Override
   public void updateView()
   {
@@ -568,6 +473,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     selectedAnnotationChanged();
   }
 
+  @Override
   public void selectedAnnotationChanged()
   {
     String currentView = AnnotationColumnChooser.NO_GRAPH_VIEW;
@@ -585,6 +491,8 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     ngFurtherActionPanel.syncState();
     ngStructureFilterPanel.syncState();
 
+    CardLayout switchableViewsLayout = (CardLayout) switchableViewsPanel
+            .getLayout();
     switchableViewsLayout.show(switchableViewsPanel, currentView);
     updateView();
   }
index b1f0edb..84f3e6c 100755 (executable)
@@ -55,7 +55,6 @@ import java.util.List;
 
 import javax.swing.JColorChooser;
 import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.Scrollable;
@@ -708,7 +707,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       return;
     }
 
-    int column = (evt.getX() / av.getCharWidth()) + av.getStartRes();
+    int column = (evt.getX() / av.getCharWidth())
+            + av.getRanges().getStartRes();
 
     if (av.hasHiddenColumns())
     {
@@ -905,7 +905,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         return;
       }
     }
-    imgWidth = (av.endRes - av.startRes + 1) * av.getCharWidth();
+    imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes() + 1)
+            * av.getCharWidth();
     if (imgWidth < 1)
     {
       return;
@@ -946,7 +947,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       imageFresh = true;
     }
 
-    drawComponent(gg, av.startRes, av.endRes + 1);
+    drawComponent(gg, av.getRanges().getStartRes(), av.getRanges()
+            .getEndRes() + 1);
     imageFresh = false;
     g.drawImage(image, 0, 0, this);
   }
@@ -976,8 +978,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     gg.copyArea(0, 0, imgWidth, getHeight(),
             -horizontal * av.getCharWidth(), 0);
     long mtime = System.currentTimeMillis();
-    int sr = av.startRes;
-    int er = av.endRes + 1;
+    int sr = av.getRanges().getStartRes();
+    int er = av.getRanges().getEndRes() + 1;
     int transX = 0;
 
     if (horizontal > 0) // scrollbar pulled right, image to the left
index 166e1ad..c2bf41b 100644 (file)
@@ -22,12 +22,21 @@ package jalview.gui;
 
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.GraphLine;
-import jalview.datamodel.SequenceGroup;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.util.MessageManager;
 
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Vector;
 
 import javax.swing.JButton;
@@ -49,22 +58,10 @@ public abstract class AnnotationRowFilter extends JPanel
 
   protected int[] annmap;
 
-  protected boolean enableSeqAss = false;
-
-  private AlignmentAnnotation currentAnnotation;
-
   protected boolean adjusting = false;
 
-  protected JCheckBox currentColours = new JCheckBox();
-
-  protected JPanel minColour = new JPanel();
-
-  protected JPanel maxColour = new JPanel();
-
   protected JCheckBox seqAssociated = new JCheckBox();
 
-  protected JCheckBox thresholdIsMin = new JCheckBox();
-
   protected JSlider slider = new JSlider();
 
   protected JTextField thresholdValue = new JTextField(20);
@@ -81,6 +78,38 @@ public abstract class AnnotationRowFilter extends JPanel
    */
   protected boolean sliderDragging = false;
 
+  protected JComboBox<String> threshold = new JComboBox<String>();
+
+  protected JComboBox<String> annotations;
+
+  /*
+   * map from annotation to its menu item display label
+   * - so we know which item to pre-select on restore
+   */
+  private Map<AlignmentAnnotation, String> annotationLabels;
+
+  private AlignmentAnnotation currentAnnotation;
+
+  /**
+   * Constructor
+   * 
+   * @param viewport
+   * @param alignPanel
+   */
+  public AnnotationRowFilter(AlignViewport viewport, final AlignmentPanel alignPanel)
+  {
+    this.av = viewport;
+    this.ap = alignPanel;
+    thresholdValue.addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusLost(FocusEvent e)
+      {
+        thresholdValue_actionPerformed();
+      }
+    });
+  }
+
   protected void addSliderChangeListener()
   {
 
@@ -130,25 +159,27 @@ public abstract class AnnotationRowFilter extends JPanel
     });
   }
 
-  public AnnotationRowFilter(AlignViewport av, final AlignmentPanel ap)
-  {
-    this.av = av;
-    this.ap = ap;
-  }
-
-  public AnnotationRowFilter()
-  {
-
-  }
-
+/**
+ * Builds and returns a list of menu items (display text) for choice of
+ * annotation. Also builds maps between annotations, their positions in the
+ * list, and their display labels in the list.
+ * 
+ * @param isSeqAssociated
+ * @return
+ */
   public Vector<String> getAnnotationItems(boolean isSeqAssociated)
   {
+    annotationLabels = new HashMap<AlignmentAnnotation, String>();
+
     Vector<String> list = new Vector<String>();
     int index = 1;
     int[] anmap = new int[av.getAlignment().getAlignmentAnnotation().length];
+    seqAssociated.setEnabled(false);
     for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
     {
-      if (av.getAlignment().getAlignmentAnnotation()[i].sequenceRef == null)
+      AlignmentAnnotation annotation = av.getAlignment()
+              .getAlignmentAnnotation()[i];
+      if (annotation.sequenceRef == null)
       {
         if (isSeqAssociated)
         {
@@ -157,30 +188,29 @@ public abstract class AnnotationRowFilter extends JPanel
       }
       else
       {
-        enableSeqAss = true;
+        seqAssociated.setEnabled(true);
       }
-      String label = av.getAlignment().getAlignmentAnnotation()[i].label;
+      String label = annotation.label;
       // add associated sequence ID if available
-      if (!isSeqAssociated
-              && av.getAlignment().getAlignmentAnnotation()[i].sequenceRef != null)
+      if (!isSeqAssociated && annotation.sequenceRef != null)
       {
-        label = label
-                + "_"
-                + av.getAlignment().getAlignmentAnnotation()[i].sequenceRef
-                        .getName();
+        label = label + "_" + annotation.sequenceRef.getName();
       }
       // make label unique
       if (!list.contains(label))
       {
         anmap[list.size()] = i;
         list.add(label);
+        annotationLabels.put(annotation, label);
       }
       else
       {
         if (!isSeqAssociated)
         {
           anmap[list.size()] = i;
-          list.add(label + "_" + (index++));
+          label = label + "_" + (index++);
+          list.add(label);
+          annotationLabels.put(annotation, label);
         }
       }
     }
@@ -203,11 +233,6 @@ public abstract class AnnotationRowFilter extends JPanel
     return selectedThresholdItem;
   }
 
-  public void modelChanged()
-  {
-    seqAssociated.setEnabled(enableSeqAss);
-  }
-
   public void ok_actionPerformed()
   {
     try
@@ -230,22 +255,22 @@ public abstract class AnnotationRowFilter extends JPanel
     }
   }
 
-  public void thresholdCheck_actionPerformed()
+  protected void thresholdCheck_actionPerformed()
   {
     updateView();
   }
 
-  public void annotations_actionPerformed()
+  protected void selectedAnnotationChanged()
   {
     updateView();
   }
 
-  public void threshold_actionPerformed()
+  protected void threshold_actionPerformed()
   {
     updateView();
   }
 
-  public void thresholdValue_actionPerformed()
+  protected void thresholdValue_actionPerformed()
   {
     try
     {
@@ -257,27 +282,34 @@ public abstract class AnnotationRowFilter extends JPanel
     }
   }
 
-  public void thresholdIsMin_actionPerformed()
+  protected void thresholdIsMin_actionPerformed()
   {
     updateView();
   }
 
-  protected void populateThresholdComboBox(JComboBox<String> threshold)
+  protected void populateThresholdComboBox(JComboBox<String> thresh)
   {
-    threshold.addItem(MessageManager
+    thresh.addItem(MessageManager
             .getString("label.threshold_feature_no_threshold"));
-    threshold.addItem(MessageManager
+    thresh.addItem(MessageManager
             .getString("label.threshold_feature_above_threshold"));
-    threshold.addItem(MessageManager
+    thresh.addItem(MessageManager
             .getString("label.threshold_feature_below_threshold"));
   }
 
-  protected void seqAssociated_actionPerformed(JComboBox<String> annotations)
+  /**
+   * Rebuilds the drop-down list of annotations to choose from when the 'per
+   * sequence only' checkbox is checked or unchecked.
+   * 
+   * @param anns
+   */
+  protected void seqAssociated_actionPerformed(JComboBox<String> anns)
   {
     adjusting = true;
-    String cursel = (String) annotations.getSelectedItem();
-    boolean isvalid = false, isseqs = seqAssociated.isSelected();
-    annotations.removeAllItems();
+    String cursel = (String) anns.getSelectedItem();
+    boolean isvalid = false;
+    boolean isseqs = seqAssociated.isSelected();
+    anns.removeAllItems();
     for (String anitem : getAnnotationItems(seqAssociated.isSelected()))
     {
       if (anitem.equals(cursel) || (isseqs && cursel.startsWith(anitem)))
@@ -285,20 +317,22 @@ public abstract class AnnotationRowFilter extends JPanel
         isvalid = true;
         cursel = anitem;
       }
-      annotations.addItem(anitem);
+      anns.addItem(anitem);
     }
-    adjusting = false;
     if (isvalid)
     {
-      annotations.setSelectedItem(cursel);
+      anns.setSelectedItem(cursel);
     }
     else
     {
-      if (annotations.getItemCount() > 0)
+      if (anns.getItemCount() > 0)
       {
-        annotations.setSelectedIndex(0);
+        anns.setSelectedIndex(0);
       }
     }
+    adjusting = false;
+
+    updateView();
   }
 
   protected void propagateSeqAssociatedThreshold(boolean allAnnotation,
@@ -329,76 +363,107 @@ public abstract class AnnotationRowFilter extends JPanel
     }
   }
 
-  protected boolean colorAlignmContaining(AlignmentAnnotation currentAnn,
-          int selectedThresholdOption)
+  public AlignmentAnnotation getCurrentAnnotation()
   {
+    return currentAnnotation;
+  }
 
-    AnnotationColourGradient acg = null;
-    if (currentColours.isSelected())
-    {
-      acg = new AnnotationColourGradient(currentAnn,
-              av.getGlobalColourScheme(), selectedThresholdOption);
-    }
-    else
-    {
-      acg = new AnnotationColourGradient(currentAnn,
-              minColour.getBackground(), maxColour.getBackground(),
-              selectedThresholdOption);
-    }
-    acg.setSeqAssociated(seqAssociated.isSelected());
+  protected void setCurrentAnnotation(AlignmentAnnotation annotation)
+  {
+    this.currentAnnotation = annotation;
+  }
 
-    if (currentAnn.graphMin == 0f && currentAnn.graphMax == 0f)
-    {
-      acg.setPredefinedColours(true);
-    }
+  protected abstract void valueChanged(boolean updateAllAnnotation);
+
+  protected abstract void updateView();
 
-    acg.thresholdIsMinMax = thresholdIsMin.isSelected();
+  protected abstract void reset();
 
-    av.setGlobalColourScheme(acg);
+  protected String getAnnotationMenuLabel(AlignmentAnnotation ann)
+  {
+    return annotationLabels.get(ann);
+  }
 
-    if (av.getAlignment().getGroups() != null)
+  protected void jbInit()
+  {
+    ok.setOpaque(false);
+    ok.setText(MessageManager.getString("action.ok"));
+    ok.addActionListener(new ActionListener()
     {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        ok_actionPerformed();
+      }
+    });
 
-      for (SequenceGroup sg : ap.av.getAlignment().getGroups())
+    cancel.setOpaque(false);
+    cancel.setText(MessageManager.getString("action.cancel"));
+    cancel.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
       {
-        if (sg.cs == null)
-        {
-          continue;
-        }
+        cancel_actionPerformed();
+      }
+    });
 
-        AnnotationColourGradient scheme = null;
-        if (currentColours.isSelected())
-        {
-          scheme = new AnnotationColourGradient(currentAnn,
-                  sg.getColourScheme(), selectedThresholdOption);
-        }
-        else
-        {
-          scheme = new AnnotationColourGradient(currentAnn,
-                  minColour.getBackground(), maxColour.getBackground(),
-                  selectedThresholdOption);
-        }
-        scheme.setSeqAssociated(seqAssociated.isSelected());
-        sg.setColourScheme(scheme);
+    annotations.addItemListener(new ItemListener()
+    {
+      @Override
+      public void itemStateChanged(ItemEvent e)
+      {
+        selectedAnnotationChanged();
       }
-    }
-    return false;
+    });
+    annotations.setToolTipText(MessageManager
+            .getString("info.select_annotation_row"));
+
+    threshold.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        threshold_actionPerformed();
+      }
+    });
+
+    thresholdValue.setEnabled(false);
+    thresholdValue.setColumns(7);
+    thresholdValue.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        thresholdValue_actionPerformed();
+      }
+    });
+
+    slider.setPaintLabels(false);
+    slider.setPaintTicks(true);
+    slider.setBackground(Color.white);
+    slider.setEnabled(false);
+    slider.setOpaque(false);
+    slider.setPreferredSize(new Dimension(100, 32));
   }
 
-  public jalview.datamodel.AlignmentAnnotation getCurrentAnnotation()
+  public JComboBox<String> getThreshold()
   {
-    return currentAnnotation;
+    return threshold;
   }
 
-  public void setCurrentAnnotation(
-          jalview.datamodel.AlignmentAnnotation currentAnnotation)
+  public void setThreshold(JComboBox<String> thresh)
   {
-    this.currentAnnotation = currentAnnotation;
+    this.threshold = thresh;
   }
 
-  public abstract void valueChanged(boolean updateAllAnnotation);
-
-  public abstract void updateView();
+  public JComboBox<String> getAnnotations()
+  {
+    return annotations;
+  }
 
-  public abstract void reset();
+  public void setAnnotations(JComboBox<String> anns)
+  {
+    this.annotations = anns;
+  }
 }
index ffb9639..5d23f49 100644 (file)
@@ -45,7 +45,6 @@ import java.util.Vector;
 
 import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JInternalFrame;
-import javax.swing.JMenu;
 import javax.swing.JPanel;
 import javax.swing.JSplitPane;
 import javax.swing.SwingUtilities;
@@ -147,7 +146,7 @@ public class AppJmol extends StructureViewerBase
   {
     super.initMenus();
 
-    viewerActionMenu = new JMenu(MessageManager.getString("label.jmol"));
+    viewerActionMenu.setText(MessageManager.getString("label.jmol"));
 
     viewerColour
             .setText(MessageManager.getString("label.colour_with_jmol"));
index 75e0c5e..f822358 100644 (file)
@@ -40,8 +40,6 @@ public class AppJmolBinding extends JalviewJmolBinding
 {
   private AppJmol appJmolWindow;
 
-  private FeatureRenderer fr = null;
-
   public AppJmolBinding(AppJmol appJmol, StructureSelectionManager sSm,
           PDBEntry[] pdbentry, SequenceI[][] sequenceIs, DataSourceType protocol)
   {
@@ -50,26 +48,6 @@ public class AppJmolBinding extends JalviewJmolBinding
   }
 
   @Override
-  public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
-  {
-    AlignmentPanel ap = (alignment == null) ? appJmolWindow
-            .getAlignmentPanel() : (AlignmentPanel) alignment;
-    if (ap.av.isShowSequenceFeatures())
-    {
-      if (fr == null)
-      {
-        fr = (jalview.gui.FeatureRenderer) ap.cloneFeatureRenderer();
-      }
-      else
-      {
-        ap.updateFeatureRenderer(fr);
-      }
-    }
-
-    return fr;
-  }
-
-  @Override
   public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment)
   {
     return new SequenceRenderer(((AlignmentPanel) alignment).av);
@@ -215,4 +193,18 @@ public class AppJmolBinding extends JalviewJmolBinding
   {
     return appJmolWindow;
   }
+
+  @Override
+  public jalview.api.FeatureRenderer getFeatureRenderer(
+          AlignmentViewPanel alignment)
+  {
+    AlignmentPanel ap = (alignment == null) ? appJmolWindow
+            .getAlignmentPanel() : (AlignmentPanel) alignment;
+    if (ap.av.isShowSequenceFeatures())
+    {
+      return ap.av.getAlignPanel().getSeqPanel().seqCanvas.fr;
+    }
+
+    return null;
+  }
 }
index 530f4fe..ec9feb7 100644 (file)
  */
 package jalview.gui;
 
+import jalview.api.FeatureRenderer;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.ext.rbvi.chimera.ChimeraCommands;
 import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.DataSourceType;
@@ -35,16 +37,22 @@ import jalview.util.Platform;
 import jalview.ws.dbsources.Pdb;
 
 import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Random;
 
 import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JInternalFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 
@@ -58,8 +66,6 @@ public class ChimeraViewFrame extends StructureViewerBase
 {
   private JalviewChimeraBinding jmb;
 
-  private boolean allChainsSelected = false;
-
   private IProgressIndicator progressBar = null;
 
   /*
@@ -71,6 +77,10 @@ public class ChimeraViewFrame extends StructureViewerBase
 
   private Random random = new Random();
 
+  private int myWidth = 500;
+
+  private int myHeight = 150;
+
   /**
    * Initialise menu options.
    */
@@ -89,6 +99,100 @@ public class ChimeraViewFrame extends StructureViewerBase
     helpItem.setText(MessageManager.getString("label.chimera_help"));
     savemenu.setVisible(false); // not yet implemented
     viewMenu.add(fitToWindow);
+
+    JMenuItem writeFeatures = new JMenuItem(
+            MessageManager.getString("label.create_chimera_attributes"));
+    writeFeatures.setToolTipText(MessageManager
+            .getString("label.create_chimera_attributes_tip"));
+    writeFeatures.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        sendFeaturesToChimera();
+      }
+    });
+    viewerActionMenu.add(writeFeatures);
+
+    final JMenu fetchAttributes = new JMenu(
+            MessageManager.getString("label.fetch_chimera_attributes"));
+    fetchAttributes.setToolTipText(MessageManager
+            .getString("label.fetch_chimera_attributes_tip"));
+    fetchAttributes.addMouseListener(new MouseAdapter()
+    {
+
+      @Override
+      public void mouseEntered(MouseEvent e)
+      {
+        buildAttributesMenu(fetchAttributes);
+      }
+    });
+    viewerActionMenu.add(fetchAttributes);
+
+  }
+
+  /**
+   * Query Chimera for its residue attribute names and add them as items off the
+   * attributes menu
+   * 
+   * @param attributesMenu
+   */
+  protected void buildAttributesMenu(JMenu attributesMenu)
+  {
+    List<String> atts = jmb.sendChimeraCommand("list resattr", true);
+    if (atts == null)
+    {
+      return;
+    }
+    attributesMenu.removeAll();
+    Collections.sort(atts);
+    for (String att : atts)
+    {
+      final String attName = att.split(" ")[1];
+
+      /*
+       * ignore 'jv_*' attributes, as these are Jalview features that have
+       * been transferred to residue attributes in Chimera!
+       */
+      if (!attName.startsWith(ChimeraCommands.NAMESPACE_PREFIX))
+      {
+        JMenuItem menuItem = new JMenuItem(attName);
+        menuItem.addActionListener(new ActionListener()
+        {
+          @Override
+          public void actionPerformed(ActionEvent e)
+          {
+            getChimeraAttributes(attName);
+          }
+        });
+        attributesMenu.add(menuItem);
+      }
+    }
+  }
+
+  /**
+   * Read residues in Chimera with the given attribute name, and set as features
+   * on the corresponding sequence positions (if any)
+   * 
+   * @param attName
+   */
+  protected void getChimeraAttributes(String attName)
+  {
+    jmb.copyStructureAttributesToFeatures(attName, getAlignmentPanel());
+  }
+
+  /**
+   * Send a command to Chimera to create residue attributes for Jalview features
+   * <p>
+   * The syntax is: setattr r <attName> <attValue> <atomSpec>
+   * <p>
+   * For example: setattr r jv:chain "Ferredoxin-1, Chloroplastic" #0:94.A
+   */
+  protected void sendFeaturesToChimera()
+  {
+    int count = jmb.sendFeaturesToViewer(getAlignmentPanel());
+    statusBar.setText(MessageManager.formatMessage("label.attributes_set",
+            count));
   }
 
   /**
@@ -146,7 +250,6 @@ public class ChimeraViewFrame extends StructureViewerBase
           SequenceI[][] seqs)
   {
     createProgressBar();
-    // FIXME extractChains needs pdbentries to match IDs to PDBEntry(s) on seqs
     jmb = new JalviewChimeraBindingModel(this,
             ap.getStructureSelectionManager(), pdbentrys, seqs, null);
     addAlignmentPanel(ap);
@@ -158,7 +261,7 @@ public class ChimeraViewFrame extends StructureViewerBase
       useAlignmentPanelForSuperposition(ap);
     }
     jmb.setColourBySequence(true);
-    setSize(400, 400); // probably should be a configurable/dynamic default here
+    setSize(myWidth, myHeight);
     initMenus();
 
     addingStructures = false;
@@ -270,7 +373,7 @@ public class ChimeraViewFrame extends StructureViewerBase
   void initChimera()
   {
     jmb.setFinishedInit(false);
-    jalview.gui.Desktop.addInternalFrame(this,
+    Desktop.addInternalFrame(this,
             jmb.getViewerTitle(getViewerName(), true), getBounds().width,
             getBounds().height);
 
@@ -294,12 +397,10 @@ public class ChimeraViewFrame extends StructureViewerBase
                         + chimeraSessionFile);
       }
     }
-    jmb.setFinishedInit(true);
 
     jmb.startChimeraListener();
   }
 
-
   /**
    * Show only the selected chain(s) in the viewer
    */
@@ -450,6 +551,7 @@ public class ChimeraViewFrame extends StructureViewerBase
 
     if (files.length() > 0)
     {
+      jmb.setFinishedInit(false);
       if (!addingStructures)
       {
         try
@@ -507,10 +609,21 @@ public class ChimeraViewFrame extends StructureViewerBase
           }
         }
       }
+
       jmb.refreshGUI();
       jmb.setFinishedInit(true);
       jmb.setLoadingFromArchive(false);
 
+      /*
+       * ensure that any newly discovered features (e.g. RESNUM)
+       * are added to any open feature settings dialog
+       */
+      FeatureRenderer fr = getBinding().getFeatureRenderer(null);
+      if (fr != null)
+      {
+        fr.featuresAdded();
+      }
+
       // refresh the sequence colours for the new structure(s)
       for (AlignmentPanel ap : _colourwith)
       {
@@ -656,7 +769,7 @@ public class ChimeraViewFrame extends StructureViewerBase
     {
       BrowserLauncher
               .openURL("https://www.cgl.ucsf.edu/chimera/docs/UsersGuide");
-    } catch (Exception ex)
+    } catch (IOException ex)
     {
     }
   }
@@ -754,4 +867,20 @@ public class ChimeraViewFrame extends StructureViewerBase
   {
     return "Chimera";
   }
+
+  /**
+   * Sends commands to align structures according to associated alignment(s).
+   * 
+   * @return
+   */
+  @Override
+  protected String alignStructs_withAllAlignPanels()
+  {
+    String reply = super.alignStructs_withAllAlignPanels();
+    if (reply != null)
+    {
+      statusBar.setText("Superposition failed: " + reply);
+    }
+    return reply;
+  }
 }
index 31780d6..19ad939 100644 (file)
@@ -3,6 +3,7 @@ package jalview.gui;
 import jalview.bin.Cache;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeLoader;
 import jalview.schemes.ColourSchemes;
 import jalview.schemes.ResidueColourScheme;
 import jalview.schemes.UserColourScheme;
@@ -258,7 +259,7 @@ public class ColourMenuHelper
     {
       try
       {
-        UserColourScheme ucs = ColourSchemes.loadColourScheme(file);
+        UserColourScheme ucs = ColourSchemeLoader.loadColourScheme(file);
         if (ucs != null
                 && ColourSchemes.getInstance().nameExists(ucs.getName()))
         {
index 8c8f228..c5ec067 100644 (file)
@@ -453,7 +453,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     pane12.add(nametf, BorderLayout.EAST);
     panel.add(pane12, BorderLayout.NORTH);
     pane12 = new JPanel(new BorderLayout());
-    pane12.add(new JLabel(MessageManager.getString("label.url")),
+    pane12.add(new JLabel(MessageManager.getString("label.url:")),
             BorderLayout.NORTH);
     pane12.add(seqs, BorderLayout.SOUTH);
     pane12.add(urltf, BorderLayout.EAST);
index 0321662..dc16a57 100644 (file)
@@ -20,7 +20,6 @@
  */
 package jalview.gui;
 
-import static jalview.util.UrlConstants.EMBLEBI_STRING;
 import static jalview.util.UrlConstants.SEQUENCE_ID;
 
 import jalview.api.AlignViewportI;
@@ -39,11 +38,14 @@ import jalview.io.JalviewFileView;
 import jalview.jbgui.GSplitFrame;
 import jalview.jbgui.GStructureViewer;
 import jalview.structure.StructureSelectionManager;
+import jalview.urls.IdOrgSettings;
 import jalview.util.ImageMaker;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.util.UrlConstants;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.ParamManager;
+import jalview.ws.utils.UrlDownloadClient;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -78,6 +80,7 @@ import java.beans.PropertyChangeListener;
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Hashtable;
@@ -392,6 +395,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
     showNews.setVisible(false);
 
+    getIdentifiersOrgData();
+
     checkURLLinks();
 
     this.addWindowListener(new WindowAdapter()
@@ -525,6 +530,29 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     });
   }
 
+  public void getIdentifiersOrgData()
+  {
+    // Thread off the identifiers fetcher
+    addDialogThread(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        Cache.log.debug("Downloading data from identifiers.org");
+        UrlDownloadClient client = new UrlDownloadClient();
+        try
+        {
+          client.download(IdOrgSettings.getUrl(),
+                  IdOrgSettings.getDownloadLocation());
+        } catch (IOException e)
+        {
+          Cache.log.debug("Exception downloading identifiers.org data"
+                  + e.getMessage());
+        }
+      }
+    });
+  }
+
   @Override
   protected void showNews_actionPerformed(ActionEvent e)
   {
@@ -2290,7 +2318,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
         {
           // check what the actual links are - if it's just the default don't
           // bother with the warning
-          Vector<String> links = Preferences.sequenceURLLinks;
+          List<String> links = Preferences.sequenceUrlLinks
+                  .getLinksForMenu();
 
           // only need to check links if there is one with a
           // SEQUENCE_ID which is not the default EMBL_EBI link
@@ -2300,7 +2329,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
           while (li.hasNext())
           {
             String link = li.next();
-            if (link.contains(SEQUENCE_ID) && !link.equals(EMBLEBI_STRING))
+            if (link.contains(SEQUENCE_ID)
+                    && !link.equals(UrlConstants.DEFAULT_STRING))
             {
               check = true;
               int barPos = link.indexOf("|");
index ed6a3c5..f519f99 100644 (file)
@@ -46,7 +46,6 @@ import java.util.Comparator;
 import javax.swing.JColorChooser;
 import javax.swing.JComboBox;
 import javax.swing.JLabel;
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JSpinner;
@@ -61,8 +60,7 @@ import javax.swing.SwingConstants;
  * @version $Revision$
  */
 public class FeatureRenderer extends
-        jalview.renderer.seqfeatures.FeatureRenderer implements
-        jalview.api.FeatureRenderer
+        jalview.renderer.seqfeatures.FeatureRenderer
 {
   Color resBoxColour;
 
@@ -339,9 +337,6 @@ public class FeatureRenderer extends
 
     if (reply == JvOptionPane.OK_OPTION && name.getText().length() > 0)
     {
-      // This ensures that the last sequence
-      // is refreshed and new features are rendered
-      lastSeq = null;
       lastFeatureAdded = name.getText().trim();
       lastFeatureGroupAdded = source.getText().trim();
       lastDescriptionAdded = description.getText().replaceAll("\n", " ");
index 37be8bc..aad0776 100755 (executable)
@@ -21,6 +21,7 @@
 package jalview.gui;
 
 import jalview.datamodel.SequenceI;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -158,33 +159,35 @@ public class IdCanvas extends JPanel
       return;
     }
 
+    ViewportRanges ranges = av.getRanges();
+
     gg.copyArea(0, 0, getWidth(), imgHeight, 0,
             -vertical * av.getCharHeight());
 
-    int ss = av.startSeq;
-    int es = av.endSeq;
+    int ss = ranges.getStartSeq();
+    int es = ranges.getEndSeq();
     int transY = 0;
 
     if (vertical > 0) // scroll down
     {
       ss = es - vertical;
 
-      if (ss < av.startSeq)
+      if (ss < ranges.getStartSeq())
       { // ie scrolling too fast, more than a page at a time
-        ss = av.startSeq;
+        ss = ranges.getStartSeq();
       }
       else
       {
-        transY = imgHeight - (vertical * av.getCharHeight());
+        transY = imgHeight - ((vertical + 1) * av.getCharHeight());
       }
     }
-    else if (vertical < 0)
+    else if (vertical < 0) // scroll up
     {
       es = ss - vertical;
 
-      if (es > av.endSeq)
+      if (es > ranges.getEndSeq())
       {
-        es = av.endSeq;
+        es = ranges.getEndSeq();
       }
     }
 
@@ -240,7 +243,7 @@ public class IdCanvas extends JPanel
     gg.setColor(Color.white);
     gg.fillRect(0, 0, getWidth(), imgHeight);
 
-    drawIds(av.getStartSeq(), av.endSeq);
+    drawIds(av.getRanges().getStartSeq(), av.getRanges().getEndSeq());
 
     g.drawImage(image, 0, 0, this);
   }
@@ -314,10 +317,11 @@ public class IdCanvas extends JPanel
 
       int cHeight = alheight * av.getCharHeight() + hgap + annotationHeight;
 
-      int rowSize = av.getEndRes() - av.getStartRes();
+      int rowSize = av.getRanges().getEndRes()
+              - av.getRanges().getStartRes();
 
       // Draw the rest of the panels
-      for (int ypos = hgap, row = av.startRes; (ypos <= getHeight())
+      for (int ypos = hgap, row = av.getRanges().getStartRes(); (ypos <= getHeight())
               && (row < maxwidth); ypos += cHeight, row += rowSize)
       {
         for (int i = starty; i < alheight; i++)
@@ -354,7 +358,7 @@ public class IdCanvas extends JPanel
 
       SequenceI sequence;
       // Now draw the id strings
-      for (int i = starty; i < endy; i++)
+      for (int i = starty; i <= endy; i++)
       {
         sequence = av.getAlignment().getSequenceAt(i);
 
index 59d12d9..2074900 100755 (executable)
@@ -27,7 +27,6 @@ import jalview.datamodel.SequenceI;
 import jalview.io.SequenceAnnotationReport;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
-import jalview.util.UrlLink;
 import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.BorderLayout;
@@ -37,9 +36,7 @@ import java.awt.event.MouseMotionListener;
 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
 import java.util.List;
-import java.util.Vector;
 
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
@@ -199,56 +196,10 @@ public class IdPanel extends JPanel implements MouseListener,
       return;
     }
 
-    Vector links = Preferences.sequenceURLLinks;
-    if (links == null || links.size() < 1)
-    {
-      return;
-    }
-
     int seq = alignPanel.getSeqPanel().findSeq(e);
-    String url = null;
-    int i = 0;
     String id = av.getAlignment().getSequenceAt(seq).getName();
-    while (url == null && i < links.size())
-    {
-      // DEFAULT LINK IS FIRST IN THE LINK LIST
-      // BUT IF ITS A REGEX AND DOES NOT MATCH THE NEXT ONE WILL BE TRIED
-      url = links.elementAt(i++).toString();
-      jalview.util.UrlLink urlLink = null;
-      try
-      {
-        urlLink = new UrlLink(url);
-      } catch (Exception foo)
-      {
-        jalview.bin.Cache.log.error("Exception for URLLink '" + url + "'",
-                foo);
-        url = null;
-        continue;
-      }
+    String url = Preferences.sequenceUrlLinks.getPrimaryUrl(id);
 
-      if (urlLink.usesDBAccession())
-      {
-        // this URL requires an accession id, not the name of a sequence
-        url = null;
-        continue;
-      }
-
-      if (!urlLink.isValid())
-      {
-        jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
-        url = null;
-        continue;
-      }
-
-      String urls[] = urlLink.makeUrls(id, true);
-      if (urls == null || urls[0] == null || urls[0].length() < 4)
-      {
-        url = null;
-        continue;
-      }
-      // just take first URL made from regex
-      url = urls[1];
-    }
     try
     {
       jalview.util.BrowserLauncher.openURL(url);
@@ -291,13 +242,14 @@ public class IdPanel extends JPanel implements MouseListener,
       return;
     }
 
-    if (mouseDragging && (e.getY() < 0) && (av.getStartSeq() > 0))
+    if (mouseDragging && (e.getY() < 0)
+            && (av.getRanges().getStartSeq() > 0))
     {
       scrollThread = new ScrollThread(true);
     }
 
     if (mouseDragging && (e.getY() >= getHeight())
-            && (av.getAlignment().getHeight() > av.getEndSeq()))
+            && (av.getAlignment().getHeight() > av.getRanges().getEndSeq()))
     {
       scrollThread = new ScrollThread(false);
     }
@@ -375,7 +327,7 @@ public class IdPanel extends JPanel implements MouseListener,
     Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq2);
     // build a new links menu based on the current links + any non-positional
     // features
-    Vector<String> nlinks = new Vector<String>(Preferences.sequenceURLLinks);
+    List<String> nlinks = Preferences.sequenceUrlLinks.getLinksForMenu();
     SequenceFeature sfs[] = sq == null ? null : sq.getSequenceFeatures();
     if (sfs != null)
     {
@@ -387,7 +339,7 @@ public class IdPanel extends JPanel implements MouseListener,
           {
             for (int l = 0, lSize = sf.links.size(); l < lSize; l++)
             {
-              nlinks.addElement(sf.links.elementAt(l));
+              nlinks.add(sf.links.elementAt(l));
             }
           }
         }
@@ -491,9 +443,10 @@ public class IdPanel extends JPanel implements MouseListener,
     int index = av.getAlignment().findIndex(list.get(0));
 
     // do we need to scroll the panel?
-    if ((av.getStartSeq() > index) || (av.getEndSeq() < index))
+    if ((av.getRanges().getStartSeq() > index)
+            || (av.getRanges().getEndSeq() < index))
     {
-      alignPanel.setScrollValues(av.getStartRes(), index);
+      alignPanel.setScrollValues(av.getRanges().getStartRes(), index);
     }
   }
 
@@ -535,11 +488,11 @@ public class IdPanel extends JPanel implements MouseListener,
         if (alignPanel.scrollUp(up))
         {
           // scroll was ok, so add new sequence to selection
-          int seq = av.getStartSeq();
+          int seq = av.getRanges().getStartSeq();
 
           if (!up)
           {
-            seq = av.getEndSeq();
+            seq = av.getRanges().getEndSeq();
           }
 
           if (seq < lastid)
index 3ac453f..c19f005 100644 (file)
@@ -29,6 +29,7 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.GraphLine;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.RnaViewerModel;
 import jalview.datamodel.SequenceGroup;
@@ -77,7 +78,6 @@ import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.schemes.FeatureColour;
-import jalview.schemes.ResidueColourScheme;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
 import jalview.structure.StructureSelectionManager;
@@ -87,6 +87,7 @@ import jalview.util.Platform;
 import jalview.util.StringUtils;
 import jalview.util.jarInputStreamProvider;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
 import jalview.ws.jws2.Jws2Discoverer;
@@ -755,6 +756,7 @@ public class Jalview2XML
     List<UserColourScheme> userColours = new ArrayList<UserColourScheme>();
 
     AlignViewport av = ap.av;
+    ViewportRanges vpRanges = av.getRanges();
 
     JalviewModel object = new JalviewModel();
     object.setVamsasModel(new jalview.schemabinding.version2.VamsasModel());
@@ -1270,8 +1272,8 @@ public class Jalview2XML
       view.setWidth(size.width);
       view.setHeight(size.height);
 
-      view.setStartRes(av.startRes);
-      view.setStartSeq(av.startSeq);
+      view.setStartRes(vpRanges.getStartRes());
+      view.setStartSeq(vpRanges.getStartSeq());
 
       if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
       {
@@ -1711,6 +1713,15 @@ public class Jalview2XML
     return matchedFile;
   }
 
+  /**
+   * Populates the AnnotationColours xml for save. This captures the settings of
+   * the options in the 'Colour by Annotation' dialog.
+   * 
+   * @param acg
+   * @param userColours
+   * @param jms
+   * @return
+   */
   private AnnotationColours constructAnnotationColours(
           AnnotationColourGradient acg, List<UserColourScheme> userColours,
           JalviewModelSequence jms)
@@ -1718,8 +1729,9 @@ public class Jalview2XML
     AnnotationColours ac = new AnnotationColours();
     ac.setAboveThreshold(acg.getAboveThreshold());
     ac.setThreshold(acg.getAnnotationThreshold());
-    ac.setAnnotation(acg.getAnnotation());
-    if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
+    // 2.10.2 save annotationId (unique) not annotation label
+    ac.setAnnotation(acg.getAnnotation().annotationId);
+    if (acg.getBaseColour() instanceof UserColourScheme)
     {
       ac.setColourScheme(setUserColourScheme(acg.getBaseColour(),
               userColours, jms));
@@ -2639,10 +2651,12 @@ public class Jalview2XML
           @Override
           public void run()
           {
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-                    finalErrorMessage, "Error "
-                            + (saving ? "saving" : "loading")
-                            + " Jalview file", JvOptionPane.WARNING_MESSAGE);
+            JvOptionPane
+                    .showInternalMessageDialog(Desktop.desktop,
+                            finalErrorMessage, "Error "
+                                    + (saving ? "saving" : "loading")
+                                    + " Jalview file",
+                            JvOptionPane.WARNING_MESSAGE);
           }
         });
       }
@@ -4448,8 +4462,8 @@ public class Jalview2XML
     af.viewport.setThresholdTextColour(view.getTextColThreshold());
     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
             .isShowUnconserved() : false);
-    af.viewport.setStartRes(view.getStartRes());
-    af.viewport.setStartSeq(view.getStartSeq());
+    af.viewport.getRanges().setStartRes(view.getStartRes());
+    af.viewport.getRanges().setStartSeq(view.getStartSeq());
     af.alignPanel.updateLayout();
     ColourSchemeI cs = null;
     // apply colourschemes
@@ -4689,12 +4703,21 @@ public class Jalview2XML
     return af;
   }
 
+  /**
+   * Reads saved data to restore Colour by Annotation settings
+   * 
+   * @param viewAnnColour
+   * @param af
+   * @param al
+   * @param jms
+   * @param checkGroupAnnColour
+   * @return
+   */
   private ColourSchemeI constructAnnotationColour(
           AnnotationColours viewAnnColour, AlignFrame af, AlignmentI al,
           JalviewModelSequence jms, boolean checkGroupAnnColour)
   {
     boolean propagateAnnColour = false;
-    ColourSchemeI cs = null;
     AlignmentI annAlignment = af != null ? af.viewport.getAlignment() : al;
     if (checkGroupAnnColour && al.getGroups() != null
             && al.getGroups().size() > 0)
@@ -4702,7 +4725,7 @@ public class Jalview2XML
       // pre 2.8.1 behaviour
       // check to see if we should transfer annotation colours
       propagateAnnColour = true;
-      for (jalview.datamodel.SequenceGroup sg : al.getGroups())
+      for (SequenceGroup sg : al.getGroups())
       {
         if (sg.getColourScheme() instanceof AnnotationColourGradient)
         {
@@ -4710,107 +4733,84 @@ public class Jalview2XML
         }
       }
     }
-    // int find annotation
-    if (annAlignment.getAlignmentAnnotation() != null)
+
+    /*
+     * 2.10.2- : saved annotationId is AlignmentAnnotation.annotationId
+     */
+    String annotationId = viewAnnColour.getAnnotation();
+    AlignmentAnnotation matchedAnnotation = annotationIds.get(annotationId);
+
+    /*
+     * pre 2.10.2: saved annotationId is AlignmentAnnotation.label
+     */
+    if (matchedAnnotation == null && annAlignment.getAlignmentAnnotation() != null)
     {
       for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
       {
-        if (annAlignment.getAlignmentAnnotation()[i].label
-                .equals(viewAnnColour.getAnnotation()))
+        if (annotationId
+                .equals(annAlignment.getAlignmentAnnotation()[i].label))
         {
-          if (annAlignment.getAlignmentAnnotation()[i].getThreshold() == null)
-          {
-            annAlignment.getAlignmentAnnotation()[i]
-                    .setThreshold(new jalview.datamodel.GraphLine(
-                            viewAnnColour.getThreshold(), "Threshold",
-                            java.awt.Color.black)
-
-                    );
-          }
-
-          if (viewAnnColour.getColourScheme().equals(
-                  ResidueColourScheme.NONE))
-          {
-            cs = new AnnotationColourGradient(
-                    annAlignment.getAlignmentAnnotation()[i],
-                    new java.awt.Color(viewAnnColour.getMinColour()),
-                    new java.awt.Color(viewAnnColour.getMaxColour()),
-                    viewAnnColour.getAboveThreshold());
-          }
-          else if (viewAnnColour.getColourScheme().startsWith("ucs"))
-          {
-            cs = new AnnotationColourGradient(
-                    annAlignment.getAlignmentAnnotation()[i],
-                    getUserColourScheme(jms,
-                            viewAnnColour.getColourScheme()),
-                    viewAnnColour.getAboveThreshold());
-          }
-          else
-          {
-            cs = new AnnotationColourGradient(
-                    annAlignment.getAlignmentAnnotation()[i],
-                    ColourSchemeProperty.getColourScheme(al,
-                            viewAnnColour.getColourScheme()),
-                    viewAnnColour.getAboveThreshold());
-          }
-          if (viewAnnColour.hasPerSequence())
-          {
-            ((AnnotationColourGradient) cs).setSeqAssociated(viewAnnColour
-                    .isPerSequence());
-          }
-          if (viewAnnColour.hasPredefinedColours())
-          {
-            ((AnnotationColourGradient) cs)
-                    .setPredefinedColours(viewAnnColour
-                            .isPredefinedColours());
-          }
-          if (propagateAnnColour && al.getGroups() != null)
-          {
-            // Also use these settings for all the groups
-            for (int g = 0; g < al.getGroups().size(); g++)
-            {
-              jalview.datamodel.SequenceGroup sg = al.getGroups().get(g);
-
-              if (sg.cs == null)
-              {
-                continue;
-              }
+          matchedAnnotation = annAlignment.getAlignmentAnnotation()[i];
+          break;
+        }
+      }
+    }
+    if (matchedAnnotation == null)
+    {
+      System.err.println("Failed to match annotation colour scheme for "
+              + annotationId);
+      return null;
+    }
+    if (matchedAnnotation.getThreshold() == null)
+    {
+      matchedAnnotation.setThreshold(new GraphLine(viewAnnColour.getThreshold(),
+              "Threshold", Color.black));
+    }
 
-              /*
-               * if (viewAnnColour.getColourScheme().equals(ResidueColourScheme.NONE)) { sg.cs =
-               * new AnnotationColourGradient(
-               * annAlignment.getAlignmentAnnotation()[i], new
-               * java.awt.Color(viewAnnColour. getMinColour()), new
-               * java.awt.Color(viewAnnColour. getMaxColour()),
-               * viewAnnColour.getAboveThreshold()); } else
-               */
-              {
-                sg.setColourScheme(new AnnotationColourGradient(
-                        annAlignment.getAlignmentAnnotation()[i], sg
-                                .getColourScheme(), viewAnnColour
-                                .getAboveThreshold()));
-                if (cs instanceof AnnotationColourGradient)
-                {
-                  if (viewAnnColour.hasPerSequence())
-                  {
-                    ((AnnotationColourGradient) cs)
-                            .setSeqAssociated(viewAnnColour.isPerSequence());
-                  }
-                  if (viewAnnColour.hasPredefinedColours())
-                  {
-                    ((AnnotationColourGradient) cs)
-                            .setPredefinedColours(viewAnnColour
-                                    .isPredefinedColours());
-                  }
-                }
-              }
+    AnnotationColourGradient cs = null;
+    if (viewAnnColour.getColourScheme().equals("None"))
+    {
+      cs = new AnnotationColourGradient(matchedAnnotation, new Color(
+              viewAnnColour.getMinColour()), new Color(
+              viewAnnColour.getMaxColour()),
+              viewAnnColour.getAboveThreshold());
+    }
+    else if (viewAnnColour.getColourScheme().startsWith("ucs"))
+    {
+      cs = new AnnotationColourGradient(matchedAnnotation, getUserColourScheme(
+              jms, viewAnnColour.getColourScheme()),
+              viewAnnColour.getAboveThreshold());
+    }
+    else
+    {
+      cs = new AnnotationColourGradient(matchedAnnotation,
+              ColourSchemeProperty.getColourScheme(al,
+                      viewAnnColour.getColourScheme()),
+              viewAnnColour.getAboveThreshold());
+    }
 
-            }
-          }
+    boolean perSequenceOnly = viewAnnColour.isPerSequence();
+    boolean useOriginalColours = viewAnnColour.isPredefinedColours();
+    cs.setSeqAssociated(perSequenceOnly);
+    cs.setPredefinedColours(useOriginalColours);
 
-          break;
+    if (propagateAnnColour && al.getGroups() != null)
+    {
+      // Also use these settings for all the groups
+      for (int g = 0; g < al.getGroups().size(); g++)
+      {
+        SequenceGroup sg = al.getGroups().get(g);
+        if (sg.getGroupColourScheme() == null)
+        {
+          continue;
         }
 
+        AnnotationColourGradient groupScheme = new AnnotationColourGradient(
+                matchedAnnotation, sg.getColourScheme(),
+                viewAnnColour.getAboveThreshold());
+        sg.setColourScheme(groupScheme);
+        groupScheme.setSeqAssociated(perSequenceOnly);
+        groupScheme.setPredefinedColours(useOriginalColours);
       }
     }
     return cs;
index e751a2c..6235cbe 100755 (executable)
@@ -367,8 +367,8 @@ public class Jalview2XML_V1
 
     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
             view.getHeight());
-    af.viewport.setStartRes(view.getStartRes());
-    af.viewport.setStartSeq(view.getStartSeq());
+    af.viewport.getRanges().setStartRes(view.getStartRes());
+    af.viewport.getRanges().setStartSeq(view.getStartSeq());
     af.viewport.setShowAnnotation(view.getShowAnnotation());
     af.viewport.setAbovePIDThreshold(view.getPidSelected());
     af.viewport.setColourText(view.getShowColourText());
index a797872..c9b35d8 100644 (file)
@@ -28,13 +28,12 @@ import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
 import jalview.io.DataSourceType;
 import jalview.structure.StructureSelectionManager;
 
+import javax.swing.SwingUtilities;
+
 public class JalviewChimeraBindingModel extends JalviewChimeraBinding
 {
   private ChimeraViewFrame cvf;
 
-  private FeatureRenderer fr = null;
-
-
   public JalviewChimeraBindingModel(ChimeraViewFrame chimeraViewFrame,
           StructureSelectionManager ssm, PDBEntry[] pdbentry,
           SequenceI[][] sequenceIs, DataSourceType protocol)
@@ -50,17 +49,10 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
             : (AlignmentPanel) alignment;
     if (ap.av.isShowSequenceFeatures())
     {
-      if (fr == null)
-      {
-        fr = (jalview.gui.FeatureRenderer) ap.cloneFeatureRenderer();
-      }
-      else
-      {
-        ap.updateFeatureRenderer(fr);
-      }
+      return ap.getSeqPanel().seqCanvas.fr;
     }
 
-    return fr;
+    return null;
   }
 
   @Override
@@ -122,24 +114,25 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
   protected void sendAsynchronousCommand(final String command,
           final String progressMsg)
   {
-    Thread thread = new Thread(new Runnable()
+    final long handle = progressMsg == null ? 0 : cvf
+            .startProgressBar(progressMsg);
+    SwingUtilities.invokeLater(new Runnable()
     {
-
       @Override
       public void run()
       {
-        long stm = cvf.startProgressBar(progressMsg);
         try
         {
           sendChimeraCommand(command, false);
         } finally
         {
-          cvf.stopProgressBar(null, stm);
+          if (progressMsg != null)
+          {
+            cvf.stopProgressBar(null, handle);
+          }
         }
       }
     });
-    thread.start();
-
   }
 
   @Override
index 1c48690..c530fdc 100755 (executable)
  */
 package jalview.gui;
 
+import jalview.datamodel.SequenceI;
 import jalview.renderer.AnnotationRenderer;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
+import jalview.viewmodel.OverviewDimensions;
 
 import java.awt.Color;
 import java.awt.Dimension;
@@ -35,57 +38,51 @@ import java.awt.image.BufferedImage;
 import javax.swing.JPanel;
 
 /**
- * DOCUMENT ME!
+ * Panel displaying an overview of the full alignment, with an interactive box
+ * representing the viewport onto the alignment.
  * 
  * @author $author$
  * @version $Revision$
  */
 public class OverviewPanel extends JPanel implements Runnable
 {
-  BufferedImage miniMe;
+  private static final Color TRANS_GREY = new Color(100, 100, 100, 25);
 
-  AlignViewport av;
+  private final AnnotationRenderer renderer = new AnnotationRenderer();
 
-  AlignmentPanel ap;
+  private OverviewDimensions od;
 
-  final AnnotationRenderer renderer = new AnnotationRenderer();
+  private BufferedImage miniMe;
 
-  float scalew = 1f;
-
-  float scaleh = 1f;
-
-  int width;
-
-  int sequencesHeight;
-
-  int graphHeight = 20;
-
-  int boxX = -1;
+  private BufferedImage lastMiniMe = null;
 
-  int boxY = -1;
+  private AlignViewport av;
 
-  int boxWidth = -1;
+  private AlignmentPanel ap;
 
-  int boxHeight = -1;
+  //
+  private boolean resizing = false;
 
-  boolean resizing = false;
+  // This is set true if the user resizes whilst
+  // the overview is being calculated
+  private boolean resizeAgain = false;
 
   // Can set different properties in this seqCanvas than
   // main visible SeqCanvas
-  SequenceRenderer sr;
+  private SequenceRenderer sr;
 
   jalview.renderer.seqfeatures.FeatureRenderer fr;
 
   /**
    * Creates a new OverviewPanel object.
    * 
-   * @param ap
-   *          DOCUMENT ME!
+   * @param alPanel
+   *          The alignment panel which is shown in the overview panel
    */
-  public OverviewPanel(AlignmentPanel ap)
+  public OverviewPanel(AlignmentPanel alPanel)
   {
-    this.av = ap.av;
-    this.ap = ap;
+    this.av = alPanel.av;
+    this.ap = alPanel;
     setLayout(null);
 
     sr = new SequenceRenderer(av);
@@ -93,44 +90,17 @@ public class OverviewPanel extends JPanel implements Runnable
     sr.forOverview = true;
     fr = new FeatureRenderer(ap);
 
-    // scale the initial size of overviewpanel to shape of alignment
-    float initialScale = (float) av.getAlignment().getWidth()
-            / (float) av.getAlignment().getHeight();
-
-    if (av.getAlignmentConservationAnnotation() == null)
-    {
-      graphHeight = 0;
-    }
-
-    if (av.getAlignment().getWidth() > av.getAlignment().getHeight())
-    {
-      // wider
-      width = 400;
-      sequencesHeight = (int) (400f / initialScale);
-      if (sequencesHeight < 40)
-      {
-        sequencesHeight = 40;
-      }
-    }
-    else
-    {
-      // taller
-      width = (int) (400f * initialScale);
-      sequencesHeight = 300;
-
-      if (width < 120)
-      {
-        width = 120;
-      }
-    }
+    od = new OverviewDimensions(av.getRanges(),
+            (av.isShowAnnotation() && av
+                    .getAlignmentConservationAnnotation() != null));
 
     addComponentListener(new ComponentAdapter()
     {
       @Override
       public void componentResized(ComponentEvent evt)
       {
-        if ((getWidth() != width)
-                || (getHeight() != (sequencesHeight + graphHeight)))
+        if ((getWidth() != od.getWidth())
+                || (getHeight() != (od.getHeight())))
         {
           updateOverviewImage();
         }
@@ -144,11 +114,10 @@ public class OverviewPanel extends JPanel implements Runnable
       {
         if (!av.getWrapAlignment())
         {
-          // TODO: feature: jv2.5 detect shift drag and update selection from
-          // it.
-          boxX = evt.getX();
-          boxY = evt.getY();
-          checkValid();
+          od.updateViewportFromMouse(evt.getX(), evt.getY(), av
+                  .getAlignment().getHiddenSequences(), av
+                  .getColumnSelection(), av.getRanges());
+          ap.setScrollValues(od.getScrollCol(), od.getScrollRow());
         }
       }
     });
@@ -160,9 +129,10 @@ public class OverviewPanel extends JPanel implements Runnable
       {
         if (!av.getWrapAlignment())
         {
-          boxX = evt.getX();
-          boxY = evt.getY();
-          checkValid();
+          od.updateViewportFromMouse(evt.getX(), evt.getY(), av
+                  .getAlignment().getHiddenSequences(), av
+                  .getColumnSelection(), av.getRanges());
+          ap.setScrollValues(od.getScrollCol(), od.getScrollRow());
         }
       }
     });
@@ -171,60 +141,7 @@ public class OverviewPanel extends JPanel implements Runnable
   }
 
   /**
-   * DOCUMENT ME!
-   */
-  void checkValid()
-  {
-    if (boxY < 0)
-    {
-      boxY = 0;
-    }
-
-    if (boxY > (sequencesHeight - boxHeight))
-    {
-      boxY = sequencesHeight - boxHeight + 1;
-    }
-
-    if (boxX < 0)
-    {
-      boxX = 0;
-    }
-
-    if (boxX > (width - boxWidth))
-    {
-      if (av.hasHiddenColumns())
-      {
-        // Try smallest possible box
-        boxWidth = (int) ((av.endRes - av.startRes + 1) * av.getCharWidth() * scalew);
-      }
-      boxX = width - boxWidth;
-    }
-
-    int col = (int) (boxX / scalew / av.getCharWidth());
-    int row = (int) (boxY / scaleh / av.getCharHeight());
-
-    if (av.hasHiddenColumns())
-    {
-      if (!av.getColumnSelection().isVisible(col))
-      {
-        return;
-      }
-
-      col = av.getColumnSelection().findColumnPosition(col);
-    }
-
-    if (av.hasHiddenRows())
-    {
-      row = av.getAlignment().getHiddenSequences()
-              .findIndexWithoutHiddenSeqs(row);
-    }
-
-    ap.setScrollValues(col, row);
-
-  }
-
-  /**
-   * DOCUMENT ME!
+   * Updates the overview image when the related alignment panel is updated
    */
   public void updateOverviewImage()
   {
@@ -238,24 +155,17 @@ public class OverviewPanel extends JPanel implements Runnable
 
     if ((getWidth() > 0) && (getHeight() > 0))
     {
-      width = getWidth();
-      sequencesHeight = getHeight() - graphHeight;
+      od.setWidth(getWidth());
+      od.setHeight(getHeight());
     }
 
-    setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));
+    setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
 
     Thread thread = new Thread(this);
     thread.start();
     repaint();
   }
 
-  // This is set true if the user resizes whilst
-  // the overview is being calculated
-  boolean resizeAgain = false;
-
-  /**
-   * DOCUMENT ME!
-   */
   @Override
   public void run()
   {
@@ -266,148 +176,47 @@ public class OverviewPanel extends JPanel implements Runnable
       fr.transferSettings(ap.getSeqPanel().seqCanvas.getFeatureRenderer());
     }
 
-    int alwidth = av.getAlignment().getWidth();
-    int alheight = av.getAlignment().getHeight()
-            + av.getAlignment().getHiddenSequences().getSize();
-
-    setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));
-
-    int fullsizeWidth = alwidth * av.getCharWidth();
-    int fullsizeHeight = alheight * av.getCharHeight();
+    // why do we need to set preferred size again? was set in
+    // updateOverviewImage
+    setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
 
-    scalew = (float) width / (float) fullsizeWidth;
-    scaleh = (float) sequencesHeight / (float) fullsizeHeight;
-
-    miniMe = new BufferedImage(width, sequencesHeight + graphHeight,
+    miniMe = new BufferedImage(od.getWidth(), od.getHeight(),
             BufferedImage.TYPE_INT_RGB);
 
     Graphics mg = miniMe.getGraphics();
     mg.setColor(Color.orange);
-    mg.fillRect(0, 0, width, miniMe.getHeight());
-
-    float sampleCol = (float) alwidth / (float) width;
-    float sampleRow = (float) alheight / (float) sequencesHeight;
+    mg.fillRect(0, 0, od.getWidth(), miniMe.getHeight());
 
-    int lastcol = -1, lastrow = -1;
-    int color = Color.white.getRGB();
-    int row, col;
-    jalview.datamodel.SequenceI seq;
-    final boolean hasHiddenRows = av.hasHiddenRows(), hasHiddenCols = av
-            .hasHiddenColumns();
-    boolean hiddenRow = false;
-    // get hidden row and hidden column map once at beginning.
-    // clone featureRenderer settings to avoid race conditions... if state is
-    // updated just need to refresh again
-    for (row = 0; row < sequencesHeight; row++)
-    {
-      if (resizeAgain)
-      {
-        break;
-      }
-      if ((int) (row * sampleRow) == lastrow)
-      {
-        // No need to recalculate the colours,
-        // Just copy from the row above
-        for (col = 0; col < width; col++)
-        {
-          if (resizeAgain)
-          {
-            break;
-          }
-          miniMe.setRGB(col, row, miniMe.getRGB(col, row - 1));
-        }
-        continue;
-      }
-
-      lastrow = (int) (row * sampleRow);
-
-      hiddenRow = false;
-      if (hasHiddenRows)
-      {
-        seq = av.getAlignment().getHiddenSequences()
-                .getHiddenSequence(lastrow);
-        if (seq == null)
-        {
-          int index = av.getAlignment().getHiddenSequences()
-                  .findIndexWithoutHiddenSeqs(lastrow);
-
-          seq = av.getAlignment().getSequenceAt(index);
-        }
-        else
-        {
-          hiddenRow = true;
-        }
-      }
-      else
-      {
-        seq = av.getAlignment().getSequenceAt(lastrow);
-      }
-
-      if (seq == null)
-      {
-        System.out.println(lastrow + " null");
-        continue;
-      }
-
-      for (col = 0; col < width; col++)
-      {
-        if (resizeAgain)
-        {
-          break;
-        }
-        if ((int) (col * sampleCol) == lastcol
-                && (int) (row * sampleRow) == lastrow)
-        {
-          miniMe.setRGB(col, row, color);
-          continue;
-        }
-
-        lastcol = (int) (col * sampleCol);
-
-        if (seq.getLength() > lastcol)
-        {
-          color = sr.getResidueBoxColour(seq, lastcol).getRGB();
-
-          if (av.isShowSequenceFeatures())
-          {
-            color = fr.findFeatureColour(color, seq, lastcol);
-          }
-        }
-        else
-        {
-          color = -1; // White
-        }
+    // calculate sampleCol and sampleRow
+    // alignment width is max number of residues/bases
+    // alignment height is number of sequences
+    int alwidth = av.getAlignment().getWidth();
+    int alheight = av.getAlignment().getAbsoluteHeight();
 
-        if (hiddenRow
-                || (hasHiddenCols && !av.getColumnSelection().isVisible(
-                        lastcol)))
-        {
-          color = new Color(color).darker().darker().getRGB();
-        }
+    // sampleCol or sampleRow is the width/height allocated to each residue
+    // in particular, sometimes we may need more than one row/col of the
+    // BufferedImage allocated
+    // sampleCol is how much of a residue to assign to each pixel
+    // sampleRow is how many sequences to assign to each pixel
+    float sampleCol = alwidth / (float) od.getWidth();
+    float sampleRow = alheight / (float) od.getSequencesHeight();
 
-        miniMe.setRGB(col, row, color);
+    buildImage(sampleRow, sampleCol);
 
-      }
-    }
-
-    if (av.getAlignmentConservationAnnotation() != null)
+    // check for conservation annotation to make sure overview works for DNA too
+    if (av.isShowAnnotation()
+            && (av.getAlignmentConservationAnnotation() != null))
     {
       renderer.updateFromAlignViewport(av);
-      for (col = 0; col < width; col++)
+      for (int col = 0; col < od.getWidth() && !resizeAgain; col++)
       {
-        if (resizeAgain)
-        {
-          break;
-        }
-        lastcol = (int) (col * sampleCol);
-        {
-          mg.translate(col, sequencesHeight);
-          renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(),
-                  av.getAlignmentConservationAnnotation().annotations,
-                  (int) (sampleCol) + 1, graphHeight,
-                  (int) (col * sampleCol), (int) (col * sampleCol) + 1);
-          mg.translate(-col, -sequencesHeight);
-        }
+        mg.translate(col, od.getSequencesHeight());
+        renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(),
+                av.getAlignmentConservationAnnotation().annotations,
+                (int) (sampleCol) + 1, od.getGraphHeight(),
+                (int) (col * sampleCol), (int) (col * sampleCol) + 1);
+        mg.translate(-col, -od.getSequencesHeight());
+
       }
     }
     System.gc();
@@ -427,66 +236,97 @@ public class OverviewPanel extends JPanel implements Runnable
     setBoxPosition();
   }
 
-  /**
-   * DOCUMENT ME!
+  /*
+   * Build the overview panel image
    */
-  public void setBoxPosition()
+  private void buildImage(float sampleRow, float sampleCol)
   {
-    int fullsizeWidth = av.getAlignment().getWidth() * av.getCharWidth();
-    int fullsizeHeight = (av.getAlignment().getHeight() + av.getAlignment()
-            .getHiddenSequences().getSize())
-            * av.getCharHeight();
+    int lastcol = -1;
+    int lastrow = -1;
+    int rgbColour = Color.white.getRGB();
 
-    int startRes = av.getStartRes();
-    int endRes = av.getEndRes();
+    SequenceI seq = null;
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
 
-    if (av.hasHiddenColumns())
+    final boolean hasHiddenCols = av.hasHiddenColumns();
+    boolean hiddenRow = false;
+    // get hidden row and hidden column map once at beginning.
+    // clone featureRenderer settings to avoid race conditions... if state is
+    // updated just need to refresh again
+    for (int row = 0; row < od.getSequencesHeight() && !resizeAgain; row++)
     {
-      startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);
-      endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);
-    }
+      boolean doCopy = true;
+      int currentrow = (int) (row * sampleRow);
+      if (currentrow != lastrow)
+      {
+        doCopy = false;
 
-    int startSeq = av.startSeq;
-    int endSeq = av.endSeq;
+        lastrow = currentrow;
 
-    if (av.hasHiddenRows())
-    {
-      startSeq = av.getAlignment().getHiddenSequences()
-              .adjustForHiddenSeqs(startSeq);
+        // get the sequence which would be at alignment index 'lastrow' if no
+        // rows were hidden, and determine whether it is hidden or not
+        hiddenRow = av.getAlignment().isHidden(lastrow);
+        seq = av.getAlignment().getSequenceAtAbsoluteIndex(lastrow);
+      }
 
-      endSeq = av.getAlignment().getHiddenSequences()
-              .adjustForHiddenSeqs(endSeq);
+      for (int col = 0; col < od.getWidth() && !resizeAgain; col++)
+      {
+        if (doCopy)
+        {
+          rgbColour = miniMe.getRGB(col, row - 1);
+        }
+        else if ((int) (col * sampleCol) != lastcol
+                || (int) (row * sampleRow) != lastrow)
+        {
+          lastcol = (int) (col * sampleCol);
+          rgbColour = getColumnColourFromSequence(seq, hiddenRow,
+                  hasHiddenCols, lastcol, finder);
+        }
+        // else we just use the color we already have , so don't need to set it
 
+        miniMe.setRGB(col, row, rgbColour);
+      }
     }
+  }
 
-    scalew = (float) width / (float) fullsizeWidth;
-    scaleh = (float) sequencesHeight / (float) fullsizeHeight;
-
-    boxX = (int) (startRes * av.getCharWidth() * scalew);
-    boxY = (int) (startSeq * av.getCharHeight() * scaleh);
+  /*
+   * Find the colour of a sequence at a specified column position
+   */
+  private int getColumnColourFromSequence(
+          jalview.datamodel.SequenceI seq,
+          boolean hiddenRow, boolean hasHiddenCols, int lastcol,
+          FeatureColourFinder finder)
+  {
+    Color color = Color.white;
 
-    if (av.hasHiddenColumns())
+    if ((seq != null) && (seq.getLength() > lastcol))
     {
-      boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
+       color = sr.getResidueColour(seq, lastcol, finder);
     }
-    else
+
+    if (hiddenRow
+            || (hasHiddenCols && !av.getColumnSelection()
+                    .isVisible(lastcol)))
     {
-      boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
+      color = color.darker().darker();
     }
 
-    boxHeight = (int) ((endSeq - startSeq) * av.getCharHeight() * scaleh);
-
-    repaint();
+    return color.getRGB();
   }
 
-  private BufferedImage lastMiniMe = null;
-
   /**
-   * DOCUMENT ME!
+   * Update the overview panel box when the associated alignment panel is
+   * changed
    * 
-   * @param g
-   *          DOCUMENT ME!
    */
+  public void setBoxPosition()
+  {
+    od.setBoxPosition(av.getAlignment()
+            .getHiddenSequences(), av.getColumnSelection(), av.getRanges());
+    repaint();
+  }
+
+
   @Override
   public void paintComponent(Graphics g)
   {
@@ -501,7 +341,7 @@ public class OverviewPanel extends JPanel implements Runnable
       {
         g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
       }
-      g.setColor(new Color(100, 100, 100, 25));
+      g.setColor(TRANS_GREY);
       g.fillRect(0, 0, getWidth(), getHeight());
     }
     else if (lastMiniMe != null)
@@ -509,13 +349,12 @@ public class OverviewPanel extends JPanel implements Runnable
       g.drawImage(lastMiniMe, 0, 0, this);
       if (lastMiniMe != miniMe)
       {
-        g.setColor(new Color(100, 100, 100, 25));
+        g.setColor(TRANS_GREY);
         g.fillRect(0, 0, getWidth(), getHeight());
       }
     }
-    // TODO: render selected regions
+
     g.setColor(Color.red);
-    g.drawRect(boxX, boxY, boxWidth, boxHeight);
-    g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
+    od.drawBox(g);
   }
 }
index c94d24d..d91fa70 100644 (file)
@@ -43,7 +43,6 @@ import jalview.io.FileFormatI;
 import jalview.io.FileFormats;
 import jalview.io.FormatAdapter;
 import jalview.io.SequenceAnnotationReport;
-import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemes;
@@ -414,7 +413,6 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
         add(menuItem);
       }
-
     }
 
     SequenceGroup sg = ap.av.getSelectionGroup();
@@ -428,6 +426,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
       ColourMenuHelper.setColourSelected(colourMenu, sg.getColourScheme());
 
+      conservationMenuItem.setEnabled(!sg.isNucleotide());
+
       if (sg.cs != null)
       {
         if (sg.cs.conservationApplied())
@@ -1178,12 +1178,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         hideInsertions_actionPerformed(e);
       }
     });
-    /*
-     * annotationMenuItem.setText("By Annotation");
-     * annotationMenuItem.addActionListener(new ActionListener() { public void
-     * actionPerformed(ActionEvent actionEvent) {
-     * annotationMenuItem_actionPerformed(actionEvent); } });
-     */
+
     groupMenu.add(sequenceSelDetails);
     add(groupMenu);
     add(sequenceMenu);
@@ -1619,24 +1614,6 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     refresh();
   }
 
-  public void annotationMenuItem_actionPerformed(ActionEvent actionEvent)
-  {
-    SequenceGroup sg = getGroup();
-    if (sg == null)
-    {
-      return;
-    }
-
-    AnnotationColourGradient acg = new AnnotationColourGradient(
-            sequence.getAnnotation()[0], null,
-            AnnotationColourGradient.NO_THRESHOLD);
-
-    acg.setPredefinedColours(true);
-    sg.setColourScheme(acg);
-
-    refresh();
-  }
-
   /**
    * DOCUMENT ME!
    * 
index d32ff46..cf80a6d 100755 (executable)
  */
 package jalview.gui;
 
-import static jalview.util.UrlConstants.DB_ACCESSION;
-import static jalview.util.UrlConstants.EMBLEBI_STRING;
-import static jalview.util.UrlConstants.SEQUENCE_ID;
-import static jalview.util.UrlConstants.SRS_STRING;
-
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.bin.Cache;
 import jalview.gui.Help.HelpId;
@@ -37,12 +32,18 @@ import jalview.jbgui.GSequenceLink;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemes;
 import jalview.schemes.ResidueColourScheme;
+import jalview.urls.UrlLinkTableModel;
+import jalview.urls.api.UrlProviderFactoryI;
+import jalview.urls.api.UrlProviderI;
+import jalview.urls.desktop.DesktopUrlProviderFactory;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.util.UrlConstants;
 import jalview.ws.sifts.SiftsSettings;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
+import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.event.ActionEvent;
@@ -51,14 +52,24 @@ import java.awt.event.MouseEvent;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.StringTokenizer;
-import java.util.Vector;
 
 import javax.help.HelpSetException;
 import javax.swing.JColorChooser;
 import javax.swing.JFileChooser;
 import javax.swing.JInternalFrame;
 import javax.swing.JPanel;
+import javax.swing.ListSelectionModel;
+import javax.swing.RowFilter;
+import javax.swing.RowSorter;
+import javax.swing.SortOrder;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableModel;
+import javax.swing.table.TableRowSorter;
 
 import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 
@@ -104,7 +115,9 @@ public class Preferences extends GPreferences
    * Holds name and link separated with | character. Sequence ID must be
    * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$
    */
-  public static Vector<String> sequenceURLLinks;
+  public static UrlProviderI sequenceUrlLinks;
+
+  public static UrlLinkTableModel dataModel;
 
   /**
    * Holds name and link separated with | character. Sequence IDS and Sequences
@@ -117,40 +130,23 @@ public class Preferences extends GPreferences
   public static List<String> groupURLLinks;
   static
   {
-    String string = Cache.getDefault("SEQUENCE_LINKS", EMBLEBI_STRING);
-    sequenceURLLinks = new Vector<String>();
-
-    try
+    // get links selected to be in the menu (SEQUENCE_LINKS)
+    // and links entered by the user but not selected (STORED_LINKS)
+    String inMenuString = Cache.getDefault("SEQUENCE_LINKS", "");
+    String notInMenuString = Cache.getDefault("STORED_LINKS", "");
+    String defaultUrl = Cache.getDefault("DEFAULT_URL",
+            UrlConstants.DEFAULT_LABEL);
+
+    // if both links lists are empty, add the DEFAULT_URL link
+    // otherwise we assume the default link is in one of the lists
+    if (inMenuString.isEmpty() && notInMenuString.isEmpty())
     {
-      StringTokenizer st = new StringTokenizer(string, "|");
-      while (st.hasMoreElements())
-      {
-        String name = st.nextToken();
-        String url = st.nextToken();
-        // check for '|' within a regex
-        int rxstart = url.indexOf("$" + DB_ACCESSION + "$");
-        if (rxstart == -1)
-        {
-          rxstart = url.indexOf("$" + SEQUENCE_ID + "$");
-        }
-        while (rxstart == -1 && url.indexOf("/=$") == -1)
-        {
-          url = url + "|" + st.nextToken();
-        }
-        sequenceURLLinks.addElement(name + "|" + url);
-      }
-    } catch (Exception ex)
-    {
-      System.out.println(ex + "\nError parsing sequence links");
-    }
-    {
-      // upgrade old SRS link
-      int srsPos = sequenceURLLinks.indexOf(SRS_STRING);
-      if (srsPos > -1)
-      {
-        sequenceURLLinks.setElementAt(EMBLEBI_STRING, srsPos);
-      }
+      inMenuString = UrlConstants.DEFAULT_STRING;
     }
+    UrlProviderFactoryI factory = new DesktopUrlProviderFactory(defaultUrl,
+            inMenuString, notInMenuString);
+    sequenceUrlLinks = factory.createUrlProvider();
+    dataModel = new UrlLinkTableModel(sequenceUrlLinks);
 
     /**
      * TODO: reformulate groupURL encoding so two or more can be stored in the
@@ -160,8 +156,6 @@ public class Preferences extends GPreferences
     groupURLLinks = new ArrayList<String>();
   }
 
-  Vector<String> nameLinks, urlLinks;
-
   JInternalFrame frame;
 
   DasSourceBrowser dasSource;
@@ -349,20 +343,128 @@ public class Preferences extends GPreferences
     /*
      * Set Connections tab defaults
      */
-    nameLinks = new Vector<String>();
-    urlLinks = new Vector<String>();
-    for (int i = 0; i < sequenceURLLinks.size(); i++)
+
+    // set up sorting
+    linkUrlTable.setModel(dataModel);
+    final TableRowSorter<TableModel> sorter = new TableRowSorter<>(
+            linkUrlTable.getModel());
+    linkUrlTable.setRowSorter(sorter);
+    List<RowSorter.SortKey> sortKeys = new ArrayList<>();
+
+    UrlLinkTableModel m = (UrlLinkTableModel) linkUrlTable.getModel();
+    sortKeys.add(new RowSorter.SortKey(m.getPrimaryColumn(),
+            SortOrder.DESCENDING));
+    sortKeys.add(new RowSorter.SortKey(m.getSelectedColumn(),
+            SortOrder.DESCENDING));
+    sortKeys.add(new RowSorter.SortKey(m.getNameColumn(),
+            SortOrder.ASCENDING));
+
+    sorter.setSortKeys(sortKeys);
+    sorter.sort();
+    
+    // set up filtering
+    ActionListener onReset;
+    onReset = new ActionListener()
     {
-      String link = sequenceURLLinks.elementAt(i).toString();
-      nameLinks.addElement(link.substring(0, link.indexOf("|")));
-      urlLinks.addElement(link.substring(link.indexOf("|") + 1));
-    }
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        filterTB.setText("");
+        sorter.setRowFilter(RowFilter.regexFilter(""));
+      }
+
+    };
+    doReset.addActionListener(onReset);
+
+    // filter to display only custom urls
+    final RowFilter<TableModel, Object> customUrlFilter = new RowFilter<TableModel, Object>()
+    {
+      @Override
+      public boolean include(
+              Entry<? extends TableModel, ? extends Object> entry)
+      {
+        return ((UrlLinkTableModel) entry.getModel()).isUserEntry(entry);
+      }
+    };
+
+    final TableRowSorter<TableModel> customSorter = new TableRowSorter<>(
+            linkUrlTable.getModel());
+    customSorter.setRowFilter(customUrlFilter);
+
+    ActionListener onCustomOnly;
+    onCustomOnly = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        filterTB.setText("");
+        sorter.setRowFilter(customUrlFilter);
+      }
+    };
+    userOnly.addActionListener(onCustomOnly);
+
+    filterTB.getDocument().addDocumentListener(new DocumentListener()
+    {
+      String caseInsensitiveFlag = "(?i)";
 
-    updateLinkData();
+      @Override
+      public void changedUpdate(DocumentEvent e)
+      {
+        sorter.setRowFilter(RowFilter.regexFilter(caseInsensitiveFlag
+                + filterTB.getText()));
+      }
+
+      @Override
+      public void removeUpdate(DocumentEvent e)
+      {
+        sorter.setRowFilter(RowFilter.regexFilter(caseInsensitiveFlag
+                + filterTB.getText()));
+      }
+
+      @Override
+      public void insertUpdate(DocumentEvent e)
+      {
+        sorter.setRowFilter(RowFilter.regexFilter(caseInsensitiveFlag
+                + filterTB.getText()));
+      }
+    });
+
+    // set up list selection functionality
+    linkUrlTable.getSelectionModel().addListSelectionListener(
+            new UrlListSelectionHandler());
+
+    // set up radio buttons
+    int onClickCol = ((UrlLinkTableModel) linkUrlTable.getModel())
+            .getPrimaryColumn();
+    String onClickName = linkUrlTable.getColumnName(onClickCol);
+    linkUrlTable.getColumn(onClickName).setCellRenderer(
+               new RadioButtonRenderer());
+    linkUrlTable.getColumn(onClickName)
+            .setCellEditor(new RadioButtonEditor());
+
+    // get boolean columns and resize those to min possible
+    for (int column = 0; column < linkUrlTable.getColumnCount(); column++)
+    {
+      if (linkUrlTable.getModel().getColumnClass(column)
+              .equals(Boolean.class))
+      {
+        TableColumn tableColumn = linkUrlTable.getColumnModel().getColumn(
+                column);
+        int preferredWidth = tableColumn.getMinWidth();
+
+        TableCellRenderer cellRenderer = linkUrlTable.getCellRenderer(0,
+                column);
+        Component c = linkUrlTable.prepareRenderer(cellRenderer, 0, column);
+        int cwidth = c.getPreferredSize().width
+                + linkUrlTable.getIntercellSpacing().width;
+        preferredWidth = Math.max(preferredWidth, cwidth);
+
+        tableColumn.setPreferredWidth(preferredWidth);
+      }
+    }
 
     useProxy.setSelected(Cache.getDefault("USE_PROXY", false));
-    proxyServerTB.setEnabled(useProxy.isSelected());
-    proxyPortTB.setEnabled(useProxy.isSelected());
+    useProxy_actionPerformed(); // make sure useProxy is correctly initialised
     proxyServerTB.setText(Cache.getDefault("PROXY_SERVER", ""));
     proxyPortTB.setText(Cache.getDefault("PROXY_PORT", ""));
 
@@ -554,28 +656,32 @@ public class Preferences extends GPreferences
 
     jalview.util.BrowserLauncher.resetBrowser();
 
-    if (nameLinks.size() > 0)
+    // save user-defined and selected links
+    String menuLinks = sequenceUrlLinks.writeUrlsAsString(true);
+    if (menuLinks.isEmpty())
+    {
+      Cache.applicationProperties.remove("SEQUENCE_LINKS");
+    }
+    else
     {
-      StringBuffer links = new StringBuffer();
-      sequenceURLLinks = new Vector<String>();
-      for (int i = 0; i < nameLinks.size(); i++)
-      {
-        sequenceURLLinks.addElement(nameLinks.elementAt(i) + "|"
-                + urlLinks.elementAt(i));
-        links.append(sequenceURLLinks.elementAt(i).toString());
-        links.append("|");
-      }
-      // remove last "|"
-      links.setLength(links.length() - 1);
       Cache.applicationProperties.setProperty("SEQUENCE_LINKS",
-              links.toString());
+              menuLinks.toString());
+    }
+
+    String nonMenuLinks = sequenceUrlLinks.writeUrlsAsString(false);
+    if (nonMenuLinks.isEmpty())
+    {
+      Cache.applicationProperties.remove("STORED_LINKS");
     }
     else
     {
-      Cache.applicationProperties.remove("SEQUENCE_LINKS");
-      sequenceURLLinks.clear();
+      Cache.applicationProperties.setProperty("STORED_LINKS",
+              nonMenuLinks.toString());
     }
 
+    Cache.applicationProperties.setProperty("DEFAULT_URL",
+            sequenceUrlLinks.getPrimaryUrlId());
+
     Cache.applicationProperties.setProperty("USE_PROXY",
             Boolean.toString(useProxy.isSelected()));
 
@@ -756,7 +862,6 @@ public class Preferences extends GPreferences
   @Override
   public void newLink_actionPerformed(ActionEvent e)
   {
-
     GSequenceLink link = new GSequenceLink();
     boolean valid = false;
     while (!valid)
@@ -767,10 +872,18 @@ public class Preferences extends GPreferences
       {
         if (link.checkValid())
         {
-          nameLinks.addElement(link.getName());
-          urlLinks.addElement(link.getURL());
-          updateLinkData();
-          valid = true;
+          if (((UrlLinkTableModel) linkUrlTable.getModel())
+                  .isUniqueName(link.getName()))
+          {
+            ((UrlLinkTableModel) linkUrlTable.getModel()).insertRow(
+                    link.getName(), link.getURL());
+            valid = true;
+          }
+          else
+          {
+            link.notifyDuplicate();
+            continue;
+          }
         }
       }
       else
@@ -785,36 +898,46 @@ public class Preferences extends GPreferences
   {
     GSequenceLink link = new GSequenceLink();
 
-    int index = linkNameList.getSelectedIndex();
+    int index = linkUrlTable.getSelectedRow();
     if (index == -1)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-              MessageManager.getString("label.no_link_selected"),
-              MessageManager.getString("label.no_link_selected"),
-              JvOptionPane.WARNING_MESSAGE);
+      // button no longer enabled if row is not selected
+      Cache.log.debug("Edit with no row selected in linkUrlTable");
       return;
     }
 
-    link.setName(nameLinks.elementAt(index).toString());
-    link.setURL(urlLinks.elementAt(index).toString());
+    int nameCol = ((UrlLinkTableModel) linkUrlTable.getModel())
+            .getNameColumn();
+    int urlCol = ((UrlLinkTableModel) linkUrlTable.getModel())
+            .getUrlColumn();
+    String oldName = linkUrlTable.getValueAt(index, nameCol).toString();
+    link.setName(oldName);
+    link.setURL(linkUrlTable.getValueAt(index, urlCol).toString());
 
     boolean valid = false;
     while (!valid)
     {
-
       if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
-              MessageManager.getString("label.new_sequence_url_link"),
+              MessageManager.getString("label.edit_sequence_url_link"),
               JvOptionPane.OK_CANCEL_OPTION, -1, null) == JvOptionPane.OK_OPTION)
       {
         if (link.checkValid())
         {
-          nameLinks.setElementAt(link.getName(), index);
-          urlLinks.setElementAt(link.getURL(), index);
-          updateLinkData();
-          valid = true;
+          if ((oldName.equals(link.getName()))
+                  || (((UrlLinkTableModel) linkUrlTable.getModel())
+                          .isUniqueName(link.getName())))
+          {
+            linkUrlTable.setValueAt(link.getName(), index, nameCol);
+            linkUrlTable.setValueAt(link.getURL(), index, urlCol);
+            valid = true;
+          }
+          else
+          {
+            link.notifyDuplicate();
+            continue;
+          }
         }
       }
-
       else
       {
         break;
@@ -825,26 +948,24 @@ public class Preferences extends GPreferences
   @Override
   public void deleteLink_actionPerformed(ActionEvent e)
   {
-    int index = linkNameList.getSelectedIndex();
+    int index = linkUrlTable.getSelectedRow();
+    int modelIndex = -1;
     if (index == -1)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-              MessageManager.getString("label.no_link_selected"),
-              MessageManager.getString("label.no_link_selected"),
-              JvOptionPane.WARNING_MESSAGE);
+      // button no longer enabled if row is not selected
+      Cache.log.debug("Delete with no row selected in linkUrlTable");
       return;
     }
-    nameLinks.removeElementAt(index);
-    urlLinks.removeElementAt(index);
-    updateLinkData();
-  }
+    else
+    {
+      modelIndex = linkUrlTable.convertRowIndexToModel(index);
+    }
 
-  void updateLinkData()
-  {
-    linkNameList.setListData(nameLinks);
-    linkURLList.setListData(urlLinks);
+    // make sure we use the model index to delete, and not the table index
+    ((UrlLinkTableModel) linkUrlTable.getModel()).removeRow(modelIndex);
   }
 
+
   @Override
   public void defaultBrowser_mouseClicked(MouseEvent e)
   {
@@ -1061,4 +1182,45 @@ public class Preferences extends GPreferences
       return name.hashCode() + code.hashCode();
     }
   }
+  
+  private class UrlListSelectionHandler implements ListSelectionListener
+  {
+
+    @Override
+    public void valueChanged(ListSelectionEvent e)
+    {
+      ListSelectionModel lsm = (ListSelectionModel) e.getSource();
+
+      int index = lsm.getMinSelectionIndex();
+      if (index == -1)
+      {
+        // no selection, so disable delete/edit buttons
+        editLink.setEnabled(false);
+        deleteLink.setEnabled(false);
+        return;
+      }
+      int modelIndex = linkUrlTable.convertRowIndexToModel(index);
+
+      // enable/disable edit and delete link buttons
+      if (((UrlLinkTableModel) linkUrlTable.getModel())
+              .isRowDeletable(modelIndex))
+      {
+        deleteLink.setEnabled(true);
+      }
+      else
+      {
+        deleteLink.setEnabled(false);
+      }
+
+      if (((UrlLinkTableModel) linkUrlTable.getModel())
+              .isRowEditable(modelIndex))
+      {
+        editLink.setEnabled(true);
+      }
+      else
+      {
+        editLink.setEnabled(false);
+      }
+    }
+}
 }
index 8961f21..de21be6 100755 (executable)
@@ -101,7 +101,7 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
   @Override
   public void mousePressed(MouseEvent evt)
   {
-    int x = (evt.getX() / av.getCharWidth()) + av.getStartRes();
+    int x = (evt.getX() / av.getCharWidth()) + av.getRanges().getStartRes();
     final int res;
 
     if (av.hasHiddenColumns())
@@ -282,7 +282,8 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
   {
     mouseDragging = false;
 
-    int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
+    int res = (evt.getX() / av.getCharWidth())
+            + av.getRanges().getStartRes();
 
     if (av.hasHiddenColumns())
     {
@@ -337,7 +338,8 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
     mouseDragging = true;
     ColumnSelection cs = av.getColumnSelection();
 
-    int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
+    int res = (evt.getX() / av.getCharWidth())
+            + av.getRanges().getStartRes();
     res = Math.max(0, res);
     res = cs.adjustForHiddenColumns(res);
     res = Math.min(res, av.getAlignment().getWidth() - 1);
@@ -389,7 +391,8 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
       return;
     }
 
-    int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
+    int res = (evt.getX() / av.getCharWidth())
+            + av.getRanges().getStartRes();
 
     res = av.getColumnSelection().adjustForHiddenColumns(res);
 
@@ -419,7 +422,8 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
   @Override
   public void paintComponent(Graphics g)
   {
-    drawScale(g, av.getStartRes(), av.getEndRes(), getWidth(), getHeight());
+    drawScale(g, av.getRanges().getStartRes(), av.getRanges().getEndRes(),
+            getWidth(), getHeight());
   }
 
   // scalewidth will normally be screenwidth,
index d015292..4557819 100755 (executable)
@@ -26,6 +26,7 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.ScaleRenderer;
 import jalview.renderer.ScaleRenderer.ScaleMark;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.BasicStroke;
 import java.awt.BorderLayout;
@@ -279,10 +280,11 @@ public class SeqCanvas extends JComponent
     gg.copyArea(horizontal * charWidth, vertical * charHeight, imgWidth,
             imgHeight, -horizontal * charWidth, -vertical * charHeight);
 
-    int sr = av.startRes;
-    int er = av.endRes;
-    int ss = av.startSeq;
-    int es = av.endSeq;
+    ViewportRanges ranges = av.getRanges();
+    int sr = ranges.getStartRes();
+    int er = ranges.getEndRes();
+    int ss = ranges.getStartSeq();
+    int es = ranges.getEndSeq();
     int transX = 0;
     int transY = 0;
 
@@ -300,22 +302,22 @@ public class SeqCanvas extends JComponent
     {
       ss = es - vertical;
 
-      if (ss < av.startSeq)
+      if (ss < ranges.getStartSeq())
       { // ie scrolling too fast, more than a page at a time
-        ss = av.startSeq;
+        ss = ranges.getStartSeq();
       }
       else
       {
-        transY = imgHeight - (vertical * charHeight);
+        transY = imgHeight - ((vertical + 1) * charHeight);
       }
     }
     else if (vertical < 0)
     {
       es = ss - vertical;
 
-      if (es > av.endSeq)
+      if (es > ranges.getEndSeq())
       {
-        es = av.endSeq;
+        es = ranges.getEndSeq();
       }
     }
 
@@ -395,13 +397,15 @@ public class SeqCanvas extends JComponent
     gg.setColor(Color.white);
     gg.fillRect(0, 0, imgWidth, imgHeight);
 
+    ViewportRanges ranges = av.getRanges();
     if (av.getWrapAlignment())
     {
-      drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
+      drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
     }
     else
     {
-      drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
+      drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
+              ranges.getStartSeq(), ranges.getEndSeq(), 0);
     }
 
     g.drawImage(lcimg, 0, 0, this);
@@ -503,7 +507,7 @@ public class SeqCanvas extends JComponent
 
     av.setWrappedWidth(cWidth);
 
-    av.endRes = av.startRes + cWidth;
+    av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth);
 
     int endx;
     int ypos = hgap;
@@ -718,7 +722,7 @@ public class SeqCanvas extends JComponent
 
     // / First draw the sequences
     // ///////////////////////////
-    for (int i = startSeq; i < endSeq; i++)
+    for (int i = startSeq; i <= endSeq; i++)
     {
       nextSeq = av.getAlignment().getSequenceAt(i);
       if (nextSeq == null)
@@ -733,7 +737,7 @@ public class SeqCanvas extends JComponent
       if (av.isShowSequenceFeatures())
       {
         fr.drawSequence(g, nextSeq, startRes, endRes, offset
-                + ((i - startSeq) * charHeight));
+                + ((i - startSeq) * charHeight), false);
       }
 
       // / Highlight search Results once all sequences have been drawn
@@ -802,7 +806,7 @@ public class SeqCanvas extends JComponent
         int top = -1;
         int bottom = -1;
 
-        for (i = startSeq; i < endSeq; i++)
+        for (i = startSeq; i <= endSeq; i++)
         {
           sx = (group.getStartRes() - startRes) * charWidth;
           sy = offset + ((i - startSeq) * charHeight);
index e402d28..db7aa36 100644 (file)
@@ -209,7 +209,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
 
       wrappedBlock = y / cHeight;
-      wrappedBlock += av.getStartRes() / cwidth;
+      wrappedBlock += av.getRanges().getStartRes() / cwidth;
 
       res = wrappedBlock * cwidth + x / av.getCharWidth();
 
@@ -222,11 +222,11 @@ public class SeqPanel extends JPanel implements MouseListener,
         // right-hand gutter
         x = seqCanvas.getX() + seqCanvas.getWidth();
       }
-      res = (x / av.getCharWidth()) + av.getStartRes();
-      if (res > av.getEndRes())
+      res = (x / av.getCharWidth()) + av.getRanges().getStartRes();
+      if (res > av.getRanges().getEndRes())
       {
         // moused off right
-        res = av.getEndRes();
+        res = av.getRanges().getEndRes();
       }
     }
 
@@ -262,7 +262,9 @@ public class SeqPanel extends JPanel implements MouseListener,
     }
     else
     {
-      seq = Math.min((y / av.getCharHeight()) + av.getStartSeq(), av
+      seq = Math.min((y / av.getCharHeight())
+              + av.getRanges().getStartSeq(),
+              av
               .getAlignment().getHeight() - 1);
     }
 
@@ -385,18 +387,18 @@ public class SeqPanel extends JPanel implements MouseListener,
     }
     else
     {
-      while (seqCanvas.cursorY < av.startSeq)
+      while (seqCanvas.cursorY < av.getRanges().getStartSeq())
       {
         ap.scrollUp(true);
       }
-      while (seqCanvas.cursorY + 1 > av.endSeq)
+      while (seqCanvas.cursorY + 1 > av.getRanges().getEndSeq())
       {
         ap.scrollUp(false);
       }
       if (!av.getWrapAlignment())
       {
         while (seqCanvas.cursorX < av.getColumnSelection()
-                .adjustForHiddenColumns(av.startRes))
+                .adjustForHiddenColumns(av.getRanges().getStartRes()))
         {
           if (!ap.scrollRight(false))
           {
@@ -404,7 +406,7 @@ public class SeqPanel extends JPanel implements MouseListener,
           }
         }
         while (seqCanvas.cursorX > av.getColumnSelection()
-                .adjustForHiddenColumns(av.endRes))
+                .adjustForHiddenColumns(av.getRanges().getEndRes()))
         {
           if (!ap.scrollRight(true))
           {
@@ -1587,34 +1589,15 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     stretchGroup = av.getSelectionGroup();
 
-    if (stretchGroup == null)
+    if (stretchGroup == null || !stretchGroup.contains(sequence, res))
     {
       stretchGroup = av.getAlignment().findGroup(sequence, res);
-      av.setSelectionGroup(stretchGroup);
-    }
-    if (stretchGroup == null
-            || !stretchGroup.getSequences(null).contains(sequence)
-            || (stretchGroup.getStartRes() > res)
-            || (stretchGroup.getEndRes() < res))
-    {
-      stretchGroup = null;
-
-      SequenceGroup[] allGroups = av.getAlignment().findAllGroups(sequence);
-
-      if (allGroups != null)
+      if (stretchGroup != null)
       {
-        for (int i = 0; i < allGroups.length; i++)
-        {
-          if ((allGroups[i].getStartRes() <= res)
-                  && (allGroups[i].getEndRes() >= res))
-          {
-            stretchGroup = allGroups[i];
-            break;
-          }
-        }
+        // only update the current selection if the popup menu has a group to
+        // focus on
+        av.setSelectionGroup(stretchGroup);
       }
-
-      av.setSelectionGroup(stretchGroup);
     }
 
     if (evt.isPopupTrigger()) // Mac: mousePressed
@@ -1664,6 +1647,7 @@ public class SeqPanel extends JPanel implements MouseListener,
         SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
                 ap.getViewName());
       }
+      // TODO: stretchGroup will always be not null. Is this a merge error ?
       if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
       {
         // Edit end res position of selected group
@@ -1790,9 +1774,9 @@ public class SeqPanel extends JPanel implements MouseListener,
       changeStartRes = true;
     }
 
-    if (res < av.getStartRes())
+    if (res < av.getRanges().getStartRes())
     {
-      res = av.getStartRes();
+      res = av.getRanges().getStartRes();
     }
 
     if (changeEndRes)
@@ -1926,13 +1910,15 @@ public class SeqPanel extends JPanel implements MouseListener,
       {
         if (evt != null)
         {
-          if (mouseDragging && (evt.getY() < 0) && (av.getStartSeq() > 0))
+          if (mouseDragging && (evt.getY() < 0)
+                  && (av.getRanges().getStartSeq() > 0))
           {
             running = ap.scrollUp(true);
           }
 
           if (mouseDragging && (evt.getY() >= getHeight())
-                  && (av.getAlignment().getHeight() > av.getEndSeq()))
+                  && (av.getAlignment().getHeight() > av.getRanges()
+                          .getEndSeq()))
           {
             running = ap.scrollUp(false);
           }
index 95c3261..1c0420d 100755 (executable)
  */
 package jalview.gui;
 
-import jalview.api.FeatureRenderer;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.ResidueShaderI;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.util.Comparison;
 
 import java.awt.Color;
@@ -53,14 +53,13 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   boolean forOverview = false;
 
   /**
-   * Creates a new SequenceRenderer object.
+   * Creates a new SequenceRenderer object
    * 
-   * @param av
-   *          DOCUMENT ME!
+   * @param viewport
    */
-  public SequenceRenderer(AlignViewport av)
+  public SequenceRenderer(AlignViewport viewport)
   {
-    this.av = av;
+    this.av = viewport;
   }
 
   /**
@@ -83,8 +82,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     this.renderGaps = renderGaps;
   }
 
-  @Override
-  public Color getResidueBoxColour(SequenceI seq, int i)
+  protected Color getResidueBoxColour(SequenceI seq, int i)
   {
     // rate limiting step when rendering overview for lots of groups
     allGroups = av.getAlignment().findAllGroups(seq);
@@ -111,20 +109,18 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
    * 
    * @param seq
    * @param position
-   * @param fr
+   * @param finder
    * @return
    */
   @Override
   public Color getResidueColour(final SequenceI seq, int position,
-          FeatureRenderer fr)
+          FeatureColourFinder finder)
   {
-    // TODO replace 8 or so code duplications with calls to this method
-    // (refactored as needed)
     Color col = getResidueBoxColour(seq, position);
 
-    if (fr != null)
+    if (finder != null)
     {
-      col = fr.findFeatureColour(col, seq, position);
+      col = finder.findFeatureColour(col, seq, position);
     }
     return col;
   }
index 34ad659..d7f7c31 100644 (file)
@@ -110,6 +110,8 @@ public abstract class StructureViewerBase extends GStructureViewer
 
   protected boolean allChainsSelected = false;
 
+  protected JMenu viewSelectionMenu;
+
   /**
    * Default constructor
    */
@@ -744,17 +746,17 @@ public abstract class StructureViewerBase extends GStructureViewer
       @Override
       public void itemStateChanged(ItemEvent e)
       {
-        alignStructs.setEnabled(_alignwith.size() > 0);
+        alignStructs.setEnabled(!_alignwith.isEmpty());
         alignStructs.setToolTipText(MessageManager.formatMessage(
                 "label.align_structures_using_linked_alignment_views",
-                new String[] { String.valueOf(_alignwith.size()) }));
+                _alignwith.size()));
       }
     };
-    JMenu alpanels = new ViewSelectionMenu(
+    viewSelectionMenu = new ViewSelectionMenu(
             MessageManager.getString("label.superpose_with"), this,
             _alignwith, handler);
     handler.itemStateChanged(null);
-    viewerActionMenu.add(alpanels);
+    viewerActionMenu.add(viewSelectionMenu, 0);
     viewerActionMenu.addMenuListener(new MenuListener()
     {
       @Override
@@ -781,16 +783,24 @@ public abstract class StructureViewerBase extends GStructureViewer
   public void setJalviewColourScheme(ColourSchemeI cs) {
     getBinding().setJalviewColourScheme(cs);
   }
+
+  /**
+   * Sends commands to the structure viewer to superimpose structures based on
+   * currently associated alignments. May optionally return an error message for
+   * the operation.
+   */
   @Override
-  protected void alignStructs_actionPerformed(ActionEvent actionEvent)
+  protected String alignStructs_actionPerformed(
+          ActionEvent actionEvent)
   {
-    alignStructs_withAllAlignPanels();
+    return alignStructs_withAllAlignPanels();
   }
-  protected void alignStructs_withAllAlignPanels()
+
+  protected String alignStructs_withAllAlignPanels()
   {
     if (getAlignmentPanel() == null)
     {
-      return;
+      return null;
     }
   
     if (_alignwith.size() == 0)
@@ -798,6 +808,7 @@ public abstract class StructureViewerBase extends GStructureViewer
       _alignwith.add(getAlignmentPanel());
     }
   
+    String reply = null;
     try
     {
       AlignmentI[] als = new Alignment[_alignwith.size()];
@@ -811,7 +822,13 @@ public abstract class StructureViewerBase extends GStructureViewer
         alm[a] = -1;
         alc[a++] = ap.av.getColumnSelection();
       }
-      getBinding().superposeStructures(als, alm, alc);
+      reply = getBinding().superposeStructures(als, alm, alc);
+      if (reply != null)
+      {
+        String text = MessageManager.formatMessage(
+                "error.superposition_failed", reply);
+        statusBar.setText(text);
+      }
     } catch (Exception e)
     {
       StringBuffer sp = new StringBuffer();
@@ -822,7 +839,9 @@ public abstract class StructureViewerBase extends GStructureViewer
       Cache.log.info("Couldn't align structures with the " + sp.toString()
               + "associated alignment panels.", e);
     }
+    return reply;
   }
+
   @Override
   public void background_actionPerformed(ActionEvent actionEvent)
   {
@@ -952,6 +971,10 @@ public abstract class StructureViewerBase extends GStructureViewer
   }
 
   protected abstract String getViewerName();
+
+  /**
+   * Configures the title and menu items of the viewer panel.
+   */
   public void updateTitleAndMenus()
   {
     AAStructureBindingModel binding = getBinding();
@@ -963,10 +986,30 @@ public abstract class StructureViewerBase extends GStructureViewer
     setChainMenuItems(binding.getChainNames());
   
     this.setTitle(binding.getViewerTitle(getViewerName(), true));
-    if (binding.getPdbFile().length > 1 && binding.getSequence().length > 1)
+
+    /*
+     * enable 'Superpose with' if more than one mapped structure
+     */
+    viewSelectionMenu.setEnabled(false);
+    if (getBinding().getPdbFile().length > 1
+            && getBinding().getSequence().length > 1)
     {
-      viewerActionMenu.setVisible(true);
+      viewSelectionMenu.setEnabled(true);
     }
+
+    /*
+     * Show action menu if it has any enabled items
+     */
+    viewerActionMenu.setVisible(false);
+    for (int i = 0; i < viewerActionMenu.getItemCount(); i++)
+    {
+      if (viewerActionMenu.getItem(i).isEnabled())
+      {
+        viewerActionMenu.setVisible(true);
+        break;
+      }
+    }
+
     if (!binding.isLoadingFromArchive())
     {
       seqColour_actionPerformed(null);
index 54eed1a..9a38d4c 100755 (executable)
@@ -549,7 +549,16 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
   public void run()
   {
     PrinterJob printJob = PrinterJob.getPrinterJob();
-    PageFormat pf = printJob.pageDialog(printJob.defaultPage());
+    PageFormat defaultPage = printJob.defaultPage();
+    PageFormat pf = printJob.pageDialog(defaultPage);
+
+    if (defaultPage == pf)
+    {
+      /*
+       * user cancelled
+       */
+      return;
+    }
 
     printJob.setPrintable(this, pf);
 
index e1a43d9..463d8cd 100755 (executable)
@@ -29,6 +29,7 @@ import jalview.jbgui.GUserDefinedColours;
 import jalview.schemabinding.version2.Colour;
 import jalview.schemabinding.version2.JalviewUserColours;
 import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeLoader;
 import jalview.schemes.ColourSchemes;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
@@ -101,7 +102,7 @@ public class UserDefinedColours extends GUserDefinedColours implements
    */
   public UserDefinedColours(AlignmentPanel ap, SequenceGroup sg)
   {
-    super();
+    this();
 
     lcaseColour.setEnabled(false);
 
@@ -142,7 +143,7 @@ public class UserDefinedColours extends GUserDefinedColours implements
   public UserDefinedColours(JalviewStructureDisplayI viewer,
           ColourSchemeI oldcs)
   {
-    super();
+    this();
     this.structureViewer = viewer;
 
     colorChooser.getSelectionModel().addChangeListener(this);
@@ -161,6 +162,12 @@ public class UserDefinedColours extends GUserDefinedColours implements
 
   }
 
+  public UserDefinedColours()
+  {
+    super();
+    selectedButtons = new ArrayList<JButton>();
+  }
+
   void showFrame()
   {
     colorChooser.getSelectionModel().addChangeListener(this);
@@ -276,9 +283,8 @@ public class UserDefinedColours extends GUserDefinedColours implements
       button.setBackground(newColour);
       button.setForeground(ColorUtils.brighterThan(newColour));
     }
-    if (button == lcaseColour)
+    if (lcaseColour.isSelected())
     {
-      button.setForeground(Color.black);
       for (int i = 0; i < lowerCaseButtons.size(); i++)
       {
         button = lowerCaseButtons.get(i);
@@ -620,7 +626,7 @@ public class UserDefinedColours extends GUserDefinedColours implements
     File choice = chooser.getSelectedFile();
     Cache.setProperty(LAST_DIRECTORY, choice.getParent());
 
-    UserColourScheme ucs = ColourSchemes.loadColourScheme(choice
+    UserColourScheme ucs = ColourSchemeLoader.loadColourScheme(choice
             .getAbsolutePath());
     Color[] colors = ucs.getColours();
     schemeName.setText(ucs.getSchemeName());
@@ -669,7 +675,7 @@ public class UserDefinedColours extends GUserDefinedColours implements
       {
         colours = colours.substring(0, colours.indexOf("|"));
       }
-      ret = ColourSchemes.loadColourScheme(colours);
+      ret = ColourSchemeLoader.loadColourScheme(colours);
     }
 
     if (ret == null)
@@ -847,29 +853,5 @@ public class UserDefinedColours extends GUserDefinedColours implements
     boolean selected = caseSensitive.isSelected();
     resetButtonPanel(selected);
     lcaseColour.setEnabled(selected);
-    lcaseColour.setForeground(Color.GRAY);
-  }
-
-  /**
-   * Action on clicking 'Lower case colour', which results in changing colour of
-   * all lower-case buttons when a colour is picked. A second click of the
-   * button turns off this behaviour.
-   */
-  @Override
-  public void lcaseColour_actionPerformed(ActionEvent e)
-  {
-    boolean enable = !selectedButtons.contains(lcaseColour);
-    selectedButtons.clear();
-    if (enable)
-    {
-      selectedButtons.add(lcaseColour);
-      lcaseColour.setForeground(lowerCaseButtons.get(0).getForeground());
-      lcaseColour.setForeground(Color.black);
-    }
-    else
-    {
-      lcaseColour.setBackground(Color.white);
-      lcaseColour.setForeground(Color.gray);
-    }
   }
 }
index 165e8f2..32671d5 100644 (file)
@@ -454,7 +454,7 @@ public class WsPreferences extends GWsPreferences
     JTextField urltf = new JTextField(url, 40);
     JPanel panel = new JPanel(new BorderLayout());
     JPanel pane12 = new JPanel(new BorderLayout());
-    pane12.add(new JLabel(MessageManager.getString("label.url")),
+    pane12.add(new JLabel(MessageManager.getString("label.url:")),
             BorderLayout.CENTER);
     pane12.add(urltf, BorderLayout.EAST);
     panel.add(pane12, BorderLayout.NORTH);
@@ -574,6 +574,7 @@ public class WsPreferences extends GWsPreferences
     new Thread(new Runnable()
     {
 
+      @Override
       public void run()
       {
         // force a refresh.
@@ -599,6 +600,7 @@ public class WsPreferences extends GWsPreferences
       new Thread(new Runnable()
       {
 
+        @Override
         public void run()
         {
           progressBar.setVisible(true);
@@ -624,6 +626,7 @@ public class WsPreferences extends GWsPreferences
       new Thread(new Runnable()
       {
 
+        @Override
         public void run()
         {
           long ct = System.currentTimeMillis();
@@ -681,6 +684,7 @@ public class WsPreferences extends GWsPreferences
     new Thread(new Runnable()
     {
 
+      @Override
       public void run()
       {
         updateWsMenuConfig(false);
index 27ebe5a..053a65e 100644 (file)
@@ -46,6 +46,7 @@ import jalview.json.binding.biojson.v1.ColourSchemeMapper;
 import jalview.json.binding.biojson.v1.SequenceFeaturesPojo;
 import jalview.json.binding.biojson.v1.SequenceGrpPojo;
 import jalview.json.binding.biojson.v1.SequencePojo;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.schemes.JalviewColourScheme;
 import jalview.schemes.ResidueColourScheme;
 import jalview.util.ColorUtils;
@@ -328,6 +329,8 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
       return sequenceFeaturesPojo;
     }
 
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
+
     for (SequenceI seq : sqs)
     {
       SequenceI dataSetSequence = seq.getDatasetSequence();
@@ -350,7 +353,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
                   String.valueOf(seq.hashCode()));
 
           String featureColour = (fr == null) ? null : jalview.util.Format
-                  .getHexString(fr.findFeatureColour(Color.white, seq,
+                  .getHexString(finder.findFeatureColour(Color.white, seq,
                           seq.findIndex(sf.getBegin())));
           jsonFeature.setXstart(seq.findIndex(sf.getBegin()) - 1);
           jsonFeature.setXend(seq.findIndex(sf.getEnd()));
index 746c4a7..0f2b0ac 100644 (file)
@@ -26,12 +26,14 @@ import jalview.schemes.FeatureSettingsAdapter;
 
 import java.awt.Color;
 
+import MCview.PDBChain;
+
 public class PDBFeatureSettings extends FeatureSettingsAdapter
 {
   // TODO find one central place to define feature names
   private static final String FEATURE_INSERTION = "INSERTION";
 
-  private static final String FEATURE_RES_NUM = "RESNUM";
+  private static final String FEATURE_RES_NUM = PDBChain.RESNUM_FEATURE;
 
   @Override
   public boolean isFeatureDisplayed(String type)
index 4f833bc..7580222 100644 (file)
@@ -221,8 +221,8 @@ public class MouseOverStructureListener extends JSFunctionExec implements
       ArrayList<String[]> ccomands = new ArrayList<String[]>();
       ArrayList<String> pdbfn = new ArrayList<String>();
       StructureMappingcommandSet[] colcommands = JmolCommands
-              .getColourBySequenceCommand(ssm, modelSet, sequence, sr, fr,
-                      ((AlignmentViewPanel) source).getAlignment());
+              .getColourBySequenceCommand(ssm, modelSet, sequence, sr,
+                      (AlignmentViewPanel) source);
       if (colcommands == null)
       {
         return;
@@ -298,6 +298,7 @@ public class MouseOverStructureListener extends JSFunctionExec implements
     return _listenerfn;
   }
 
+  @Override
   public void finalize() throws Throwable
   {
     jvlite = null;
index 90053f5..dda06b4 100755 (executable)
@@ -45,6 +45,7 @@ import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 
+import javax.swing.AbstractCellEditor;
 import javax.swing.BorderFactory;
 import javax.swing.ButtonGroup;
 import javax.swing.DefaultListCellRenderer;
@@ -53,11 +54,11 @@ import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JFileChooser;
 import javax.swing.JLabel;
-import javax.swing.JList;
 import javax.swing.JPanel;
 import javax.swing.JRadioButton;
 import javax.swing.JScrollPane;
 import javax.swing.JTabbedPane;
+import javax.swing.JTable;
 import javax.swing.JTextField;
 import javax.swing.ListSelectionModel;
 import javax.swing.SwingConstants;
@@ -67,8 +68,8 @@ import javax.swing.border.EtchedBorder;
 import javax.swing.border.TitledBorder;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
 
 /**
  * Base class for the Preferences panel.
@@ -180,7 +181,21 @@ public class GPreferences extends JPanel
   /*
    * Connections tab components
    */
-  protected JList linkURLList = new JList();
+  protected JTable linkUrlTable = new JTable();
+
+  protected JButton editLink = new JButton();
+
+  protected JButton deleteLink = new JButton();
+
+  protected JTextField filterTB = new JTextField();
+
+  protected JButton doReset = new JButton();
+
+  protected JButton userOnly = new JButton();
+
+  protected JLabel portLabel = new JLabel();
+
+  protected JLabel serverLabel = new JLabel();
 
   protected JTextField proxyServerTB = new JTextField();
 
@@ -188,8 +203,6 @@ public class GPreferences extends JPanel
 
   protected JTextField defaultBrowser = new JTextField();
 
-  protected JList linkNameList = new JList();
-
   protected JCheckBox useProxy = new JCheckBox();
 
   protected JCheckBox usagestats = new JCheckBox();
@@ -285,6 +298,9 @@ public class GPreferences extends JPanel
     tabbedPane.add(initConnectionsTab(),
             MessageManager.getString("label.connections"));
 
+    tabbedPane.add(initLinksTab(),
+            MessageManager.getString("label.urllinks"));
+
     tabbedPane.add(initOutputTab(),
             MessageManager.getString("label.output"));
 
@@ -483,40 +499,196 @@ public class GPreferences extends JPanel
   {
     JPanel connectTab = new JPanel();
     connectTab.setLayout(new GridBagLayout());
-    JLabel serverLabel = new JLabel();
-    serverLabel.setText(MessageManager.getString("label.address"));
-    serverLabel.setHorizontalAlignment(SwingConstants.RIGHT);
-    serverLabel.setFont(LABEL_FONT);
-    proxyServerTB.setFont(LABEL_FONT);
-    proxyPortTB.setFont(LABEL_FONT);
-    JLabel portLabel = new JLabel();
-    portLabel.setFont(LABEL_FONT);
-    portLabel.setHorizontalAlignment(SwingConstants.RIGHT);
-    portLabel.setText(MessageManager.getString("label.port"));
+
+    // Label for browser text box
     JLabel browserLabel = new JLabel();
-    browserLabel.setFont(new java.awt.Font("SansSerif", 0, 11));
+    browserLabel.setFont(LABEL_FONT);
     browserLabel.setHorizontalAlignment(SwingConstants.TRAILING);
     browserLabel.setText(MessageManager
             .getString("label.default_browser_unix"));
     defaultBrowser.setFont(LABEL_FONT);
     defaultBrowser.setText("");
-    usagestats.setText(MessageManager
-            .getString("label.send_usage_statistics"));
-    usagestats.setFont(LABEL_FONT);
-    usagestats.setHorizontalAlignment(SwingConstants.RIGHT);
-    usagestats.setHorizontalTextPosition(SwingConstants.LEADING);
-    questionnaire.setText(MessageManager
-            .getString("label.check_for_questionnaires"));
-    questionnaire.setFont(LABEL_FONT);
-    questionnaire.setHorizontalAlignment(SwingConstants.RIGHT);
-    questionnaire.setHorizontalTextPosition(SwingConstants.LEADING);
-    versioncheck.setText(MessageManager
-            .getString("label.check_for_latest_version"));
-    versioncheck.setFont(LABEL_FONT);
-    versioncheck.setHorizontalAlignment(SwingConstants.RIGHT);
-    versioncheck.setHorizontalTextPosition(SwingConstants.LEADING);
+
+    defaultBrowser.addMouseListener(new MouseAdapter()
+    {
+      @Override
+      public void mouseClicked(MouseEvent e)
+      {
+        if (e.getClickCount() > 1)
+        {
+          defaultBrowser_mouseClicked(e);
+        }
+      }
+    });
+
+    JPanel proxyPanel = initConnTabProxyPanel();
+    initConnTabCheckboxes();
+
+    // Add default Browser text box
+    connectTab.add(browserLabel, new GridBagConstraints(0, 0, 1, 1, 0.0,
+            0.0, GridBagConstraints.WEST, GridBagConstraints.NONE,
+            new Insets(10, 0, 5, 5), 5, 1));
+    defaultBrowser.setFont(LABEL_FONT);
+    defaultBrowser.setText("");
+
+    connectTab.add(defaultBrowser, new GridBagConstraints(1, 0, 1, 1, 1.0,
+            0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+            new Insets(10, 0, 5, 10), 30, 1));
+
+    // Add proxy server panel
+    connectTab.add(proxyPanel, new GridBagConstraints(0, 1, 2, 1, 1.0, 0.0,
+            GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+            new Insets(10, 0, 5, 12), 4, 10));
+
+    // Add usage stats, version check and questionnaire checkboxes
+    connectTab.add(usagestats, new GridBagConstraints(0, 2, 1, 1, 1.0, 0.0,
+            GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+            new Insets(0, 2, 5, 5), 70, 1));
+    connectTab.add(questionnaire, new GridBagConstraints(1, 2, 1, 1, 1.0,
+            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+            new Insets(0, 2, 5, 10), 70, 1));
+    connectTab.add(versioncheck, new GridBagConstraints(0, 3, 1, 1, 1.0,
+            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+            new Insets(0, 2, 5, 5), 70, 1));
+
+    // Add padding so the panel doesn't look ridiculous
+    JPanel spacePanel = new JPanel();
+    connectTab.add(spacePanel, new GridBagConstraints(0, 4, 1, 1, 1.0, 1.0,
+            GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0,
+                    0, 0, 5), 70, 1));
+
+    return connectTab;
+  }
+
+  /**
+   * Initialises the Links tabbed panel.
+   * 
+   * @return
+   */
+  private JPanel initLinksTab()
+  {
+    JPanel linkTab = new JPanel();
+    linkTab.setLayout(new GridBagLayout());
+
+    // Set up table for Url links
+    linkUrlTable.setFillsViewportHeight(true);
+    linkUrlTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+    linkUrlTable.setAutoCreateRowSorter(true);
+    linkUrlTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+
+    // adjust row height so radio buttons actually fit
+    // don't do this in the renderer, it causes the awt thread to activate
+    // constantly
+    JRadioButton temp = new JRadioButton();
+    linkUrlTable.setRowHeight(temp.getMinimumSize().height);
+
+    // Table in scrollpane so that the table is given a scrollbar
+    JScrollPane linkScrollPane = new JScrollPane(linkUrlTable);
+    linkScrollPane.setBorder(null);
+
+    // Panel for links functionality
+    JPanel linkPanel = new JPanel(new GridBagLayout());
+    linkPanel.setBorder(new TitledBorder(MessageManager
+            .getString("label.url_linkfrom_sequence_id")));
+
+    // Put the Url links panel together
+
+    // Buttons go at top right, resizing only resizes the blank space vertically
+    JPanel buttonPanel = initLinkTabUrlButtons();
+    GridBagConstraints linkConstraints1 = new GridBagConstraints();
+    linkConstraints1.insets = new Insets(0, 0, 5, 0);
+    linkConstraints1.gridx = 0;
+    linkConstraints1.gridy = 0;
+    linkConstraints1.weightx = 1.0;
+    linkConstraints1.fill = GridBagConstraints.HORIZONTAL;
+    linkTab.add(buttonPanel, linkConstraints1);
+
+    // Links table goes at top left, resizing resizes the table
+    GridBagConstraints linkConstraints2 = new GridBagConstraints();
+    linkConstraints2.insets = new Insets(0, 0, 5, 5);
+    linkConstraints2.gridx = 0;
+    linkConstraints2.gridy = 1;
+    linkConstraints2.weightx = 1.0;
+    linkConstraints2.weighty = 1.0;
+    linkConstraints2.fill = GridBagConstraints.BOTH;
+    linkTab.add(linkScrollPane, linkConstraints2);
+
+    // Filter box and buttons goes at bottom left, resizing resizes the text box
+    JPanel filterPanel = initLinkTabFilterPanel();
+    GridBagConstraints linkConstraints3 = new GridBagConstraints();
+    linkConstraints3.insets = new Insets(0, 0, 0, 5);
+    linkConstraints3.gridx = 0;
+    linkConstraints3.gridy = 2;
+    linkConstraints3.weightx = 1.0;
+    linkConstraints3.fill = GridBagConstraints.HORIZONTAL;
+    linkTab.add(filterPanel, linkConstraints3);
+
+    return linkTab;
+  }
+
+  private JPanel initLinkTabFilterPanel()
+  {
+    // Filter textbox and reset button
+    JLabel filterLabel = new JLabel(
+            MessageManager.getString("label.filter"));
+    filterLabel.setFont(LABEL_FONT);
+    filterLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+    filterLabel.setHorizontalTextPosition(SwingConstants.LEADING);
+
+    filterTB.setFont(LABEL_FONT);
+    filterTB.setText("");
+
+    doReset.setText(MessageManager.getString("action.showall"));
+    userOnly.setText(MessageManager.getString("action.customfilter"));
+
+    // Panel for filter functionality
+    JPanel filterPanel = new JPanel(new GridBagLayout());
+    filterPanel.setBorder(new TitledBorder("Filter"));
+    GridBagConstraints gbc = new GridBagConstraints();
+    gbc.gridx = 0;
+    gbc.gridy = 0;
+    gbc.fill = GridBagConstraints.NONE;
+    gbc.anchor = GridBagConstraints.WEST;
+
+    filterPanel.add(filterLabel, gbc);
+
+    GridBagConstraints gbc1 = new GridBagConstraints();
+    gbc1.gridx = 1;
+    gbc1.gridwidth = 2;
+    gbc1.fill = GridBagConstraints.HORIZONTAL;
+    gbc1.anchor = GridBagConstraints.WEST;
+    gbc1.weightx = 1.0;
+    filterPanel.add(filterTB, gbc1);
+
+    GridBagConstraints gbc2 = new GridBagConstraints();
+    gbc2.gridx = 3;
+    gbc2.fill = GridBagConstraints.NONE;
+    gbc2.anchor = GridBagConstraints.WEST;
+    filterPanel.add(doReset, gbc2);
+
+    GridBagConstraints gbc3 = new GridBagConstraints();
+    gbc3.gridx = 4;
+    gbc3.fill = GridBagConstraints.NONE;
+    gbc3.anchor = GridBagConstraints.WEST;
+    filterPanel.add(userOnly, gbc3);
+
+    return filterPanel;
+  }
+
+  private JPanel initLinkTabUrlButtons()
+  {
+    // Buttons for new / edit / delete Url links
     JButton newLink = new JButton();
     newLink.setText(MessageManager.getString("action.new"));
+
+    editLink.setText(MessageManager.getString("action.edit"));
+
+    deleteLink.setText(MessageManager.getString("action.delete"));
+
+    // no current selection, so initially disable delete/edit buttons
+    editLink.setEnabled(false);
+    deleteLink.setEnabled(false);
+    
     newLink.addActionListener(new java.awt.event.ActionListener()
     {
       @Override
@@ -525,7 +697,7 @@ public class GPreferences extends JPanel
         newLink_actionPerformed(e);
       }
     });
-    JButton editLink = new JButton();
+
     editLink.setText(MessageManager.getString("action.edit"));
     editLink.addActionListener(new java.awt.event.ActionListener()
     {
@@ -535,7 +707,7 @@ public class GPreferences extends JPanel
         editLink_actionPerformed(e);
       }
     });
-    JButton deleteLink = new JButton();
+
     deleteLink.setText(MessageManager.getString("action.delete"));
     deleteLink.addActionListener(new java.awt.event.ActionListener()
     {
@@ -546,55 +718,60 @@ public class GPreferences extends JPanel
       }
     });
 
-    linkURLList.addListSelectionListener(new ListSelectionListener()
-    {
-      @Override
-      public void valueChanged(ListSelectionEvent e)
-      {
-        int index = linkURLList.getSelectedIndex();
-        linkNameList.setSelectedIndex(index);
-      }
-    });
+    JPanel buttonPanel = new JPanel(new GridBagLayout());
+    buttonPanel.setBorder(new TitledBorder("Edit links"));
+    GridBagConstraints gbc = new GridBagConstraints();
+    gbc.gridx = 0;
+    gbc.gridy = 0;
+    gbc.fill = GridBagConstraints.NONE;
+    buttonPanel.add(newLink, gbc);
+
+    GridBagConstraints gbc1 = new GridBagConstraints();
+    gbc1.gridx = 1;
+    gbc1.gridy = 0;
+    gbc1.fill = GridBagConstraints.NONE;
+    buttonPanel.add(editLink, gbc1);
+
+    GridBagConstraints gbc2 = new GridBagConstraints();
+    gbc2.gridx = 2;
+    gbc2.gridy = 0;
+    gbc2.fill = GridBagConstraints.NONE;
+    buttonPanel.add(deleteLink, gbc2);
+
+    GridBagConstraints gbc3 = new GridBagConstraints();
+    gbc3.gridx = 3;
+    gbc3.gridy = 0;
+    gbc3.fill = GridBagConstraints.HORIZONTAL;
+    gbc3.weightx = 1.0;
+    JPanel spacePanel = new JPanel();
+    spacePanel.setBorder(null);
+    buttonPanel.add(spacePanel, gbc3);
+
+    return buttonPanel;
+  }
 
-    linkNameList.addListSelectionListener(new ListSelectionListener()
-    {
-      @Override
-      public void valueChanged(ListSelectionEvent e)
-      {
-        int index = linkNameList.getSelectedIndex();
-        linkURLList.setSelectedIndex(index);
-      }
-    });
+  /**
+   * Initialises the proxy server panel in the Connections tab
+   * 
+   * @return the proxy server panel
+   */
+  private JPanel initConnTabProxyPanel()
+  {
+    // Label for server text box
+    serverLabel.setText(MessageManager.getString("label.address"));
+    serverLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+    serverLabel.setFont(LABEL_FONT);
 
-    JScrollPane linkScrollPane = new JScrollPane();
-    linkScrollPane.setBorder(null);
-    JPanel linkPanel = new JPanel();
-    linkPanel.setBorder(new TitledBorder(MessageManager
-            .getString("label.url_linkfrom_sequence_id")));
-    linkPanel.setLayout(new BorderLayout());
-    GridLayout gridLayout1 = new GridLayout();
-    JPanel editLinkButtons = new JPanel();
-    editLinkButtons.setLayout(gridLayout1);
-    gridLayout1.setRows(3);
-    linkNameList.setFont(LABEL_FONT);
-    linkNameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-    BorderLayout borderLayout3 = new BorderLayout();
-    JPanel linkPanel2 = new JPanel();
-    linkPanel2.setLayout(borderLayout3);
-    linkURLList.setFont(LABEL_FONT);
-    linkURLList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+    // Proxy server and port text boxes
+    proxyServerTB.setFont(LABEL_FONT);
+    proxyPortTB.setFont(LABEL_FONT);
 
-    defaultBrowser.addMouseListener(new MouseAdapter()
-    {
-      @Override
-      public void mouseClicked(MouseEvent e)
-      {
-        if (e.getClickCount() > 1)
-        {
-          defaultBrowser_mouseClicked(e);
-        }
-      }
-    });
+    // Label for Port text box
+    portLabel.setFont(LABEL_FONT);
+    portLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+    portLabel.setText(MessageManager.getString("label.port"));
+
+    // Use proxy server checkbox
     useProxy.setFont(LABEL_FONT);
     useProxy.setHorizontalAlignment(SwingConstants.RIGHT);
     useProxy.setHorizontalTextPosition(SwingConstants.LEADING);
@@ -607,56 +784,57 @@ public class GPreferences extends JPanel
         useProxy_actionPerformed();
       }
     });
-    linkPanel.add(editLinkButtons, BorderLayout.EAST);
-    editLinkButtons.add(newLink, null);
-    editLinkButtons.add(editLink, null);
-    editLinkButtons.add(deleteLink, null);
-    linkPanel.add(linkScrollPane, BorderLayout.CENTER);
-    linkScrollPane.getViewport().add(linkPanel2, null);
-    linkPanel2.add(linkURLList, BorderLayout.CENTER);
-    linkPanel2.add(linkNameList, BorderLayout.WEST);
-    JPanel jPanel1 = new JPanel();
+
+    // Make proxy server panel
+    JPanel proxyPanel = new JPanel();
     TitledBorder titledBorder1 = new TitledBorder(
             MessageManager.getString("label.proxy_server"));
-    jPanel1.setBorder(titledBorder1);
-    jPanel1.setLayout(new GridBagLayout());
-    jPanel1.add(serverLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,
-                    2, 4, 0), 5, 0));
-    jPanel1.add(portLabel, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,
-                    0, 4, 0), 11, 6));
-    connectTab.add(linkPanel, new GridBagConstraints(0, 0, 2, 1, 1.0, 1.0,
-            GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
-                    16, 0, 0, 12), 359, -17));
-    connectTab.add(jPanel1, new GridBagConstraints(0, 2, 2, 1, 1.0, 1.0,
-            GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
-                    21, 0, 35, 12), 4, 6));
-    connectTab.add(browserLabel, new GridBagConstraints(0, 1, 1, 1, 0.0,
+    proxyPanel.setBorder(titledBorder1);
+    proxyPanel.setLayout(new GridBagLayout());
+    proxyPanel.add(serverLabel, new GridBagConstraints(0, 1, 1, 1, 0.0,
             0.0, GridBagConstraints.WEST, GridBagConstraints.NONE,
-            new Insets(16, 0, 0, 0), 5, 1));
-    jPanel1.add(useProxy, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0,
+            new Insets(0, 2, 2, 0), 5, 0));
+    proxyPanel.add(portLabel, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0,
+            GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,
+                    0, 2, 0), 11, 0));
+    proxyPanel.add(useProxy, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0,
             GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,
                     2, 5, 185), 2, -4));
-    jPanel1.add(proxyPortTB, new GridBagConstraints(3, 1, 1, 1, 1.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(0, 2, 4, 2), 54, 1));
-    jPanel1.add(proxyServerTB, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(0, 2, 4, 0), 263, 1));
-    connectTab.add(defaultBrowser, new GridBagConstraints(1, 1, 1, 1, 1.0,
-            0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(15, 0, 0, 15), 307, 1));
-    connectTab.add(usagestats, new GridBagConstraints(0, 4, 1, 1, 1.0, 0.0,
-            GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(0, 2, 4, 2), 70, 1));
-    connectTab.add(questionnaire, new GridBagConstraints(1, 4, 1, 1, 1.0,
+    proxyPanel.add(proxyPortTB, new GridBagConstraints(3, 1, 1, 1, 1.0,
             0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(0, 2, 4, 2), 70, 1));
-    connectTab.add(versioncheck, new GridBagConstraints(0, 5, 1, 1, 1.0,
+            new Insets(0, 2, 2, 2), 54, 1));
+    proxyPanel.add(proxyServerTB, new GridBagConstraints(1, 1, 1, 1, 1.0,
             0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-            new Insets(0, 2, 4, 2), 70, 1));
-    return connectTab;
+            new Insets(0, 2, 2, 0), 263, 1));
+
+    return proxyPanel;
+  }
+
+  /**
+   * Initialises the checkboxes in the Connections tab
+   */
+  private void initConnTabCheckboxes()
+  {
+    // Usage stats checkbox label
+    usagestats.setText(MessageManager
+            .getString("label.send_usage_statistics"));
+    usagestats.setFont(LABEL_FONT);
+    usagestats.setHorizontalAlignment(SwingConstants.RIGHT);
+    usagestats.setHorizontalTextPosition(SwingConstants.LEADING);
+
+    // Questionnaire checkbox label
+    questionnaire.setText(MessageManager
+            .getString("label.check_for_questionnaires"));
+    questionnaire.setFont(LABEL_FONT);
+    questionnaire.setHorizontalAlignment(SwingConstants.RIGHT);
+    questionnaire.setHorizontalTextPosition(SwingConstants.LEADING);
+
+    // Check for latest version checkbox label
+    versioncheck.setText(MessageManager
+            .getString("label.check_for_latest_version"));
+    versioncheck.setFont(LABEL_FONT);
+    versioncheck.setHorizontalAlignment(SwingConstants.RIGHT);
+    versioncheck.setHorizontalTextPosition(SwingConstants.LEADING);
   }
 
   /**
@@ -1357,8 +1535,82 @@ public class GPreferences extends JPanel
 
   public void useProxy_actionPerformed()
   {
-    proxyServerTB.setEnabled(useProxy.isSelected());
-    proxyPortTB.setEnabled(useProxy.isSelected());
+    boolean enabled = useProxy.isSelected();
+    portLabel.setEnabled(enabled);
+    serverLabel.setEnabled(enabled);
+    proxyServerTB.setEnabled(enabled);
+    proxyPortTB.setEnabled(enabled);
+  }
+
+  /**
+   * Customer renderer for JTable: supports column of radio buttons
+   */
+  public class RadioButtonRenderer extends JRadioButton implements
+          TableCellRenderer
+  {
+    public RadioButtonRenderer()
+    {
+      setHorizontalAlignment(CENTER);
+      setToolTipText(MessageManager.getString("label.urltooltip"));
+    }
+
+    @Override
+    public Component getTableCellRendererComponent(JTable table,
+            Object value, boolean isSelected, boolean hasFocus, int row,
+            int column)
+    {
+      setSelected((boolean) value);
+
+      // set colours to match rest of table
+      if (isSelected)
+       {
+         setBackground(table.getSelectionBackground());
+         setForeground(table.getSelectionForeground());
+       }
+       else
+       {
+         setBackground(table.getBackground());
+         setForeground(table.getForeground());
+      }
+      return this;
+    }
   }
 
+  /**
+   * Customer cell editor for JTable: supports column of radio buttons in
+   * conjunction with renderer
+   */
+  public class RadioButtonEditor extends AbstractCellEditor implements
+            TableCellEditor
+    {
+      private JRadioButton button = new JRadioButton();
+
+      public RadioButtonEditor()
+      {
+      button.setHorizontalAlignment(SwingConstants.CENTER);
+      this.button.addActionListener(new ActionListener()
+        {
+          @Override
+          public void actionPerformed(ActionEvent e)
+          {
+            fireEditingStopped();
+          }
+        });
+      }
+
+      @Override
+      public Component getTableCellEditorComponent(JTable table,
+              Object value, boolean isSelected, int row, int column)
+      {
+      button.setSelected((boolean) value);
+        return button;
+      }
+
+      @Override
+      public Object getCellEditorValue()
+      {
+      return button.isSelected();
+      }
+
+  }
 }
index dbce5f3..ab3ea2c 100755 (executable)
@@ -29,19 +29,48 @@ import java.awt.Font;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
 import java.awt.Insets;
-import java.awt.Panel;
 import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.awt.event.KeyAdapter;
 import java.awt.event.KeyEvent;
 
 import javax.swing.BorderFactory;
+import javax.swing.JButton;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 
-public class GSequenceLink extends Panel
+public class GSequenceLink extends JPanel
 {
+
+  JTextField nameTB = new JTextField();
+
+  JTextField urlTB = new JTextField();
+
+  JButton insertSeq = new JButton();
+
+  JButton insertDBAcc = new JButton();
+
+  JLabel insert = new JLabel();
+
+  JLabel jLabel1 = new JLabel();
+
+  JLabel jLabel2 = new JLabel();
+
+  JLabel jLabel3 = new JLabel();
+
+  JLabel jLabel4 = new JLabel();
+
+  JLabel jLabel5 = new JLabel();
+
+  JLabel jLabel6 = new JLabel();
+
+  JPanel jPanel1 = new JPanel();
+
+  GridBagLayout gridBagLayout1 = new GridBagLayout();
+
   public GSequenceLink()
   {
     try
@@ -77,23 +106,53 @@ public class GSequenceLink extends Panel
         urlTB_keyTyped(e);
       }
     });
+
+    insertSeq.setLocation(77, 75);
+    insertSeq.setSize(141, 24);
+    insertSeq.setText(MessageManager.getString("action.seq_id"));
+    insertSeq.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        insertSeq_action(e);
+      }
+    });
+
+    insertDBAcc.setLocation(210, 75);
+    insertDBAcc.setSize(141, 24);
+    insertDBAcc.setText(MessageManager.getString("action.db_acc"));
+    insertDBAcc.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        insertDBAcc_action(e);
+      }
+    });
+
+    insert.setText(MessageManager.getString("label.insert"));
+    insert.setFont(JvSwingUtils.getLabelFont());
+    insert.setHorizontalAlignment(SwingConstants.RIGHT);
+    insert.setBounds(17, 78, 58, 16);
+
     jLabel1.setFont(JvSwingUtils.getLabelFont());
     jLabel1.setHorizontalAlignment(SwingConstants.TRAILING);
     jLabel1.setText(MessageManager.getString("label.link_name"));
     jLabel1.setBounds(new Rectangle(4, 10, 71, 24));
     jLabel2.setFont(JvSwingUtils.getLabelFont());
     jLabel2.setHorizontalAlignment(SwingConstants.TRAILING);
-    jLabel2.setText(MessageManager.getString("label.url"));
+    jLabel2.setText(MessageManager.getString("label.url:"));
     jLabel2.setBounds(new Rectangle(17, 37, 54, 27));
     jLabel3.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
     jLabel3.setText(MessageManager.getString("label.use_sequence_id_1"));
-    jLabel3.setBounds(new Rectangle(21, 72, 351, 15));
+    jLabel3.setBounds(new Rectangle(21, 102, 351, 15));
     jLabel4.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
     jLabel4.setText(MessageManager.getString("label.use_sequence_id_2"));
-    jLabel4.setBounds(new Rectangle(21, 88, 351, 15));
+    jLabel4.setBounds(new Rectangle(21, 118, 351, 15));
     jLabel5.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
     jLabel5.setText(MessageManager.getString("label.use_sequence_id_3"));
-    jLabel5.setBounds(new Rectangle(21, 106, 351, 15));
+    jLabel5.setBounds(new Rectangle(21, 136, 351, 15));
 
     String lastLabel = MessageManager.getString("label.use_sequence_id_4");
     if (lastLabel.length() > 0)
@@ -101,7 +160,7 @@ public class GSequenceLink extends Panel
       // e.g. Spanish version has longer text
       jLabel6.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
       jLabel6.setText(lastLabel);
-      jLabel6.setBounds(new Rectangle(21, 122, 351, 15));
+      jLabel6.setBounds(new Rectangle(21, 152, 351, 15));
     }
 
     jPanel1.setBorder(BorderFactory.createEtchedBorder());
@@ -109,16 +168,19 @@ public class GSequenceLink extends Panel
     jPanel1.add(jLabel1);
     jPanel1.add(nameTB);
     jPanel1.add(urlTB);
+    jPanel1.add(insertSeq);
+    jPanel1.add(insertDBAcc);
+    jPanel1.add(insert);
     jPanel1.add(jLabel2);
     jPanel1.add(jLabel3);
     jPanel1.add(jLabel4);
     jPanel1.add(jLabel5);
 
-    int height = 130;
+    int height = 160;
     if (lastLabel.length() > 0)
     {
       jPanel1.add(jLabel6);
-      height = 146;
+      height = 176;
     }
 
     this.add(jPanel1, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0,
@@ -163,25 +225,13 @@ public class GSequenceLink extends Panel
     return false;
   }
 
-  JTextField nameTB = new JTextField();
-
-  JTextField urlTB = new JTextField();
-
-  JLabel jLabel1 = new JLabel();
-
-  JLabel jLabel2 = new JLabel();
-
-  JLabel jLabel3 = new JLabel();
-
-  JLabel jLabel4 = new JLabel();
-
-  JLabel jLabel5 = new JLabel();
-
-  JLabel jLabel6 = new JLabel();
-
-  JPanel jPanel1 = new JPanel();
-
-  GridBagLayout gridBagLayout1 = new GridBagLayout();
+  public void notifyDuplicate()
+  {
+    JvOptionPane.showInternalMessageDialog(jalview.gui.Desktop.desktop,
+            MessageManager.getString("warn.name_cannot_be_duplicate"),
+            MessageManager.getString("label.invalid_name"),
+            JvOptionPane.WARNING_MESSAGE);
+  }
 
   public void nameTB_keyTyped(KeyEvent e)
   {
@@ -200,4 +250,23 @@ public class GSequenceLink extends Panel
     // }
 
   }
+
+  public void insertSeq_action(ActionEvent e)
+  {
+    insertIntoUrl(insertSeq.getText());
+  }
+
+  public void insertDBAcc_action(ActionEvent e)
+  {
+    insertIntoUrl(insertDBAcc.getText());
+  }
+
+  private void insertIntoUrl(String insertion)
+  {
+    int pos = urlTB.getCaretPosition();
+    String text = urlTB.getText();
+    String newText = text.substring(0, pos) + insertion
+            + text.substring(pos);
+    urlTB.setText(newText);
+  }
 }
index 6b89ab4..d8f3f61 100644 (file)
@@ -170,8 +170,8 @@ public abstract class GStructureViewer extends JInternalFrame implements
       }
     });
     alignStructs = new JMenuItem();
-    alignStructs
-            .setText(MessageManager.getString("label.align_structures"));
+    alignStructs.setText(MessageManager
+            .getString("label.superpose_structures"));
     alignStructs.addActionListener(new ActionListener()
     {
       @Override
@@ -181,7 +181,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
       }
     });
 
-    viewerActionMenu = new JMenu();
+    viewerActionMenu = new JMenu(); // text set in sub-classes
     viewerActionMenu.setVisible(false);
     viewerActionMenu.add(alignStructs);
     colourMenu = new JMenu();
@@ -219,9 +219,8 @@ public abstract class GStructureViewer extends JInternalFrame implements
   {
   }
 
-  protected void alignStructs_actionPerformed(ActionEvent actionEvent)
-  {
-  }
+  protected abstract String alignStructs_actionPerformed(
+          ActionEvent actionEvent);
 
   public void pdbFile_actionPerformed(ActionEvent actionEvent)
   {
index fe20baf..aa5319c 100755 (executable)
@@ -104,7 +104,7 @@ public class GUserDefinedColours extends JPanel
 
   protected JCheckBox caseSensitive = new JCheckBox();
 
-  protected JButton lcaseColour = new JButton();
+  protected JCheckBox lcaseColour = new JCheckBox();
 
   protected List<JButton> selectedButtons;
 
@@ -223,14 +223,8 @@ public class GUserDefinedColours extends JPanel
     });
     lcaseColour
             .setText(MessageManager.getString("label.lower_case_colour"));
-    lcaseColour.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        lcaseColour_actionPerformed(e);
-      }
-    });
+    lcaseColour.setToolTipText(MessageManager
+            .getString("label.lower_case_tip"));
 
     saveLoadPanel.add(savebutton);
     saveLoadPanel.add(loadbutton);
index 647fc3a..de0bf77 100755 (executable)
@@ -28,36 +28,43 @@ import java.io.PrintStream;
 /**
  * A class to model rectangular matrices of double values and operations on them
  */
-public class Matrix
+public class Matrix implements MatrixI
 {
   /*
    * the cell values in row-major order
    */
-  public double[][] value;
+  private double[][] value;
 
   /*
    * the number of rows
    */
-  public int rows;
+  protected int rows;
 
   /*
    * the number of columns
    */
-  public int cols;
+  protected int cols;
 
-  /** DOCUMENT ME!! */
-  public double[] d; // Diagonal
+  protected double[] d; // Diagonal
 
-  /** DOCUMENT ME!! */
-  public double[] e; // off diagonal
+  protected double[] e; // off diagonal
 
   /**
    * maximum number of iterations for tqli
    * 
    */
-  int maxIter = 45; // fudge - add 15 iterations, just in case
+  private static final int maxIter = 45; // fudge - add 15 iterations, just in
+                                         // case
 
   /**
+   * Default constructor
+   */
+  public Matrix()
+  {
+
+  }
+  
+  /**
    * Creates a new Matrix object. For example
    * 
    * <pre>
@@ -82,11 +89,12 @@ public class Matrix
   }
 
   /**
-   * Returns a new matrix which is the transposes of this one
+   * Returns a new matrix which is the transpose of this one
    * 
    * @return DOCUMENT ME!
    */
-  public Matrix transpose()
+  @Override
+  public MatrixI transpose()
   {
     double[][] out = new double[cols][rows];
 
@@ -106,14 +114,16 @@ public class Matrix
    * 
    * @param ps
    *          DOCUMENT ME!
+   * @param format
    */
-  public void print(PrintStream ps)
+  @Override
+  public void print(PrintStream ps, String format)
   {
     for (int i = 0; i < rows; i++)
     {
       for (int j = 0; j < cols; j++)
       {
-        Format.print(ps, "%8.2f", value[i][j]);
+        Format.print(ps, format, getValue(i, j));
       }
 
       ps.println();
@@ -132,24 +142,27 @@ public class Matrix
    *           if the number of columns in the pre-multiplier is not equal to
    *           the number of rows in the multiplicand (this)
    */
-  public Matrix preMultiply(Matrix in)
+  @Override
+  public MatrixI preMultiply(MatrixI in)
   {
-    if (in.cols != this.rows)
+    if (in.width() != rows)
     {
       throw new IllegalArgumentException("Can't pre-multiply " + this.rows
-              + " rows by " + in.cols + " columns");
+              + " rows by " + in.width() + " columns");
     }
-    double[][] tmp = new double[in.rows][this.cols];
+    double[][] tmp = new double[in.height()][this.cols];
 
-    for (int i = 0; i < in.rows; i++)
+    for (int i = 0; i < in.height(); i++)
     {
       for (int j = 0; j < this.cols; j++)
       {
-        tmp[i][j] = 0.0;
-
-        for (int k = 0; k < in.cols; k++)
+        /*
+         * result[i][j] is the vector product of 
+         * in.row[i] and this.column[j]
+         */
+        for (int k = 0; k < in.width(); k++)
         {
-          tmp[i][j] += (in.value[i][k] * this.value[k][j]);
+          tmp[i][j] += (in.getValue(i, k) * this.value[k][j]);
         }
       }
     }
@@ -195,12 +208,13 @@ public class Matrix
    *           number of columns in the multiplicand (this)
    * @see #preMultiply(Matrix)
    */
-  public Matrix postMultiply(Matrix in)
+  @Override
+  public MatrixI postMultiply(MatrixI in)
   {
-    if (in.rows != this.cols)
+    if (in.height() != this.cols)
     {
       throw new IllegalArgumentException("Can't post-multiply " + this.cols
-              + " columns by " + in.rows + " rows");
+              + " columns by " + in.height() + " rows");
     }
     return in.preMultiply(this);
   }
@@ -210,7 +224,8 @@ public class Matrix
    * 
    * @return
    */
-  public Matrix copy()
+  @Override
+  public MatrixI copy()
   {
     double[][] newmat = new double[rows][cols];
 
@@ -225,10 +240,10 @@ public class Matrix
   /**
    * DOCUMENT ME!
    */
+  @Override
   public void tred()
   {
     int n = rows;
-    int l;
     int k;
     int j;
     int i;
@@ -244,7 +259,7 @@ public class Matrix
 
     for (i = n; i >= 2; i--)
     {
-      l = i - 1;
+      final int l = i - 1;
       h = 0.0;
       scale = 0.0;
 
@@ -252,22 +267,23 @@ public class Matrix
       {
         for (k = 1; k <= l; k++)
         {
-          scale += Math.abs(value[i - 1][k - 1]);
+          double v = Math.abs(getValue(i - 1, k - 1));
+          scale += v;
         }
 
         if (scale == 0.0)
         {
-          e[i - 1] = value[i - 1][l - 1];
+          e[i - 1] = getValue(i - 1, l - 1);
         }
         else
         {
           for (k = 1; k <= l; k++)
           {
-            value[i - 1][k - 1] /= scale;
-            h += (value[i - 1][k - 1] * value[i - 1][k - 1]);
+            double v = divideValue(i - 1, k - 1, scale);
+            h += v * v;
           }
 
-          f = value[i - 1][l - 1];
+          f = getValue(i - 1, l - 1);
 
           if (f > 0)
           {
@@ -280,46 +296,48 @@ public class Matrix
 
           e[i - 1] = scale * g;
           h -= (f * g);
-          value[i - 1][l - 1] = f - g;
+          setValue(i - 1, l - 1, f - g);
           f = 0.0;
 
           for (j = 1; j <= l; j++)
           {
-            value[j - 1][i - 1] = value[i - 1][j - 1] / h;
+            double val = getValue(i - 1, j - 1) / h;
+            setValue(j - 1, i - 1, val);
             g = 0.0;
 
             for (k = 1; k <= j; k++)
             {
-              g += (value[j - 1][k - 1] * value[i - 1][k - 1]);
+              g += (getValue(j - 1, k - 1) * getValue(i - 1, k - 1));
             }
 
             for (k = j + 1; k <= l; k++)
             {
-              g += (value[k - 1][j - 1] * value[i - 1][k - 1]);
+              g += (getValue(k - 1, j - 1) * getValue(i - 1, k - 1));
             }
 
             e[j - 1] = g / h;
-            f += (e[j - 1] * value[i - 1][j - 1]);
+            f += (e[j - 1] * getValue(i - 1, j - 1));
           }
 
           hh = f / (h + h);
 
           for (j = 1; j <= l; j++)
           {
-            f = value[i - 1][j - 1];
+            f = getValue(i - 1, j - 1);
             g = e[j - 1] - (hh * f);
             e[j - 1] = g;
 
             for (k = 1; k <= j; k++)
             {
-              value[j - 1][k - 1] -= ((f * e[k - 1]) + (g * value[i - 1][k - 1]));
+              double val = (f * e[k - 1]) + (g * getValue(i - 1, k - 1));
+              addValue(j - 1, k - 1, -val);
             }
           }
         }
       }
       else
       {
-        e[i - 1] = value[i - 1][l - 1];
+        e[i - 1] = getValue(i - 1, l - 1);
       }
 
       d[i - 1] = h;
@@ -330,7 +348,7 @@ public class Matrix
 
     for (i = 1; i <= n; i++)
     {
-      l = i - 1;
+      final int l = i - 1;
 
       if (d[i - 1] != 0.0)
       {
@@ -340,30 +358,66 @@ public class Matrix
 
           for (k = 1; k <= l; k++)
           {
-            g += (value[i - 1][k - 1] * value[k - 1][j - 1]);
+            g += (getValue(i - 1, k - 1) * getValue(k - 1, j - 1));
           }
 
           for (k = 1; k <= l; k++)
           {
-            value[k - 1][j - 1] -= (g * value[k - 1][i - 1]);
+            addValue(k - 1, j - 1, -(g * getValue(k - 1, i - 1)));
           }
         }
       }
 
-      d[i - 1] = value[i - 1][i - 1];
-      value[i - 1][i - 1] = 1.0;
+      d[i - 1] = getValue(i - 1, i - 1);
+      setValue(i - 1, i - 1, 1.0);
 
       for (j = 1; j <= l; j++)
       {
-        value[j - 1][i - 1] = 0.0;
-        value[i - 1][j - 1] = 0.0;
+        setValue(j - 1, i - 1, 0.0);
+        setValue(i - 1, j - 1, 0.0);
       }
     }
   }
 
   /**
+   * Adds f to the value at [i, j] and returns the new value
+   * 
+   * @param i
+   * @param j
+   * @param f
+   */
+  protected double addValue(int i, int j, double f)
+  {
+    double v = value[i][j] + f;
+    value[i][j] = v;
+    return v;
+  }
+
+  /**
+   * Divides the value at [i, j] by divisor and returns the new value. If d is
+   * zero, returns the unchanged value.
+   * 
+   * @param i
+   * @param j
+   * @param divisor
+   * @return
+   */
+  protected double divideValue(int i, int j, double divisor)
+  {
+    if (divisor == 0d)
+    {
+      return getValue(i, j);
+    }
+    double v = value[i][j];
+    v = v / divisor;
+    value[i][j] = v;
+    return v;
+  }
+
+  /**
    * DOCUMENT ME!
    */
+  @Override
   public void tqli() throws Exception
   {
     int n = rows;
@@ -376,7 +430,6 @@ public class Matrix
     double s;
     double r;
     double p;
-    ;
 
     double g;
     double f;
@@ -459,9 +512,9 @@ public class Matrix
 
             for (k = 1; k <= n; k++)
             {
-              f = value[k - 1][i];
-              value[k - 1][i] = (s * value[k - 1][i - 1]) + (c * f);
-              value[k - 1][i - 1] = (c * value[k - 1][i - 1]) - (s * f);
+              f = getValue(k - 1, i);
+              setValue(k - 1, i, (s * getValue(k - 1, i - 1)) + (c * f));
+              setValue(k - 1, i - 1, (c * getValue(k - 1, i - 1)) - (s * f));
             }
           }
 
@@ -473,6 +526,17 @@ public class Matrix
     }
   }
 
+  @Override
+  public double getValue(int i, int j)
+  {
+    return value[i][j];
+  }
+
+  public void setValue(int i, int j, double val)
+  {
+    value[i][j] = val;
+  }
+
   /**
    * DOCUMENT ME!
    */
@@ -725,16 +789,14 @@ public class Matrix
   }
 
   /**
-   * DOCUMENT ME!
+   * Answers the first argument with the sign of the second argument
    * 
    * @param a
-   *          DOCUMENT ME!
    * @param b
-   *          DOCUMENT ME!
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
-  public double sign(double a, double b)
+  static double sign(double a, double b)
   {
     if (b < 0)
     {
@@ -770,12 +832,14 @@ public class Matrix
    * 
    * @param ps
    *          DOCUMENT ME!
+   * @param format
    */
-  public void printD(PrintStream ps)
+  @Override
+  public void printD(PrintStream ps, String format)
   {
     for (int j = 0; j < rows; j++)
     {
-      Format.print(ps, "%15.4e", d[j]);
+      Format.print(ps, format, d[j]);
     }
   }
 
@@ -784,12 +848,45 @@ public class Matrix
    * 
    * @param ps
    *          DOCUMENT ME!
+   * @param format TODO
    */
-  public void printE(PrintStream ps)
+  @Override
+  public void printE(PrintStream ps, String format)
   {
     for (int j = 0; j < rows; j++)
     {
-      Format.print(ps, "%15.4e", e[j]);
+      Format.print(ps, format, e[j]);
     }
   }
+
+  @Override
+  public double[] getD()
+  {
+    return d;
+  }
+
+  @Override
+  public double[] getE()
+  {
+    return e;
+  }
+  
+  @Override
+  public int height() {
+    return rows;
+  }
+
+  @Override
+  public int width()
+  {
+    return cols;
+  }
+
+  @Override
+  public double[] getRow(int i)
+  {
+    double[] row = new double[cols];
+    System.arraycopy(value[i], 0, row, 0, cols);
+    return row;
+  }
 }
diff --git a/src/jalview/math/MatrixI.java b/src/jalview/math/MatrixI.java
new file mode 100644 (file)
index 0000000..d74a98b
--- /dev/null
@@ -0,0 +1,59 @@
+package jalview.math;
+
+import java.io.PrintStream;
+
+public interface MatrixI
+{
+  /**
+   * Answers the number of columns
+   * 
+   * @return
+   */
+  int width();
+
+  /**
+   * Answers the number of rows
+   * 
+   * @return
+   */
+  int height();
+
+  /**
+   * Answers the value at row i, column j
+   * 
+   * @param i
+   * @param j
+   * @return
+   */
+  double getValue(int i, int j);
+
+  /**
+   * Answers a copy of the values in the i'th row
+   * 
+   * @return
+   */
+  double[] getRow(int i);
+  
+  MatrixI copy();
+
+  MatrixI transpose();
+
+  MatrixI preMultiply(MatrixI m);
+
+  MatrixI postMultiply(MatrixI m);
+
+  double[] getD();
+
+  double[] getE();
+
+  void print(PrintStream ps, String format);
+
+  void printD(PrintStream ps, String format);
+
+  void printE(PrintStream ps, String format);
+
+  void tqli() throws Exception;
+
+  void tred();
+
+}
diff --git a/src/jalview/math/SparseMatrix.java b/src/jalview/math/SparseMatrix.java
new file mode 100644 (file)
index 0000000..72f0963
--- /dev/null
@@ -0,0 +1,218 @@
+package jalview.math;
+
+import jalview.ext.android.SparseDoubleArray;
+
+/**
+ * A variant of Matrix intended for use for sparse (mostly zero) matrices. This
+ * class uses a SparseDoubleArray to hold each row of the matrix. The sparse
+ * array only stores non-zero values. This gives a smaller memory footprint, and
+ * fewer matrix calculation operations, for mostly zero matrices.
+ * 
+ * @author gmcarstairs
+ */
+public class SparseMatrix extends Matrix
+{
+  /*
+   * we choose columns for the sparse arrays as this allows
+   * optimisation of the preMultiply() method used in PCA.run()
+   */
+  SparseDoubleArray[] sparseColumns;
+
+  /**
+   * Constructor given data in [row][column] order
+   * 
+   * @param v
+   */
+  public SparseMatrix(double[][] v)
+  {
+    rows = v.length;
+    if (rows > 0) {
+      cols = v[0].length;
+    }
+    sparseColumns = new SparseDoubleArray[cols];
+
+    /*
+     * transpose v[row][col] into [col][row] order
+     */
+    for (int col = 0; col < cols; col++)
+    {
+      SparseDoubleArray sparseColumn = new SparseDoubleArray();
+      sparseColumns[col] = sparseColumn;
+      for (int row = 0; row < rows; row++)
+      {
+        double value = v[row][col];
+        if (value != 0d)
+        {
+          sparseColumn.put(row, value);
+        }
+      }
+    }
+  }
+
+  /**
+   * Answers the value at row i, column j
+   */
+  @Override
+  public double getValue(int i, int j)
+  {
+    return sparseColumns[j].get(i);
+  }
+
+  /**
+   * Sets the value at row i, column j to val
+   */
+  @Override
+  public void setValue(int i, int j, double val)
+  {
+    if (val == 0d)
+    {
+      sparseColumns[j].delete(i);
+    }
+    else
+    {
+      sparseColumns[j].put(i, val);
+    }
+  }
+
+  @Override
+  public double[] getColumn(int i)
+  {
+    double[] col = new double[height()];
+
+    SparseDoubleArray vals = sparseColumns[i];
+    for (int nonZero = 0; nonZero < vals.size(); nonZero++)
+    {
+      col[vals.keyAt(nonZero)] = vals.valueAt(nonZero);
+    }
+    return col;
+  }
+
+  @Override
+  public MatrixI copy()
+  {
+    double[][] vals = new double[height()][width()];
+    for (int i = 0; i < height(); i++)
+    {
+      vals[i] = getRow(i);
+    }
+    return new SparseMatrix(vals);
+  }
+
+  @Override
+  public MatrixI transpose()
+  {
+    double[][] out = new double[cols][rows];
+
+    /*
+     * for each column...
+     */
+    for (int i = 0; i < cols; i++)
+    {
+      /*
+       * put non-zero values into the corresponding row
+       * of the transposed matrix
+       */
+      SparseDoubleArray vals = sparseColumns[i];
+      for (int nonZero = 0; nonZero < vals.size(); nonZero++)
+      {
+        out[i][vals.keyAt(nonZero)] = vals.valueAt(nonZero);
+      }
+    }
+
+    return new SparseMatrix(out);
+  }
+
+  /**
+   * Answers a new matrix which is the product in.this. If the product contains
+   * less than 20% non-zero values, it is returned as a SparseMatrix, else as a
+   * Matrix.
+   * <p>
+   * This method is optimised for the sparse arrays which store column values
+   * for a SparseMatrix. Note that postMultiply is not so optimised. That would
+   * require redundantly also storing sparse arrays for the rows, which has not
+   * been done. Currently only preMultiply is used in Jalview.
+   */
+  @Override
+  public MatrixI preMultiply(MatrixI in)
+  {
+    if (in.width() != rows)
+    {
+      throw new IllegalArgumentException("Can't pre-multiply " + this.rows
+              + " rows by " + in.width() + " columns");
+    }
+    double[][] tmp = new double[in.height()][this.cols];
+
+    long count = 0L;
+    for (int i = 0; i < in.height(); i++)
+    {
+      for (int j = 0; j < this.cols; j++)
+      {
+        /*
+         * result[i][j] is the vector product of 
+         * in.row[i] and this.column[j]
+         * we only need to use non-zero values from the column
+         */
+        SparseDoubleArray vals = sparseColumns[j];
+        boolean added = false;
+        for (int nonZero = 0; nonZero < vals.size(); nonZero++)
+        {
+          int myRow = vals.keyAt(nonZero);
+          double myValue = vals.valueAt(nonZero);
+          tmp[i][j] += (in.getValue(i, myRow) * myValue);
+          added = true;
+        }
+        if (added && tmp[i][j] != 0d)
+        {
+          count++; // non-zero entry in product
+        }
+      }
+    }
+
+    /*
+     * heuristic rule - if product is more than 80% zero
+     * then construct a SparseMatrix, else a Matrix
+     */
+    if (count * 5 < in.height() * cols)
+    {
+      return new SparseMatrix(tmp);
+    }
+    else
+    {
+      return new Matrix(tmp);
+    }
+  }
+
+  @Override
+  protected double divideValue(int i, int j, double divisor)
+  {
+    if (divisor == 0d)
+    {
+      return getValue(i, j);
+    }
+    double v = sparseColumns[j].divide(i, divisor);
+    return v;
+  }
+
+  @Override
+  protected double addValue(int i, int j, double addend)
+  {
+    double v = sparseColumns[j].add(i, addend);
+    return v;
+  }
+
+  /**
+   * Returns the fraction of the whole matrix size that is actually modelled in
+   * sparse arrays (normally, the non-zero values)
+   * 
+   * @return
+   */
+  public float getFillRatio()
+  {
+    long count = 0L;
+    for (SparseDoubleArray col : sparseColumns)
+    {
+      count += col.size();
+    }
+    return count / (float) (height() * width());
+  }
+}
index 6f84a2e..3a27c7d 100644 (file)
@@ -307,7 +307,7 @@ public class AnnotationRenderer
   public void updateFromAlignViewport(AlignViewportI av)
   {
     charWidth = av.getCharWidth();
-    endRes = av.getEndRes();
+    endRes = av.getRanges().getEndRes();
     charHeight = av.getCharHeight();
     hasHiddenColumns = av.hasHiddenColumns();
     validCharWidth = av.isValidCharWidth();
index 3f5cd11..b6f7fe6 100644 (file)
@@ -47,7 +47,7 @@ public class ResidueShader implements ResidueShaderI
   private boolean conservationColouring;
 
   /*
-   * the phsyico-chemical property conservation scores for columns, with values
+   * the physico-chemical property conservation scores for columns, with values
    * 0-9, '+' (all properties conserved), '*' (residue fully conserved) or '-' (gap)
    * (may be null if colour by conservation is not selected)
    */
diff --git a/src/jalview/renderer/seqfeatures/FeatureColourFinder.java b/src/jalview/renderer/seqfeatures/FeatureColourFinder.java
new file mode 100644 (file)
index 0000000..1db2004
--- /dev/null
@@ -0,0 +1,124 @@
+package jalview.renderer.seqfeatures;
+
+import jalview.api.FeatureRenderer;
+import jalview.api.FeaturesDisplayedI;
+import jalview.datamodel.SequenceI;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+
+/**
+ * A helper class to find feature colour using an associated FeatureRenderer
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class FeatureColourFinder
+{
+  /*
+   * the class we delegate feature finding to
+   */
+  private FeatureRenderer featureRenderer;
+
+  /*
+   * a 1-pixel image on which features can be drawn, for the case where
+   * transparency allows 'see-through' of multiple feature colours
+   */
+  private BufferedImage offscreenImage;
+
+  /**
+   * Constructor
+   * 
+   * @param fr
+   */
+  public FeatureColourFinder(FeatureRenderer fr)
+  {
+    featureRenderer = fr;
+    offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
+  }
+
+  /**
+   * Answers the feature colour to show for the given sequence and column
+   * position. This delegates to the FeatureRenderer to find the colour, which
+   * will depend on feature location, visibility, ordering, colour scheme, and
+   * whether or not transparency is applied. For feature rendering with
+   * transparency, this class provides a dummy 'offscreen' graphics context
+   * where multiple feature colours can be overlaid and the combined colour read
+   * back.
+   * <p>
+   * This method is not thread-safe when transparency is applied, since a shared
+   * BufferedImage would be used by all threads to hold the composite colour at
+   * a position. Each thread should use a separate instance of this class.
+   * 
+   * @param defaultColour
+   * @param seq
+   * @param column
+   *          alignment column position (base zero)
+   * @return
+   */
+  public Color findFeatureColour(Color defaultColour, SequenceI seq,
+          int column)
+  {
+    if (noFeaturesDisplayed())
+    {
+      return defaultColour;
+    }
+
+    Graphics g = null;
+
+    /*
+     * if transparency applies, provide a notional 1x1 graphics context 
+     * that has been primed with the default colour
+     */
+    if (featureRenderer.getTransparency() != 1f)
+    {
+      g = offscreenImage.getGraphics();
+      if (defaultColour != null)
+      {
+        offscreenImage.setRGB(0, 0, defaultColour.getRGB());
+      }
+    }
+
+    Color c = featureRenderer.findFeatureColour(seq, column, g);
+    if (c == null)
+    {
+      return defaultColour;
+    }
+
+    if (g != null)
+    {
+      c = new Color(offscreenImage.getRGB(0, 0));
+    }
+    return c;
+  }
+
+  /**
+   * Answers true if feature display is turned off, or there are no features
+   * configured to be visible
+   * 
+   * @return
+   */
+  boolean noFeaturesDisplayed()
+  {
+    if (featureRenderer == null
+            || !featureRenderer.getViewport().isShowSequenceFeatures())
+    {
+      return true;
+    }
+
+    if (!((FeatureRendererModel) featureRenderer).hasRenderOrder())
+    {
+      return true;
+    }
+
+    FeaturesDisplayedI displayed = featureRenderer.getFeaturesDisplayed();
+    if (displayed == null || displayed.getVisibleFeatureCount() == 0)
+    {
+      return true;
+    }
+
+    return false;
+  }
+}
index 9e0089f..72ac2c8 100644 (file)
@@ -23,6 +23,7 @@ package jalview.renderer.seqfeatures;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 
 import java.awt.AlphaComposite;
@@ -30,28 +31,11 @@ import java.awt.Color;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
 
 public class FeatureRenderer extends FeatureRendererModel
 {
-
-  FontMetrics fm;
-
-  int charOffset;
-
-  boolean offscreenRender = false;
-
-  protected SequenceI lastSeq;
-
-  char s;
-
-  int i;
-
-  int av_charHeight, av_charWidth;
-
-  boolean av_validCharWidth, av_isShowSeqFeatureHeight;
-
-  private Integer currentColour;
+  private static final AlphaComposite NO_TRANSPARENCY = AlphaComposite
+          .getInstance(AlphaComposite.SRC_OVER, 1.0f);
 
   /**
    * Constructor given a viewport
@@ -63,273 +47,252 @@ public class FeatureRenderer extends FeatureRendererModel
     this.av = viewport;
   }
 
-  protected void updateAvConfig()
+  /**
+   * Renders the sequence using the given feature colour between the given start
+   * and end columns. Returns true if at least one column is drawn, else false
+   * (the feature range does not overlap the start and end positions).
+   * 
+   * @param g
+   * @param seq
+   * @param featureStart
+   * @param featureEnd
+   * @param featureColour
+   * @param start
+   * @param end
+   * @param y1
+   * @param colourOnly
+   * @return
+   */
+  boolean renderFeature(Graphics g, SequenceI seq, int featureStart,
+          int featureEnd, Color featureColour, int start, int end, int y1,
+          boolean colourOnly)
   {
-    av_charHeight = av.getCharHeight();
-    av_charWidth = av.getCharWidth();
-    av_validCharWidth = av.isValidCharWidth();
-    av_isShowSeqFeatureHeight = av.isShowSequenceFeaturesHeight();
-  }
+    int charHeight = av.getCharHeight();
+    int charWidth = av.getCharWidth();
+    boolean validCharWidth = av.isValidCharWidth();
 
-  void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
-          Color featureColour, int start, int end, int y1)
-  {
-    updateAvConfig();
-    if (((fstart <= end) && (fend >= start)))
+    if (featureStart > end || featureEnd < start)
     {
-      if (fstart < start)
-      { // fix for if the feature we have starts before the sequence start,
-        fstart = start; // but the feature end is still valid!!
-      }
-
-      if (fend >= end)
-      {
-        fend = end;
-      }
-      int pady = (y1 + av_charHeight) - av_charHeight / 5;
-      for (i = fstart; i <= fend; i++)
-      {
-        s = seq.getCharAt(i);
-
-        if (jalview.util.Comparison.isGap(s))
-        {
-          continue;
-        }
-
-        g.setColor(featureColour);
-
-        g.fillRect((i - start) * av_charWidth, y1, av_charWidth,
-                av_charHeight);
-
-        if (offscreenRender || !av_validCharWidth)
-        {
-          continue;
-        }
-
-        g.setColor(Color.white);
-        charOffset = (av_charWidth - fm.charWidth(s)) / 2;
-        g.drawString(String.valueOf(s), charOffset
-                + (av_charWidth * (i - start)), pady);
+      return false;
+    }
 
-      }
+    if (featureStart < start)
+    {
+      featureStart = start;
     }
-  }
+    if (featureEnd >= end)
+    {
+      featureEnd = end;
+    }
+    int pady = (y1 + charHeight) - charHeight / 5;
 
-  void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
-          Color featureColour, int start, int end, int y1, byte[] bs)
-  {
-    updateAvConfig();
-    if (((fstart <= end) && (fend >= start)))
+    FontMetrics fm = g.getFontMetrics();
+    for (int i = featureStart; i <= featureEnd; i++)
     {
-      if (fstart < start)
-      { // fix for if the feature we have starts before the sequence start,
-        fstart = start; // but the feature end is still valid!!
-      }
+      char s = seq.getCharAt(i);
 
-      if (fend >= end)
-      {
-        fend = end;
-      }
-      int pady = (y1 + av_charHeight) - av_charHeight / 5;
-      int ystrt = 0, yend = av_charHeight;
-      if (bs[0] != 0)
-      {
-        // signed - zero is always middle of residue line.
-        if (bs[1] < 128)
-        {
-          yend = av_charHeight * (128 - bs[1]) / 512;
-          ystrt = av_charHeight - yend / 2;
-        }
-        else
-        {
-          ystrt = av_charHeight / 2;
-          yend = av_charHeight * (bs[1] - 128) / 512;
-        }
-      }
-      else
+      if (Comparison.isGap(s))
       {
-        yend = av_charHeight * bs[1] / 255;
-        ystrt = av_charHeight - yend;
-
+        continue;
       }
-      for (i = fstart; i <= fend; i++)
-      {
-        s = seq.getCharAt(i);
-
-        if (jalview.util.Comparison.isGap(s))
-        {
-          continue;
-        }
 
-        g.setColor(featureColour);
-        int x = (i - start) * av_charWidth;
-        g.drawRect(x, y1, av_charWidth, av_charHeight);
-        g.fillRect(x, y1 + ystrt, av_charWidth, yend);
+      g.setColor(featureColour);
 
-        if (offscreenRender || !av_validCharWidth)
-        {
-          continue;
-        }
+      g.fillRect((i - start) * charWidth, y1, charWidth,
+              charHeight);
 
-        g.setColor(Color.black);
-        charOffset = (av_charWidth - fm.charWidth(s)) / 2;
-        g.drawString(String.valueOf(s), charOffset
-                + (av_charWidth * (i - start)), pady);
+      if (colourOnly || !validCharWidth)
+      {
+        continue;
       }
-    }
-  }
-
-  BufferedImage offscreenImage;
 
-  @Override
-  public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
-  {
-    return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
+      g.setColor(Color.white);
+      int charOffset = (charWidth - fm.charWidth(s)) / 2;
+      g.drawString(String.valueOf(s), charOffset
+              + (charWidth * (i - start)), pady);
+    }
+    return true;
   }
 
   /**
-   * This is used by Structure Viewers and the Overview Window to get the
-   * feature colour of the rendered sequence, returned as an RGB value
+   * Renders the sequence using the given SCORE feature colour between the given
+   * start and end columns. Returns true if at least one column is drawn, else
+   * false (the feature range does not overlap the start and end positions).
    * 
-   * @param defaultColour
+   * @param g
    * @param seq
-   * @param column
+   * @param fstart
+   * @param fend
+   * @param featureColour
+   * @param start
+   * @param end
+   * @param y1
+   * @param bs
+   * @param colourOnly
    * @return
    */
-  public synchronized int findFeatureColour(int defaultColour,
-          final SequenceI seq, int column)
+  boolean renderScoreFeature(Graphics g, SequenceI seq, int fstart,
+          int fend, Color featureColour, int start, int end, int y1,
+          byte[] bs, boolean colourOnly)
   {
-    if (!av.isShowSequenceFeatures())
+    if (fstart > end || fend < start)
     {
-      return defaultColour;
+      return false;
     }
 
-    SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
-    if (seq != lastSeq)
+    if (fstart < start)
+    { // fix for if the feature we have starts before the sequence start,
+      fstart = start; // but the feature end is still valid!!
+    }
+
+    if (fend >= end)
+    {
+      fend = end;
+    }
+    int charHeight = av.getCharHeight();
+    int pady = (y1 + charHeight) - charHeight / 5;
+    int ystrt = 0, yend = charHeight;
+    if (bs[0] != 0)
     {
-      lastSeq = seq;
-      lastSequenceFeatures = sequenceFeatures;
-      if (lastSequenceFeatures != null)
+      // signed - zero is always middle of residue line.
+      if (bs[1] < 128)
       {
-        sfSize = lastSequenceFeatures.length;
+        yend = charHeight * (128 - bs[1]) / 512;
+        ystrt = charHeight - yend / 2;
+      }
+      else
+      {
+        ystrt = charHeight / 2;
+        yend = charHeight * (bs[1] - 128) / 512;
       }
     }
     else
     {
-      if (lastSequenceFeatures != sequenceFeatures)
+      yend = charHeight * bs[1] / 255;
+      ystrt = charHeight - yend;
+
+    }
+
+    FontMetrics fm = g.getFontMetrics();
+    int charWidth = av.getCharWidth();
+
+    for (int i = fstart; i <= fend; i++)
+    {
+      char s = seq.getCharAt(i);
+
+      if (Comparison.isGap(s))
       {
-        lastSequenceFeatures = sequenceFeatures;
-        if (lastSequenceFeatures != null)
-        {
-          sfSize = lastSequenceFeatures.length;
-        }
+        continue;
       }
+
+      g.setColor(featureColour);
+      int x = (i - start) * charWidth;
+      g.drawRect(x, y1, charWidth, charHeight);
+      g.fillRect(x, y1 + ystrt, charWidth, yend);
+
+      if (colourOnly || !av.isValidCharWidth())
+      {
+        continue;
+      }
+
+      g.setColor(Color.black);
+      int charOffset = (charWidth - fm.charWidth(s)) / 2;
+      g.drawString(String.valueOf(s), charOffset
+              + (charWidth * (i - start)), pady);
     }
+    return true;
+  }
 
-    if (lastSequenceFeatures == null || sfSize == 0)
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Color findFeatureColour(SequenceI seq, int column, Graphics g)
+  {
+    if (!av.isShowSequenceFeatures())
     {
-      return defaultColour;
+      return null;
     }
 
-    if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
+    SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
+
+    if (sequenceFeatures == null || sequenceFeatures.length == 0)
     {
-      return Color.white.getRGB();
+      return null;
     }
 
-    // Only bother making an offscreen image if transparency is applied
-    if (transparency != 1.0f && offscreenImage == null)
+    if (Comparison.isGap(seq.getCharAt(column)))
     {
-      offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
+      return Color.white;
     }
 
-    currentColour = null;
-    // TODO: non-threadsafe - each rendering thread needs its own instance of
-    // the feature renderer - or this should be synchronized.
-    offscreenRender = true;
-
-    if (offscreenImage != null)
+    Color renderedColour = null;
+    if (transparency == 1.0f)
     {
-      offscreenImage.setRGB(0, 0, defaultColour);
-      drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
-
-      return offscreenImage.getRGB(0, 0);
+      /*
+       * simple case - just find the topmost rendered visible feature colour
+       */
+      renderedColour = findFeatureColour(seq, seq.findPosition(column));
     }
     else
     {
-      drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
-
-      if (currentColour == null)
-      {
-        return defaultColour;
-      }
-      else
-      {
-        return currentColour.intValue();
-      }
+      /*
+       * transparency case - draw all visible features in render order to
+       * build up a composite colour on the graphics context
+       */
+      renderedColour = drawSequence(g, seq, column, column, 0, true);
     }
-
+    return renderedColour;
   }
 
-  private volatile SequenceFeature[] lastSequenceFeatures;
-
-  int sfSize;
-
-  int sfindex;
-
-  int spos;
-
-  int epos;
-
   /**
-   * Draws the sequence on the graphics context, or just determines the colour
-   * that would be drawn (if flag offscreenrender is true).
+   * Draws the sequence features on the graphics context, or just determines the
+   * colour that would be drawn (if flag colourOnly is true). Returns the last
+   * colour drawn (which may not be the effective colour if transparency
+   * applies), or null if no feature is drawn in the range given.
    * 
    * @param g
+   *          the graphics context to draw on (may be null if colourOnly==true)
    * @param seq
    * @param start
-   *          start column (or sequence position in offscreenrender mode)
+   *          start column
    * @param end
-   *          end column (not used in offscreenrender mode)
+   *          end column
    * @param y1
    *          vertical offset at which to draw on the graphics
+   * @param colourOnly
+   *          if true, only do enough to determine the colour for the position,
+   *          do not draw the character
+   * @return
    */
-  public synchronized void drawSequence(Graphics g, final SequenceI seq,
-          int start, int end, int y1)
+  public synchronized Color drawSequence(final Graphics g,
+          final SequenceI seq, int start, int end, int y1,
+          boolean colourOnly)
   {
     SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
     if (sequenceFeatures == null || sequenceFeatures.length == 0)
     {
-      return;
-    }
-
-    if (g != null)
-    {
-      fm = g.getFontMetrics();
+      return null;
     }
 
     updateFeatures();
 
-    if (lastSeq == null || seq != lastSeq
-            || sequenceFeatures != lastSequenceFeatures)
-    {
-      lastSeq = seq;
-      lastSequenceFeatures = sequenceFeatures;
-    }
-
-    if (transparency != 1 && g != null)
+    if (transparency != 1f && g != null)
     {
       Graphics2D g2 = (Graphics2D) g;
       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
               transparency));
     }
 
-    if (!offscreenRender)
-    {
-      spos = lastSeq.findPosition(start);
-      epos = lastSeq.findPosition(end);
-    }
+    int startPos = seq.findPosition(start);
+    int endPos = seq.findPosition(end);
+
+    int sfSize = sequenceFeatures.length;
+    Color drawnColour = null;
 
-    sfSize = lastSequenceFeatures.length;
+    /*
+     * iterate over features in ordering of their rendering (last is on top)
+     */
     for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
     {
       String type = renderOrder[renderIndex];
@@ -340,27 +303,29 @@ public class FeatureRenderer extends FeatureRendererModel
 
       // loop through all features in sequence to find
       // current feature to render
-      for (sfindex = 0; sfindex < sfSize; sfindex++)
+      for (int sfindex = 0; sfindex < sfSize; sfindex++)
       {
-        final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
+        final SequenceFeature sequenceFeature = sequenceFeatures[sfindex];
         if (!sequenceFeature.type.equals(type))
         {
           continue;
         }
 
+        /*
+         * a feature type may be flagged as shown but the group 
+         * an instance of it belongs to may be hidden
+         */
         if (featureGroupNotShown(sequenceFeature))
         {
           continue;
         }
 
         /*
-         * check feature overlaps the visible part of the alignment, 
-         * unless doing offscreenRender (to the Overview window or a 
-         * structure viewer) which is not limited 
+         * check feature overlaps the target range
+         * TODO: efficient retrieval of features overlapping a range
          */
-        if (!offscreenRender
-                && (sequenceFeature.getBegin() > epos || sequenceFeature
-                        .getEnd() < spos))
+        if (sequenceFeature.getBegin() > endPos
+                || sequenceFeature.getEnd() < startPos)
         {
           continue;
         }
@@ -368,58 +333,46 @@ public class FeatureRenderer extends FeatureRendererModel
         Color featureColour = getColour(sequenceFeature);
         boolean isContactFeature = sequenceFeature.isContactFeature();
 
-        if (offscreenRender && offscreenImage == null)
-        {
-          /*
-           * offscreen mode with no image (image is only needed if transparency 
-           * is applied to feature colours) - just check feature is rendered at 
-           * the requested position (start == sequence position in this mode)
-           */
-          boolean featureIsAtPosition = sequenceFeature.begin <= start
-                  && sequenceFeature.end >= start;
-          if (isContactFeature)
-          {
-            featureIsAtPosition = sequenceFeature.begin == start
-                    || sequenceFeature.end == start;
-          }
-          if (featureIsAtPosition)
-          {
-            // this is passed out to the overview and other sequence renderers
-            // (e.g. molecule viewer) to get displayed colour for rendered
-            // sequence
-            currentColour = new Integer(featureColour.getRGB());
-            // used to be retreived from av.featuresDisplayed
-            // currentColour = av.featuresDisplayed
-            // .get(sequenceFeatures[sfindex].type);
-
-          }
-        }
-        else if (isContactFeature)
+        if (isContactFeature)
         {
-          renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
+          boolean drawn = renderFeature(g, seq,
+                  seq.findIndex(sequenceFeature.begin) - 1,
                   seq.findIndex(sequenceFeature.begin) - 1, featureColour,
-                  start, end, y1);
-          renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
+                  start, end, y1, colourOnly);
+          drawn |= renderFeature(g, seq,
+                  seq.findIndex(sequenceFeature.end) - 1,
                   seq.findIndex(sequenceFeature.end) - 1, featureColour,
-                  start, end, y1);
-
+                  start, end, y1, colourOnly);
+          if (drawn)
+          {
+            drawnColour = featureColour;
+          }
         }
         else if (showFeature(sequenceFeature))
         {
-          if (av_isShowSeqFeatureHeight
+          if (av.isShowSequenceFeaturesHeight()
                   && !Float.isNaN(sequenceFeature.score))
           {
-            renderScoreFeature(g, seq,
+            boolean drawn = renderScoreFeature(g, seq,
                     seq.findIndex(sequenceFeature.begin) - 1,
-                    seq.findIndex(sequenceFeature.end) - 1,
-                    featureColour, start, end, y1,
-                    normaliseScore(sequenceFeature));
+                    seq.findIndex(sequenceFeature.end) - 1, featureColour,
+                    start, end, y1, normaliseScore(sequenceFeature),
+                    colourOnly);
+            if (drawn)
+            {
+              drawnColour = featureColour;
+            }
           }
           else
           {
-            renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
-                    seq.findIndex(sequenceFeature.end) - 1,
-                    featureColour, start, end, y1);
+            boolean drawn = renderFeature(g, seq,
+                    seq.findIndex(sequenceFeature.begin) - 1,
+                    seq.findIndex(sequenceFeature.end) - 1, featureColour,
+                    start, end, y1, colourOnly);
+            if (drawn)
+            {
+              drawnColour = featureColour;
+            }
           }
         }
       }
@@ -427,10 +380,14 @@ public class FeatureRenderer extends FeatureRendererModel
 
     if (transparency != 1.0f && g != null)
     {
+      /*
+       * reset transparency
+       */
       Graphics2D g2 = (Graphics2D) g;
-      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
-              1.0f));
+      g2.setComposite(NO_TRANSPARENCY);
     }
+
+    return drawnColour;
   }
 
   /**
@@ -459,7 +416,78 @@ public class FeatureRenderer extends FeatureRendererModel
   @Override
   public void featuresAdded()
   {
-    lastSeq = null;
     findAllFeatures();
   }
+
+  /**
+   * Returns the sequence feature colour rendered at the given sequence
+   * position, or null if none found. The feature of highest render order (i.e.
+   * on top) is found, subject to both feature type and feature group being
+   * visible, and its colour returned.
+   * 
+   * @param seq
+   * @param pos
+   * @return
+   */
+  Color findFeatureColour(SequenceI seq, int pos)
+  {
+    SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
+    if (sequenceFeatures == null || sequenceFeatures.length == 0)
+    {
+      return null;
+    }
+  
+    /*
+     * check for new feature added while processing
+     */
+    updateFeatures();
+
+    /*
+     * inspect features in reverse renderOrder (the last in the array is 
+     * displayed on top) until we find one that is rendered at the position
+     */
+    for (int renderIndex = renderOrder.length - 1; renderIndex >= 0; renderIndex--)
+    {
+      String type = renderOrder[renderIndex];
+      if (!showFeatureOfType(type))
+      {
+        continue;
+      }
+
+      for (int sfindex = 0; sfindex < sequenceFeatures.length; sfindex++)
+      {
+        SequenceFeature sequenceFeature = sequenceFeatures[sfindex];
+        if (!sequenceFeature.type.equals(type))
+        {
+          continue;
+        }
+
+        if (featureGroupNotShown(sequenceFeature))
+        {
+          continue;
+        }
+
+        /*
+         * check the column position is within the feature range
+         * (or is one of the two contact positions for a contact feature)
+         */
+        boolean featureIsAtPosition = sequenceFeature.begin <= pos
+                && sequenceFeature.end >= pos;
+        if (sequenceFeature.isContactFeature())
+        {
+          featureIsAtPosition = sequenceFeature.begin == pos
+                  || sequenceFeature.end == pos;
+        }
+        if (featureIsAtPosition)
+        {
+          return getColour(sequenceFeature);
+        }
+      }
+    }
+  
+    /*
+     * no displayed feature found at position
+     */
+    return null;
+  }
 }
index 1a3e9ef..220d3ab 100755 (executable)
@@ -27,6 +27,8 @@ import jalview.datamodel.Annotation;
 import jalview.datamodel.GraphLine;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
+import jalview.renderer.AnnotationRenderer;
+import jalview.util.Comparison;
 
 import java.awt.Color;
 import java.util.IdentityHashMap;
@@ -40,15 +42,25 @@ public class AnnotationColourGradient extends FollowerColourScheme
 
   public static final int ABOVE_THRESHOLD = 1;
 
-  public AlignmentAnnotation annotation;
+  private final AlignmentAnnotation annotation;
 
-  int aboveAnnotationThreshold = -1;
+  private final int aboveAnnotationThreshold;
 
   public boolean thresholdIsMinMax = false;
 
-  GraphLine annotationThreshold;
+  private GraphLine annotationThreshold;
 
-  float r1, g1, b1, rr, gg, bb;
+  private int redMin;
+
+  private int greenMin;
+
+  private int blueMin;
+
+  private int redRange;
+
+  private int greenRange;
+
+  private int blueRange;
 
   private boolean predefinedColours = false;
 
@@ -61,7 +73,7 @@ public class AnnotationColourGradient extends FollowerColourScheme
    */
   private boolean noGradient = false;
 
-  IdentityHashMap<SequenceI, AlignmentAnnotation> seqannot = null;
+  private IdentityHashMap<SequenceI, AlignmentAnnotation> seqannot = null;
 
   @Override
   public ColourSchemeI getInstance(AnnotatedCollectionI sg,
@@ -72,12 +84,12 @@ public class AnnotationColourGradient extends FollowerColourScheme
     acg.thresholdIsMinMax = thresholdIsMinMax;
     acg.annotationThreshold = (annotationThreshold == null) ? null
             : new GraphLine(annotationThreshold);
-    acg.r1 = r1;
-    acg.g1 = g1;
-    acg.b1 = b1;
-    acg.rr = rr;
-    acg.gg = gg;
-    acg.bb = bb;
+    acg.redMin = redMin;
+    acg.greenMin = greenMin;
+    acg.blueMin = blueMin;
+    acg.redRange = redRange;
+    acg.greenRange = greenRange;
+    acg.blueRange = blueRange;
     acg.predefinedColours = predefinedColours;
     acg.seqAssociated = seqAssociated;
     acg.noGradient = noGradient;
@@ -109,12 +121,12 @@ public class AnnotationColourGradient extends FollowerColourScheme
       annotationThreshold = annotation.threshold;
     }
     // clear values so we don't get weird black bands...
-    r1 = 254;
-    g1 = 254;
-    b1 = 254;
-    rr = 0;
-    gg = 0;
-    bb = 0;
+    redMin = 254;
+    greenMin = 254;
+    blueMin = 254;
+    redRange = 0;
+    greenRange = 0;
+    blueRange = 0;
 
     noGradient = true;
     checkLimits();
@@ -135,13 +147,13 @@ public class AnnotationColourGradient extends FollowerColourScheme
       annotationThreshold = annotation.threshold;
     }
 
-    r1 = minColour.getRed();
-    g1 = minColour.getGreen();
-    b1 = minColour.getBlue();
+    redMin = minColour.getRed();
+    greenMin = minColour.getGreen();
+    blueMin = minColour.getBlue();
 
-    rr = maxColour.getRed() - r1;
-    gg = maxColour.getGreen() - g1;
-    bb = maxColour.getBlue() - b1;
+    redRange = maxColour.getRed() - redMin;
+    greenRange = maxColour.getGreen() - greenMin;
+    blueRange = maxColour.getBlue() - blueMin;
 
     noGradient = false;
     checkLimits();
@@ -211,9 +223,9 @@ public class AnnotationColourGradient extends FollowerColourScheme
 
   float aamin = 0f, aamax = 0f;
 
-  public String getAnnotation()
+  public AlignmentAnnotation getAnnotation()
   {
-    return annotation.label;
+    return annotation;
   }
 
   public int getAboveThreshold()
@@ -235,12 +247,13 @@ public class AnnotationColourGradient extends FollowerColourScheme
 
   public Color getMinColour()
   {
-    return new Color((int) r1, (int) g1, (int) b1);
+    return new Color(redMin, greenMin, blueMin);
   }
 
   public Color getMaxColour()
   {
-    return new Color((int) (r1 + rr), (int) (g1 + gg), (int) (b1 + bb));
+    return new Color(redMin + redRange, greenMin + greenRange, blueMin
+            + blueRange);
   }
 
   /**
@@ -258,137 +271,157 @@ public class AnnotationColourGradient extends FollowerColourScheme
   }
 
   /**
-   * DOCUMENT ME!
+   * Returns the colour for a given character and position in a sequence
    * 
-   * @param n
-   *          DOCUMENT ME!
+   * @param c
+   *          the residue character
    * @param j
-   *          DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
+   *          the aligned position
+   * @param seq
+   *          the sequence
+   * @return
    */
   @Override
   public Color findColour(char c, int j, SequenceI seq)
   {
-    Color currentColour = Color.white;
-    AlignmentAnnotation annotation = (seqAssociated && seqannot != null ? seqannot
+    /*
+     * locate the annotation we are configured to colour by
+     */
+    AlignmentAnnotation ann = (seqAssociated && seqannot != null ? seqannot
             .get(seq) : this.annotation);
-    if (annotation == null)
+
+    /*
+     * if gap or no annotation at position, no colour (White)
+     */
+    if (ann == null || ann.annotations == null
+            || j >= ann.annotations.length || ann.annotations[j] == null
+            || Comparison.isGap(c))
     {
-      return currentColour;
+      return Color.white;
     }
-    // if ((threshold == 0) || aboveThreshold(c, j))
-    // {
-    if (annotation.annotations != null && j < annotation.annotations.length
-            && annotation.annotations[j] != null
-            && !jalview.util.Comparison.isGap(c))
+
+    Annotation aj = ann.annotations[j];
+    // 'use original colours' => colourScheme != null
+    // -> look up colour to be used
+    // predefined colours => preconfigured shading
+    // -> only use original colours reference if thresholding enabled &
+    // minmax exists
+    // annotation.hasIcons => null or black colours replaced with glyph
+    // colours
+    // -> reuse original colours if present
+    // -> if thresholding enabled then return colour on non-whitespace glyph
+
+    /*
+     * if threshold applies, and annotation fails the test - no colour (white)
+     */
+    if (annotationThreshold != null)
     {
-      Annotation aj = annotation.annotations[j];
-      // 'use original colours' => colourScheme != null
-      // -> look up colour to be used
-      // predefined colours => preconfigured shading
-      // -> only use original colours reference if thresholding enabled &
-      // minmax exists
-      // annotation.hasIcons => null or black colours replaced with glyph
-      // colours
-      // -> reuse original colours if present
-      // -> if thresholding enabled then return colour on non-whitespace glyph
-
-      if (aboveAnnotationThreshold == NO_THRESHOLD
-              || (annotationThreshold != null && (aboveAnnotationThreshold == ABOVE_THRESHOLD ? aj.value >= annotationThreshold.value
-                      : aj.value <= annotationThreshold.value)))
+      if ((aboveAnnotationThreshold == ABOVE_THRESHOLD && aj.value < annotationThreshold.value)
+              || (aboveAnnotationThreshold == BELOW_THRESHOLD && aj.value > annotationThreshold.value))
       {
-        if (predefinedColours && aj.colour != null
-                && !aj.colour.equals(Color.black))
-        {
-          currentColour = aj.colour;
-        }
-        else if (annotation.hasIcons
-                && annotation.graph == AlignmentAnnotation.NO_GRAPH)
+        return Color.white;
+      }
+    }
+
+    /*
+     * If 'use original colours' then return the colour of the annotation
+     * at the aligned position - computed using the background colour scheme
+     */
+    if (predefinedColours && aj.colour != null
+            && !aj.colour.equals(Color.black))
+    {
+      return aj.colour;
+    }
+
+    Color result = Color.white;
+    if (ann.hasIcons && ann.graph == AlignmentAnnotation.NO_GRAPH)
+    {
+      /*
+       * secondary structure symbol colouring
+       */
+      if (aj.secondaryStructure > ' ' && aj.secondaryStructure != '.'
+              && aj.secondaryStructure != '-')
+      {
+        if (getColourScheme() != null)
         {
-          if (aj.secondaryStructure > ' ' && aj.secondaryStructure != '.'
-                  && aj.secondaryStructure != '-')
-          {
-            if (getColourScheme() != null)
-            {
-              currentColour = getColourScheme().findColour(c, j, seq, null,
-                      0f);
-            }
-            else
-            {
-              if (annotation.isRNA())
-              {
-                currentColour = ColourSchemeProperty.rnaHelices[(int) aj.value];
-              }
-              else
-              {
-                currentColour = annotation.annotations[j].secondaryStructure == 'H' ? jalview.renderer.AnnotationRenderer.HELIX_COLOUR
-                        : annotation.annotations[j].secondaryStructure == 'E' ? jalview.renderer.AnnotationRenderer.SHEET_COLOUR
-                                : jalview.renderer.AnnotationRenderer.STEM_COLOUR;
-              }
-            }
-          }
-          else
-          {
-            //
-            return Color.white;
-          }
+          result = getColourScheme().findColour(c, j, seq, null, 0f);
         }
-        else if (noGradient)
+        else
         {
-          if (getColourScheme() != null)
+          if (ann.isRNA())
           {
-            currentColour = getColourScheme().findColour(c, j, seq, null,
-                    0f);
+            result = ColourSchemeProperty.rnaHelices[(int) aj.value];
           }
           else
           {
-            if (aj.colour != null)
-            {
-              currentColour = aj.colour;
-            }
+            result = ann.annotations[j].secondaryStructure == 'H' ? AnnotationRenderer.HELIX_COLOUR
+                    : ann.annotations[j].secondaryStructure == 'E' ? AnnotationRenderer.SHEET_COLOUR
+                            : AnnotationRenderer.STEM_COLOUR;
           }
         }
-        else
+      }
+      else
+      {
+        return Color.white;
+      }
+    }
+    else if (noGradient)
+    {
+      if (getColourScheme() != null)
+      {
+        result = getColourScheme().findColour(c, j, seq, null, 0f);
+      }
+      else
+      {
+        if (aj.colour != null)
         {
-          currentColour = shadeCalculation(annotation, j);
+          result = aj.colour;
         }
       }
-      // if (conservationColouring)
-      // {
-      // currentColour = applyConservation(currentColour, j);
-      // }
     }
-    // }
-    return currentColour;
+    else
+    {
+      result = shadeCalculation(ann, j);
+    }
+
+    return result;
   }
 
-  private Color shadeCalculation(AlignmentAnnotation annotation, int j)
+  /**
+   * Returns a graduated colour for the annotation at the given column. If there
+   * is a threshold value, and it is used as the top/bottom of the colour range,
+   * and the value satisfies the threshold condition, then a colour
+   * proportionate to the range from the threshold is calculated. For all other
+   * cases, a colour proportionate to the annotation's min-max range is
+   * calulated. Note that thresholding is _not_ done here (a colour is computed
+   * even if threshold is not passed).
+   * 
+   * @param ann
+   * @param col
+   * @return
+   */
+  Color shadeCalculation(AlignmentAnnotation ann, int col)
   {
-
-    // calculate a shade
     float range = 1f;
-    if (thresholdIsMinMax
-            && annotation.threshold != null
+    float value = ann.annotations[col].value;
+    if (thresholdIsMinMax && ann.threshold != null
             && aboveAnnotationThreshold == ABOVE_THRESHOLD
-            && annotation.annotations[j].value >= annotation.threshold.value)
+            && value >= ann.threshold.value)
     {
-      range = (annotation.annotations[j].value - annotation.threshold.value)
-              / (annotation.graphMax - annotation.threshold.value);
+      range = (value - ann.threshold.value)
+              / (ann.graphMax - ann.threshold.value);
     }
-    else if (thresholdIsMinMax && annotation.threshold != null
+    else if (thresholdIsMinMax && ann.threshold != null
             && aboveAnnotationThreshold == BELOW_THRESHOLD
-            && annotation.annotations[j].value >= annotation.graphMin)
+            && value <= ann.threshold.value)
     {
-      range = (annotation.annotations[j].value - annotation.graphMin)
-              / (annotation.threshold.value - annotation.graphMin);
+      range = (value - ann.graphMin) / (ann.threshold.value - ann.graphMin);
     }
     else
     {
-      if (annotation.graphMax != annotation.graphMin)
+      if (ann.graphMax != ann.graphMin)
       {
-        range = (annotation.annotations[j].value - annotation.graphMin)
-                / (annotation.graphMax - annotation.graphMin);
+        range = (value - ann.graphMin) / (ann.graphMax - ann.graphMin);
       }
       else
       {
@@ -396,11 +429,11 @@ public class AnnotationColourGradient extends FollowerColourScheme
       }
     }
 
-    int dr = (int) (rr * range + r1), dg = (int) (gg * range + g1), db = (int) (bb
-            * range + b1);
+    int dr = (int) (redRange * range + redMin);
+    int dg = (int) (greenRange * range + greenMin);
+    int db = (int) (blueRange * range + blueMin);
 
     return new Color(dr, dg, db);
-
   }
 
   public boolean isPredefinedColours()
@@ -423,6 +456,16 @@ public class AnnotationColourGradient extends FollowerColourScheme
     seqAssociated = sassoc;
   }
 
+  public boolean isThresholdIsMinMax()
+  {
+    return thresholdIsMinMax;
+  }
+
+  public void setThresholdIsMinMax(boolean minMax)
+  {
+    this.thresholdIsMinMax = minMax;
+  }
+
   @Override
   public String getSchemeName()
   {
index 53670e3..f35b886 100755 (executable)
@@ -54,10 +54,9 @@ public class Blosum62ColourScheme extends ResidueColourScheme
           String consensusResidue, float pid)
   {
     /*
-     * compare as upper case; note toUpperCase does nothing 
-     * if the string is already uppercase
+     * compare as upper case; note consensusResidue is 
+     * always computed as uppercase
      */
-    consensusResidue = consensusResidue.toUpperCase();
     if ('a' <= res && res <= 'z')
     {
       res -= ('a' - 'A');
diff --git a/src/jalview/schemes/ColourSchemeLoader.java b/src/jalview/schemes/ColourSchemeLoader.java
new file mode 100644 (file)
index 0000000..8660f3e
--- /dev/null
@@ -0,0 +1,125 @@
+package jalview.schemes;
+
+import jalview.binding.JalviewUserColours;
+
+import java.awt.Color;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+
+import org.exolab.castor.xml.Unmarshaller;
+
+public class ColourSchemeLoader
+{
+
+  /**
+   * Loads a user defined colour scheme from file. The file should contain a
+   * definition of residue colours in XML format as defined in
+   * JalviewUserColours.xsd.
+   * 
+   * @param filePath
+   * 
+   * @return
+   */
+  public static UserColourScheme loadColourScheme(String filePath)
+  {
+    UserColourScheme ucs = null;
+    Color[] newColours = null;
+    File file = new File(filePath);
+    try
+    {
+      InputStreamReader in = new InputStreamReader(
+              new FileInputStream(file), "UTF-8");
+  
+      jalview.schemabinding.version2.JalviewUserColours jucs = new jalview.schemabinding.version2.JalviewUserColours();
+  
+      org.exolab.castor.xml.Unmarshaller unmar = new org.exolab.castor.xml.Unmarshaller(
+              jucs);
+      jucs = (jalview.schemabinding.version2.JalviewUserColours) unmar
+              .unmarshal(in);
+  
+      /*
+       * non-case-sensitive colours are for 20 amino acid codes,
+       * B, Z, X and Gap
+       * optionally, lower-case alternatives for all except Gap
+       */
+      newColours = new Color[24];
+      Color[] lowerCase = new Color[23];
+      boolean caseSensitive = false;
+  
+      String name;
+      int index;
+      for (int i = 0; i < jucs.getColourCount(); i++)
+      {
+        name = jucs.getColour(i).getName();
+        if (ResidueProperties.aa3Hash.containsKey(name))
+        {
+          index = ResidueProperties.aa3Hash.get(name).intValue();
+        }
+        else
+        {
+          index = ResidueProperties.aaIndex[name.charAt(0)];
+        }
+        if (index == -1)
+        {
+          continue;
+        }
+  
+        Color color = new Color(Integer.parseInt(jucs.getColour(i)
+                .getRGB(), 16));
+        if (name.toLowerCase().equals(name))
+        {
+          caseSensitive = true;
+          lowerCase[index] = color;
+        }
+        else
+        {
+          newColours[index] = color;
+        }
+      }
+  
+      /*
+       * instantiate the colour scheme
+       */
+      ucs = new UserColourScheme(newColours);
+      ucs.setName(jucs.getSchemeName());
+      if (caseSensitive)
+      {
+        ucs.setLowerCaseColours(lowerCase);
+      }
+    } catch (Exception ex)
+    {
+      // Could be old Jalview Archive format
+      try
+      {
+        InputStreamReader in = new InputStreamReader(new FileInputStream(
+                file), "UTF-8");
+  
+        jalview.binding.JalviewUserColours jucs = new jalview.binding.JalviewUserColours();
+  
+        jucs = JalviewUserColours.unmarshal(in);
+  
+        newColours = new Color[jucs.getColourCount()];
+  
+        for (int i = 0; i < 24; i++)
+        {
+          newColours[i] = new Color(Integer.parseInt(jucs.getColour(i)
+                  .getRGB(), 16));
+        }
+        ucs = new UserColourScheme(newColours);
+        ucs.setName(jucs.getSchemeName());
+      } catch (Exception ex2)
+      {
+        ex2.printStackTrace();
+      }
+  
+      if (newColours == null)
+      {
+        System.out.println("Error loading User ColourFile\n" + ex);
+      }
+    }
+  
+    return ucs;
+  }
+
+}
index 817fb01..dc7e403 100644 (file)
@@ -1,14 +1,9 @@
 package jalview.schemes;
 
-import jalview.binding.JalviewUserColours;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
 
-import java.awt.Color;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStreamReader;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
@@ -177,114 +172,4 @@ public class ColourSchemes
     }
     return false;
   }
-
-  /**
-   * Loads a user defined colour scheme from file. The file should contain a
-   * definition of residue colours in XML format as defined in
-   * JalviewUserColours.xsd.
-   * 
-   * @param filePath
-   * 
-   * @return
-   */
-  public static UserColourScheme loadColourScheme(String filePath)
-  {
-    UserColourScheme ucs = null;
-    Color[] newColours = null;
-    File file = new File(filePath);
-    try
-    {
-      InputStreamReader in = new InputStreamReader(
-              new FileInputStream(file), "UTF-8");
-  
-      jalview.schemabinding.version2.JalviewUserColours jucs = new jalview.schemabinding.version2.JalviewUserColours();
-  
-      org.exolab.castor.xml.Unmarshaller unmar = new org.exolab.castor.xml.Unmarshaller(
-              jucs);
-      jucs = (jalview.schemabinding.version2.JalviewUserColours) unmar
-              .unmarshal(in);
-  
-      /*
-       * non-case-sensitive colours are for 20 amino acid codes,
-       * B, Z, X and Gap
-       * optionally, lower-case alternatives for all except Gap
-       */
-      newColours = new Color[24];
-      Color[] lowerCase = new Color[23];
-      boolean caseSensitive = false;
-  
-      String name;
-      int index;
-      for (int i = 0; i < jucs.getColourCount(); i++)
-      {
-        name = jucs.getColour(i).getName();
-        if (ResidueProperties.aa3Hash.containsKey(name))
-        {
-          index = ResidueProperties.aa3Hash.get(name).intValue();
-        }
-        else
-        {
-          index = ResidueProperties.aaIndex[name.charAt(0)];
-        }
-        if (index == -1)
-        {
-          continue;
-        }
-  
-        Color color = new Color(Integer.parseInt(jucs.getColour(i)
-                .getRGB(), 16));
-        if (name.toLowerCase().equals(name))
-        {
-          caseSensitive = true;
-          lowerCase[index] = color;
-        }
-        else
-        {
-          newColours[index] = color;
-        }
-      }
-  
-      /*
-       * instantiate the colour scheme
-       */
-      ucs = new UserColourScheme(newColours);
-      ucs.setName(jucs.getSchemeName());
-      if (caseSensitive)
-      {
-        ucs.setLowerCaseColours(lowerCase);
-      }
-    } catch (Exception ex)
-    {
-      // Could be old Jalview Archive format
-      try
-      {
-        InputStreamReader in = new InputStreamReader(new FileInputStream(
-                file), "UTF-8");
-  
-        jalview.binding.JalviewUserColours jucs = new jalview.binding.JalviewUserColours();
-  
-        jucs = JalviewUserColours.unmarshal(in);
-  
-        newColours = new Color[jucs.getColourCount()];
-  
-        for (int i = 0; i < 24; i++)
-        {
-          newColours[i] = new Color(Integer.parseInt(jucs.getColour(i)
-                  .getRGB(), 16));
-        }
-        ucs = new UserColourScheme(newColours);
-        ucs.setName(jucs.getSchemeName());
-      } catch (Exception ex2)
-      {
-        ex2.printStackTrace();
-      }
-  
-      if (newColours == null)
-      {
-        System.out.println("Error loading User ColourFile\n" + ex);
-      }
-    }
-  
-    return ucs;
-  }
 }
index f213abe..657d6b0 100755 (executable)
@@ -47,10 +47,9 @@ public class PIDColourScheme extends ResidueColourScheme
           String consensusResidue, float pid)
   {
     /*
-     * make everything uppercase; note this does nothing
-     * if consensusResidue is already uppercase
+     * compare as upper case; note consensusResidue is 
+     * always computed as uppercase
      */
-    consensusResidue = consensusResidue.toUpperCase();
     if ('a' <= c && c <= 'z')
     {
       c -= ('a' - 'A');
index 406814a..1e6142d 100755 (executable)
@@ -486,7 +486,7 @@ public class ResidueProperties
           -3, 3, 0, -1, -4 },
       { -2, -2, 1, 6, -3, 0, 2, -1, -1, -3, -4, -1, -3, -3, -1, 0, -1, -4,
           -3, -3, 4, 1, -1, -4 },
-      { 0, 3, -3, -3, 9, -3, -4, -3, -3, -1, -1, -3, -1, -2, -3, -1, -1,
+      { 0, -3, -3, -3, 9, -3, -4, -3, -3, -1, -1, -3, -1, -2, -3, -1, -1,
           -2, -2, -1, -3, -3, -2, -4 },
       { -1, 1, 0, 0, -3, 5, 2, -2, 0, -3, -2, 1, 0, -3, -1, 0, -1, -2, -1,
           -2, 0, 3, -1, -4 },
index 5e5fa8d..d82f54c 100644 (file)
 package jalview.schemes;
 
 import jalview.analysis.scoremodels.PairwiseSeqScoreModel;
-import jalview.api.analysis.ScoreModelI;
+import jalview.math.Matrix;
+import jalview.math.MatrixI;
 
-public class ScoreMatrix extends PairwiseSeqScoreModel implements
-        ScoreModelI
+public class ScoreMatrix extends PairwiseSeqScoreModel
 {
   String name;
 
@@ -79,19 +79,21 @@ public class ScoreMatrix extends PairwiseSeqScoreModel implements
   }
 
   /**
+   * Answers the score for substituting first char in A1 with first char in A2
    * 
    * @param A1
    * @param A2
-   * @return score for substituting first char in A1 with first char in A2
+   * @return
    */
   public int getPairwiseScore(String A1, String A2)
   {
     return getPairwiseScore(A1.charAt(0), A2.charAt(0));
   }
 
+  @Override
   public int getPairwiseScore(char c, char d)
   {
-    int pog = 0;
+    int score = 0;
 
     try
     {
@@ -99,19 +101,19 @@ public class ScoreMatrix extends PairwiseSeqScoreModel implements
               : ResidueProperties.nucleotideIndex[c];
       int b = (type == 0) ? ResidueProperties.aaIndex[d]
               : ResidueProperties.nucleotideIndex[d];
-
-      pog = matrix[a][b];
+      score = matrix[a][b];
     } catch (Exception e)
     {
       // System.out.println("Unknown residue in " + A1 + " " + A2);
     }
 
-    return pog;
+    return score;
   }
 
   /**
    * pretty print the matrix
    */
+  @Override
   public String toString()
   {
     return outputMatrix(false);
@@ -170,4 +172,47 @@ public class ScoreMatrix extends PairwiseSeqScoreModel implements
     }
     return sb.toString();
   }
+
+  /**
+   * Computes an NxN matrix where N is the number of sequences, and entry [i, j]
+   * is sequence[i] pairwise multiplied with sequence[j], as a sum of scores
+   * computed using the current score matrix. For example
+   * <ul>
+   * <li>Sequences:</li>
+   * <li>FKL</li>
+   * <li>R-D</li>
+   * <li>QIA</li>
+   * <li>GWC</li>
+   * <li>Score matrix is BLOSUM62</li>
+   * <li>Gaps treated same as X (unknown)</li>
+   * <li>product [0, 0] = F.F + K.K + L.L = 6 + 5 + 4 = 15</li>
+   * <li>product [1, 1] = R.R + -.- + D.D = 5 + -1 + 6 = 10</li>
+   * <li>product [2, 2] = Q.Q + I.I + A.A = 5 + 4 + 4 = 13</li>
+   * <li>product [3, 3] = G.G + W.W + C.C = 6 + 11 + 9 = 26</li>
+   * <li>product[0, 1] = F.R + K.- + L.D = -3 + -1 + -3 = -8
+   * <li>and so on</li>
+   * </ul>
+   */
+  public MatrixI computePairwiseScores(String[] seqs)
+  {
+    double[][] values = new double[seqs.length][];
+    for (int row = 0; row < seqs.length; row++)
+    {
+      values[row] = new double[seqs.length];
+      for (int col = 0; col < seqs.length; col++)
+      {
+        int total = 0;
+        int width = Math.min(seqs[row].length(), seqs[col].length());
+        for (int i = 0; i < width; i++)
+        {
+          char c1 = seqs[row].charAt(i);
+          char c2 = seqs[col].charAt(i);
+          int score = getPairwiseScore(c1, c2);
+          total += score;
+        }
+        values[row][col] = total;
+      }
+    }
+    return new Matrix(values);
+  }
 }
index 85bf54e..8e58c20 100755 (executable)
@@ -73,7 +73,7 @@ public class UserColourScheme extends ResidueColourScheme
     schemeName = from.schemeName;
     if (from.lowerCaseColours != null)
     {
-      lowerCaseColours = new Color[lowerCaseColours.length];
+      lowerCaseColours = new Color[from.lowerCaseColours.length];
       System.arraycopy(from.lowerCaseColours, 0, lowerCaseColours, 0,
               from.lowerCaseColours.length);
     }
index a19acef..f20cd31 100644 (file)
@@ -29,9 +29,8 @@ package jalview.structure;
  */
 public class AtomSpec
 {
-  // TODO clarify do we want pdbFile here, or pdbId?
-  // compare highlightAtom in 2.8.2 for JalviewJmolBinding and
-  // javascript.MouseOverStructureListener
+  int modelNo;
+
   private String pdbFile;
 
   private String chain;
@@ -41,6 +40,60 @@ public class AtomSpec
   private int atomIndex;
 
   /**
+   * Parses a Chimera atomspec e.g. #1:12.A to construct an AtomSpec model (with
+   * null pdb file name)
+   * 
+   * @param spec
+   * @return
+   * @throw IllegalArgumentException if the spec cannot be parsed, or represents
+   *        more than one residue
+   */
+  public static AtomSpec fromChimeraAtomspec(String spec)
+  {
+    int colonPos = spec.indexOf(":");
+    if (colonPos == -1)
+    {
+      throw new IllegalArgumentException(spec);
+    }
+
+    int hashPos = spec.indexOf("#");
+    if (hashPos == -1 && colonPos != 0)
+    {
+      // # is missing but something precedes : - reject
+      throw new IllegalArgumentException(spec);
+    }
+
+    String modelSubmodel = spec.substring(hashPos + 1, colonPos);
+    int dotPos = modelSubmodel.indexOf(".");
+    int modelId = 0;
+    try
+    {
+      modelId = Integer.valueOf(dotPos == -1 ? modelSubmodel
+              : modelSubmodel.substring(0, dotPos));
+    } catch (NumberFormatException e)
+    {
+      // ignore, default to model 0
+    }
+
+    String residueChain = spec.substring(colonPos + 1);
+    dotPos = residueChain.indexOf(".");
+    int resNum = 0;
+    try
+    {
+      resNum = Integer.parseInt(dotPos == -1 ? residueChain
+              : residueChain.substring(0, dotPos));
+    } catch (NumberFormatException e)
+    {
+      // could be a range e.g. #1:4-7.B
+      throw new IllegalArgumentException(spec);
+    }
+
+    String chainId = dotPos == -1 ? "" : residueChain.substring(dotPos + 1);
+
+    return new AtomSpec(modelId, chainId, resNum, 0);
+  }
+
+  /**
    * Constructor
    * 
    * @param pdbFile
@@ -56,6 +109,22 @@ public class AtomSpec
     this.atomIndex = atomNo;
   }
 
+  /**
+   * Constructor
+   * 
+   * @param modelId
+   * @param chainId
+   * @param resNo
+   * @param atomNo
+   */
+  public AtomSpec(int modelId, String chainId, int resNo, int atomNo)
+  {
+    this.modelNo = modelId;
+    this.chain = chainId;
+    this.pdbResNum = resNo;
+    this.atomIndex = atomNo;
+  }
+
   public String getPdbFile()
   {
     return pdbFile;
@@ -76,6 +145,16 @@ public class AtomSpec
     return atomIndex;
   }
 
+  public int getModelNumber()
+  {
+    return modelNo;
+  }
+
+  public void setPdbFile(String file)
+  {
+    pdbFile = file;
+  }
+
   @Override
   public String toString()
   {
index 78634e0..40789ed 100644 (file)
@@ -23,7 +23,9 @@ package jalview.structure;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceI;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 
 public class StructureMapping
 {
@@ -47,6 +49,18 @@ public class StructureMapping
   // and atomNo
   HashMap<Integer, int[]> mapping;
 
+  /**
+   * Constructor
+   * 
+   * @param seq
+   * @param pdbfile
+   * @param pdbid
+   * @param chain
+   * @param mapping
+   *          a map from sequence to two values, { resNo, atomNo } in the
+   *          structure
+   * @param mappingDetails
+   */
   public StructureMapping(SequenceI seq, String pdbfile, String pdbid,
           String chain, HashMap<Integer, int[]> mapping,
           String mappingDetails)
@@ -111,6 +125,70 @@ public class StructureMapping
   }
 
   /**
+   * Returns a (possibly empty) list of [start, end] residue positions in the
+   * mapped structure, corresponding to the given range of sequence positions
+   * 
+   * @param fromSeqPos
+   * @param toSeqPos
+   * @return
+   */
+  public List<int[]> getPDBResNumRanges(int fromSeqPos, int toSeqPos)
+  {
+    List<int[]> result = new ArrayList<int[]>();
+    int startRes = -1;
+    int endRes = -1;
+
+    for (int i = fromSeqPos; i <= toSeqPos; i++)
+    {
+      int resNo = getPDBResNum(i);
+      if (resNo == UNASSIGNED_VALUE)
+      {
+        continue; // no mapping from this sequence position
+      }
+      if (startRes == -1)
+      {
+        startRes = resNo;
+        endRes = resNo;
+      }
+      if (resNo >= startRes && resNo <= endRes)
+      {
+        // within the current range - no change
+        continue;
+      }
+      if (resNo == startRes - 1)
+      {
+        // extend beginning of current range
+        startRes--;
+        continue;
+      }
+      if (resNo == endRes + 1)
+      {
+        // extend end of current range
+        endRes++;
+        continue;
+      }
+
+      /*
+       * resNo is not within or contiguous with last range,
+       * so write out the last range
+       */
+      result.add(new int[] { startRes, endRes });
+      startRes = resNo;
+      endRes = resNo;
+    }
+
+    /*
+     * and add the last range
+     */
+    if (startRes != -1)
+    {
+      result.add(new int[] { startRes, endRes });
+    }
+
+    return result;
+  }
+
+  /**
    * 
    * @param pdbResNum
    * @return -1 or the corresponding sequence position for a pdb residue number
index 65fd5e7..3ab642f 100644 (file)
@@ -589,11 +589,9 @@ public class StructureSelectionManager
     return pdb;
   }
 
-  private boolean isCIFFile(String filename)
+  public void addStructureMapping(StructureMapping sm)
   {
-    String fileExt = filename.substring(filename.lastIndexOf(".") + 1,
-            filename.length());
-    return "cif".equalsIgnoreCase(fileExt);
+    mappings.add(sm);
   }
 
   /**
@@ -806,6 +804,27 @@ public class StructureSelectionManager
       return;
     }
 
+    SearchResultsI results = findAlignmentPositionsForStructurePositions(atoms);
+    for (Object li : listeners)
+    {
+      if (li instanceof SequenceListener)
+      {
+        ((SequenceListener) li).highlightSequence(results);
+      }
+    }
+  }
+
+  /**
+   * Constructs a SearchResults object holding regions (if any) in the Jalview
+   * alignment which have a mapping to the structure viewer positions in the
+   * supplied list
+   * 
+   * @param atoms
+   * @return
+   */
+  public SearchResultsI findAlignmentPositionsForStructurePositions(
+          List<AtomSpec> atoms)
+  {
     SearchResultsI results = new SearchResults();
     for (AtomSpec atom : atoms)
     {
@@ -831,13 +850,7 @@ public class StructureSelectionManager
         }
       }
     }
-    for (Object li : listeners)
-    {
-      if (li instanceof SequenceListener)
-      {
-        ((SequenceListener) li).highlightSequence(results);
-      }
-    }
+    return results;
   }
 
   /**
index fda08fd..84475fe 100644 (file)
@@ -21,7 +21,6 @@
 package jalview.structures.models;
 
 import jalview.api.AlignmentViewPanel;
-import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
 import jalview.api.StructureSelectionManagerProvider;
 import jalview.api.structures.JalviewStructureDisplayI;
@@ -42,6 +41,7 @@ import jalview.util.MessageManager;
 import java.awt.Color;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.List;
 
 /**
@@ -521,15 +521,15 @@ public abstract class AAStructureBindingModel extends
    *          the sequence alignment which is the basis of structure
    *          superposition
    * @param matched
-   *          an array of booleans, indexed by alignment column, where true
-   *          indicates that every structure has a mapped residue present in the
-   *          column (so the column can participate in structure alignment)
+   *          a BitSet, where bit j is set to indicate that every structure has
+   *          a mapped residue present in column j (so the column can
+   *          participate in structure alignment)
    * @param structures
    *          an array of data beans corresponding to pdb file index
    * @return
    */
   protected int findSuperposableResidues(AlignmentI alignment,
-          boolean[] matched, SuperposeData[] structures)
+          BitSet matched, SuperposeData[] structures)
   {
     int refStructure = -1;
     String[] files = getPdbFile();
@@ -559,16 +559,16 @@ public abstract class AAStructureBindingModel extends
             {
               refStructure = pdbfnum;
             }
-            for (int r = 0; r < matched.length; r++)
+            for (int r = 0; r < alignment.getWidth(); r++)
             {
-              if (!matched[r])
+              if (!matched.get(r))
               {
                 continue;
               }
               int pos = getMappedPosition(theSequence, r, mapping);
               if (pos < 1 || pos == lastPos)
               {
-                matched[r] = false;
+                matched.clear(r);
                 continue;
               }
               lastPos = pos;
@@ -700,24 +700,29 @@ public abstract class AAStructureBindingModel extends
 
   public abstract void setJalviewColourScheme(ColourSchemeI cs);
 
-  public abstract void superposeStructures(AlignmentI[] als, int[] alm,
-          ColumnSelection[] alc);
-
-  public abstract void setBackgroundColour(Color col);
-
-  protected abstract StructureMappingcommandSet[] getColourBySequenceCommands(
-          String[] files, SequenceRenderer sr, FeatureRenderer fr,
-          AlignmentI alignment);
-
   /**
-   * returns the current featureRenderer that should be used to colour the
-   * structures
-   * 
-   * @param alignment
+   * Constructs and sends a command to align structures against a reference
+   * structure, based on one or more sequence alignments. May optionally return
+   * an error or warning message for the alignment command.
    * 
+   * @param alignments
+   *          an array of alignments to process
+   * @param structureIndices
+   *          an array of corresponding reference structures (index into pdb
+   *          file array); if a negative value is passed, the first PDB file
+   *          mapped to an alignment sequence is used as the reference for
+   *          superposition
+   * @param hiddenCols
+   *          an array of corresponding hidden columns for each alignment
    * @return
    */
-  public abstract FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment);
+  public abstract String superposeStructures(AlignmentI[] alignments, int[] structureIndices,
+          ColumnSelection[] hiddenCols);
+
+  public abstract void setBackgroundColour(Color col);
+
+  protected abstract StructureMappingcommandSet[] getColourBySequenceCommands(
+          String[] files, SequenceRenderer sr, AlignmentViewPanel avp);
 
   /**
    * returns the current sequenceRenderer that should be used to colour the
@@ -743,8 +748,6 @@ public abstract class AAStructureBindingModel extends
    */
   public void colourBySequence(AlignmentViewPanel alignmentv)
   {
-    boolean showFeatures = alignmentv.getAlignViewport()
-            .isShowSequenceFeatures();
     if (!colourBySequence || !isLoadingFinished())
     {
       return;
@@ -757,15 +760,8 @@ public abstract class AAStructureBindingModel extends
   
     SequenceRenderer sr = getSequenceRenderer(alignmentv);
   
-    FeatureRenderer fr = null;
-    if (showFeatures)
-    {
-      fr = getFeatureRenderer(alignmentv);
-    }
-    AlignmentI alignment = alignmentv.getAlignment();
-  
     StructureMappingcommandSet[] colourBySequenceCommands = getColourBySequenceCommands(
-            files, sr, fr, alignment);
+            files, sr, alignmentv);
     colourBySequence(colourBySequenceCommands);
   }
 
@@ -773,4 +769,7 @@ public abstract class AAStructureBindingModel extends
   {
     return fileLoadingError != null && fileLoadingError.length() > 0;
   }
+
+  public abstract jalview.api.FeatureRenderer getFeatureRenderer(
+          AlignmentViewPanel alignment);
 }
diff --git a/src/jalview/urls/CustomUrlProvider.java b/src/jalview/urls/CustomUrlProvider.java
new file mode 100644 (file)
index 0000000..07f4068
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.urls;
+
+import static jalview.util.UrlConstants.DB_ACCESSION;
+import static jalview.util.UrlConstants.DELIM;
+import static jalview.util.UrlConstants.SEP;
+import static jalview.util.UrlConstants.SEQUENCE_ID;
+
+import jalview.util.MessageManager;
+import jalview.util.UrlConstants;
+import jalview.util.UrlLink;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.StringTokenizer;
+
+/**
+ * 
+ * Implements the UrlProviderI interface for a UrlProvider object which serves
+ * custom URLs defined by the user
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+public class CustomUrlProvider extends UrlProviderImpl
+{
+  // Default sequence URL link label for SRS
+  private static final String SRS_LABEL = "SRS";
+
+  // map of string ids to urlLinks (selected)
+  private HashMap<String, UrlLink> selectedUrls;
+
+  // map of string ids to urlLinks (not selected)
+  private HashMap<String, UrlLink> nonselectedUrls;
+
+  /**
+   * Construct UrlProvider for custom (user-entered) URLs
+   * 
+   * @param inMenuUrlList
+   *          list of URLs set to be displayed in menu, in form stored in Cache.
+   *          i.e. SEP delimited string
+   * @param storedUrlList
+   *          list of custom URLs entered by user but not currently displayed in
+   *          menu, in form stored in Cache
+   */
+  public CustomUrlProvider(String inMenuUrlList, String storedUrlList)
+  {
+    try
+    {
+      selectedUrls = parseUrlStrings(inMenuUrlList);
+      nonselectedUrls = parseUrlStrings(storedUrlList);
+    } catch (Exception ex)
+    {
+      System.out
+              .println(ex.getMessage() + "\nError parsing sequence links");
+    }
+  }
+
+  /**
+   * Construct UrlProvider for custom (user-entered) URLs
+   * 
+   * @param urlList
+   *          list of URLs to be displayed in menu, as (label,url) pairs
+   * @param storedUrlList
+   *          list of custom URLs entered by user but not currently displayed in
+   *          menu, as (label,url) pairs
+   */
+  public CustomUrlProvider(Map<String, String> inMenuUrlList,
+          Map<String, String> storedUrlList)
+  {
+    try
+    {
+      selectedUrls = parseUrlList(inMenuUrlList);
+      nonselectedUrls = parseUrlList(storedUrlList);
+    } catch (Exception ex)
+    {
+      System.out
+              .println(ex.getMessage() + "\nError parsing sequence links");
+    }
+  }
+
+  private HashMap<String, UrlLink> parseUrlStrings(String urlStrings)
+  {
+    // cachedUrlList is in form <label>|<url>|<label>|<url>...
+    // parse cachedUrlList into labels (used as id) and url links
+    HashMap<String, UrlLink> urls = new HashMap<String, UrlLink>();
+
+    StringTokenizer st = new StringTokenizer(urlStrings, SEP);
+    while (st.hasMoreElements())
+    {
+      String name = st.nextToken();
+
+      if (!isMiriamId(name))
+      {
+        // this one of our custom urls
+        String url = st.nextToken();
+        // check for '|' within a regex
+        int rxstart = url.indexOf(DELIM + DB_ACCESSION + DELIM);
+        if (rxstart == -1)
+        {
+          rxstart = url.indexOf(DELIM + SEQUENCE_ID + DELIM);
+        }
+        while (rxstart == -1 && url.indexOf("/=" + DELIM) == -1
+                && st.hasMoreTokens())
+        {
+          url = url + SEP + st.nextToken();
+        }
+        urls.put(name, new UrlLink(name, url, name));
+      }
+    }
+    upgradeOldLinks(urls);
+    return urls;
+  }
+
+  private HashMap<String, UrlLink> parseUrlList(Map<String, String> urlList)
+  {
+    HashMap<String, UrlLink> urls = new HashMap<String, UrlLink>();
+    if (urlList == null)
+    {
+      return urls;
+    }
+
+    Iterator<Map.Entry<String, String>> it = urlList.entrySet().iterator();
+    while (it.hasNext())
+    {
+      Map.Entry<String, String> pair = it.next();
+      urls.put(pair.getKey(),
+ new UrlLink(pair.getKey(), pair.getValue(),
+              pair.getKey()));
+    }
+    upgradeOldLinks(urls);
+    return urls;
+  }
+
+  /*
+   * Upgrade any legacy links which may have been left lying around
+   */
+  private void upgradeOldLinks(HashMap<String, UrlLink> urls)
+  {
+    // upgrade old SRS link
+    if (urls.containsKey(SRS_LABEL))
+    {
+      urls.remove(SRS_LABEL);
+      UrlLink link = new UrlLink(UrlConstants.DEFAULT_STRING);
+      link.setLabel(UrlConstants.DEFAULT_LABEL);
+      urls.put(UrlConstants.DEFAULT_LABEL, link);
+    }
+  }
+
+  @Override
+  public List<String> getLinksForMenu()
+  {
+    List<String> links = new ArrayList<String>();
+    Iterator<Map.Entry<String, UrlLink>> it = selectedUrls.entrySet()
+            .iterator();
+    while (it.hasNext())
+    {
+      Map.Entry<String, UrlLink> pair = it.next();
+      links.add(pair.getValue().toString());
+    }
+    return links;
+  }
+
+  @Override
+  public List<UrlLinkDisplay> getLinksForTable()
+  {
+    ArrayList<UrlLinkDisplay> displayLinks = new ArrayList<UrlLinkDisplay>();
+    displayLinks = getLinksForTable(selectedUrls, true);
+    displayLinks.addAll(getLinksForTable(nonselectedUrls, false));
+    return displayLinks;
+  }
+
+  private ArrayList<UrlLinkDisplay> getLinksForTable(
+          HashMap<String, UrlLink> urlList, boolean selected)
+  {
+    return super.getLinksForTable(urlList, null, selected);
+  }
+
+  @Override
+  public boolean setPrimaryUrl(String id)
+  {
+    if (selectedUrls.containsKey(id))
+    {
+      primaryUrl = id;
+    }
+    else if (nonselectedUrls.containsKey(id))
+    {
+      primaryUrl = id;
+    }
+    else
+    {
+      primaryUrl = null;
+    }
+
+    return (primaryUrl != null);
+  }
+
+  @Override
+  public String writeUrlsAsString(boolean selected)
+  {
+    StringBuffer links = new StringBuffer();
+    HashMap<String, UrlLink> urls;
+    if (selected)
+    {
+      urls = selectedUrls;
+    }
+    else
+    {
+      urls = nonselectedUrls;
+    }
+    if (urls.size() > 0)
+    {
+      for (Entry<String, UrlLink> entry : urls.entrySet())
+      {
+        links.append(entry.getValue().toString());
+        links.append(SEP);
+      }
+
+      // remove last SEP
+      links.setLength(links.length() - 1);
+    }
+    else
+    {
+      urls.clear();
+    }
+    return links.toString();
+  }
+
+  @Override
+  public String getPrimaryUrl(String seqid)
+  {
+    String result = super.getPrimaryUrl(seqid, selectedUrls);
+    if (result == null)
+    {
+      result = super.getPrimaryUrl(seqid, nonselectedUrls);
+    }
+    return result;
+  }
+
+  @Override
+  public String getPrimaryUrlId()
+  {
+    return primaryUrl;
+  }
+
+  @Override
+  public String getPrimaryTarget(String seqid)
+  {
+    return selectedUrls.get(primaryUrl).getTarget();
+  }
+
+  @Override
+  public void setUrlData(List<UrlLinkDisplay> links)
+  {
+    HashMap<String, UrlLink> unselurls = new HashMap<String, UrlLink>();
+    HashMap<String, UrlLink> selurls = new HashMap<String, UrlLink>();
+
+    Iterator<UrlLinkDisplay> it = links.iterator();
+    while (it.hasNext())
+    {
+      UrlLinkDisplay link = it.next();
+
+      // MIRIAM ids will be handled by a different UrlProvider class
+      if (!isMiriamId(link.getId()))
+      {
+        // don't allow duplicate key names as entries will be overwritten
+        if (unselurls.containsKey(link.getId())
+                || selurls.containsKey(link.getId()))
+        {
+          throw new IllegalArgumentException(MessageManager.formatMessage(
+                  "exception.url_cannot_have_duplicate_id", link.getId()));
+        }
+        if (link.getIsSelected())
+        {
+          selurls.put(link.getId(),
+                  new UrlLink(link.getDescription(), link.getUrl(), link.getDescription()));
+        }
+        else
+        {
+          unselurls
+                  .put(link.getId(),
+                          new UrlLink(link.getDescription(), link.getUrl(), link
+                                  .getDescription()));
+        }
+        // sort out primary and selected ids
+        if (link.getIsPrimary())
+        {
+          setPrimaryUrl(link.getId());
+        }
+      }
+
+    }
+    nonselectedUrls = unselurls;
+    selectedUrls = selurls;
+  }
+
+  @Override
+  public String choosePrimaryUrl()
+  {
+    // unilaterally set the primary id to the EMBL_EBI link
+    if ((!nonselectedUrls.containsKey(UrlConstants.DEFAULT_LABEL))
+            && (!selectedUrls.containsKey(UrlConstants.DEFAULT_LABEL)))
+    {
+      UrlLink link = new UrlLink(UrlConstants.DEFAULT_STRING);
+      link.setLabel(UrlConstants.DEFAULT_LABEL);
+      selectedUrls.put(UrlConstants.DEFAULT_LABEL, link);
+    }
+    primaryUrl = UrlConstants.DEFAULT_LABEL;
+    return UrlConstants.DEFAULT_LABEL;
+  }
+
+  @Override
+  public boolean contains(String id)
+  {
+    return (selectedUrls.containsKey(id) || nonselectedUrls.containsKey(id));
+  }
+
+}
diff --git a/src/jalview/urls/IdOrgSettings.java b/src/jalview/urls/IdOrgSettings.java
new file mode 100644 (file)
index 0000000..7dd1a19
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.urls;
+
+/**
+ * Holds settings for identifiers.org e.g. url, download location
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+public class IdOrgSettings
+{
+  private static String url;
+
+  private static String location;
+
+  public static void setUrl(String seturl)
+  {
+    url = seturl;
+  }
+
+  public static void setDownloadLocation(String setloc)
+  {
+    location = setloc;
+  }
+
+  public static String getUrl()
+  {
+    return url;
+  }
+
+  public static String getDownloadLocation()
+  {
+    return location;
+  }
+}
diff --git a/src/jalview/urls/IdentifiersUrlProvider.java b/src/jalview/urls/IdentifiersUrlProvider.java
new file mode 100644 (file)
index 0000000..c938666
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.urls;
+
+import static jalview.util.UrlConstants.DB_ACCESSION;
+import static jalview.util.UrlConstants.DELIM;
+import static jalview.util.UrlConstants.SEP;
+
+import jalview.util.UrlLink;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+/**
+ * 
+ * Implements the UrlProviderI interface for a UrlProvider object which serves
+ * URLs from identifiers.org
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+public class IdentifiersUrlProvider extends UrlProviderImpl
+{
+
+  private static final String LOCAL_KEY = "Local";
+
+  private static final String ID_ORG_KEY = "identifiers.org";
+
+  // map of string ids to urls
+  private HashMap<String, UrlLink> urls;
+
+  // list of selected urls
+  private ArrayList<String> selectedUrls;
+
+  public IdentifiersUrlProvider(String cachedUrlList)
+  {
+    urls = readIdentifiers(IdOrgSettings.getDownloadLocation());
+    selectedUrls = new ArrayList<String>();
+    checkSelectionMatchesUrls(cachedUrlList);
+  }
+
+  /**
+   * Read data from an identifiers.org download file
+   * 
+   * @param idFileName
+   *          name of identifiers.org download file
+   * @return hashmap of identifiers.org data, keyed by MIRIAM id
+   */
+  private HashMap<String, UrlLink> readIdentifiers(
+          String idFileName)
+  {
+    JSONParser parser = new JSONParser();
+
+    // identifiers.org data
+    HashMap<String, UrlLink> idData = new HashMap<String, UrlLink>();
+
+    try
+    {
+      FileReader reader = new FileReader(idFileName);
+      String key = "";
+      JSONObject obj = (JSONObject) parser.parse(reader);
+      if (obj.containsKey(ID_ORG_KEY))
+      {
+        key = ID_ORG_KEY;
+      }
+      else if (obj.containsKey(LOCAL_KEY))
+      {
+        key = LOCAL_KEY;
+      }
+      else
+      {
+        System.out
+                .println("Unexpected key returned from identifiers jalview service");
+        return idData;
+      }
+
+      JSONArray jsonarray = (JSONArray) obj.get(key);
+
+      // loop over each entry in JSON array and build HashMap entry
+      for (int i = 0; i < jsonarray.size(); i++)
+      {
+        JSONObject item = (JSONObject) jsonarray.get(i);
+
+        String url = (String) item.get("url") + "/" + DELIM + DB_ACCESSION
+                + DELIM;
+        UrlLink link = new UrlLink((String) item.get("name"), url,
+                (String) item.get("prefix"));
+        idData.put((String) item.get("id"), link);
+      }
+    } catch (FileNotFoundException e)
+    {
+      e.printStackTrace();
+      idData.clear();
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+      idData.clear();
+    } catch (ParseException e)
+    {
+      e.printStackTrace();
+      idData.clear();
+    }
+    return idData;
+  }
+
+  private void checkSelectionMatchesUrls(String cachedUrlList)
+  {
+    StringTokenizer st = new StringTokenizer(cachedUrlList, SEP);
+    while (st.hasMoreElements())
+    {
+      String id = st.nextToken();
+
+      if (isMiriamId(id))
+      {
+        // this is an identifiers.org MIRIAM id
+        if (urls.containsKey(id))
+        {
+          selectedUrls.add(id);
+        }
+      }
+    }
+
+    // reset defaultUrl in case it is no longer selected
+    setPrimaryUrl(primaryUrl);
+  }
+
+  @Override
+  public boolean setPrimaryUrl(String id)
+  {
+    if (urls.containsKey(id))
+    {
+      primaryUrl = id;
+    }
+    else
+    {
+      primaryUrl = null;
+    }
+
+    return urls.containsKey(id);
+  }
+
+  @Override
+  public String writeUrlsAsString(boolean selected)
+  {
+    if (!selected)
+    {
+      return ""; // we don't cache unselected identifiers.org urls
+    }
+
+    StringBuffer links = new StringBuffer();
+    if (!selectedUrls.isEmpty())
+    {
+      for (String k : selectedUrls)
+      {
+        links.append(k);
+        links.append(SEP);
+      }
+      // remove last SEP
+      links.setLength(links.length() - 1);
+    }
+    return links.toString();
+  }
+
+  @Override
+  public List<String> getLinksForMenu()
+  {
+    List<String> links = new ArrayList<String>();
+    for (String key : selectedUrls)
+    {
+      links.add(urls.get(key).toStringWithTarget());
+    }
+    return links;
+  }
+
+  @Override
+  public List<UrlLinkDisplay> getLinksForTable()
+  {
+    return super.getLinksForTable(urls, selectedUrls, false);
+  }
+
+  @Override
+  public void setUrlData(List<UrlLinkDisplay> links)
+  {
+    selectedUrls = new ArrayList<String>();
+
+    Iterator<UrlLinkDisplay> it = links.iterator();
+    while (it.hasNext())
+    {
+      UrlLinkDisplay link = it.next();
+
+      // Handle links with MIRIAM ids only
+      if (isMiriamId(link.getId()))
+      {
+        // select/deselect links accordingly and set default url
+        if (urls.containsKey(link.getId()))
+        {
+          if (link.getIsSelected())
+          {
+            selectedUrls.add(link.getId());
+          }
+          if (link.getIsPrimary())
+          {
+            setPrimaryUrl(link.getId());
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public String getPrimaryUrl(String seqid)
+  {
+    return super.getPrimaryUrl(seqid, urls);
+  }
+
+  @Override
+  public String getPrimaryUrlId()
+  {
+    return primaryUrl;
+  }
+
+  @Override
+  public String getPrimaryTarget(String seqid)
+  {
+    return null;
+  }
+
+  @Override
+  public String choosePrimaryUrl()
+  {
+    return null;
+  }
+
+  @Override
+  public boolean contains(String id)
+  {
+    return (urls.containsKey(id));
+  }
+}
diff --git a/src/jalview/urls/UrlLinkDisplay.java b/src/jalview/urls/UrlLinkDisplay.java
new file mode 100644 (file)
index 0000000..0eabff7
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.urls;
+
+import jalview.util.MessageManager;
+import jalview.util.UrlLink;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * UrlLink table row definition
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+
+public class UrlLinkDisplay
+{
+  private String id; // id is not supplied to display, but used to identify
+                     // entries when saved
+
+  private boolean isPrimary;
+
+  private boolean isSelected;
+
+  private UrlLink link;
+
+  // Headers for columns in table
+  private final static List<String> colNames = new ArrayList<String>()
+  {
+    {
+      add(MessageManager.formatMessage("label.database"));
+      add(MessageManager.formatMessage("label.name"));
+      add(MessageManager.formatMessage("label.url"));
+      add(MessageManager.formatMessage("label.inmenu"));
+      add(MessageManager.formatMessage("label.primary"));
+      add(MessageManager.formatMessage("label.id"));
+    }
+  };
+
+  // column positions
+  public final static int DATABASE = 0;
+
+  public final static int NAME = 1;
+
+  public final static int URL = 2;
+
+  public final static int SELECTED = 3;
+
+  public final static int PRIMARY = 4;
+
+  public final static int ID = 5;
+
+  public UrlLinkDisplay(String rowId, UrlLink rowLink,
+          boolean rowSelected, boolean rowDefault)
+  {
+    id = rowId;
+    isPrimary = rowDefault;
+    isSelected = rowSelected;
+
+    link = rowLink;
+  }
+
+  // getters/setters
+  public String getId()
+  {
+    return id;
+  }
+
+  public String getDescription()
+  {
+    return link.getLabel();
+  }
+
+  public String getDBName()
+  {
+    return link.getTarget();
+  }
+
+  public String getUrl()
+  {
+    return link.getUrlWithToken();
+  }
+
+  public boolean getIsPrimary()
+  {
+    return isPrimary;
+  }
+
+  public boolean getIsSelected()
+  {
+    return isSelected;
+  }
+
+  public void setDBName(String name)
+  {
+    link.setTarget(name);
+  }
+
+  public void setUrl(String rowUrl)
+  {
+    link = new UrlLink(getDescription(), rowUrl, getDBName());
+  }
+
+  public void setDescription(String desc)
+  {
+    link.setLabel(desc);
+  }
+
+  public void setIsDefault(boolean rowDefault)
+  {
+    isPrimary = rowDefault;
+  }
+
+  public void setIsSelected(boolean rowSelected)
+  {
+    isSelected = rowSelected;
+  }
+
+  public Object getValue(int index)
+  {
+    switch (index)
+    {
+    case ID:
+      return id;
+    case URL:
+      return getUrl();
+    case PRIMARY:
+      return isPrimary;
+    case SELECTED:
+      return isSelected;
+    case NAME:
+      return getDescription();
+    case DATABASE:
+      return getDBName();
+    default:
+      return null;
+    }
+  }
+
+  public void setValue(int index, Object value)
+  {
+    switch (index)
+    {
+    case ID:
+      id = (String) value;
+      break;
+    case URL:
+      setUrl((String) value);
+      break;
+    case PRIMARY:
+      isPrimary = (boolean) value;
+      break;
+    case SELECTED:
+      isSelected = (boolean) value;
+      break;
+    case NAME:
+      setDescription((String) value);
+    case DATABASE:
+      setDBName((String) value);
+      break;
+    default:
+      // do nothing
+    }
+  }
+
+  /**
+   * Identify editable columns
+   * 
+   * @param index
+   *          index of column
+   * @return whether column can be edited in table
+   */
+  public boolean isEditable(int index)
+  {
+    if (index == PRIMARY)
+    {
+      // primary link must not be a $DB_ACCESSION$ link
+      // so only allow editing if it is not
+      return (!link.usesDBAccession());
+    }
+    else if (index == SELECTED)
+    {
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  /**
+   * Get list of column names to display in UI
+   * 
+   * @return column names
+   */
+  public static List<String> getDisplayColumnNames()
+  {
+    // Display names between DESCRIPTION and ID (excludes ID)
+    return colNames.subList(DATABASE, ID);
+  }
+}
diff --git a/src/jalview/urls/UrlLinkTableModel.java b/src/jalview/urls/UrlLinkTableModel.java
new file mode 100644 (file)
index 0000000..d6d26b5
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.urls;
+
+import jalview.bin.Cache;
+import jalview.urls.api.UrlProviderI;
+import jalview.util.UrlLink;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.RowFilter.Entry;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableModel;
+
+/**
+ * TableModel for UrlLinks table
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+
+public class UrlLinkTableModel extends AbstractTableModel
+{
+  // local storage of data
+  private List<UrlLinkDisplay> data;
+
+  // supplier of url data
+  private UrlProviderI dataProvider;
+
+  // list of columns to display in table in correct order
+  private List<String> displayColumns;
+
+  // row in table which is currently the primary
+  private int primaryRow;
+
+  /**
+   * UrlLinkTableModel constructor
+   * 
+   * @param baseData
+   *          base data set to be presented in table
+   * @param entryNames
+   *          keys of entries in baseData's nested hashmap. Should match order
+   *          in displayColNames
+   * @param displayColNames
+   *          names of columns to display in order.
+   * @param keyColName
+   *          name of column corresponding to keys in baseData
+   */
+  public UrlLinkTableModel(UrlProviderI baseData)
+  {
+    dataProvider = baseData;
+    data = baseData.getLinksForTable();
+    displayColumns = UrlLinkDisplay.getDisplayColumnNames();
+
+    // find the primary row
+    primaryRow = 0;
+    Iterator<UrlLinkDisplay> it = data.iterator();
+    while (it.hasNext())
+    {
+      if (it.next().getIsPrimary())
+      {
+        break;
+      }
+      else
+      {
+        primaryRow++;
+      }
+    }
+
+    // set up listener which updates data source when table changes
+    this.addTableModelListener(new TableModelListener()
+    {
+      @Override
+      public void tableChanged(TableModelEvent e)
+      {
+        try
+        {
+          // update the UrlProvider from data list
+          dataProvider.setUrlData(data);
+        } catch (IllegalArgumentException ex)
+        {
+          Cache.log.error(ex.getMessage());
+        }
+      }
+    });
+
+  }
+
+  @Override
+  public int getRowCount()
+  {
+    if (data == null)
+    {
+      return 0;
+    }
+    else
+    {
+      return data.size();
+    }
+  }
+
+  @Override
+  public int getColumnCount()
+  {
+    return displayColumns.size();
+  }
+
+  @Override
+  public Object getValueAt(int rowIndex, int columnIndex)
+  {
+    return data.get(rowIndex).getValue(columnIndex);
+  }
+
+  @Override
+  public boolean isCellEditable(int rowIndex, int columnIndex)
+  {
+    return data.get(rowIndex).isEditable(columnIndex);
+  }
+
+  /**
+   * Determine if a row is editable indirectly (rather than directly in table as
+   * in isCellEditable)
+   * 
+   * @param rowIndex
+   * @return true if row can be edited indirectly
+   */
+  public boolean isRowEditable(int rowIndex)
+  {
+    // to edit, row must be a user entered row
+    return (dataProvider.isUserEntry(data.get(rowIndex).getId()));
+  }
+
+  /**
+   * Determine if a row is deletable
+   * 
+   * @param rowIndex
+   *          the row to be tested
+   * @return true if row can be deleted
+   */
+  public boolean isRowDeletable(int rowIndex)
+  {
+    // to delete, row must be a user entered row, and not the default row
+    return (dataProvider.isUserEntry(data.get(rowIndex).getId()) && !data
+            .get(rowIndex).getIsPrimary());
+  }
+
+  @Override
+  public void setValueAt(Object aValue, int rowIndex, int columnIndex)
+  {
+    if (columnIndex == UrlLinkDisplay.PRIMARY)
+    {
+      // Default url column: exactly one row must always be true
+      if (rowIndex != primaryRow)
+      {
+        // selected row is not currently the default
+        // set the current default to false
+        data.get(primaryRow).setValue(columnIndex, false);
+        fireTableRowsUpdated(primaryRow, primaryRow);
+
+        // set the default to be the selected row
+        primaryRow = rowIndex;
+        data.get(rowIndex).setValue(columnIndex, aValue);
+
+        fireTableRowsUpdated(rowIndex, rowIndex);
+      }
+    }
+    else
+    {
+      data.get(rowIndex).setValue(columnIndex, aValue);
+      fireTableRowsUpdated(rowIndex, rowIndex);
+    }
+  }
+
+  @Override
+  public Class<?> getColumnClass(int columnIndex)
+  {
+    return getValueAt(0, columnIndex).getClass();
+  }
+
+  @Override
+  public String getColumnName(int columnIndex)
+  {
+    return displayColumns.get(columnIndex);
+  }
+
+  public void removeRow(int rowIndex)
+  {
+    // remove the row from data
+    data.remove(rowIndex);
+
+    // update default row
+    if (primaryRow > rowIndex)
+    {
+      primaryRow--;
+    }
+
+    // fire update which will update data source
+    fireTableRowsDeleted(rowIndex, rowIndex);
+  }
+
+  public int insertRow(String name, String url)
+  {
+    // add a row to the data
+    UrlLink link = new UrlLink(name, url, name);
+    UrlLinkDisplay u = new UrlLinkDisplay(name, link, true, false);
+    int index = data.size();
+    data.add(u);
+
+    // fire update which will update data source
+    fireTableRowsInserted(index, index);
+    return index;
+  }
+
+  public int getPrimaryColumn()
+  {
+    return UrlLinkDisplay.PRIMARY;
+  }
+
+  public int getNameColumn()
+  {
+    return UrlLinkDisplay.NAME;
+  }
+
+  public int getDatabaseColumn()
+  {
+    return UrlLinkDisplay.DATABASE;
+  }
+
+  public int getIdColumn()
+  {
+    return UrlLinkDisplay.ID;
+  }
+
+  public int getUrlColumn()
+  {
+    return UrlLinkDisplay.URL;
+  }
+
+  public int getSelectedColumn()
+  {
+    return UrlLinkDisplay.SELECTED;
+  }
+
+  public boolean isUserEntry(
+          Entry<? extends TableModel, ? extends Object> entry)
+  {
+    return dataProvider
+            .isUserEntry(entry.getStringValue(UrlLinkDisplay.ID));
+  }
+
+  public boolean isUniqueName(String name)
+  {
+    return !dataProvider.contains(name);
+  }
+}
diff --git a/src/jalview/urls/UrlProvider.java b/src/jalview/urls/UrlProvider.java
new file mode 100644 (file)
index 0000000..bd01e89
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.urls;
+
+import static jalview.util.UrlConstants.SEP;
+
+import jalview.urls.api.UrlProviderI;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * 
+ * Implements the UrlProviderI interface for a composite UrlProvider object
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+public class UrlProvider implements UrlProviderI
+{
+  // List of actual URL link providers
+  private List<UrlProviderI> providers;
+
+  // Specific reference to custom URL link provider
+  private UrlProviderI customProvider;
+
+  /**
+   * Constructor for UrlProvider composite
+   * 
+   * @param defaultUrlString
+   *          id of default url
+   * @param allProviders
+   *          list of UrlProviders this provider gives access to
+   */
+  public UrlProvider(String defaultUrlString,
+          List<UrlProviderI> allProviders)
+  {
+    providers = allProviders;
+
+    customProvider = findCustomProvider();
+
+    // check that the defaultUrl still exists
+    if (!contains(defaultUrlString))
+    {
+      // if the defaultUrl can't be found in any of the providers
+      // set up a custom default url
+      choosePrimaryUrl();
+    }
+    else
+    {
+      setPrimaryUrl(defaultUrlString);
+    }
+  }
+
+  /*
+   * Store ref to custom url provider
+   */
+  private UrlProviderI findCustomProvider()
+  {
+    for (UrlProviderI p : providers)
+    {
+      if (p instanceof CustomUrlProvider)
+      {
+        return p;
+      }
+    }
+
+    System.out
+            .println("Error initialising UrlProvider - no custom url provider");
+    return null;
+  }
+  
+  @Override
+  public boolean setPrimaryUrl(String id)
+  {
+    boolean outcome = false;
+    for (UrlProviderI p : providers)
+    {
+      if (p.setPrimaryUrl(id))
+      {
+        outcome = true;
+      }
+    }
+    if (!outcome)
+    {
+      throw new IllegalArgumentException();
+    }
+    return outcome;
+  }
+
+  @Override
+  public boolean contains(String id)
+  {
+    boolean outcome = false;
+    for (UrlProviderI p : providers)
+    {
+      if (p.contains(id))
+      {
+        outcome = true;
+      }
+    }
+    return outcome;
+  }
+  
+  @Override
+  public String writeUrlsAsString(boolean selected)
+  {
+    String result = "";
+    for (UrlProviderI p : providers)
+    {
+      String next = p.writeUrlsAsString(selected);
+      if (!next.isEmpty())
+      {
+        result += next;
+        result += SEP;
+      }
+    }
+    // remove last sep
+    if (!result.isEmpty())
+    {
+      result = result.substring(0, result.length() - 1);
+    }
+    return result;
+  }
+
+  @Override
+  public Vector<String> getLinksForMenu()
+  {
+    Vector<String> fullLinks = new Vector<String>();
+    for (UrlProviderI p : providers)
+    {
+      List<String> links = p.getLinksForMenu();
+      if (links != null)
+      {
+        // will obliterate links with same keys from different providers
+        // must have checks in place to prevent user from duplicating ids
+        fullLinks.addAll(links);
+      }
+    }
+    return fullLinks;
+  }
+
+  @Override
+  public List<UrlLinkDisplay> getLinksForTable()
+  {
+    ArrayList<UrlLinkDisplay> displayLinks = new ArrayList<UrlLinkDisplay>();
+    for (UrlProviderI p : providers)
+    {
+      displayLinks.addAll(p.getLinksForTable());
+    }
+    return displayLinks;
+  }
+
+  @Override
+  public void setUrlData(List<UrlLinkDisplay> links)
+  {
+    for (UrlProviderI p : providers)
+    {
+      p.setUrlData(links);
+    }
+  }
+
+  @Override
+  public String getPrimaryUrl(String seqid)
+  {
+    String link = null;
+    for (UrlProviderI p : providers)
+    {
+      if (p.getPrimaryUrl(seqid) == null)
+      {
+        continue;
+      }
+      else
+      {
+        link = p.getPrimaryUrl(seqid);
+        break;
+      }
+    }
+    return link;
+  }
+
+  @Override
+  public String getPrimaryUrlId()
+  {
+    String id = null;
+    for (UrlProviderI p : providers)
+    {
+      if (p.getPrimaryUrlId() == null)
+      {
+        continue;
+      }
+      else
+      {
+        id = p.getPrimaryUrlId();
+        break;
+      }
+    }
+    return id;
+  }
+
+  @Override
+  public String getPrimaryTarget(String seqid)
+  {
+    String target = null;
+    for (UrlProviderI p : providers)
+    {
+      if (p.getPrimaryTarget(seqid) == null)
+      {
+        continue;
+      }
+      else
+      {
+        target = p.getPrimaryTarget(seqid);
+        break;
+      }
+    }
+    return target;
+  }
+  
+  @Override
+  public String choosePrimaryUrl()
+  {
+    // choose a custom url default
+    return customProvider.choosePrimaryUrl();
+  }
+
+  @Override
+  public boolean isUserEntry(String id)
+  {
+    return customProvider.isUserEntry(id);
+  }
+}
diff --git a/src/jalview/urls/UrlProviderImpl.java b/src/jalview/urls/UrlProviderImpl.java
new file mode 100644 (file)
index 0000000..c1a57ca
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.urls;
+
+import jalview.urls.api.UrlProviderI;
+import jalview.util.UrlLink;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+/**
+ * Leaf node of UrlProvider composite
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+
+public abstract class UrlProviderImpl implements UrlProviderI
+{
+  // minimum length of substitution in url link string
+  protected static final int MIN_SUBST_LENGTH = 4;
+
+  private static final Pattern MIRIAM_PATTERN = Pattern
+          .compile("^MIR:\\d{8}$");
+
+  protected String primaryUrl;
+
+  protected String getPrimaryUrl(String seqid, HashMap<String, UrlLink> urls)
+  {
+    if (seqid.length() < MIN_SUBST_LENGTH)
+    {
+      return null;
+    }
+    else if (primaryUrl == null)
+    {
+      return null;
+    }
+    else if (!urls.containsKey(primaryUrl))
+    {
+      return null;
+    }
+    else
+    {
+      String url = null;
+      UrlLink urlLink = urls.get(primaryUrl);
+      String[] primaryUrls = urlLink.makeUrls(seqid, true);
+      if (primaryUrls == null || primaryUrls[0] == null
+              || primaryUrls[0].length() < MIN_SUBST_LENGTH)
+      {
+        url = null;
+      }
+      else
+      {
+        // just take first URL made from regex
+        url = primaryUrls[1];
+      }
+      return url;
+    }
+  }
+
+  @Override
+  public List<UrlLinkDisplay> getLinksForTable()
+  {
+    return null;
+  }
+
+  protected ArrayList<UrlLinkDisplay> getLinksForTable(
+          HashMap<String, UrlLink> urls, ArrayList<String> selectedUrls,
+          boolean selected)
+  {
+    ArrayList<UrlLinkDisplay> displayLinks = new ArrayList<UrlLinkDisplay>();
+    for (Entry<String, UrlLink> entry : urls.entrySet())
+    {
+      String key = entry.getKey();
+      boolean isPrimary = (key.equals(primaryUrl));
+      boolean isSelected;
+      if (selectedUrls != null)
+      {
+        isSelected = selectedUrls.contains(key);
+      }
+      else
+      {
+        isSelected = selected;
+      }
+      displayLinks.add(new UrlLinkDisplay(key, entry.getValue(),
+              isSelected, isPrimary));
+    }
+    return displayLinks;
+  }
+
+  protected boolean isMiriamId(String id)
+  {
+    return MIRIAM_PATTERN.matcher(id).matches();
+  }
+
+  @Override
+  public boolean isUserEntry(String id)
+  {
+    return !isMiriamId(id);
+  }
+}
+
diff --git a/src/jalview/urls/api/UrlProviderFactoryI.java b/src/jalview/urls/api/UrlProviderFactoryI.java
new file mode 100644 (file)
index 0000000..b96113e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.urls.api;
+
+
+/**
+ * Interface to UrlProvider factories
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+public interface UrlProviderFactoryI
+{
+  public UrlProviderI createUrlProvider();
+
+}
diff --git a/src/jalview/urls/api/UrlProviderI.java b/src/jalview/urls/api/UrlProviderI.java
new file mode 100644 (file)
index 0000000..728d9be
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.urls.api;
+
+import jalview.urls.UrlLinkDisplay;
+
+import java.util.List;
+
+/**
+ * Methods for providing consistent access to up-to-date URLs
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+public interface UrlProviderI
+{
+
+  /**
+   * Get names and urls in the UrlProvider as strings for display
+   * 
+   */
+  List<String> getLinksForMenu();
+
+  /**
+   * Get names and urls as strings for display
+   * 
+   */
+  List<UrlLinkDisplay> getLinksForTable();
+
+  /**
+   * Set names and urls from display settings
+   */
+  void setUrlData(List<UrlLinkDisplay> links);
+
+  /**
+   * Get the link for the primary URL
+   * 
+   * @seqid sequence id for which to build link
+   * @return link for the primary URL
+   */
+  String getPrimaryUrl(String seqid);
+
+  /**
+   * Get the primary URL id
+   * 
+   * @return id for primary URL
+   */
+  String getPrimaryUrlId();
+
+  /**
+   * Get the target of the link for the primary URL
+   * 
+   * @seqid sequence id for which to build link
+   * @return target of link for the primary URL
+   */
+  String getPrimaryTarget(String seqid);
+
+  /**
+   * Set the primary URL: if only one URL can be used, this URL is the one which
+   * should be chosen, e.g. provides the URL to be used on double-click of a
+   * sequence id
+   * 
+   * @param id
+   *          the id of the URL to set as primary
+   * @return true if setting is successful
+   * @throws IllegalArgumentException
+   *           if id does not exist as a url in the UrlProvider
+   */
+  boolean setPrimaryUrl(String id) throws IllegalArgumentException;
+
+  /**
+   * Test if UrlProvider contains a url
+   * 
+   * @param id
+   *          to test for
+   * @return true of UrlProvider contains this id, false otherwise
+   */
+  boolean contains(String id);
+
+  /**
+   * Write out all URLs as a string suitable for serialising
+   * 
+   * @return string representation of available URLs
+   */
+  String writeUrlsAsString(boolean selected);
+
+  /**
+   * Choose the primary URL in the event of the selected primary being
+   * unavailable
+   * 
+   * @return id of chosen primary url
+   */
+  String choosePrimaryUrl();
+
+  /**
+   * Determine if id is for a user-defined URL
+   */
+  boolean isUserEntry(String id);
+}
diff --git a/src/jalview/urls/applet/AppletUrlProviderFactory.java b/src/jalview/urls/applet/AppletUrlProviderFactory.java
new file mode 100644 (file)
index 0000000..fcedaa4
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.urls.applet;
+
+import jalview.urls.CustomUrlProvider;
+import jalview.urls.UrlProvider;
+import jalview.urls.api.UrlProviderFactoryI;
+import jalview.urls.api.UrlProviderI;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * UrlProvider factory for applet code
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+
+public class AppletUrlProviderFactory implements UrlProviderFactoryI
+{
+  private String provDefaultUrl;
+
+  private Map<String, String> provUrlList;
+
+  public AppletUrlProviderFactory(String defaultUrlString,
+          Map<String, String> urlList)
+  {
+    provDefaultUrl = defaultUrlString;
+    provUrlList = urlList;
+  }
+
+  @Override
+  public UrlProviderI createUrlProvider()
+  {
+    // create all the UrlProviders we need
+    List<UrlProviderI> providers = new ArrayList<UrlProviderI>();
+    UrlProviderI customProvider = new CustomUrlProvider(provUrlList, null);
+    providers.add(customProvider);
+
+    UrlProviderI prov = new UrlProvider(provDefaultUrl, providers);
+    return prov;
+  }
+
+}
diff --git a/src/jalview/urls/desktop/DesktopUrlProviderFactory.java b/src/jalview/urls/desktop/DesktopUrlProviderFactory.java
new file mode 100644 (file)
index 0000000..ce933c2
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.urls.desktop;
+
+import jalview.urls.CustomUrlProvider;
+import jalview.urls.IdentifiersUrlProvider;
+import jalview.urls.UrlProvider;
+import jalview.urls.api.UrlProviderFactoryI;
+import jalview.urls.api.UrlProviderI;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * UrlProvider factory for desktop code
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+
+public class DesktopUrlProviderFactory implements UrlProviderFactoryI
+{
+
+  private String provDefaultUrl;
+
+  private String menuUrlList;
+
+  private String nonMenuUrlList;
+
+  public DesktopUrlProviderFactory(String defaultUrlString,
+          String cachedUrlList, String userUrlList)
+  {
+    provDefaultUrl = defaultUrlString;
+    menuUrlList = cachedUrlList;
+    nonMenuUrlList = userUrlList;
+  }
+
+  @Override
+  public UrlProviderI createUrlProvider()
+  {
+    // create all the UrlProviders we need
+    List<UrlProviderI> providers = new ArrayList<UrlProviderI>();
+
+    UrlProviderI idProvider = new IdentifiersUrlProvider(menuUrlList);
+    UrlProviderI customProvider = new CustomUrlProvider(menuUrlList,
+            nonMenuUrlList);
+    providers.add(idProvider);
+    providers.add(customProvider);
+
+    return new UrlProvider(provDefaultUrl, providers);
+  }
+
+}
diff --git a/src/jalview/util/RangeComparator.java b/src/jalview/util/RangeComparator.java
new file mode 100644 (file)
index 0000000..f911a9b
--- /dev/null
@@ -0,0 +1,25 @@
+package jalview.util;
+
+import java.util.Comparator;
+
+/**
+ * A comparator to order [from, to] ranges into ascending or descending order of
+ * their start position
+ */
+public class RangeComparator implements Comparator<int[]>
+{
+  boolean forwards;
+
+  public RangeComparator(boolean forward)
+  {
+    forwards = forward;
+  }
+
+  @Override
+  public int compare(int[] o1, int[] o2)
+  {
+    int compared = Integer.compare(o1[0], o2[0]);
+    return forwards ? compared : -compared;
+  }
+
+}
\ No newline at end of file
index 1910bff..3347cc7 100644 (file)
@@ -37,14 +37,25 @@ public class UrlConstants
   public static final String SEQUENCE_ID = "SEQUENCE_ID";
 
   /*
-   * Default sequence URL link string for EMBL-EBI search
+   * Separator character used in Url links
+   */
+  public static final String SEP = "|";
+
+  /*
+   * Delimiter character used in Url links
    */
-  public static final String EMBLEBI_STRING = "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$";
+  public static final String DELIM = "$";
 
   /*
-   * Default sequence URL link string for SRS 
+   * Default sequence URL link label for EMBL-EBI search
+   */
+  public static final String DEFAULT_LABEL = "EMBL-EBI Search";
+
+  /*
+   * Default sequence URL link string for EMBL-EBI search
    */
-  public static final String SRS_STRING = "SRS|http://srs.ebi.ac.uk/srsbin/cgi-bin/wgetz?-newId+(([uniprot-all:$SEQUENCE_ID$]))+-view+SwissEntry";
+  public static final String DEFAULT_STRING = DEFAULT_LABEL
+          + "|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$";
 
   /*
    * not instantiable
index 3ee6432..0529c73 100644 (file)
@@ -21,6 +21,8 @@
 package jalview.util;
 
 import static jalview.util.UrlConstants.DB_ACCESSION;
+import static jalview.util.UrlConstants.DELIM;
+import static jalview.util.UrlConstants.SEP;
 import static jalview.util.UrlConstants.SEQUENCE_ID;
 
 import jalview.datamodel.DBRefEntry;
@@ -43,10 +45,9 @@ public class UrlLink
    * documentation todo.
    */
 
-  // Internal constants
-  private static final String SEP = "|";
+  private static final String EQUALS = "=";
 
-  private static final String DELIM = "$";
+  private static final String SPACE = " ";
 
   private String urlSuffix;
 
@@ -56,6 +57,8 @@ public class UrlLink
 
   private String label;
 
+  private String dbname;
+
   private String regexReplace;
 
   private boolean dynamic = false;
@@ -81,23 +84,38 @@ public class UrlLink
       dynamic = true;
       usesDBaccession = true;
 
-      sep = parseTargetAndLabel(sep, psqid, link);
+      sep = parseLabel(sep, psqid, link);
 
-      parseUrl(link, DB_ACCESSION, psqid, sep);
+      int endOfRegex = parseUrl(link, DB_ACCESSION, psqid, sep);
+      parseTarget(link, sep, endOfRegex);
     }
     else if (nsqid > -1)
     {
       dynamic = true;
-      sep = parseTargetAndLabel(sep, nsqid, link);
+      sep = parseLabel(sep, nsqid, link);
+
+      int endOfRegex = parseUrl(link, SEQUENCE_ID, nsqid, sep);
 
-      parseUrl(link, SEQUENCE_ID, nsqid, sep);
+      parseTarget(link, sep, endOfRegex);
     }
     else
     {
-      target = link.substring(0, sep);
-      sep = link.lastIndexOf(SEP);
-      label = link.substring(0, sep);
-      urlPrefix = link.substring(sep + 1).trim();
+      label = link.substring(0, sep).trim();
+
+      // if there's a third element in the url link string
+      // it is the target name, otherwise target=label
+      int lastsep = link.lastIndexOf(SEP);
+      if (lastsep != sep)
+      {
+        urlPrefix = link.substring(sep + 1, lastsep).trim();
+        target = link.substring(lastsep + 1).trim();
+      }
+      else
+      {
+        urlPrefix = link.substring(sep + 1).trim();
+        target = label;
+      }
+
       regexReplace = null; // implies we trim any prefix if necessary //
       urlSuffix = null;
     }
@@ -107,9 +125,24 @@ public class UrlLink
   }
 
   /**
+   * Alternative constructor for separate name, link and description
+   * 
+   * @param name
+   *          The string used to match the link to a DB reference id
+   * @param url
+   *          The url to link to
+   * @param desc
+   *          The description of the associated target DB
+   */
+  public UrlLink(String name, String url, String desc)
+  {
+    this(name + SEP + url + SEP + desc);
+  }
+
+  /**
    * @return the url_suffix
    */
-  public String getUrl_suffix()
+  public String getUrlSuffix()
   {
     return urlSuffix;
   }
@@ -117,7 +150,7 @@ public class UrlLink
   /**
    * @return the url_prefix
    */
-  public String getUrl_prefix()
+  public String getUrlPrefix()
   {
     return urlPrefix;
   }
@@ -138,6 +171,16 @@ public class UrlLink
     return label;
   }
 
+  public String getUrlWithToken()
+  {
+    String var = (usesDBaccession ? DB_ACCESSION : SEQUENCE_ID);
+
+    return urlPrefix
+            + (dynamic ? (DELIM + var + ((regexReplace != null) ? EQUALS
+                    + regexReplace + EQUALS + DELIM : DELIM)) : "")
+            + ((urlSuffix == null) ? "" : urlSuffix);
+  }
+
   /**
    * @return the regexReplace
    */
@@ -194,6 +237,16 @@ public class UrlLink
   }
 
   /**
+   * Set the target
+   * 
+   * @param desc
+   */
+  public void setTarget(String desc)
+  {
+    target = desc;
+  }
+
+  /**
    * return one or more URL strings by applying regex to the given idstring
    * 
    * @param idstring
@@ -234,7 +287,7 @@ public class UrlLink
                       + rg.stringMatched(s) + "'");
             }
             // try to collate subgroup matches
-            Vector subs = new Vector();
+            Vector<String> subs = new Vector<String>();
             // have to loop through submatches, collating them at top level
             // match
             int s = 0; // 1;
@@ -278,7 +331,7 @@ public class UrlLink
             String[] res = new String[subs.size()];
             for (int r = 0, rs = subs.size(); r < rs; r++)
             {
-              res[r] = (String) subs.elementAt(r);
+              res[r] = subs.elementAt(r);
             }
             subs.removeAllElements();
             return res;
@@ -307,17 +360,19 @@ public class UrlLink
   @Override
   public String toString()
   {
-    String var = (usesDBaccession ? DB_ACCESSION : SEQUENCE_ID);
+    return label + SEP + getUrlWithToken();
+  }
 
-    return label
-            + SEP
-            + urlPrefix
-            + (dynamic ? (DELIM + var + ((regexReplace != null) ? "="
-                    + regexReplace + "=" + DELIM : DELIM)) : "")
-            + ((urlSuffix == null) ? "" : urlSuffix);
+  /**
+   * @return delimited string containing label, url and target
+   */
+  public String toStringWithTarget()
+  {
+    return label + SEP + getUrlWithToken() + SEP + target;
   }
 
   /**
+   * Parse the label from the link string
    * 
    * @param firstSep
    *          Location of first occurrence of separator in link string
@@ -327,7 +382,7 @@ public class UrlLink
    *          Link string containing database name and url
    * @return Position of last separator symbol prior to any regex symbols
    */
-  protected int parseTargetAndLabel(int firstSep, int psqid, String link)
+  protected int parseLabel(int firstSep, int psqid, String link)
   {
     int p = firstSep;
     int sep = firstSep;
@@ -339,21 +394,44 @@ public class UrlLink
     // Assuming that the URL itself does not contain any SEP symbols
     // sep now contains last pipe symbol position prior to any regex symbols
     label = link.substring(0, sep);
-    if (label.indexOf(SEP) > -1)
-    {
-      // SEP terminated database name / www target at start of Label
-      target = label.substring(0, label.indexOf(SEP));
-    }
-    else if (label.indexOf(" ") > 2)
+
+    return sep;
+  }
+
+  /**
+   * Parse the target from the link string
+   * 
+   * @param link
+   *          Link string containing database name and url
+   * @param sep
+   *          Location of first separator symbol
+   * @param endOfRegex
+   *          Location of end of any regular expression in link string
+   */
+  protected void parseTarget(String link, int sep, int endOfRegex)
+  {
+    int lastsep = link.lastIndexOf(SEP);
+
+    if ((lastsep != sep) && (lastsep > endOfRegex))
     {
-      // space separated Label - matches database name
-      target = label.substring(0, label.indexOf(" "));
+      // final element in link string is the target
+      target = link.substring(lastsep + 1).trim();
     }
     else
     {
       target = label;
     }
-    return sep;
+
+    if (target.indexOf(SEP) > -1)
+    {
+      // SEP terminated database name / www target at start of Label
+      target = target.substring(0, target.indexOf(SEP));
+    }
+    else if (target.indexOf(SPACE) > 2)
+    {
+      // space separated label - first word matches database name
+      target = target.substring(0, target.indexOf(SPACE));
+    }
   }
 
   /**
@@ -367,8 +445,9 @@ public class UrlLink
    *          Position of id or name in link string
    * @param sep
    *          Position of separator in link string
+   * @return Location of end of any regex in link string
    */
-  protected void parseUrl(String link, String varName, int sqidPos, int sep)
+  protected int parseUrl(String link, String varName, int sqidPos, int sep)
   {
     urlPrefix = link.substring(sep + 1, sqidPos).trim();
 
@@ -411,7 +490,14 @@ public class UrlLink
       // verify format is really correct.
       if (link.indexOf(DELIM + varName + DELIM) == sqidPos)
       {
-        urlSuffix = link.substring(sqidPos + startLength - 1);
+        int lastsep = link.lastIndexOf(SEP);
+        if (lastsep < sqidPos + startLength - 1)
+        {
+          // the last SEP character was before the regex, ignore
+          lastsep = link.length();
+        }
+        urlSuffix = link.substring(sqidPos + startLength - 1, lastsep)
+                .trim();
         regexReplace = null;
       }
       else
@@ -420,6 +506,8 @@ public class UrlLink
                 + link;
       }
     }
+
+    return p;
   }
 
   /**
@@ -453,11 +541,11 @@ public class UrlLink
    */
   protected void createStaticLink(Map<String, List<String>> linkset)
   {
-    if (!linkset.containsKey(label + SEP + getUrl_prefix()))
+    if (!linkset.containsKey(label + SEP + getUrlPrefix()))
     {
       // Add a non-dynamic link
-      linkset.put(label + SEP + getUrl_prefix(),
-              Arrays.asList(target, label, null, getUrl_prefix()));
+      linkset.put(label + SEP + getUrlPrefix(),
+              Arrays.asList(target, label, null, getUrlPrefix()));
     }
   }
 
@@ -539,82 +627,4 @@ public class UrlLink
       }
     }
   }
-
-  private static void testUrls(UrlLink ul, String idstring, String[] urls)
-  {
-
-    if (urls == null)
-    {
-      System.out.println("Created NO urls.");
-    }
-    else
-    {
-      System.out.println("Created " + (urls.length / 2) + " Urls.");
-      for (int uls = 0; uls < urls.length; uls += 2)
-      {
-        System.out.println("URL Replacement text : " + urls[uls]
-                + " : URL : " + urls[uls + 1]);
-      }
-    }
-  }
-
-  public static void main(String argv[])
-  {
-    String[] links = new String[] {
-    /*
-     * "AlinkT|Target|http://foo.foo.soo/",
-     * "myUrl1|http://$SEQUENCE_ID=/[0-9]+/=$.someserver.org/foo",
-     * "myUrl2|http://$SEQUENCE_ID=/(([0-9]+).+([A-Za-z]+))/=$.someserver.org/foo"
-     * ,
-     * "myUrl3|http://$SEQUENCE_ID=/([0-9]+).+([A-Za-z]+)/=$.someserver.org/foo"
-     * , "myUrl4|target|http://$SEQUENCE_ID$.someserver.org/foo|too",
-     * "PF1|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/(?:PFAM:)?(.+)/=$"
-     * ,
-     * "PF2|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/(PFAM:)?(.+)/=$"
-     * ,
-     * "PF3|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/PFAM:(.+)/=$"
-     * , "NOTFER|http://notfer.org/$SEQUENCE_ID=/(?<!\\s)(.+)/=$",
-     */
-    "NESTED|http://nested/$" + DB_ACCESSION
-            + "=/^(?:Label:)?(?:(?:gi\\|(\\d+))|([^:]+))/=$/nested" };
-    String[] idstrings = new String[] {
-    /*
-     * //"LGUL_human", //"QWIQW_123123", "uniprot|why_do+_12313_foo",
-     * //"123123312", "123123 ABCDE foo", "PFAM:PF23943",
-     */
-    "Label:gi|9234|pdb|102L|A" };
-    // TODO: test the setLabel method.
-    for (int i = 0; i < links.length; i++)
-    {
-      UrlLink ul = new UrlLink(links[i]);
-      if (ul.isValid())
-      {
-        System.out.println("\n\n\n");
-        System.out.println("Link " + i + " " + links[i] + " : "
-                + ul.toString());
-        System.out.println(" pref : "
-                + ul.getUrl_prefix()
-                + "\n suf : "
-                + ul.getUrl_suffix()
-                + "\n : "
-                + ((ul.getRegexReplace() != null) ? ul.getRegexReplace()
-                        : ""));
-        for (int ids = 0; ids < idstrings.length; ids++)
-        {
-          System.out.println("ID String : " + idstrings[ids]
-                  + "\nWithout onlyIfMatches:");
-          String[] urls = ul.makeUrls(idstrings[ids], false);
-          testUrls(ul, idstrings[ids], urls);
-          System.out.println("With onlyIfMatches set.");
-          urls = ul.makeUrls(idstrings[ids], true);
-          testUrls(ul, idstrings[ids], urls);
-        }
-      }
-      else
-      {
-        System.err.println("Invalid URLLink : " + links[i] + " : "
-                + ul.getInvalidMessage());
-      }
-    }
-  }
 }
index 230864e..3547757 100644 (file)
  */
 package jalview.viewmodel;
 
+import java.awt.Color;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.Conservation;
 import jalview.api.AlignCalcManagerI;
@@ -57,17 +68,6 @@ import jalview.workers.ComplementConsensusThread;
 import jalview.workers.ConsensusThread;
 import jalview.workers.StrucConsensusThread;
 
-import java.awt.Color;
-import java.beans.PropertyChangeSupport;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
 /**
  * base class holding visualization and analysis attributes and common logic for
  * an active alignment view displayed in the GUI
@@ -78,6 +78,8 @@ import java.util.Map;
 public abstract class AlignmentViewport implements AlignViewportI,
         CommandListener, VamsasSource
 {
+  protected ViewportRanges ranges;
+
   protected ViewStyleI viewStyle = new ViewStyle();
 
   /**
@@ -672,6 +674,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   protected AlignmentAnnotation complementConsensus;
 
+  protected AlignmentAnnotation gapcounts;
+
   protected AlignmentAnnotation strucConsensus;
 
   protected AlignmentAnnotation conservation;
@@ -774,6 +778,12 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
+  public AlignmentAnnotation getAlignmentGapAnnotation()
+  {
+    return gapcounts;
+  }
+
+  @Override
   public AlignmentAnnotation getComplementConsensusAnnotation()
   {
     return complementConsensus;
@@ -813,7 +823,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void updateConsensus(final AlignmentViewPanel ap)
   {
     // see note in mantis : issue number 8585
-    if (consensus == null || !autoCalculateConsensus)
+    if ((consensus == null || gapcounts == null) || !autoCalculateConsensus)
     {
       return;
     }
@@ -1073,7 +1083,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   /**
-   * Set the selection group for this window.
+   * Set the selection group for this window. Also sets the current alignment as
+   * the context for the group, if it does not already have one.
    * 
    * @param sg
    *          - group holding references to sequences in this alignment view
@@ -1083,6 +1094,10 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void setSelectionGroup(SequenceGroup sg)
   {
     selectionGroup = sg;
+    if (sg != null && sg.getContext() == null)
+    {
+      sg.setContext(alignment);
+    }
   }
 
   public void setHiddenColumns(ColumnSelection colsel)
@@ -1280,15 +1295,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   private boolean followHighlight = true;
 
-  // TODO private with getters and setters?
-  public int startRes;
-
-  public int endRes;
-
-  public int startSeq;
-
-  public int endSeq;
-
   /**
    * Property change listener for changes in alignment
    * 
@@ -1887,6 +1893,11 @@ public abstract class AlignmentViewport implements AlignViewportI,
       consensus = new AlignmentAnnotation("Consensus", "PID",
               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
       initConsensus(consensus);
+      gapcounts = new AlignmentAnnotation("Occupancy",
+              "Number of aligned positions",
+              new Annotation[1], 0f, alignment.getHeight(),
+              AlignmentAnnotation.BAR_GRAPH);
+      initGapCounts(gapcounts);
 
       initComplementConsensus();
     }
@@ -1939,6 +1950,20 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
   }
 
+  // these should be extracted from the view model - style and settings for
+  // derived annotation
+  private void initGapCounts(AlignmentAnnotation counts)
+  {
+    counts.hasText = false;
+    counts.autoCalculated = true;
+    counts.graph = AlignmentAnnotation.BAR_GRAPH;
+
+    if (showConsensus)
+    {
+      alignment.addAnnotation(counts);
+    }
+  }
+
   private void initConservation()
   {
     if (showConservation)
@@ -2425,6 +2450,11 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void setViewStyle(ViewStyleI settingsForView)
   {
     viewStyle = new ViewStyle(settingsForView);
+    if (residueShading != null)
+    {
+      residueShading.setConservationApplied(settingsForView
+              .isConservationColourSelected());
+    }
   }
 
   @Override
@@ -2635,63 +2665,10 @@ public abstract class AlignmentViewport implements AlignViewportI,
     this.followHighlight = b;
   }
 
-  public int getStartRes()
-  {
-    return startRes;
-  }
-
   @Override
-  public int getEndRes()
-  {
-    return endRes;
-  }
-
-  public int getStartSeq()
-  {
-    return startSeq;
-  }
-
-  public void setStartRes(int res)
-  {
-    this.startRes = res;
-  }
-
-  public void setStartSeq(int seq)
+  public ViewportRanges getRanges()
   {
-    this.startSeq = seq;
-  }
-
-  public void setEndRes(int res)
-  {
-    if (res > alignment.getWidth() - 1)
-    {
-      // log.System.out.println(" Corrected res from " + res + " to maximum " +
-      // (alignment.getWidth()-1));
-      res = alignment.getWidth() - 1;
-    }
-    if (res < 0)
-    {
-      res = 0;
-    }
-    this.endRes = res;
-  }
-
-  public void setEndSeq(int seq)
-  {
-    if (seq > alignment.getHeight())
-    {
-      seq = alignment.getHeight();
-    }
-    if (seq < 0)
-    {
-      seq = 0;
-    }
-    this.endSeq = seq;
-  }
-
-  public int getEndSeq()
-  {
-    return endSeq;
+    return ranges;
   }
 
   /**
@@ -2731,7 +2708,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
      * locate 'middle' column (true middle if an odd number visible, left of
      * middle if an even number visible)
      */
-    int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
+    int middleColumn = ranges.getStartRes()
+            + (ranges.getEndRes() - ranges.getStartRes()) / 2;
     final HiddenSequences hiddenSequences = getAlignment()
             .getHiddenSequences();
 
@@ -2741,7 +2719,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
      */
     int lastSeq = alignment.getHeight() - 1;
     List<AlignedCodonFrame> seqMappings = null;
-    for (int seqNo = getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
+    for (int seqNo = ranges.getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
     {
       sequence = getAlignment().getSequenceAt(seqNo);
       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
@@ -2809,7 +2787,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   private boolean selectionIsDefinedGroup = false;
 
-
   @Override
   public boolean isSelectionDefinedGroup()
   {
diff --git a/src/jalview/viewmodel/OverviewDimensions.java b/src/jalview/viewmodel/OverviewDimensions.java
new file mode 100644 (file)
index 0000000..43680b5
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.viewmodel;
+
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenSequences;
+
+import java.awt.Graphics;
+
+public class OverviewDimensions
+{
+  // Default width and height values
+  private static final int DEFAULT_GRAPH_HEIGHT = 20;
+
+  private static final int MAX_WIDTH = 400;
+
+  private static final int MIN_WIDTH = 120;
+
+  private static final int MIN_SEQ_HEIGHT = 40;
+
+  private static final int MAX_SEQ_HEIGHT = 300;
+
+  // width of the overview panel
+  private int width;
+
+  // height of sequences part of the overview panel
+  private int sequencesHeight;
+
+  // height of the graphs part of the overview panel
+  private int graphHeight = DEFAULT_GRAPH_HEIGHT;
+
+  // dimensions of box outlining current extent of view in alignment panel
+  // location of left side of box
+  private int boxX = -1;
+
+  // location of bottom of box
+  private int boxY = -1;
+
+  // width of box
+  private int boxWidth = -1;
+
+  // height of box
+  private int boxHeight = -1;
+
+  // scroll position in viewport corresponding to boxX
+  private int scrollCol = -1;
+
+  // scroll position in viewport corresponding to boxY
+  private int scrollRow = -1;
+
+  /**
+   * Create an OverviewDimensions object
+   * 
+   * @param ranges
+   *          positional properties of the viewport
+   * @param showAnnotationPanel
+   *          true if the annotation panel is to be shown, false otherwise
+   */
+  public OverviewDimensions(ViewportRanges ranges,
+          boolean showAnnotationPanel)
+  {
+    // scale the initial size of overviewpanel to shape of alignment
+    float initialScale = (float) ranges.getAbsoluteAlignmentWidth()
+            / (float) ranges.getAbsoluteAlignmentHeight();
+
+    if (!showAnnotationPanel)
+    {
+      graphHeight = 0;
+    }
+
+    if (ranges.getAbsoluteAlignmentWidth() > ranges
+            .getAbsoluteAlignmentHeight())
+    {
+      // wider
+      width = MAX_WIDTH;
+      sequencesHeight = Math.round(MAX_WIDTH / initialScale);
+      if (sequencesHeight < MIN_SEQ_HEIGHT)
+      {
+        sequencesHeight = MIN_SEQ_HEIGHT;
+      }
+    }
+    else
+    {
+      // taller
+      width = Math.round(MAX_WIDTH * initialScale);
+      sequencesHeight = MAX_SEQ_HEIGHT;
+
+      if (width < MIN_WIDTH)
+      {
+        width = MIN_WIDTH;
+      }
+    }
+  }
+
+  /**
+   * Check box dimensions and scroll positions and correct if necessary
+   * 
+   * @param mousex
+   *          x position in overview panel
+   * @param mousey
+   *          y position in overview panel
+   * @param hiddenSeqs
+   *          hidden sequences
+   * @param hiddenCols
+   *          hidden columns
+   * @param ranges
+   *          viewport position properties
+   */
+  public void updateViewportFromMouse(int mousex, int mousey,
+          HiddenSequences hiddenSeqs, ColumnSelection hiddenCols,
+          ViewportRanges ranges)
+  {
+    int x = mousex;
+    int y = mousey;
+
+    int alwidth = ranges.getAbsoluteAlignmentWidth();
+    int alheight = ranges.getAbsoluteAlignmentHeight();
+
+    if (x < 0)
+    {
+      x = 0;
+    }
+
+    if (y < 0)
+    {
+      y = 0;
+    }
+
+    //
+    // Convert x value to residue position
+    //
+
+    // need to determine where scrollCol should be, given x
+    // to do this also need to know width of viewport, and some hidden column
+    // correction
+
+    // convert x to residues - this is an absolute position
+    int xAsRes = Math.round((float) x * alwidth / width);
+
+    // get viewport width in residues
+    int vpwidth = ranges.getEndRes() - ranges.getStartRes() + 1;
+
+    // get where x should be when accounting for hidden cols
+    // if x is in a hidden col region, shift to left - but we still need
+    // absolute position
+    // so convert back after getting visible region position
+    int visXAsRes = hiddenCols.findColumnPosition(xAsRes);
+
+    // check in case we went off the edge of the alignment
+    int visAlignWidth = hiddenCols.findColumnPosition(alwidth - 1);
+    if (visXAsRes + vpwidth - 1 > visAlignWidth)
+    {
+      // went past the end of the alignment, adjust backwards
+
+      // if last position was before the end of the alignment, need to update
+      if ((scrollCol + vpwidth - 1) < visAlignWidth)
+      {
+        visXAsRes = hiddenCols.findColumnPosition(hiddenCols
+                .subtractVisibleColumns(vpwidth - 1, alwidth - 1));
+      }
+      else
+      {
+        visXAsRes = scrollCol;
+      }
+    }
+
+    //
+    // Convert y value to sequence position
+    //
+
+    // convert y to residues
+    int yAsSeq = Math.round((float) y * alheight / sequencesHeight);
+
+    // get viewport height in sequences
+    // add 1 because height includes both endSeq and startSeq
+    int vpheight = ranges.getEndSeq() - ranges.getStartSeq() + 1;
+
+    // get where y should be when accounting for hidden rows
+    // if y is in a hidden row region, shift up - but we still need absolute
+    // position,
+    // so convert back after getting visible region position
+    yAsSeq = hiddenSeqs.adjustForHiddenSeqs(hiddenSeqs
+            .findIndexWithoutHiddenSeqs(yAsSeq));
+
+    // check in case we went off the edge of the alignment
+    int visAlignHeight = hiddenSeqs.findIndexWithoutHiddenSeqs(alheight);
+    int visYAsRes = hiddenSeqs.findIndexWithoutHiddenSeqs(yAsSeq);
+    if (visYAsRes + vpheight - 1 > visAlignHeight)
+    {
+      // went past the end of the alignment, adjust backwards
+      if ((scrollRow + vpheight - 1) < visAlignHeight)
+      {
+        visYAsRes = hiddenSeqs.findIndexWithoutHiddenSeqs(hiddenSeqs
+                .subtractVisibleRows(vpheight - 1, alheight - 1));
+      }
+      else
+      {
+        visYAsRes = scrollRow;
+      }
+    }
+
+    // update scroll values
+    scrollCol = visXAsRes;
+    scrollRow = visYAsRes;
+
+  }
+
+  /**
+   * Update the overview panel box when the associated alignment panel is
+   * changed
+   * 
+   * @param hiddenSeqs
+   *          hidden sequences
+   * @param hiddenCols
+   *          hidden columns
+   * @param ranges
+   *          viewport position properties
+   */
+  public void setBoxPosition(HiddenSequences hiddenSeqs,
+          ColumnSelection hiddenCols, ViewportRanges ranges)
+  {
+    int alwidth = ranges.getAbsoluteAlignmentWidth();
+    int alheight = ranges.getAbsoluteAlignmentHeight();
+
+    // work with absolute values of startRes and endRes
+    int startRes = hiddenCols.adjustForHiddenColumns(ranges.getStartRes());
+    int endRes = hiddenCols.adjustForHiddenColumns(ranges.getEndRes());
+
+    // work with absolute values of startSeq and endSeq
+    int startSeq = hiddenSeqs.adjustForHiddenSeqs(ranges.getStartSeq());
+    int endSeq = hiddenSeqs.adjustForHiddenSeqs(ranges.getEndSeq());
+
+    // boxX, boxY is the x,y location equivalent to startRes, startSeq
+    boxX = Math.round((float) startRes * width / alwidth);
+    boxY = Math.round((float) startSeq * sequencesHeight / alheight);
+
+    // boxWidth is the width in residues translated to pixels
+    // since the box includes both the start and end residues, add 1 to the
+    // difference
+    boxWidth = Math
+            .round((float) (endRes - startRes + 1) * width / alwidth);
+    // boxHeight is the height in sequences translated to pixels
+    boxHeight = Math.round((float) (endSeq - startSeq + 1)
+            * sequencesHeight
+            / alheight);
+  }
+
+  /**
+   * Draw the overview panel's viewport box on a graphics object
+   * 
+   * @param g
+   *          the graphics object to draw on
+   */
+  public void drawBox(Graphics g)
+  {
+    g.drawRect(boxX, boxY, boxWidth, boxHeight);
+    g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
+  }
+
+  public int getScrollCol()
+  {
+    return scrollCol;
+  }
+
+  public int getScrollRow()
+  {
+    return scrollRow;
+  }
+
+  // TODO should be removed, when unit test has mock Graphics object available
+  // to check boxX/boxY
+  public int getBoxX()
+  {
+    return boxX;
+  }
+
+  // TODO should be removed, when unit test has mock Graphics object available
+  // to check boxX/boxY
+  public int getBoxY()
+  {
+    return boxY;
+  }
+
+  // TODO should be removed, when unit test has mock Graphics object available
+  public int getBoxWidth()
+  {
+    return boxWidth;
+  }
+
+  // TODO should be removed, when unit test has mock Graphics object available
+  public int getBoxHeight()
+  {
+    return boxHeight;
+  }
+
+  public void setWidth(int w)
+  {
+    width = w;
+  }
+
+  public void setHeight(int h)
+  {
+    sequencesHeight = h - graphHeight;
+  }
+
+  public int getWidth()
+  {
+    return width;
+  }
+
+  public int getHeight()
+  {
+    return sequencesHeight + graphHeight;
+  }
+
+  public int getSequencesHeight()
+  {
+    return sequencesHeight;
+  }
+
+  public int getGraphHeight()
+  {
+    return graphHeight;
+  }
+}
index b0af302..0623dab 100644 (file)
@@ -30,6 +30,13 @@ import java.util.Vector;
 
 public class PCAModel
 {
+  /*
+   * Jalview 2.10.1 treated gaps as X (peptide) or N (nucleotide)
+   * for pairwise scoring; 2.10.2 uses gap score (last column) in
+   * score matrix (JAL-2397)
+   * Set this flag to true (via Groovy) for 2.10.1 behaviour
+   */
+  private static boolean scoreGapAsAny = false;
 
   public PCAModel(AlignmentView seqstrings2, SequenceI[] seqs2,
           boolean nucleotide2)
@@ -69,8 +76,9 @@ public class PCAModel
 
   public void run()
   {
-
-    pca = new PCA(seqstrings.getSequenceStrings(' '), nucleotide,
+    char gapChar = scoreGapAsAny ? (nucleotide ? 'N' : 'X') : ' ';
+    String[] sequenceStrings = seqstrings.getSequenceStrings(gapChar);
+    pca = new PCA(sequenceStrings, nucleotide,
             score_matrix);
     pca.setJvCalcMode(jvCalcMode);
     pca.run();
@@ -83,32 +91,23 @@ public class PCAModel
       ii++;
     }
 
-    double[][] comps = new double[ii][ii];
-
-    for (int i = 0; i < ii; i++)
-    {
-      if (pca.getEigenvalue(i) > 1e-4)
-      {
-        comps[i] = pca.component(i);
-      }
-    }
-
-    top = pca.getM().rows - 1;
+    int height = pca.getHeight();
+    // top = pca.getM().height() - 1;
+    top = height - 1;
 
     points = new Vector<SequencePoint>();
     float[][] scores = pca.getComponents(top - 1, top - 2, top - 3, 100);
 
-    for (int i = 0; i < pca.getM().rows; i++)
+    for (int i = 0; i < height; i++)
     {
       SequencePoint sp = new SequencePoint(seqs[i], scores[i]);
       points.addElement(sp);
     }
-
   }
 
   public void updateRc(RotatableCanvasI rc)
   {
-    rc.setPoints(points, pca.getM().rows);
+    rc.setPoints(points, pca.getHeight());
   }
 
   public boolean isNucleotide()
@@ -146,9 +145,9 @@ public class PCAModel
     // note: actual indices for components are dim1-1, etc (patch for JAL-1123)
     float[][] scores = pca.getComponents(dim1 - 1, dim2 - 1, dim3 - 1, 100);
 
-    for (int i = 0; i < pca.getM().rows; i++)
+    for (int i = 0; i < pca.getHeight(); i++)
     {
-      ((SequencePoint) points.elementAt(i)).coord = scores[i];
+      points.elementAt(i).coord = scores[i];
     }
   }
 
diff --git a/src/jalview/viewmodel/ViewportProperties.java b/src/jalview/viewmodel/ViewportProperties.java
new file mode 100644 (file)
index 0000000..246806e
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.viewmodel;
+
+public abstract class ViewportProperties
+{
+
+}
diff --git a/src/jalview/viewmodel/ViewportRanges.java b/src/jalview/viewmodel/ViewportRanges.java
new file mode 100644 (file)
index 0000000..c91d2d9
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.viewmodel;
+
+import jalview.datamodel.AlignmentI;
+
+/**
+ * Embryonic class which: Supplies and updates viewport properties relating to
+ * position such as: start and end residues and sequences; ideally will serve
+ * hidden columns/rows too. Intention also to support calculations for
+ * positioning, scrolling etc. such as finding the middle of the viewport,
+ * checking for scrolls off screen
+ */
+public class ViewportRanges extends ViewportProperties
+{
+  // start residue of viewport
+  private int startRes;
+
+  // end residue of viewport
+  private int endRes;
+
+  // start sequence of viewport
+  private int startSeq;
+
+  // end sequence of viewport
+  private int endSeq;
+
+  // alignment
+  private AlignmentI al;
+
+  /**
+   * Constructor
+   * 
+   * @param alignment
+   *          the viewport's alignment
+   */
+  public ViewportRanges(AlignmentI alignment)
+  {
+    // initial values of viewport settings
+    this.startRes = 0;
+    this.endRes = alignment.getWidth() - 1;
+    this.startSeq = 0;
+    this.endSeq = alignment.getHeight() - 1;
+    this.al = alignment;
+  }
+
+  /**
+   * Get alignment width in cols, including hidden cols
+   */
+  public int getAbsoluteAlignmentWidth()
+  {
+    return al.getWidth();
+  }
+
+  /**
+   * Get alignment height in rows, including hidden rows
+   */
+  public int getAbsoluteAlignmentHeight()
+  {
+    return al.getHeight() + al.getHiddenSequences().getSize();
+  }
+
+  /**
+   * Set first residue visible in the viewport
+   * 
+   * @param res
+   *          residue position
+   */
+  public void setStartRes(int res)
+  {
+    if (res > al.getWidth() - 1)
+    {
+      res = al.getWidth() - 1;
+    }
+    else if (res < 0)
+    {
+      res = 0;
+    }
+    this.startRes = res;
+  }
+
+  /**
+   * Set last residue visible in the viewport
+   * 
+   * @param res
+   *          residue position
+   */
+  public void setEndRes(int res)
+  {
+    if (res >= al.getWidth())
+    {
+      res = al.getWidth() - 1;
+    }
+    else if (res < 0)
+    {
+      res = 0;
+    }
+    this.endRes = res;
+  }
+
+  /**
+   * Set the first sequence visible in the viewport
+   * 
+   * @param seq
+   *          sequence position
+   */
+  public void setStartSeq(int seq)
+  {
+    if (seq > al.getHeight() - 1)
+    {
+      seq = al.getHeight() - 1;
+    }
+    else if (seq < 0)
+    {
+      seq = 0;
+    }
+    this.startSeq = seq;
+  }
+
+  /**
+   * Set the last sequence visible in the viewport
+   * 
+   * @param seq
+   *          sequence position
+   */
+  public void setEndSeq(int seq)
+  {
+    if (seq >= al.getHeight())
+    {
+      seq = al.getHeight() - 1;
+    }
+    else if (seq < 0)
+    {
+      seq = 0;
+    }
+    this.endSeq = seq;
+  }
+
+  /**
+   * Get start residue of viewport
+   */
+  public int getStartRes()
+  {
+    return startRes;
+  }
+
+  /**
+   * Get end residue of viewport
+   */
+  public int getEndRes()
+  {
+    return endRes;
+  }
+
+  /**
+   * Get start sequence of viewport
+   */
+  public int getStartSeq()
+  {
+    return startSeq;
+  }
+
+  /**
+   * Get end sequence of viewport
+   */
+  public int getEndSeq()
+  {
+    return endSeq;
+  }
+}
index 8468329..84c9477 100644 (file)
@@ -550,11 +550,13 @@ public abstract class FeatureRendererModel implements
   }
 
   /**
-   * calculate the render colour for a specific feature using current feature
-   * settings.
+   * Returns the configured colour for a particular feature instance. This
+   * includes calculation of 'colour by label', or of a graduated score colour,
+   * if applicable. It does not take into account feature visibility or colour
+   * transparency.
    * 
    * @param feature
-   * @return render colour for the given feature
+   * @return
    */
   public Color getColour(SequenceFeature feature)
   {
@@ -586,11 +588,13 @@ public abstract class FeatureRendererModel implements
     featureColours.put(featureType, col);
   }
 
+  @Override
   public void setTransparency(float value)
   {
     transparency = value;
   }
 
+  @Override
   public float getTransparency()
   {
     return transparency;
@@ -822,7 +826,7 @@ public abstract class FeatureRendererModel implements
    * @return list of groups
    */
   @Override
-  public List getGroups(boolean visible)
+  public List<String> getGroups(boolean visible)
   {
     if (featureGroups != null)
     {
index 0c7e69a..284f1a1 100644 (file)
@@ -50,7 +50,8 @@ public class ConsensusThread extends AlignCalcWorker
     try
     {
       AlignmentAnnotation consensus = getConsensusAnnotation();
-      if (consensus == null || calcMan.isPending(this))
+      AlignmentAnnotation gap = getGapAnnotation();
+      if ((consensus == null && gap == null) || calcMan.isPending(this))
       {
         calcMan.workerComplete(this);
         return;
@@ -117,6 +118,11 @@ public class ConsensusThread extends AlignCalcWorker
   {
     AlignmentAnnotation consensus = getConsensusAnnotation();
     consensus.annotations = new Annotation[aWidth];
+    AlignmentAnnotation gap = getGapAnnotation();
+    if (gap != null)
+    {
+      gap.annotations = new Annotation[aWidth];
+    }
   }
 
   /**
@@ -127,8 +133,8 @@ public class ConsensusThread extends AlignCalcWorker
 
     SequenceI[] aseqs = getSequences();
     int width = alignment.getWidth();
-    ProfilesI hconsensus = AAFrequency.calculate(aseqs, width, 0,
-            width, true);
+    ProfilesI hconsensus = AAFrequency.calculate(aseqs, width, 0, width,
+            true);
 
     alignViewport.setSequenceConsensusHash(hconsensus);
     setColourSchemeConsensus(hconsensus);
@@ -165,6 +171,16 @@ public class ConsensusThread extends AlignCalcWorker
   }
 
   /**
+   * Get the Gap annotation for the alignment
+   * 
+   * @return
+   */
+  protected AlignmentAnnotation getGapAnnotation()
+  {
+    return alignViewport.getAlignmentGapAnnotation();
+  }
+
+  /**
    * update the consensus annotation from the sequence profile data using
    * current visualization settings.
    */
@@ -182,6 +198,11 @@ public class ConsensusThread extends AlignCalcWorker
             && hconsensus != null)
     {
       deriveConsensus(consensus, hconsensus);
+      AlignmentAnnotation gap = getGapAnnotation();
+      if (gap != null)
+      {
+        deriveGap(gap, hconsensus);
+      }
     }
   }
 
@@ -200,13 +221,29 @@ public class ConsensusThread extends AlignCalcWorker
 
     long nseq = getSequences().length;
     AAFrequency.completeConsensus(consensusAnnotation, hconsensus,
-            hconsensus.getStartColumn(),
-            hconsensus.getEndColumn() + 1,
+            hconsensus.getStartColumn(), hconsensus.getEndColumn() + 1,
             alignViewport.isIgnoreGapsConsensus(),
             alignViewport.isShowSequenceLogo(), nseq);
   }
 
   /**
+   * Convert the computed consensus data into a gap annotation row for display.
+   * 
+   * @param gapAnnotation
+   *          the annotation to be populated
+   * @param hconsensus
+   *          the computed consensus data
+   */
+  protected void deriveGap(AlignmentAnnotation gapAnnotation,
+          ProfilesI hconsensus)
+  {
+    long nseq = getSequences().length;
+    AAFrequency.completeGapAnnot(gapAnnotation, hconsensus,
+            hconsensus.getStartColumn(), hconsensus.getEndColumn() + 1,
+            nseq);
+  }
+
+  /**
    * Get the consensus data stored on the viewport.
    * 
    * @return
index e71c4f5..571234c 100644 (file)
@@ -55,7 +55,7 @@ public class ConservationThread extends AlignCalcWorker
     {
       calcMan.notifyStart(this); // updatingConservation = true;
 
-      while (!calcMan.notifyWorking(this))
+      while ((calcMan != null) && (!calcMan.notifyWorking(this)))
       {
         try
         {
@@ -69,7 +69,8 @@ public class ConservationThread extends AlignCalcWorker
           ex.printStackTrace();
         }
       }
-      if (alignViewport.isClosed())
+      if ((alignViewport == null) || (calcMan == null)
+              || (alignViewport.isClosed()))
       {
         abortAndDestroy();
         return;
@@ -114,6 +115,12 @@ public class ConservationThread extends AlignCalcWorker
     }
     calcMan.workerComplete(this);
 
+    if ((alignViewport == null) || (calcMan == null)
+            || (alignViewport.isClosed()))
+    {
+      abortAndDestroy();
+      return;
+    }
     if (ap != null)
     {
       ap.paintAlignment(true);
index 676a4b6..4d3dd2f 100644 (file)
@@ -617,8 +617,8 @@ public class DasSequenceFeatureFetcher
     }
     af.getFeatureRenderer().featuresAdded();
 
-    int start = af.getViewport().getStartSeq();
-    int end = af.getViewport().getEndSeq();
+    int start = af.getViewport().getRanges().getStartSeq();
+    int end = af.getViewport().getRanges().getEndSeq();
     int index;
     for (index = start; index < end; index++)
     {
index 8bae5ba..ea65125 100644 (file)
@@ -74,6 +74,12 @@ import MCview.PDBChain;
 
 public class SiftsClient implements SiftsClientI
 {
+  /*
+   * for use in mocking out file fetch for tests only
+   * - reset to null after testing!
+   */
+  private static File mockSiftsFile;
+
   private Entry siftsEntry;
 
   private StructureFile pdb;
@@ -187,6 +193,14 @@ public class SiftsClient implements SiftsClientI
    */
   public static File getSiftsFile(String pdbId) throws SiftsException
   {
+    /*
+     * return mocked file if it has been set
+     */
+    if (mockSiftsFile != null)
+    {
+      return mockSiftsFile;
+    }
+
     String siftsFileName = SiftsSettings.getSiftDownloadDirectory()
             + pdbId.toLowerCase() + ".xml.gz";
     File siftsFile = new File(siftsFileName);
@@ -1097,4 +1111,9 @@ public class SiftsClient implements SiftsClientI
     return siftsEntry.getDbVersion();
   }
 
+  public static void setMockSiftsFile(File file)
+  {
+    mockSiftsFile = file;
+  }
+
 }
diff --git a/src/jalview/ws/utils/UrlDownloadClient.java b/src/jalview/ws/utils/UrlDownloadClient.java
new file mode 100644 (file)
index 0000000..86e3f76
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.ws.utils;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+
+public class UrlDownloadClient
+{
+  public UrlDownloadClient()
+  {
+
+  }
+
+  /**
+   * Download and save a file from a URL
+   * 
+   * @param urlstring
+   *          url to download from, as string
+   * @param outfile
+   *          the name of file to save the URLs to
+   * @throws IOException
+   */
+  public void download(String urlstring, String outfile) throws IOException
+  {
+    FileOutputStream fos = null;
+    ReadableByteChannel rbc = null;
+    Path temp = null;
+    try
+    {
+      temp = Files.createTempFile(".jalview_", ".tmp");
+
+      URL url = new URL(urlstring);
+      rbc = Channels.newChannel(url.openStream());
+      fos = new FileOutputStream(temp.toString());
+      fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+
+      // copy tempfile to outfile once our download completes
+      // incase something goes wrong
+      Files.copy(temp, Paths.get(outfile),
+              StandardCopyOption.REPLACE_EXISTING);
+    } catch (IOException e)
+    {
+      throw e;
+    } finally
+    {
+      try
+      {
+        if (fos != null)
+        {
+          fos.close();
+        }
+      } catch (IOException e)
+      {
+        System.out
+                .println("Exception while closing download file output stream: "
+                        + e.getMessage());
+      }
+      try
+      {
+        if (rbc != null)
+        {
+          rbc.close();
+        }
+      } catch (IOException e)
+      {
+        System.out.println("Exception while closing download channel: "
+                        + e.getMessage());
+      }
+      try
+      {
+        if (temp != null)
+        {
+          Files.deleteIfExists(temp);
+        }
+      } catch (IOException e)
+      {
+        System.out.println("Exception while deleting download temp file: "
+                        + e.getMessage());
+      }
+    }
+  }
+}
index 7af77f5..68933bd 100644 (file)
@@ -27,6 +27,7 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.analysis.AlignmentGenerator;
 import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
 import jalview.gui.JvOptionPane;
 import jalview.io.DataSourceType;
@@ -1196,4 +1197,67 @@ public class AlignmentTest
     assertNull(a.findGroup(seq2, 8));
   }
 
+  @Test(groups = { "Functional" })
+  public void testDeleteSequenceByIndex()
+  {
+    // create random alignment
+    AlignmentGenerator gen = new AlignmentGenerator(false);
+    AlignmentI a = gen.generate(20, 15, 123, 5, 5);
+
+    // delete sequence 10, alignment reduced by 1
+    int height = a.getAbsoluteHeight();
+    a.deleteSequence(10);
+    assertEquals(a.getAbsoluteHeight(), height - 1);
+
+    // try to delete -ve index, nothing happens
+    a.deleteSequence(-1);
+    assertEquals(a.getAbsoluteHeight(), height - 1);
+
+    // try to delete beyond end of alignment, nothing happens
+    a.deleteSequence(14);
+    assertEquals(a.getAbsoluteHeight(), height - 1);
+  }
+
+  @Test(groups = { "Functional" })
+  public void testDeleteSequenceBySeq()
+  {
+    // create random alignment
+    AlignmentGenerator gen = new AlignmentGenerator(false);
+    AlignmentI a = gen.generate(20, 15, 123, 5, 5);
+
+    // delete sequence 10, alignment reduced by 1
+    int height = a.getAbsoluteHeight();
+    SequenceI seq = a.getSequenceAt(10);
+    a.deleteSequence(seq);
+    assertEquals(a.getAbsoluteHeight(), height - 1);
+
+    // try to delete non-existent sequence, nothing happens
+    seq = new Sequence("cds", "GCCTCGGAT");
+    assertEquals(a.getAbsoluteHeight(), height - 1);
+  }
+
+  @Test(groups = { "Functional" })
+  public void testDeleteHiddenSequence()
+  {
+    // create random alignment
+    AlignmentGenerator gen = new AlignmentGenerator(false);
+    AlignmentI a = gen.generate(20, 15, 123, 5, 5);
+
+    // delete a sequence which is hidden, check it is NOT removed from hidden
+    // sequences
+    int height = a.getAbsoluteHeight();
+    SequenceI seq = a.getSequenceAt(2);
+    a.getHiddenSequences().hideSequence(seq);
+    assertEquals(a.getHiddenSequences().getSize(), 1);
+    a.deleteSequence(2);
+    assertEquals(a.getAbsoluteHeight(), height - 1);
+    assertEquals(a.getHiddenSequences().getSize(), 1);
+
+    // delete a sequence which is not hidden, check hiddenSequences are not
+    // affected
+    a.deleteSequence(10);
+    assertEquals(a.getAbsoluteHeight(), height - 2);
+    assertEquals(a.getHiddenSequences().getSize(), 1);
+  }
+
 }
index 1d819c9..4d3f611 100644 (file)
@@ -105,9 +105,97 @@ public class ColumnSelectionTest
     cs.hideColumns(4, 4);
     assertEquals(4, cs.findColumnPosition(5));
 
+    // hiding column 4 moves column 4 to position 3
+    assertEquals(3, cs.findColumnPosition(4));
+
     // hiding columns 1 and 2 moves column 5 to column 2
     cs.hideColumns(1, 2);
     assertEquals(2, cs.findColumnPosition(5));
+
+    // check with > 1 hidden column regions
+    // where some columns are in the hidden regions
+    ColumnSelection cs2 = new ColumnSelection();
+    cs2.hideColumns(5, 10);
+    cs2.hideColumns(20, 27);
+    cs2.hideColumns(40, 44);
+
+    // hiding columns 5-10 and 20-27 moves column 8 to column 4
+    assertEquals(4, cs2.findColumnPosition(8));
+
+    // and moves column 24 to 13
+    assertEquals(13, cs2.findColumnPosition(24));
+
+    // and moves column 28 to 14
+    assertEquals(14, cs2.findColumnPosition(28));
+
+    // and moves column 40 to 25
+    assertEquals(25, cs2.findColumnPosition(40));
+
+    // check when hidden columns start at 0 that the visible column
+    // is returned as 0
+    ColumnSelection cs3 = new ColumnSelection();
+    cs3.hideColumns(0, 4);
+    assertEquals(0, cs3.findColumnPosition(2));
+
+  }
+
+  /**
+   * Test the method that finds the visible column position a given distance
+   * before another column
+   */
+  @Test(groups = { "Functional" })
+  public void testFindColumnNToLeft()
+  {
+    ColumnSelection cs = new ColumnSelection();
+
+    // test that without hidden columns, findColumnNToLeft returns
+    // position n to left of provided position
+    int pos = cs.subtractVisibleColumns(3, 10);
+    assertEquals(7, pos);
+
+    // 0 returns same position
+    pos = cs.subtractVisibleColumns(0, 10);
+    assertEquals(10, pos);
+
+    // overflow to left returns negative number
+    pos = cs.subtractVisibleColumns(3, 0);
+    assertEquals(-3, pos);
+
+    // test that with hidden columns to left of result column
+    // behaviour is the same as above
+    cs.hideColumns(1, 3);
+
+    // position n to left of provided position
+    pos = cs.subtractVisibleColumns(3, 10);
+    assertEquals(7, pos);
+
+    // 0 returns same position
+    pos = cs.subtractVisibleColumns(0, 10);
+    assertEquals(10, pos);
+
+    // test with one set of hidden columns between start and required position
+    cs.hideColumns(12, 15);
+    pos = cs.subtractVisibleColumns(8, 17);
+    assertEquals(5, pos);
+
+    // test with two sets of hidden columns between start and required position
+    cs.hideColumns(20, 21);
+    pos = cs.subtractVisibleColumns(8, 23);
+    assertEquals(9, pos);
+
+    // repeat last 2 tests with no hidden columns to left of required position
+    cs.revealAllHiddenColumns();
+
+    // test with one set of hidden columns between start and required position
+    cs.hideColumns(12, 15);
+    pos = cs.subtractVisibleColumns(8, 17);
+    assertEquals(5, pos);
+
+    // test with two sets of hidden columns between start and required position
+    cs.hideColumns(20, 21);
+    pos = cs.subtractVisibleColumns(8, 23);
+    assertEquals(9, pos);
+
   }
 
   /**
index cae3536..7795988 100644 (file)
@@ -49,7 +49,7 @@ public class HiddenSequencesTest
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
-  static int SEQ_COUNT = 10;
+  static int SEQ_COUNT = 25;
 
   SequenceI[] seqs;
 
@@ -62,8 +62,9 @@ public class HiddenSequencesTest
     seqs = new SequenceI[SEQ_COUNT];
     for (int i = 0; i < SEQ_COUNT; i++)
     {
-      // sequence lengths are 1, 2, ... 10
-      seqs[i] = new Sequence("Seq" + i, "abcdefghijk".substring(0, i + 1));
+      // sequence lengths are 1, 2, ... 25
+      seqs[i] = new Sequence("Seq" + i,
+              "abcdefghijklmnopqrstuvwxy".substring(0, i + 1));
     }
   }
 
@@ -89,7 +90,7 @@ public class HiddenSequencesTest
     /*
      * alignment is now seq0/2/3/4/7/8/9
      */
-    assertEquals(7, al.getHeight());
+    assertEquals(SEQ_COUNT - 3, al.getHeight());
     assertEquals(0, hs.adjustForHiddenSeqs(0));
     assertEquals(2, hs.adjustForHiddenSeqs(1));
     assertEquals(3, hs.adjustForHiddenSeqs(2));
@@ -193,7 +194,7 @@ public class HiddenSequencesTest
     /*
      * alignment is now seq0/2/3/4/7/8/9
      */
-    assertEquals(7, al.getHeight());
+    assertEquals(SEQ_COUNT - 3, al.getHeight());
     assertEquals(0, hs.findIndexWithoutHiddenSeqs(0));
     assertEquals(0, hs.findIndexWithoutHiddenSeqs(1));
     assertEquals(1, hs.findIndexWithoutHiddenSeqs(2));
@@ -207,6 +208,76 @@ public class HiddenSequencesTest
   }
 
   /**
+   * Test the method that finds the visible row position a given distance before
+   * another row
+   */
+  @Test(groups = { "Functional" })
+  public void testFindIndexNFromRow()
+  {
+    AlignmentI al = new Alignment(seqs);
+    HiddenSequences hs = new HiddenSequences(al);
+
+    // test that without hidden rows, findIndexNFromRow returns
+    // position n above provided position
+    int pos = hs.subtractVisibleRows(3, 10);
+    assertEquals(7, pos);
+
+    // 0 returns same position
+    pos = hs.subtractVisibleRows(0, 10);
+    assertEquals(10, pos);
+
+    // overflow to top returns negative number
+    pos = hs.subtractVisibleRows(3, 0);
+    assertEquals(-3, pos);
+
+    // test that with hidden rows above result row
+    // behaviour is the same as above
+    hs.hideSequence(seqs[1]);
+    hs.hideSequence(seqs[2]);
+    hs.hideSequence(seqs[3]);
+
+    // position n above provided position
+    pos = hs.subtractVisibleRows(3, 10);
+    assertEquals(7, pos);
+
+    // 0 returns same position
+    pos = hs.subtractVisibleRows(0, 10);
+    assertEquals(10, pos);
+
+    // test with one set of hidden rows between start and required position
+    hs.hideSequence(seqs[12]);
+    hs.hideSequence(seqs[13]);
+    hs.hideSequence(seqs[14]);
+    hs.hideSequence(seqs[15]);
+    pos = hs.subtractVisibleRows(8, 17);
+    assertEquals(5, pos);
+
+    // test with two sets of hidden rows between start and required position
+    hs.hideSequence(seqs[20]);
+    hs.hideSequence(seqs[21]);
+    pos = hs.subtractVisibleRows(8, 23);
+    assertEquals(9, pos);
+
+    // repeat last 2 tests with no hidden columns to left of required position
+    hs.showAll(null);
+
+    // test with one set of hidden rows between start and required position
+    hs.hideSequence(seqs[12]);
+    hs.hideSequence(seqs[13]);
+    hs.hideSequence(seqs[14]);
+    hs.hideSequence(seqs[15]);
+    pos = hs.subtractVisibleRows(8, 17);
+    assertEquals(5, pos);
+
+    // test with two sets of hidden rows between start and required position
+    hs.hideSequence(seqs[20]);
+    hs.hideSequence(seqs[21]);
+    pos = hs.subtractVisibleRows(8, 23);
+    assertEquals(9, pos);
+
+  }
+
+  /**
    * Test the method that reconstructs (sort of) the full alignment including
    * hidden sequences
    */
@@ -289,7 +360,7 @@ public class HiddenSequencesTest
     assertTrue(al.getSequences().contains(seqs[1]));
     HiddenSequences hs = al.getHiddenSequences();
     assertEquals(0, hs.getSize());
-    assertEquals(10, al.getHeight());
+    assertEquals(SEQ_COUNT, al.getHeight());
 
     /*
      * hide the second sequence in the alignment
@@ -299,7 +370,7 @@ public class HiddenSequencesTest
     assertTrue(hs.isHidden(seqs[1]));
     assertFalse(al.getSequences().contains(seqs[1]));
     assertEquals(1, hs.getSize());
-    assertEquals(9, al.getHeight());
+    assertEquals(SEQ_COUNT - 1, al.getHeight());
     assertSame(seqs[2], al.getSequenceAt(1));
 
     /*
@@ -312,7 +383,7 @@ public class HiddenSequencesTest
     assertFalse(al.getSequences().contains(seqs[1]));
     assertFalse(al.getSequences().contains(seqs[2]));
     assertEquals(2, hs.getSize());
-    assertEquals(8, al.getHeight());
+    assertEquals(SEQ_COUNT - 2, al.getHeight());
 
     /*
      * perform 'reveal' on what is now the second sequence in the alignment
@@ -323,7 +394,54 @@ public class HiddenSequencesTest
     assertTrue(revealed.contains(seqs[1]));
     assertTrue(revealed.contains(seqs[2]));
     assertEquals(0, hs.getSize());
-    assertEquals(10, al.getHeight());
+    assertEquals(SEQ_COUNT, al.getHeight());
+  }
+
+  /**
+   * Test the method that adds a sequence to the hidden sequences and deletes it
+   * from the alignment, and its converse, where the first hidden sequences are
+   * at the bottom of the alignment (JAL-2437)
+   */
+  @Test(groups = "Functional")
+  public void testHideShowLastSequences()
+  {
+    AlignmentI al = new Alignment(seqs);
+    assertTrue(al.getSequences().contains(seqs[1]));
+    HiddenSequences hs = al.getHiddenSequences();
+    assertEquals(0, hs.getSize());
+    assertEquals(SEQ_COUNT, al.getHeight());
+
+    /*
+     * hide the last sequence in the alignment
+     */
+    hs.hideSequence(seqs[SEQ_COUNT - 1]);
+    assertFalse(hs.isHidden(seqs[SEQ_COUNT - 2]));
+    assertTrue(hs.isHidden(seqs[SEQ_COUNT - 1]));
+    assertFalse(al.getSequences().contains(seqs[SEQ_COUNT - 1]));
+    assertEquals(1, hs.getSize());
+    assertEquals(SEQ_COUNT - 1, al.getHeight());
+
+    /*
+     * hide the third last sequence in the alignment
+     */
+    hs.hideSequence(seqs[SEQ_COUNT - 3]);
+    assertFalse(hs.isHidden(seqs[SEQ_COUNT - 2]));
+    assertTrue(hs.isHidden(seqs[SEQ_COUNT - 3]));
+    assertFalse(al.getSequences().contains(seqs[SEQ_COUNT - 3]));
+    assertEquals(2, hs.getSize());
+    assertEquals(SEQ_COUNT - 2, al.getHeight());
+
+    /*
+     * reveal all the sequences, which should be reinstated in the same order as they started in
+     */
+    hs.showAll(null);
+    assertFalse(hs.isHidden(seqs[SEQ_COUNT - 3]));
+    assertFalse(hs.isHidden(seqs[SEQ_COUNT - 1]));
+    assertEquals(seqs[SEQ_COUNT - 3], al.getSequences().get(SEQ_COUNT - 3));
+    assertEquals(seqs[SEQ_COUNT - 2], al.getSequences().get(SEQ_COUNT - 2));
+    assertEquals(seqs[SEQ_COUNT - 1], al.getSequences().get(SEQ_COUNT - 1));
+    assertEquals(0, hs.getSize());
+    assertEquals(SEQ_COUNT, al.getHeight());
   }
 
   @Test(groups = "Functional")
index 53ac181..65549f2 100644 (file)
@@ -6,6 +6,7 @@ import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 import jalview.schemes.NucleotideColourScheme;
 
@@ -13,7 +14,7 @@ import org.testng.annotations.Test;
 
 public class SequenceGroupTest
 {
-  @Test
+  @Test(groups={"Functional"})
   public void testAddSequence()
   {
     SequenceGroup sg = new SequenceGroup();
@@ -41,7 +42,7 @@ public class SequenceGroupTest
     assertTrue(sg.getSequences().contains(seq3));
   }
 
-  @Test
+  @Test(groups={"Functional"})
   public void testAddOrRemove()
   {
     SequenceGroup sg = new SequenceGroup();
@@ -65,7 +66,7 @@ public class SequenceGroupTest
     assertFalse(sg.getSequences().contains(seq1));
   }
 
-  @Test
+  @Test(groups={"Functional"})
   public void testGetColourScheme()
   {
     SequenceGroup sg = new SequenceGroup();
@@ -79,4 +80,107 @@ public class SequenceGroupTest
     sg.setColourScheme(scheme);
     assertSame(scheme, sg.getColourScheme());
   }
+
+  @Test(groups={"Functional"})
+  public void testSetContext()
+  {
+    SequenceGroup sg1 = new SequenceGroup();
+    SequenceGroup sg2 = new SequenceGroup();
+    SequenceGroup sg3 = new SequenceGroup();
+    assertNull(sg1.getContext());
+    sg1.setContext(null);
+    assertNull(sg1.getContext());
+    try
+    {
+      sg1.setContext(sg1); // self-reference :-O
+      fail("Expected exception");
+    } catch (IllegalArgumentException e)
+    {
+      // expected
+      assertNull(sg1.getContext());
+    }
+    sg1.setContext(sg2);
+    assertSame(sg2, sg1.getContext());
+    sg2.setContext(sg3);
+    try
+    {
+      sg3.setContext(sg1); // circular reference :-O
+      fail("Expected exception");
+    } catch (IllegalArgumentException e)
+    {
+      // expected
+      assertNull(sg3.getContext());
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void testContains()
+  {
+    /* 
+     * essentially the same tests as AlignmentI.findGroup 
+     * but from a particular group's perspective
+     */
+
+    SequenceI seq1 = new Sequence("seq1", "ABCDEF---GHI");
+    SequenceI seq2 = new Sequence("seq2", "---JKLMNO---");
+    AlignmentI a = new Alignment(new SequenceI[] { seq1, seq2 });
+    /*
+     * add a group consisting of just "DEF"
+     */
+    SequenceGroup sg1 = new SequenceGroup();
+    sg1.addSequence(seq1, false);
+    sg1.setStartRes(3);
+    sg1.setEndRes(5);
+
+    /*
+     * test sequence membership
+     */
+    assertTrue(sg1.contains(seq1));
+    assertFalse(sg1.contains(seq2));
+
+    /*
+     * test sequence+position
+     */
+
+    assertFalse(sg1.contains(seq1, 2)); // position not in group
+    assertFalse(sg1.contains(seq1, 6)); // position not in group
+    assertFalse(sg1.contains(seq2, 5)); // sequence not in group
+    assertTrue(sg1.contains(seq1, 3)); // yes
+    assertTrue(sg1.contains(seq1, 4));
+    assertTrue(sg1.contains(seq1, 5));
+
+    /*
+     * add a group consisting of 
+     * EF--
+     * KLMN
+     */
+    SequenceGroup sg2 = new SequenceGroup();
+    sg2.addSequence(seq1, false);
+    sg2.addSequence(seq2, false);
+    sg2.setStartRes(4);
+    sg2.setEndRes(7);
+    a.addGroup(sg2);
+
+    /*
+     * if a residue is in more than one group, method returns
+     * the first found (in order groups were added)
+     */
+    assertTrue(sg2.contains(seq1, 4));
+    assertTrue(sg2.contains(seq1, 5));
+
+    /*
+     * seq2 only belongs to the second group
+     */
+    assertTrue(sg2.contains(seq2, 4));
+    assertTrue(sg2.contains(seq2, 5));
+    assertTrue(sg2.contains(seq2, 6));
+    assertTrue(sg2.contains(seq2, 7));
+    assertFalse(sg2.contains(seq2, 3));
+    assertFalse(sg2.contains(seq2, 8));
+    sg2.setEndRes(8);
+    assertTrue(sg2.contains(seq2, 8));
+    sg2.deleteSequence(seq2, false);
+    assertFalse(sg2.contains(seq2));
+
+  }
 }
diff --git a/test/jalview/ext/android/SparseDoubleArrayTest.java b/test/jalview/ext/android/SparseDoubleArrayTest.java
new file mode 100644 (file)
index 0000000..7d64a28
--- /dev/null
@@ -0,0 +1,53 @@
+package jalview.ext.android;
+
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+public class SparseDoubleArrayTest
+{
+
+  @Test
+  public void testConstructor()
+  {
+    double[] d = new double[] { 0d, 0d, 1.2d, 0d, 0d, 3.4d };
+    SparseDoubleArray s = new SparseDoubleArray(d);
+    for (int i = 0; i < d.length; i++)
+    {
+      assertEquals(s.get(i), d[i], "At [" + i + "]");
+    }
+  }
+
+  @Test
+  public void testAdd()
+  {
+    double[] d = new double[] { 0d, 0d, 1.2d, 0d, 0d, 3.4d };
+    SparseDoubleArray s = new SparseDoubleArray(d);
+    // add to zero (absent)
+    s.add(0, 3.2d);
+    assertEquals(s.get(0), 3.2d);
+    // add to non-zero
+    s.add(0, 2.5d);
+    assertEquals(s.get(0), 5.7d);
+    // add negative value
+    s.add(2, -5.3d);
+    assertEquals(s.get(2), -4.1d);
+    // add to unset value
+    s.add(12, 9.8d);
+    assertEquals(s.get(12), 9.8d);
+  }
+
+  @Test
+  public void testDivide()
+  {
+    double delta = 1.0e-10;
+    double[] d = new double[] { 0d, 2.4d, 1.2d, 0d, -4.8d, -3.6d };
+    SparseDoubleArray s = new SparseDoubleArray(d);
+    assertEquals(s.divide(0, 1d), 0d); // no such entry
+    assertEquals(s.divide(2, 0d), 0d); // zero divisor
+    assertEquals(s.divide(1, 2d), 1.2d, delta); // + / +
+    assertEquals(s.divide(2, -2d), -0.6d, delta); // + / -
+    assertEquals(s.divide(4, 3d), -1.6d, delta); // - / +
+    assertEquals(s.divide(5, -3d), 1.2d, delta); // - / -
+  }
+}
index 439e188..2c23311 100644 (file)
@@ -58,6 +58,6 @@ public class JmolCommandsTest
     // need some mappings!
 
     StructureMappingcommandSet[] commands = JmolCommands
-            .getColourBySequenceCommand(ssm, files, seqs, sr, null, al);
+            .getColourBySequenceCommand(ssm, files, seqs, sr, af.alignPanel);
   }
 }
index 959ecab..995b099 100644 (file)
@@ -24,6 +24,7 @@ import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
+import jalview.bin.Jalview;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.JvOptionPane;
@@ -53,8 +54,7 @@ public class JmolViewerTest
   @BeforeClass(alwaysRun = true)
   public static void setUpBeforeClass() throws Exception
   {
-    jalview.bin.Jalview.main(new String[] {
-        "-noquestionnaire -nonews -props",
+    Jalview.main(new String[] { "-noquestionnaire", "-nonews", "-props",
         "test/jalview/ext/rbvi/chimera/testProps.jvprops" });
   }
 
diff --git a/test/jalview/ext/rbvi/chimera/4zho.pdb b/test/jalview/ext/rbvi/chimera/4zho.pdb
new file mode 100644 (file)
index 0000000..e4ee6df
--- /dev/null
@@ -0,0 +1,3526 @@
+HEADER    ELECTRON TRANSPORT                      26-APR-15   4ZHO              
+TITLE     THE CRYSTAL STRUCTURE OF ARABIDOPSIS FERREDOXIN 2 WITH 2FE-2S CLUSTER 
+COMPND    MOL_ID: 1;                                                            
+COMPND   2 MOLECULE: FERREDOXIN-2, CHLOROPLASTIC;                               
+COMPND   3 CHAIN: A, B;                                                         
+COMPND   4 SYNONYM: ATFD2;                                                      
+COMPND   5 ENGINEERED: YES                                                      
+SOURCE    MOL_ID: 1;                                                            
+SOURCE   2 ORGANISM_SCIENTIFIC: ARABIDOPSIS THALIANA;                           
+SOURCE   3 ORGANISM_COMMON: MOUSE-EAR CRESS;                                    
+SOURCE   4 ORGANISM_TAXID: 3702;                                                
+SOURCE   5 GENE: FD2, PETF, PETF1, AT1G60950, T7P1.9;                           
+SOURCE   6 EXPRESSION_SYSTEM: ESCHERICHIA COLI BL21(DE3);                       
+SOURCE   7 EXPRESSION_SYSTEM_TAXID: 469008                                      
+KEYWDS    FERREDOXIN 2FE-2S CLUSTER ELECTRON TRANSFER CHLOROPLAST, ELECTRON     
+KEYWDS   2 TRANSPORT                                                            
+EXPDTA    X-RAY DIFFRACTION                                                     
+AUTHOR    R.GRINTER,I.JOSTS,A.W.ROSZAK,R.J.COGDELL,D.WALKER                     
+REVDAT   2   09-NOV-16 4ZHO    1       JRNL                                     
+REVDAT   1   31-AUG-16 4ZHO    0                                                
+JRNL        AUTH   R.GRINTER,I.JOSTS,K.MOSBAHI,A.W.ROSZAK,R.J.COGDELL,          
+JRNL        AUTH 2 A.M.BONVIN,J.J.MILNER,S.M.KELLY,O.BYRON,B.O.SMITH,D.WALKER   
+JRNL        TITL   STRUCTURE OF THE BACTERIAL PLANT-FERREDOXIN RECEPTOR FUSA.   
+JRNL        REF    NAT COMMUN                    V.   7 13308 2016              
+JRNL        REFN                   ESSN 2041-1723                               
+JRNL        PMID   27796364                                                     
+JRNL        DOI    10.1038/NCOMMS13308                                          
+REMARK   2                                                                      
+REMARK   2 RESOLUTION.    2.34 ANGSTROMS.                                       
+REMARK   3                                                                      
+REMARK   3 REFINEMENT.                                                          
+REMARK   3   PROGRAM     : REFMAC 5.8.0049                                      
+REMARK   3   AUTHORS     : MURSHUDOV,VAGIN,DODSON                               
+REMARK   3                                                                      
+REMARK   3    REFINEMENT TARGET : MAXIMUM LIKELIHOOD                            
+REMARK   3                                                                      
+REMARK   3  DATA USED IN REFINEMENT.                                            
+REMARK   3   RESOLUTION RANGE HIGH (ANGSTROMS) : 2.34                           
+REMARK   3   RESOLUTION RANGE LOW  (ANGSTROMS) : 60.73                          
+REMARK   3   DATA CUTOFF            (SIGMA(F)) : NULL                           
+REMARK   3   COMPLETENESS FOR RANGE        (%) : 99.5                           
+REMARK   3   NUMBER OF REFLECTIONS             : 12221                          
+REMARK   3                                                                      
+REMARK   3  FIT TO DATA USED IN REFINEMENT.                                     
+REMARK   3   CROSS-VALIDATION METHOD          : THROUGHOUT                      
+REMARK   3   FREE R VALUE TEST SET SELECTION  : RANDOM                          
+REMARK   3   R VALUE     (WORKING + TEST SET) : 0.198                           
+REMARK   3   R VALUE            (WORKING SET) : 0.197                           
+REMARK   3   FREE R VALUE                     : 0.216                           
+REMARK   3   FREE R VALUE TEST SET SIZE   (%) : 4.900                           
+REMARK   3   FREE R VALUE TEST SET COUNT      : 627                             
+REMARK   3                                                                      
+REMARK   3  FIT IN THE HIGHEST RESOLUTION BIN.                                  
+REMARK   3   TOTAL NUMBER OF BINS USED           : 20                           
+REMARK   3   BIN RESOLUTION RANGE HIGH       (A) : 2.34                         
+REMARK   3   BIN RESOLUTION RANGE LOW        (A) : 2.40                         
+REMARK   3   REFLECTION IN BIN     (WORKING SET) : 864                          
+REMARK   3   BIN COMPLETENESS (WORKING+TEST) (%) : 98.58                        
+REMARK   3   BIN R VALUE           (WORKING SET) : 0.2390                       
+REMARK   3   BIN FREE R VALUE SET COUNT          : 39                           
+REMARK   3   BIN FREE R VALUE                    : 0.3090                       
+REMARK   3                                                                      
+REMARK   3  NUMBER OF NON-HYDROGEN ATOMS USED IN REFINEMENT.                    
+REMARK   3   PROTEIN ATOMS            : 1440                                    
+REMARK   3   NUCLEIC ACID ATOMS       : 0                                       
+REMARK   3   HETEROGEN ATOMS          : 10                                      
+REMARK   3   SOLVENT ATOMS            : 38                                      
+REMARK   3                                                                      
+REMARK   3  B VALUES.                                                           
+REMARK   3   FROM WILSON PLOT           (A**2) : NULL                           
+REMARK   3   MEAN B VALUE      (OVERALL, A**2) : 59.55                          
+REMARK   3   OVERALL ANISOTROPIC B VALUE.                                       
+REMARK   3    B11 (A**2) : 2.75000                                              
+REMARK   3    B22 (A**2) : 2.75000                                              
+REMARK   3    B33 (A**2) : -5.50000                                             
+REMARK   3    B12 (A**2) : 0.00000                                              
+REMARK   3    B13 (A**2) : 0.00000                                              
+REMARK   3    B23 (A**2) : 0.00000                                              
+REMARK   3                                                                      
+REMARK   3  ESTIMATED OVERALL COORDINATE ERROR.                                 
+REMARK   3   ESU BASED ON R VALUE                            (A): 0.229         
+REMARK   3   ESU BASED ON FREE R VALUE                       (A): 0.180         
+REMARK   3   ESU BASED ON MAXIMUM LIKELIHOOD                 (A): 0.136         
+REMARK   3   ESU FOR B VALUES BASED ON MAXIMUM LIKELIHOOD (A**2): 12.808        
+REMARK   3                                                                      
+REMARK   3 CORRELATION COEFFICIENTS.                                            
+REMARK   3   CORRELATION COEFFICIENT FO-FC      : 0.949                         
+REMARK   3   CORRELATION COEFFICIENT FO-FC FREE : 0.952                         
+REMARK   3                                                                      
+REMARK   3  RMS DEVIATIONS FROM IDEAL VALUES        COUNT    RMS    WEIGHT      
+REMARK   3   BOND LENGTHS REFINED ATOMS        (A):  1468 ; 0.017 ; 0.019       
+REMARK   3   BOND LENGTHS OTHERS               (A):  1296 ; 0.001 ; 0.020       
+REMARK   3   BOND ANGLES REFINED ATOMS   (DEGREES):  1990 ; 1.933 ; 1.975       
+REMARK   3   BOND ANGLES OTHERS          (DEGREES):  3026 ; 0.912 ; 3.000       
+REMARK   3   TORSION ANGLES, PERIOD 1    (DEGREES):   192 ; 7.288 ; 5.000       
+REMARK   3   TORSION ANGLES, PERIOD 2    (DEGREES):    66 ;34.298 ;26.970       
+REMARK   3   TORSION ANGLES, PERIOD 3    (DEGREES):   234 ;14.918 ;15.000       
+REMARK   3   TORSION ANGLES, PERIOD 4    (DEGREES):     2 ; 5.922 ;15.000       
+REMARK   3   CHIRAL-CENTER RESTRAINTS       (A**3):   230 ; 0.115 ; 0.200       
+REMARK   3   GENERAL PLANES REFINED ATOMS      (A):  1682 ; 0.007 ; 0.020       
+REMARK   3   GENERAL PLANES OTHERS             (A):   276 ; 0.001 ; 0.020       
+REMARK   3   NON-BONDED CONTACTS REFINED ATOMS (A):  NULL ;  NULL ;  NULL       
+REMARK   3   NON-BONDED CONTACTS OTHERS        (A):  NULL ;  NULL ;  NULL       
+REMARK   3   NON-BONDED TORSION REFINED ATOMS  (A):  NULL ;  NULL ;  NULL       
+REMARK   3   NON-BONDED TORSION OTHERS         (A):  NULL ;  NULL ;  NULL       
+REMARK   3   H-BOND (X...Y) REFINED ATOMS      (A):  NULL ;  NULL ;  NULL       
+REMARK   3   H-BOND (X...Y) OTHERS             (A):  NULL ;  NULL ;  NULL       
+REMARK   3   POTENTIAL METAL-ION REFINED ATOMS (A):  NULL ;  NULL ;  NULL       
+REMARK   3   POTENTIAL METAL-ION OTHERS        (A):  NULL ;  NULL ;  NULL       
+REMARK   3   SYMMETRY VDW REFINED ATOMS        (A):  NULL ;  NULL ;  NULL       
+REMARK   3   SYMMETRY VDW OTHERS               (A):  NULL ;  NULL ;  NULL       
+REMARK   3   SYMMETRY H-BOND REFINED ATOMS     (A):  NULL ;  NULL ;  NULL       
+REMARK   3   SYMMETRY H-BOND OTHERS            (A):  NULL ;  NULL ;  NULL       
+REMARK   3   SYMMETRY METAL-ION REFINED ATOMS  (A):  NULL ;  NULL ;  NULL       
+REMARK   3   SYMMETRY METAL-ION OTHERS         (A):  NULL ;  NULL ;  NULL       
+REMARK   3                                                                      
+REMARK   3  ISOTROPIC THERMAL FACTOR RESTRAINTS.     COUNT   RMS    WEIGHT      
+REMARK   3   MAIN-CHAIN BOND REFINED ATOMS  (A**2):   774 ; 3.602 ; 4.642       
+REMARK   3   MAIN-CHAIN BOND OTHER ATOMS    (A**2):   773 ; 3.584 ; 4.636       
+REMARK   3   MAIN-CHAIN ANGLE REFINED ATOMS (A**2):   964 ; 4.741 ; 6.949       
+REMARK   3   MAIN-CHAIN ANGLE OTHER ATOMS   (A**2):   965 ; 4.738 ; 6.957       
+REMARK   3   SIDE-CHAIN BOND REFINED ATOMS  (A**2):   694 ; 5.685 ; 5.263       
+REMARK   3   SIDE-CHAIN BOND OTHER ATOMS    (A**2):   691 ; 5.624 ; 5.277       
+REMARK   3   SIDE-CHAIN ANGLE REFINED ATOMS (A**2):  NULL ;  NULL ;  NULL       
+REMARK   3   SIDE-CHAIN ANGLE OTHER ATOMS   (A**2):  1021 ; 8.298 ; 7.648       
+REMARK   3   LONG RANGE B REFINED ATOMS     (A**2):  1582 ; 9.824 ;37.488       
+REMARK   3   LONG RANGE B OTHER ATOMS       (A**2):  1580 ; 9.825 ;37.492       
+REMARK   3                                                                      
+REMARK   3 ANISOTROPIC THERMAL FACTOR RESTRAINTS.    COUNT   RMS   WEIGHT       
+REMARK   3   RIGID-BOND RESTRAINTS          (A**2):  NULL ;  NULL ;  NULL       
+REMARK   3   SPHERICITY; FREE ATOMS         (A**2):  NULL ;  NULL ;  NULL       
+REMARK   3   SPHERICITY; BONDED ATOMS       (A**2):  NULL ;  NULL ;  NULL       
+REMARK   3                                                                      
+REMARK   3  NCS RESTRAINTS STATISTICS                                           
+REMARK   3   NUMBER OF DIFFERENT NCS GROUPS : NULL                              
+REMARK   3                                                                      
+REMARK   3  TLS DETAILS                                                         
+REMARK   3   NUMBER OF TLS GROUPS  : 2                                          
+REMARK   3                                                                      
+REMARK   3   TLS GROUP : 1                                                      
+REMARK   3    NUMBER OF COMPONENTS GROUP : 1                                    
+REMARK   3    COMPONENTS        C SSSEQI   TO  C SSSEQI                         
+REMARK   3    RESIDUE RANGE :   A     2        A   118                          
+REMARK   3    ORIGIN FOR THE GROUP (A): -11.9068  -7.9134 -35.1151              
+REMARK   3    T TENSOR                                                          
+REMARK   3      T11:   0.0724 T22:   0.0486                                     
+REMARK   3      T33:   0.0466 T12:  -0.0255                                     
+REMARK   3      T13:   0.0362 T23:  -0.0257                                     
+REMARK   3    L TENSOR                                                          
+REMARK   3      L11:   3.9618 L22:   2.5662                                     
+REMARK   3      L33:   3.5301 L12:  -0.2237                                     
+REMARK   3      L13:  -2.4244 L23:  -0.7777                                     
+REMARK   3    S TENSOR                                                          
+REMARK   3      S11:  -0.1452 S12:   0.2312 S13:   0.0678                       
+REMARK   3      S21:   0.1082 S22:   0.0201 S23:   0.1702                       
+REMARK   3      S31:   0.1983 S32:  -0.3971 S33:   0.1251                       
+REMARK   3                                                                      
+REMARK   3   TLS GROUP : 2                                                      
+REMARK   3    NUMBER OF COMPONENTS GROUP : 1                                    
+REMARK   3    COMPONENTS        C SSSEQI   TO  C SSSEQI                         
+REMARK   3    RESIDUE RANGE :   B     2        B   122                          
+REMARK   3    ORIGIN FOR THE GROUP (A): -27.4515 -16.6414 -12.3927              
+REMARK   3    T TENSOR                                                          
+REMARK   3      T11:   0.0153 T22:   0.0305                                     
+REMARK   3      T33:   0.0110 T12:   0.0014                                     
+REMARK   3      T13:   0.0124 T23:  -0.0038                                     
+REMARK   3    L TENSOR                                                          
+REMARK   3      L11:   2.2921 L22:   2.7795                                     
+REMARK   3      L33:   6.4597 L12:   0.0122                                     
+REMARK   3      L13:   0.2226 L23:  -0.5396                                     
+REMARK   3    S TENSOR                                                          
+REMARK   3      S11:   0.0678 S12:  -0.2191 S13:   0.0982                       
+REMARK   3      S21:   0.1361 S22:   0.0490 S23:   0.0984                       
+REMARK   3      S31:  -0.1717 S32:  -0.0459 S33:  -0.1168                       
+REMARK   3                                                                      
+REMARK   3  BULK SOLVENT MODELLING.                                             
+REMARK   3   METHOD USED : MASK                                                 
+REMARK   3   PARAMETERS FOR MASK CALCULATION                                    
+REMARK   3   VDW PROBE RADIUS   : 1.20                                          
+REMARK   3   ION PROBE RADIUS   : 0.80                                          
+REMARK   3   SHRINKAGE RADIUS   : 0.80                                          
+REMARK   3                                                                      
+REMARK   3  OTHER REFINEMENT REMARKS: HYDROGENS HAVE BEEN ADDED IN THE RIDING   
+REMARK   3  POSITIONS                                                           
+REMARK   4                                                                      
+REMARK   4 4ZHO COMPLIES WITH FORMAT V. 3.30, 13-JUL-11                         
+REMARK 100                                                                      
+REMARK 100 THIS ENTRY HAS BEEN PROCESSED BY PDBE ON 27-APR-15.                  
+REMARK 100 THE DEPOSITION ID IS D_1000209256.                                   
+REMARK 200                                                                      
+REMARK 200 EXPERIMENTAL DETAILS                                                 
+REMARK 200  EXPERIMENT TYPE                : X-RAY DIFFRACTION                  
+REMARK 200  DATE OF DATA COLLECTION        : 21-JUL-14                          
+REMARK 200  TEMPERATURE           (KELVIN) : 100                                
+REMARK 200  PH                             : 8.5                                
+REMARK 200  NUMBER OF CRYSTALS USED        : NULL                               
+REMARK 200                                                                      
+REMARK 200  SYNCHROTRON              (Y/N) : Y                                  
+REMARK 200  RADIATION SOURCE               : DIAMOND                            
+REMARK 200  BEAMLINE                       : I02                                
+REMARK 200  X-RAY GENERATOR MODEL          : NULL                               
+REMARK 200  MONOCHROMATIC OR LAUE    (M/L) : M                                  
+REMARK 200  WAVELENGTH OR RANGE        (A) : 1.74                               
+REMARK 200  MONOCHROMATOR                  : SILICON CRYSTAL                    
+REMARK 200  OPTICS                         : NULL                               
+REMARK 200                                                                      
+REMARK 200  DETECTOR TYPE                  : PIXEL                              
+REMARK 200  DETECTOR MANUFACTURER          : PSI PILATUS 6M                     
+REMARK 200  INTENSITY-INTEGRATION SOFTWARE : XDS                                
+REMARK 200  DATA SCALING SOFTWARE          : AIMLESS                            
+REMARK 200                                                                      
+REMARK 200  NUMBER OF UNIQUE REFLECTIONS   : 12894                              
+REMARK 200  RESOLUTION RANGE HIGH      (A) : 2.340                              
+REMARK 200  RESOLUTION RANGE LOW       (A) : 60.730                             
+REMARK 200  REJECTION CRITERIA  (SIGMA(I)) : NULL                               
+REMARK 200                                                                      
+REMARK 200 OVERALL.                                                             
+REMARK 200  COMPLETENESS FOR RANGE     (%) : 99.4                               
+REMARK 200  DATA REDUNDANCY                : 12.30                              
+REMARK 200  R MERGE                    (I) : 0.06600                            
+REMARK 200  R SYM                      (I) : NULL                               
+REMARK 200  <I/SIGMA(I)> FOR THE DATA SET  : 23.2000                            
+REMARK 200                                                                      
+REMARK 200 IN THE HIGHEST RESOLUTION SHELL.                                     
+REMARK 200  HIGHEST RESOLUTION SHELL, RANGE HIGH (A) : 2.34                     
+REMARK 200  HIGHEST RESOLUTION SHELL, RANGE LOW  (A) : 2.40                     
+REMARK 200  COMPLETENESS FOR SHELL     (%) : 98.4                               
+REMARK 200  DATA REDUNDANCY IN SHELL       : 11.80                              
+REMARK 200  R MERGE FOR SHELL          (I) : 0.59800                            
+REMARK 200  R SYM FOR SHELL            (I) : NULL                               
+REMARK 200  <I/SIGMA(I)> FOR SHELL         : 4.200                              
+REMARK 200                                                                      
+REMARK 200 DIFFRACTION PROTOCOL: SINGLE WAVELENGTH                              
+REMARK 200 METHOD USED TO DETERMINE THE STRUCTURE: NULL                         
+REMARK 200 SOFTWARE USED: PHASER                                                
+REMARK 200 STARTING MODEL: NULL                                                 
+REMARK 200                                                                      
+REMARK 200 REMARK: THIN PLATES, DEEP RED BROWN COLOUR DUE TO 2FE-2S IRON        
+REMARK 200  SULPHUR CLUSTER                                                     
+REMARK 280                                                                      
+REMARK 280 CRYSTAL                                                              
+REMARK 280 SOLVENT CONTENT, VS   (%): 64.33                                     
+REMARK 280 MATTHEWS COEFFICIENT, VM (ANGSTROMS**3/DA): 3.45                     
+REMARK 280                                                                      
+REMARK 280 CRYSTALLIZATION CONDITIONS: 0.2 M MGCL2, 0.1 M TRIS, 20 % PEG        
+REMARK 280  8000, PH 8.5, VAPOR DIFFUSION, SITTING DROP, TEMPERATURE 294K       
+REMARK 290                                                                      
+REMARK 290 CRYSTALLOGRAPHIC SYMMETRY                                            
+REMARK 290 SYMMETRY OPERATORS FOR SPACE GROUP: P 42 21 2                        
+REMARK 290                                                                      
+REMARK 290      SYMOP   SYMMETRY                                                
+REMARK 290     NNNMMM   OPERATOR                                                
+REMARK 290       1555   X,Y,Z                                                   
+REMARK 290       2555   -X,-Y,Z                                                 
+REMARK 290       3555   -Y+1/2,X+1/2,Z+1/2                                      
+REMARK 290       4555   Y+1/2,-X+1/2,Z+1/2                                      
+REMARK 290       5555   -X+1/2,Y+1/2,-Z+1/2                                     
+REMARK 290       6555   X+1/2,-Y+1/2,-Z+1/2                                     
+REMARK 290       7555   Y,X,-Z                                                  
+REMARK 290       8555   -Y,-X,-Z                                                
+REMARK 290                                                                      
+REMARK 290     WHERE NNN -> OPERATOR NUMBER                                     
+REMARK 290           MMM -> TRANSLATION VECTOR                                  
+REMARK 290                                                                      
+REMARK 290 CRYSTALLOGRAPHIC SYMMETRY TRANSFORMATIONS                            
+REMARK 290 THE FOLLOWING TRANSFORMATIONS OPERATE ON THE ATOM/HETATM             
+REMARK 290 RECORDS IN THIS ENTRY TO PRODUCE CRYSTALLOGRAPHICALLY                
+REMARK 290 RELATED MOLECULES.                                                   
+REMARK 290   SMTRY1   1  1.000000  0.000000  0.000000        0.00000            
+REMARK 290   SMTRY2   1  0.000000  1.000000  0.000000        0.00000            
+REMARK 290   SMTRY3   1  0.000000  0.000000  1.000000        0.00000            
+REMARK 290   SMTRY1   2 -1.000000  0.000000  0.000000        0.00000            
+REMARK 290   SMTRY2   2  0.000000 -1.000000  0.000000        0.00000            
+REMARK 290   SMTRY3   2  0.000000  0.000000  1.000000        0.00000            
+REMARK 290   SMTRY1   3  0.000000 -1.000000  0.000000       30.36500            
+REMARK 290   SMTRY2   3  1.000000  0.000000  0.000000       30.36500            
+REMARK 290   SMTRY3   3  0.000000  0.000000  1.000000       77.36500            
+REMARK 290   SMTRY1   4  0.000000  1.000000  0.000000       30.36500            
+REMARK 290   SMTRY2   4 -1.000000  0.000000  0.000000       30.36500            
+REMARK 290   SMTRY3   4  0.000000  0.000000  1.000000       77.36500            
+REMARK 290   SMTRY1   5 -1.000000  0.000000  0.000000       30.36500            
+REMARK 290   SMTRY2   5  0.000000  1.000000  0.000000       30.36500            
+REMARK 290   SMTRY3   5  0.000000  0.000000 -1.000000       77.36500            
+REMARK 290   SMTRY1   6  1.000000  0.000000  0.000000       30.36500            
+REMARK 290   SMTRY2   6  0.000000 -1.000000  0.000000       30.36500            
+REMARK 290   SMTRY3   6  0.000000  0.000000 -1.000000       77.36500            
+REMARK 290   SMTRY1   7  0.000000  1.000000  0.000000        0.00000            
+REMARK 290   SMTRY2   7  1.000000  0.000000  0.000000        0.00000            
+REMARK 290   SMTRY3   7  0.000000  0.000000 -1.000000        0.00000            
+REMARK 290   SMTRY1   8  0.000000 -1.000000  0.000000        0.00000            
+REMARK 290   SMTRY2   8 -1.000000  0.000000  0.000000        0.00000            
+REMARK 290   SMTRY3   8  0.000000  0.000000 -1.000000        0.00000            
+REMARK 290                                                                      
+REMARK 290 REMARK: NULL                                                         
+REMARK 300                                                                      
+REMARK 300 BIOMOLECULE: 1, 2                                                    
+REMARK 300 SEE REMARK 350 FOR THE AUTHOR PROVIDED AND/OR PROGRAM                
+REMARK 300 GENERATED ASSEMBLY INFORMATION FOR THE STRUCTURE IN                  
+REMARK 300 THIS ENTRY. THE REMARK MAY ALSO PROVIDE INFORMATION ON               
+REMARK 300 BURIED SURFACE AREA.                                                 
+REMARK 350                                                                      
+REMARK 350 COORDINATES FOR A COMPLETE MULTIMER REPRESENTING THE KNOWN           
+REMARK 350 BIOLOGICALLY SIGNIFICANT OLIGOMERIZATION STATE OF THE                
+REMARK 350 MOLECULE CAN BE GENERATED BY APPLYING BIOMT TRANSFORMATIONS          
+REMARK 350 GIVEN BELOW.  BOTH NON-CRYSTALLOGRAPHIC AND                          
+REMARK 350 CRYSTALLOGRAPHIC OPERATIONS ARE GIVEN.                               
+REMARK 350                                                                      
+REMARK 350 BIOMOLECULE: 1                                                       
+REMARK 350 AUTHOR DETERMINED BIOLOGICAL UNIT: MONOMERIC                         
+REMARK 350 APPLY THE FOLLOWING TO CHAINS: A                                     
+REMARK 350   BIOMT1   1  1.000000  0.000000  0.000000        0.00000            
+REMARK 350   BIOMT2   1  0.000000  1.000000  0.000000        0.00000            
+REMARK 350   BIOMT3   1  0.000000  0.000000  1.000000        0.00000            
+REMARK 350                                                                      
+REMARK 350 BIOMOLECULE: 2                                                       
+REMARK 350 AUTHOR DETERMINED BIOLOGICAL UNIT: MONOMERIC                         
+REMARK 350 APPLY THE FOLLOWING TO CHAINS: B                                     
+REMARK 350   BIOMT1   1  1.000000  0.000000  0.000000        0.00000            
+REMARK 350   BIOMT2   1  0.000000  1.000000  0.000000        0.00000            
+REMARK 350   BIOMT3   1  0.000000  0.000000  1.000000        0.00000            
+REMARK 375                                                                      
+REMARK 375 SPECIAL POSITION                                                     
+REMARK 375 THE FOLLOWING ATOMS ARE FOUND TO BE WITHIN 0.15 ANGSTROMS            
+REMARK 375 OF A SYMMETRY RELATED ATOM AND ARE ASSUMED TO BE ON SPECIAL          
+REMARK 375 POSITIONS.                                                           
+REMARK 375                                                                      
+REMARK 375 ATOM RES CSSEQI                                                      
+REMARK 375      HOH B 319  LIES ON A SPECIAL POSITION.                          
+REMARK 465                                                                      
+REMARK 465 MISSING RESIDUES                                                     
+REMARK 465 THE FOLLOWING RESIDUES WERE NOT LOCATED IN THE                       
+REMARK 465 EXPERIMENT. (M=MODEL NUMBER; RES=RESIDUE NAME; C=CHAIN               
+REMARK 465 IDENTIFIER; SSSEQ=SEQUENCE NUMBER; I=INSERTION CODE.)                
+REMARK 465                                                                      
+REMARK 465   M RES C SSSEQI                                                     
+REMARK 465     MET A     1                                                      
+REMARK 465     GLU A    99                                                      
+REMARK 465     HIS A   100                                                      
+REMARK 465     HIS A   101                                                      
+REMARK 465     HIS A   102                                                      
+REMARK 465     HIS A   103                                                      
+REMARK 465     HIS A   104                                                      
+REMARK 465     HIS A   105                                                      
+REMARK 465     MET B     1                                                      
+REMARK 465     GLU B    99                                                      
+REMARK 465     HIS B   100                                                      
+REMARK 465     HIS B   101                                                      
+REMARK 465     HIS B   102                                                      
+REMARK 465     HIS B   103                                                      
+REMARK 465     HIS B   104                                                      
+REMARK 465     HIS B   105                                                      
+REMARK 470                                                                      
+REMARK 470 MISSING ATOM                                                         
+REMARK 470 THE FOLLOWING RESIDUES HAVE MISSING ATOMS (M=MODEL NUMBER;           
+REMARK 470 RES=RESIDUE NAME; C=CHAIN IDENTIFIER; SSEQ=SEQUENCE NUMBER;          
+REMARK 470 I=INSERTION CODE):                                                   
+REMARK 470   M RES CSSEQI  ATOMS                                                
+REMARK 470     LEU A  98    CG   CD1  CD2                                       
+REMARK 470     LEU B  98    CG   CD1  CD2                                       
+REMARK 500                                                                      
+REMARK 500 GEOMETRY AND STEREOCHEMISTRY                                         
+REMARK 500 SUBTOPIC: TORSION ANGLES                                             
+REMARK 500                                                                      
+REMARK 500 TORSION ANGLES OUTSIDE THE EXPECTED RAMACHANDRAN REGIONS:            
+REMARK 500 (M=MODEL NUMBER; RES=RESIDUE NAME; C=CHAIN IDENTIFIER;               
+REMARK 500 SSEQ=SEQUENCE NUMBER; I=INSERTION CODE).                             
+REMARK 500                                                                      
+REMARK 500 STANDARD TABLE:                                                      
+REMARK 500 FORMAT:(10X,I3,1X,A3,1X,A1,I4,A1,4X,F7.2,3X,F7.2)                    
+REMARK 500                                                                      
+REMARK 500 EXPECTED VALUES: GJ KLEYWEGT AND TA JONES (1996). PHI/PSI-           
+REMARK 500 CHOLOGY: RAMACHANDRAN REVISITED. STRUCTURE 4, 1395 - 1400            
+REMARK 500                                                                      
+REMARK 500  M RES CSSEQI        PSI       PHI                                   
+REMARK 500    SER A  39      -64.23   -146.55                                   
+REMARK 500    MET A  97      -67.17    -99.01                                   
+REMARK 500    SER B  39      -78.49   -140.11                                   
+REMARK 500    SER B  63      -12.80   -148.10                                   
+REMARK 500                                                                      
+REMARK 500 REMARK: NULL                                                         
+REMARK 620                                                                      
+REMARK 620 METAL COORDINATION                                                   
+REMARK 620 (M=MODEL NUMBER; RES=RESIDUE NAME; C=CHAIN IDENTIFIER;               
+REMARK 620 SSEQ=SEQUENCE NUMBER; I=INSERTION CODE):                             
+REMARK 620                                                                      
+REMARK 620 COORDINATION ANGLES FOR:  M RES CSSEQI METAL                         
+REMARK 620                             FES A 201  FE1                           
+REMARK 620 N RES CSSEQI ATOM                                                    
+REMARK 620 1 CYS A  40   SG                                                     
+REMARK 620 2 FES A 201   S1  124.8                                              
+REMARK 620 3 FES A 201   S2  100.1  99.4                                        
+REMARK 620 4 CYS A  45   SG  105.3 110.0 117.8                                  
+REMARK 620 N                    1     2     3                                   
+REMARK 620                                                                      
+REMARK 620 COORDINATION ANGLES FOR:  M RES CSSEQI METAL                         
+REMARK 620                             FES A 201  FE2                           
+REMARK 620 N RES CSSEQI ATOM                                                    
+REMARK 620 1 CYS A  48   SG                                                     
+REMARK 620 2 FES A 201   S1  114.4                                              
+REMARK 620 3 FES A 201   S2  108.9 101.7                                        
+REMARK 620 4 CYS A  78   SG  105.9 122.0 102.7                                  
+REMARK 620 N                    1     2     3                                   
+REMARK 620                                                                      
+REMARK 620 COORDINATION ANGLES FOR:  M RES CSSEQI METAL                         
+REMARK 620                             FES B 201  FE1                           
+REMARK 620 N RES CSSEQI ATOM                                                    
+REMARK 620 1 CYS B  40   SG                                                     
+REMARK 620 2 FES B 201   S1  119.9                                              
+REMARK 620 3 FES B 201   S2  102.0  97.8                                        
+REMARK 620 4 CYS B  45   SG  109.5 108.1 119.7                                  
+REMARK 620 N                    1     2     3                                   
+REMARK 620                                                                      
+REMARK 620 COORDINATION ANGLES FOR:  M RES CSSEQI METAL                         
+REMARK 620                             FES B 201  FE2                           
+REMARK 620 N RES CSSEQI ATOM                                                    
+REMARK 620 1 CYS B  48   SG                                                     
+REMARK 620 2 FES B 201   S1  111.1                                              
+REMARK 620 3 FES B 201   S2  115.5  99.1                                        
+REMARK 620 4 CYS B  78   SG  110.9 117.2 102.5                                  
+REMARK 620 N                    1     2     3                                   
+REMARK 800                                                                      
+REMARK 800 SITE                                                                 
+REMARK 800 SITE_IDENTIFIER: AC1                                                 
+REMARK 800 EVIDENCE_CODE: SOFTWARE                                              
+REMARK 800 SITE_DESCRIPTION: binding site for residue FES A 201                 
+REMARK 800                                                                      
+REMARK 800 SITE_IDENTIFIER: AC2                                                 
+REMARK 800 EVIDENCE_CODE: SOFTWARE                                              
+REMARK 800 SITE_DESCRIPTION: binding site for residue CL A 202                  
+REMARK 800                                                                      
+REMARK 800 SITE_IDENTIFIER: AC3                                                 
+REMARK 800 EVIDENCE_CODE: SOFTWARE                                              
+REMARK 800 SITE_DESCRIPTION: binding site for residue FES B 201                 
+REMARK 800                                                                      
+REMARK 800 SITE_IDENTIFIER: AC4                                                 
+REMARK 800 EVIDENCE_CODE: SOFTWARE                                              
+REMARK 800 SITE_DESCRIPTION: binding site for residue CL B 202                  
+DBREF  4ZHO A    1    94  UNP    P16972   FER2_ARATH      52    145             
+DBREF  4ZHO B    1    94  UNP    P16972   FER2_ARATH      52    145             
+SEQADV 4ZHO ALA A   95  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO ILE A   96  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO MET A   97  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO LEU A   98  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO GLU A   99  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS A  100  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS A  101  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS A  102  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS A  103  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS A  104  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS A  105  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO ALA B   95  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO ILE B   96  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO MET B   97  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO LEU B   98  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO GLU B   99  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS B  100  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS B  101  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS B  102  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS B  103  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS B  104  UNP  P16972              EXPRESSION TAG                 
+SEQADV 4ZHO HIS B  105  UNP  P16972              EXPRESSION TAG                 
+SEQRES   1 A  105  MET ALA THR TYR LYS VAL LYS PHE ILE THR PRO GLU GLY          
+SEQRES   2 A  105  GLU LEU GLU VAL GLU CYS ASP ASP ASP VAL TYR VAL LEU          
+SEQRES   3 A  105  ASP ALA ALA GLU GLU ALA GLY ILE ASP LEU PRO TYR SER          
+SEQRES   4 A  105  CYS ARG ALA GLY SER CYS SER SER CYS ALA GLY LYS VAL          
+SEQRES   5 A  105  VAL SER GLY SER VAL ASP GLN SER ASP GLN SER PHE LEU          
+SEQRES   6 A  105  ASP ASP GLU GLN ILE GLY GLU GLY PHE VAL LEU THR CYS          
+SEQRES   7 A  105  ALA ALA TYR PRO THR SER ASP VAL THR ILE GLU THR HIS          
+SEQRES   8 A  105  LYS GLU GLU ALA ILE MET LEU GLU HIS HIS HIS HIS HIS          
+SEQRES   9 A  105  HIS                                                          
+SEQRES   1 B  105  MET ALA THR TYR LYS VAL LYS PHE ILE THR PRO GLU GLY          
+SEQRES   2 B  105  GLU LEU GLU VAL GLU CYS ASP ASP ASP VAL TYR VAL LEU          
+SEQRES   3 B  105  ASP ALA ALA GLU GLU ALA GLY ILE ASP LEU PRO TYR SER          
+SEQRES   4 B  105  CYS ARG ALA GLY SER CYS SER SER CYS ALA GLY LYS VAL          
+SEQRES   5 B  105  VAL SER GLY SER VAL ASP GLN SER ASP GLN SER PHE LEU          
+SEQRES   6 B  105  ASP ASP GLU GLN ILE GLY GLU GLY PHE VAL LEU THR CYS          
+SEQRES   7 B  105  ALA ALA TYR PRO THR SER ASP VAL THR ILE GLU THR HIS          
+SEQRES   8 B  105  LYS GLU GLU ALA ILE MET LEU GLU HIS HIS HIS HIS HIS          
+SEQRES   9 B  105  HIS                                                          
+HET    FES  A 201       4                                                       
+HET     CL  A 202       1                                                       
+HET    FES  B 201       4                                                       
+HET     CL  B 202       1                                                       
+HETNAM     FES FE2/S2 (INORGANIC) CLUSTER                                       
+HETNAM      CL CHLORIDE ION                                                     
+FORMUL   3  FES    2(FE2 S2)                                                    
+FORMUL   4   CL    2(CL 1-)                                                     
+FORMUL   7  HOH   *38(H2 O)                                                     
+HELIX    1 AA1 TYR A   24  ALA A   32  1                                   9    
+HELIX    2 AA2 ASP A   66  GLU A   72  1                                   7    
+HELIX    3 AA3 CYS A   78  ALA A   80  5                                   3    
+HELIX    4 AA5 TYR B   24  ALA B   32  1                                   9    
+HELIX    5 AA6 ASP B   66  GLU B   72  1                                   7    
+HELIX    6 AA7 CYS B   78  ALA B   80  5                                   3    
+HELIX    7 AA8 LYS B   92  MET B   97  5                                   6    
+SHEET    1 AA1 5 GLU A  14  ASP A  20  0                                        
+SHEET    2 AA1 5 THR A   3  ILE A   9 -1  N  PHE A   8   O  LEU A  15           
+SHEET    3 AA1 5 VAL A  86  GLU A  89  1  O  ILE A  88   N  LYS A   7           
+SHEET    4 AA1 5 ALA A  49  SER A  54 -1  N  SER A  54   O  THR A  87           
+SHEET    5 AA1 5 PHE A  74  LEU A  76 -1  O  VAL A  75   N  GLY A  50           
+SHEET    1 AA2 2 VAL A  57  ASP A  58  0                                        
+SHEET    2 AA2 2 TYR A  81  PRO A  82 -1  O  TYR A  81   N  ASP A  58           
+SHEET    1 AA3 5 GLY B  13  ASP B  20  0                                        
+SHEET    2 AA3 5 THR B   3  THR B  10 -1  N  PHE B   8   O  LEU B  15           
+SHEET    3 AA3 5 VAL B  86  GLU B  89  1  O  ILE B  88   N  LYS B   7           
+SHEET    4 AA3 5 ALA B  49  SER B  54 -1  N  LYS B  51   O  GLU B  89           
+SHEET    5 AA3 5 PHE B  74  LEU B  76 -1  O  VAL B  75   N  GLY B  50           
+SHEET    1 AA4 2 VAL B  57  ASP B  58  0                                        
+SHEET    2 AA4 2 TYR B  81  PRO B  82 -1  O  TYR B  81   N  ASP B  58           
+LINK         SG  CYS A  40                FE1  FES A 201     1555   1555  2.33  
+LINK         SG  CYS A  45                FE1  FES A 201     1555   1555  2.28  
+LINK         SG  CYS A  48                FE2  FES A 201     1555   1555  2.28  
+LINK         SG  CYS A  78                FE2  FES A 201     1555   1555  2.34  
+LINK         SG  CYS B  40                FE1  FES B 201     1555   1555  2.22  
+LINK         SG  CYS B  45                FE1  FES B 201     1555   1555  2.25  
+LINK         SG  CYS B  48                FE2  FES B 201     1555   1555  2.18  
+LINK         SG  CYS B  78                FE2  FES B 201     1555   1555  2.36  
+SITE     1 AC1  8 SER A  39  CYS A  40  ARG A  41  GLY A  43                    
+SITE     2 AC1  8 SER A  44  CYS A  45  CYS A  48  CYS A  78                    
+SITE     1 AC2  2 SER A  84  ASP A  85                                          
+SITE     1 AC3  8 SER B  39  CYS B  40  ARG B  41  GLY B  43                    
+SITE     2 AC3  8 SER B  44  CYS B  45  CYS B  48  CYS B  78                    
+SITE     1 AC4  2 SER B  84  ASP B  85                                          
+CRYST1   60.730   60.730  154.730  90.00  90.00  90.00 P 42 21 2    16          
+ORIGX1      1.000000  0.000000  0.000000        0.00000                         
+ORIGX2      0.000000  1.000000  0.000000        0.00000                         
+ORIGX3      0.000000  0.000000  1.000000        0.00000                         
+SCALE1      0.016466  0.000000  0.000000        0.00000                         
+SCALE2      0.000000  0.016466  0.000000        0.00000                         
+SCALE3      0.000000  0.000000  0.006463        0.00000                         
+ATOM      1  N   ALA A   2      -1.257 -15.807 -48.124  1.00 50.77           N  
+ANISOU    1  N   ALA A   2     7787   5804   5698   -219   1819   -481       N  
+ATOM      2  CA  ALA A   2      -1.523 -14.339 -48.197  1.00 50.70           C  
+ANISOU    2  CA  ALA A   2     7568   6034   5660   -183   1548   -344       C  
+ATOM      3  C   ALA A   2      -1.438 -13.722 -46.830  1.00 45.70           C  
+ANISOU    3  C   ALA A   2     6785   5347   5230    -97   1396   -189       C  
+ATOM      4  O   ALA A   2      -1.809 -14.345 -45.825  1.00 44.98           O  
+ANISOU    4  O   ALA A   2     6732   5134   5221   -136   1414   -240       O  
+ATOM      5  CB  ALA A   2      -2.903 -14.040 -48.848  1.00 48.96           C  
+ANISOU    5  CB  ALA A   2     7316   6077   5207   -370   1390   -508       C  
+ATOM      6  N   THR A   3      -1.069 -12.455 -46.833  1.00 45.54           N  
+ANISOU    6  N   THR A   3     6600   5431   5271     -1   1249    -16       N  
+ATOM      7  CA  THR A   3      -0.949 -11.641 -45.631  1.00 47.35           C  
+ANISOU    7  CA  THR A   3     6681   5636   5674     60   1092    109       C  
+ATOM      8  C   THR A   3      -1.689 -10.384 -45.941  1.00 43.11           C  
+ANISOU    8  C   THR A   3     6022   5274   5083     31    915    145       C  
+ATOM      9  O   THR A   3      -1.531  -9.803 -47.012  1.00 41.92           O  
+ANISOU    9  O   THR A   3     5843   5236   4848     63    924    210       O  
+ATOM     10  CB  THR A   3       0.543 -11.321 -45.328  1.00 50.05           C  
+ANISOU   10  CB  THR A   3     6935   5888   6195    216   1141    301       C  
+ATOM     11  OG1 THR A   3       1.229 -12.551 -45.036  1.00 50.95           O  
+ANISOU   11  OG1 THR A   3     7151   5846   6362    283   1328    306       O  
+ATOM     12  CG2 THR A   3       0.673 -10.438 -44.115  1.00 54.46           C  
+ANISOU   12  CG2 THR A   3     7341   6448   6902    242    968    390       C  
+ATOM     13  N   TYR A   4      -2.484  -9.944 -44.989  1.00 46.99           N  
+ANISOU   13  N   TYR A   4     6443   5784   5625     -9    776    123       N  
+ATOM     14  CA  TYR A   4      -3.347  -8.768 -45.177  1.00 48.43           C  
+ANISOU   14  CA  TYR A   4     6510   6116   5775    -19    632    167       C  
+ATOM     15  C   TYR A   4      -3.023  -7.694 -44.144  1.00 47.22           C  
+ANISOU   15  C   TYR A   4     6246   5881   5813     46    540    272       C  
+ATOM     16  O   TYR A   4      -2.521  -7.988 -43.069  1.00 48.33           O  
+ANISOU   16  O   TYR A   4     6396   5904   6063     58    537    263       O  
+ATOM     17  CB  TYR A   4      -4.801  -9.196 -45.059  1.00 47.01           C  
+ANISOU   17  CB  TYR A   4     6341   6049   5468   -146    570     20       C  
+ATOM     18  CG  TYR A   4      -5.208 -10.134 -46.157  1.00 43.84           C  
+ANISOU   18  CG  TYR A   4     6035   5763   4857   -258    645   -122       C  
+ATOM     19  CD1 TYR A   4      -5.280  -9.700 -47.461  1.00 48.21           C  
+ANISOU   19  CD1 TYR A   4     6559   6515   5243   -243    630    -80       C  
+ATOM     20  CD2 TYR A   4      -5.473 -11.470 -45.898  1.00 48.38           C  
+ANISOU   20  CD2 TYR A   4     6743   6241   5397   -383    752   -303       C  
+ATOM     21  CE1 TYR A   4      -5.629 -10.561 -48.480  1.00 48.93           C  
+ANISOU   21  CE1 TYR A   4     6746   6739   5104   -368    694   -244       C  
+ATOM     22  CE2 TYR A   4      -5.802 -12.362 -46.923  1.00 48.52           C  
+ANISOU   22  CE2 TYR A   4     6868   6342   5221   -522    844   -480       C  
+ATOM     23  CZ  TYR A   4      -5.914 -11.892 -48.199  1.00 48.26           C  
+ANISOU   23  CZ  TYR A   4     6798   6543   4992   -526    799   -466       C  
+ATOM     24  OH  TYR A   4      -6.226 -12.778 -49.220  1.00 52.35           O  
+ANISOU   24  OH  TYR A   4     7434   7172   5284   -686    885   -675       O  
+ATOM     25  N   LYS A   5      -3.307  -6.453 -44.489  1.00 47.90           N  
+ANISOU   25  N   LYS A   5     6234   6036   5930     90    478    373       N  
+ATOM     26  CA  LYS A   5      -3.126  -5.320 -43.582  1.00 49.46           C  
+ANISOU   26  CA  LYS A   5     6343   6138   6309    126    414    439       C  
+ATOM     27  C   LYS A   5      -4.415  -5.114 -42.819  1.00 44.72           C  
+ANISOU   27  C   LYS A   5     5725   5576   5688     87    334    362       C  
+ATOM     28  O   LYS A   5      -5.464  -4.904 -43.423  1.00 45.76           O  
+ANISOU   28  O   LYS A   5     5820   5852   5714     87    308    374       O  
+ATOM     29  CB  LYS A   5      -2.776  -4.038 -44.357  1.00 51.93           C  
+ANISOU   29  CB  LYS A   5     6573   6454   6702    205    443    602       C  
+ATOM     30  CG  LYS A   5      -1.497  -4.126 -45.113  1.00 62.94           C  
+ANISOU   30  CG  LYS A   5     7967   7811   8137    249    542    698       C  
+ATOM     31  CD  LYS A   5      -0.351  -4.665 -44.240  1.00 71.74           C  
+ANISOU   31  CD  LYS A   5     9078   8803   9377    228    553    662       C  
+ATOM     32  CE  LYS A   5      -0.083  -3.798 -43.025  1.00 78.38           C  
+ANISOU   32  CE  LYS A   5     9836   9541  10403    192    473    648       C  
+ATOM     33  NZ  LYS A   5       0.491  -2.513 -43.496  1.00 87.36           N  
+ANISOU   33  NZ  LYS A   5    10881  10609  11701    215    524    774       N  
+ATOM     34  N   VAL A   6      -4.356  -5.236 -41.505  1.00 40.33           N  
+ANISOU   34  N   VAL A   6     5186   4922   5213     58    297    290       N  
+ATOM     35  CA  VAL A   6      -5.506  -4.978 -40.686  1.00 43.48           C  
+ANISOU   35  CA  VAL A   6     5572   5340   5608     33    246    224       C  
+ATOM     36  C   VAL A   6      -5.286  -3.624 -40.015  1.00 44.26           C  
+ANISOU   36  C   VAL A   6     5616   5334   5865     73    225    265       C  
+ATOM     37  O   VAL A   6      -4.317  -3.413 -39.286  1.00 45.54           O  
+ANISOU   37  O   VAL A   6     5782   5395   6125     57    211    245       O  
+ATOM     38  CB  VAL A   6      -5.704  -6.072 -39.614  1.00 47.03           C  
+ANISOU   38  CB  VAL A   6     6100   5753   6016    -24    246    110       C  
+ATOM     39  CG1 VAL A   6      -6.982  -5.808 -38.849  1.00 45.34           C  
+ANISOU   39  CG1 VAL A   6     5866   5571   5789    -46    216     50       C  
+ATOM     40  CG2 VAL A   6      -5.764  -7.451 -40.254  1.00 46.02           C  
+ANISOU   40  CG2 VAL A   6     6052   5664   5769    -78    315     53       C  
+ATOM     41  N   LYS A   7      -6.163  -2.682 -40.330  1.00 45.38           N  
+ANISOU   41  N   LYS A   7     5700   5508   6034    123    235    327       N  
+ATOM     42  CA  LYS A   7      -6.143  -1.373 -39.740  1.00 45.98           C  
+ANISOU   42  CA  LYS A   7     5748   5450   6273    160    263    351       C  
+ATOM     43  C   LYS A   7      -7.143  -1.363 -38.592  1.00 47.81           C  
+ANISOU   43  C   LYS A   7     6003   5668   6491    143    248    245       C  
+ATOM     44  O   LYS A   7      -8.347  -1.536 -38.787  1.00 51.78           O  
+ANISOU   44  O   LYS A   7     6468   6287   6918    173    249    263       O  
+ATOM     45  CB  LYS A   7      -6.506  -0.347 -40.788  1.00 48.29           C  
+ANISOU   45  CB  LYS A   7     5968   5763   6617    264    329    520       C  
+ATOM     46  CG  LYS A   7      -6.637   1.070 -40.268  1.00 58.39           C  
+ANISOU   46  CG  LYS A   7     7234   6859   8090    316    413    556       C  
+ATOM     47  CD  LYS A   7      -6.631   2.026 -41.447  1.00 68.64           C  
+ANISOU   47  CD  LYS A   7     8467   8148   9461    441    515    776       C  
+ATOM     48  CE  LYS A   7      -6.930   3.448 -41.018  1.00 82.52           C  
+ANISOU   48  CE  LYS A   7    10225   9693  11435    515    651    833       C  
+ATOM     49  NZ  LYS A   7      -6.213   4.399 -41.926  1.00 90.34           N  
+ANISOU   49  NZ  LYS A   7    11188  10561  12575    587    785   1022       N  
+ATOM     50  N   PHE A   8      -6.640  -1.148 -37.392  1.00 43.94           N  
+ANISOU   50  N   PHE A   8     5567   5061   6068     93    233    136       N  
+ATOM     51  CA  PHE A   8      -7.513  -1.002 -36.238  1.00 50.30           C  
+ANISOU   51  CA  PHE A   8     6412   5840   6859     87    244     34       C  
+ATOM     52  C   PHE A   8      -7.808   0.440 -35.906  1.00 44.05           C  
+ANISOU   52  C   PHE A   8     5618   4900   6218    131    331     33       C  
+ATOM     53  O   PHE A   8      -6.905   1.228 -35.663  1.00 48.85           O  
+ANISOU   53  O   PHE A   8     6245   5366   6948     88    353     -5       O  
+ATOM     54  CB  PHE A   8      -6.843  -1.646 -35.018  1.00 45.89           C  
+ANISOU   54  CB  PHE A   8     5927   5267   6239     10    184    -93       C  
+ATOM     55  CG  PHE A   8      -6.679  -3.122 -35.144  1.00 46.40           C  
+ANISOU   55  CG  PHE A   8     6019   5437   6174    -10    148    -85       C  
+ATOM     56  CD1 PHE A   8      -7.769  -3.967 -35.038  1.00 48.26           C  
+ANISOU   56  CD1 PHE A   8     6277   5749   6311    -14    173   -106       C  
+ATOM     57  CD2 PHE A   8      -5.438  -3.668 -35.354  1.00 44.91           C  
+ANISOU   57  CD2 PHE A   8     5828   5253   5981    -27    113    -54       C  
+ATOM     58  CE1 PHE A   8      -7.601  -5.339 -35.131  1.00 47.89           C  
+ANISOU   58  CE1 PHE A   8     6276   5748   6170    -46    182   -111       C  
+ATOM     59  CE2 PHE A   8      -5.279  -5.027 -35.445  1.00 45.32           C  
+ANISOU   59  CE2 PHE A   8     5923   5360   5935    -28    125    -39       C  
+ATOM     60  CZ  PHE A   8      -6.356  -5.857 -35.342  1.00 47.01           C  
+ANISOU   60  CZ  PHE A   8     6184   5615   6059    -43    168    -74       C  
+ATOM     61  N   ILE A   9      -9.069   0.763 -35.795  1.00 45.19           N  
+ANISOU   61  N   ILE A   9     5739   5067   6363    208    395     63       N  
+ATOM     62  CA  ILE A   9      -9.459   2.100 -35.309  1.00 51.68           C  
+ANISOU   62  CA  ILE A   9     6584   5712   7339    270    525     51       C  
+ATOM     63  C   ILE A   9      -9.826   1.880 -33.879  1.00 50.92           C  
+ANISOU   63  C   ILE A   9     6580   5587   7179    217    529   -126       C  
+ATOM     64  O   ILE A   9     -10.824   1.229 -33.599  1.00 53.51           O  
+ANISOU   64  O   ILE A   9     6889   6036   7407    248    527   -124       O  
+ATOM     65  CB  ILE A   9     -10.635   2.697 -36.124  1.00 55.53           C  
+ANISOU   65  CB  ILE A   9     6967   6250   7879    433    624    240       C  
+ATOM     66  CG1 ILE A   9     -10.204   2.918 -37.592  1.00 55.29           C  
+ANISOU   66  CG1 ILE A   9     6852   6282   7873    497    619    438       C  
+ATOM     67  CG2 ILE A   9     -11.137   4.018 -35.502  1.00 55.96           C  
+ANISOU   67  CG2 ILE A   9     7065   6086   8109    523    808    232       C  
+ATOM     68  CD1 ILE A   9     -11.385   2.981 -38.527  1.00 58.25           C  
+ANISOU   68  CD1 ILE A   9     7085   6859   8188    645    639    641       C  
+ATOM     69  N   THR A  10      -8.967   2.329 -32.980  1.00 50.67           N  
+ANISOU   69  N   THR A  10     6641   5425   7186    121    526   -284       N  
+ATOM     70  CA  THR A  10      -9.185   2.107 -31.561  1.00 51.69           C  
+ANISOU   70  CA  THR A  10     6873   5558   7208     65    520   -464       C  
+ATOM     71  C   THR A  10      -9.612   3.432 -30.915  1.00 50.65           C  
+ANISOU   71  C   THR A  10     6822   5218   7204     89    689   -568       C  
+ATOM     72  O   THR A  10      -9.544   4.466 -31.525  1.00 57.26           O  
+ANISOU   72  O   THR A  10     7639   5887   8228    134    806   -501       O  
+ATOM     73  CB  THR A  10      -7.930   1.531 -30.864  1.00 50.80           C  
+ANISOU   73  CB  THR A  10     6804   5512   6986    -68    378   -588       C  
+ATOM     74  OG1 THR A  10      -6.968   2.554 -30.671  1.00 53.43           O  
+ANISOU   74  OG1 THR A  10     7157   5704   7440   -168    390   -696       O  
+ATOM     75  CG2 THR A  10      -7.278   0.462 -31.693  1.00 50.38           C  
+ANISOU   75  CG2 THR A  10     6674   5593   6874    -73    264   -466       C  
+ATOM     76  N   PRO A  11     -10.106   3.393 -29.679  1.00 56.03           N  
+ANISOU   76  N   PRO A  11     7607   5897   7785     72    733   -722       N  
+ATOM     77  CA  PRO A  11     -10.427   4.652 -28.979  1.00 58.93           C  
+ANISOU   77  CA  PRO A  11     8085   6039   8264     79    922   -863       C  
+ATOM     78  C   PRO A  11      -9.259   5.590 -28.772  1.00 63.22           C  
+ANISOU   78  C   PRO A  11     8697   6403   8918    -73    935  -1031       C  
+ATOM     79  O   PRO A  11      -9.508   6.756 -28.521  1.00 69.60           O  
+ANISOU   79  O   PRO A  11     9594   6966   9884    -65   1137  -1123       O  
+ATOM     80  CB  PRO A  11     -10.886   4.178 -27.593  1.00 59.38           C  
+ANISOU   80  CB  PRO A  11     8256   6185   8120     51    924  -1030       C  
+ATOM     81  CG  PRO A  11     -11.306   2.750 -27.777  1.00 55.35           C  
+ANISOU   81  CG  PRO A  11     7663   5915   7451     96    802   -896       C  
+ATOM     82  CD  PRO A  11     -10.443   2.207 -28.868  1.00 52.67           C  
+ANISOU   82  CD  PRO A  11     7215   5657   7140     56    649   -769       C  
+ATOM     83  N   GLU A  12      -8.030   5.041 -28.776  1.00 70.08           N  
+ANISOU   83  N   GLU A  12     9526   7398   9704   -217    738  -1080       N  
+ATOM     84  CA  GLU A  12      -6.732   5.771 -28.699  1.00 69.59           C  
+ANISOU   84  CA  GLU A  12     9470   7225   9747   -402    705  -1227       C  
+ATOM     85  C   GLU A  12      -6.252   6.249 -30.096  1.00 71.32           C  
+ANISOU   85  C   GLU A  12     9578   7319  10199   -369    757  -1033       C  
+ATOM     86  O   GLU A  12      -5.575   7.279 -30.200  1.00 82.47           O  
+ANISOU   86  O   GLU A  12    11009   8520  11805   -484    854  -1123       O  
+ATOM     87  CB  GLU A  12      -5.618   4.877 -28.105  1.00 80.51           C  
+ANISOU   87  CB  GLU A  12    10809   8855  10925   -546    460  -1320       C  
+ATOM     88  CG  GLU A  12      -5.569   4.624 -26.578  1.00 89.15           C  
+ANISOU   88  CG  GLU A  12    12010  10093  11769   -643    381  -1555       C  
+ATOM     89  CD  GLU A  12      -4.271   3.871 -26.137  1.00 96.47           C  
+ANISOU   89  CD  GLU A  12    12846  11289  12519   -771    133  -1597       C  
+ATOM     90  OE1 GLU A  12      -3.440   4.458 -25.401  1.00 86.42           O  
+ANISOU   90  OE1 GLU A  12    11584  10049  11202   -968     64  -1827       O  
+ATOM     91  OE2 GLU A  12      -4.044   2.692 -26.542  1.00 87.89           O  
+ANISOU   91  OE2 GLU A  12    11665  10388  11340   -678     12  -1398       O  
+ATOM     92  N   GLY A  13      -6.566   5.500 -31.160  1.00 63.49           N  
+ANISOU   92  N   GLY A  13     8479   6460   9184   -227    703   -780       N  
+ATOM     93  CA  GLY A  13      -6.219   5.913 -32.504  1.00 59.26           C  
+ANISOU   93  CA  GLY A  13     7850   5841   8826   -167    764   -575       C  
+ATOM     94  C   GLY A  13      -6.111   4.770 -33.479  1.00 56.21           C  
+ANISOU   94  C   GLY A  13     7353   5681   8322    -92    628   -378       C  
+ATOM     95  O   GLY A  13      -6.534   3.652 -33.197  1.00 53.58           O  
+ANISOU   95  O   GLY A  13     7018   5544   7795    -65    517   -379       O  
+ATOM     96  N   GLU A  14      -5.519   5.057 -34.631  1.00 54.33           N  
+ANISOU   96  N   GLU A  14     7036   5399   8206    -65    661   -215       N  
+ATOM     97  CA  GLU A  14      -5.492   4.115 -35.751  1.00 60.06           C  
+ANISOU   97  CA  GLU A  14     7674   6319   8826     19    577    -23       C  
+ATOM     98  C   GLU A  14      -4.126   3.540 -35.940  1.00 51.22           C  
+ANISOU   98  C   GLU A  14     6504   5271   7684    -88    464    -38       C  
+ATOM     99  O   GLU A  14      -3.198   4.277 -36.050  1.00 52.79           O  
+ANISOU   99  O   GLU A  14     6674   5338   8045   -172    512    -55       O  
+ATOM    100  CB  GLU A  14      -5.953   4.814 -37.053  1.00 67.15           C  
+ANISOU  100  CB  GLU A  14     8513   7162   9837    174    715    218       C  
+ATOM    101  CG  GLU A  14      -7.367   5.360 -36.842  1.00 82.24           C  
+ANISOU  101  CG  GLU A  14    10442   9031  11772    314    834    268       C  
+ATOM    102  CD  GLU A  14      -7.938   6.250 -37.944  1.00 92.51           C  
+ANISOU  102  CD  GLU A  14    11677  10275  13196    506    998    534       C  
+ATOM    103  OE1 GLU A  14      -7.274   6.424 -38.992  1.00104.38           O  
+ANISOU  103  OE1 GLU A  14    13129  11783  14748    536   1022    696       O  
+ATOM    104  OE2 GLU A  14      -9.080   6.772 -37.742  1.00 86.19           O  
+ANISOU  104  OE2 GLU A  14    10870   9437  12438    645   1116    601       O  
+ATOM    105  N   LEU A  15      -4.066   2.220 -36.076  1.00 51.18           N  
+ANISOU  105  N   LEU A  15     6481   5467   7497    -72    343    -11       N  
+ATOM    106  CA  LEU A  15      -2.856   1.402 -36.095  1.00 54.47           C  
+ANISOU  106  CA  LEU A  15     6850   5982   7861   -141    239    -15       C  
+ATOM    107  C   LEU A  15      -3.099   0.276 -37.078  1.00 53.30           C  
+ANISOU  107  C   LEU A  15     6690   5980   7581    -52    221    115       C  
+ATOM    108  O   LEU A  15      -4.183  -0.290 -37.098  1.00 48.80           O  
+ANISOU  108  O   LEU A  15     6160   5493   6889      1    217    115       O  
+ATOM    109  CB  LEU A  15      -2.708   0.632 -34.773  1.00 53.82           C  
+ANISOU  109  CB  LEU A  15     6808   6004   7634   -208    122   -164       C  
+ATOM    110  CG  LEU A  15      -2.570   1.415 -33.509  1.00 64.22           C  
+ANISOU  110  CG  LEU A  15     8165   7251   8981   -319    102   -355       C  
+ATOM    111  CD1 LEU A  15      -2.755   0.471 -32.348  1.00 68.91           C  
+ANISOU  111  CD1 LEU A  15     8813   8000   9367   -328      0   -448       C  
+ATOM    112  CD2 LEU A  15      -1.203   2.057 -33.474  1.00 66.88           C  
+ANISOU  112  CD2 LEU A  15     8412   7542   9455   -454     71   -403       C  
+ATOM    113  N   GLU A  16      -2.062  -0.144 -37.776  1.00 49.83           N  
+ANISOU  113  N   GLU A  16     6195   5579   7157    -55    214    200       N  
+ATOM    114  CA  GLU A  16      -2.191  -1.180 -38.784  1.00 52.00           C  
+ANISOU  114  CA  GLU A  16     6480   5971   7306     14    227    299       C  
+ATOM    115  C   GLU A  16      -1.165  -2.260 -38.528  1.00 44.99           C  
+ANISOU  115  C   GLU A  16     5581   5146   6365     -5    183    291       C  
+ATOM    116  O   GLU A  16      -0.027  -1.953 -38.247  1.00 41.15           O  
+ANISOU  116  O   GLU A  16     5017   4632   5983    -49    162    300       O  
+ATOM    117  CB  GLU A  16      -1.924  -0.519 -40.111  1.00 56.47           C  
+ANISOU  117  CB  GLU A  16     6996   6506   7952     73    320    454       C  
+ATOM    118  CG  GLU A  16      -2.598  -1.111 -41.299  1.00 64.79           C  
+ANISOU  118  CG  GLU A  16     8072   7693   8851    152    352    546       C  
+ATOM    119  CD  GLU A  16      -2.299  -0.276 -42.542  1.00 77.28           C  
+ANISOU  119  CD  GLU A  16     9604   9258  10500    229    453    725       C  
+ATOM    120  OE1 GLU A  16      -1.932  -0.864 -43.609  1.00 78.06           O  
+ANISOU  120  OE1 GLU A  16     9708   9456  10491    269    492    807       O  
+ATOM    121  OE2 GLU A  16      -2.392   0.977 -42.427  1.00 67.17           O  
+ANISOU  121  OE2 GLU A  16     8290   7846   9385    251    517    784       O  
+ATOM    122  N   VAL A  17      -1.538  -3.520 -38.672  1.00 44.95           N  
+ANISOU  122  N   VAL A  17     5642   5223   6212     27    187    284       N  
+ATOM    123  CA  VAL A  17      -0.578  -4.609 -38.466  1.00 43.94           C  
+ANISOU  123  CA  VAL A  17     5513   5132   6048     46    189    310       C  
+ATOM    124  C   VAL A  17      -0.754  -5.509 -39.643  1.00 44.55           C  
+ANISOU  124  C   VAL A  17     5652   5239   6033     92    285    360       C  
+ATOM    125  O   VAL A  17      -1.732  -5.387 -40.374  1.00 43.66           O  
+ANISOU  125  O   VAL A  17     5577   5162   5847     87    308    346       O  
+ATOM    126  CB  VAL A  17      -0.860  -5.372 -37.157  1.00 48.00           C  
+ANISOU  126  CB  VAL A  17     6081   5682   6472     37    134    232       C  
+ATOM    127  CG1 VAL A  17      -0.799  -4.395 -35.985  1.00 48.79           C  
+ANISOU  127  CG1 VAL A  17     6138   5776   6622    -25     37    146       C  
+ATOM    128  CG2 VAL A  17      -2.229  -6.014 -37.217  1.00 46.07           C  
+ANISOU  128  CG2 VAL A  17     5942   5449   6111     34    172    170       C  
+ATOM    129  N   GLU A  18       0.203  -6.379 -39.858  1.00 39.17           N  
+ANISOU  129  N   GLU A  18     4974   4558   5351    139    347    420       N  
+ATOM    130  CA  GLU A  18       0.027  -7.446 -40.834  1.00 45.65           C  
+ANISOU  130  CA  GLU A  18     5895   5385   6065    169    468    423       C  
+ATOM    131  C   GLU A  18      -0.549  -8.660 -40.120  1.00 42.91           C  
+ANISOU  131  C   GLU A  18     5658   5017   5628    157    500    342       C  
+ATOM    132  O   GLU A  18      -0.225  -8.908 -38.983  1.00 50.61           O  
+ANISOU  132  O   GLU A  18     6614   5981   6632    182    459    356       O  
+ATOM    133  CB  GLU A  18       1.373  -7.869 -41.404  1.00 49.05           C  
+ANISOU  133  CB  GLU A  18     6290   5791   6555    246    574    536       C  
+ATOM    134  CG  GLU A  18       1.849  -7.008 -42.535  1.00 63.96           C  
+ANISOU  134  CG  GLU A  18     8110   7691   8497    264    618    626       C  
+ATOM    135  CD  GLU A  18       3.316  -7.261 -42.867  1.00 75.06           C  
+ANISOU  135  CD  GLU A  18     9436   9075  10006    344    719    757       C  
+ATOM    136  OE1 GLU A  18       3.833  -8.337 -42.508  1.00 68.87           O  
+ANISOU  136  OE1 GLU A  18     8682   8270   9214    408    790    779       O  
+ATOM    137  OE2 GLU A  18       3.953  -6.347 -43.437  1.00 86.26           O  
+ANISOU  137  OE2 GLU A  18    10749  10492  11532    351    741    855       O  
+ATOM    138  N   CYS A  19      -1.403  -9.397 -40.824  1.00 42.18           N  
+ANISOU  138  N   CYS A  19     5675   4930   5420    110    580    258       N  
+ATOM    139  CA  CYS A  19      -2.100 -10.522 -40.319  1.00 42.40           C  
+ANISOU  139  CA  CYS A  19     5816   4913   5379     67    646    167       C  
+ATOM    140  C   CYS A  19      -2.196 -11.521 -41.439  1.00 43.87           C  
+ANISOU  140  C   CYS A  19     6123   5068   5477     29    804     99       C  
+ATOM    141  O   CYS A  19      -2.743 -11.212 -42.516  1.00 43.45           O  
+ANISOU  141  O   CYS A  19     6070   5112   5326    -37    790     39       O  
+ATOM    142  CB  CYS A  19      -3.524 -10.140 -39.826  1.00 45.74           C  
+ANISOU  142  CB  CYS A  19     6229   5397   5751    -23    551     67       C  
+ATOM    143  SG  CYS A  19      -4.342 -11.539 -39.028  1.00 48.02           S  
+ANISOU  143  SG  CYS A  19     6646   5607   5990    -86    658    -31       S  
+ATOM    144  N   ASP A  20      -1.642 -12.721 -41.204  1.00 45.23           N  
+ANISOU  144  N   ASP A  20     6401   5109   5674     79    970    112       N  
+ATOM    145  CA  ASP A  20      -1.719 -13.795 -42.164  1.00 47.07           C  
+ANISOU  145  CA  ASP A  20     6786   5262   5833     28   1168     13       C  
+ATOM    146  C   ASP A  20      -3.181 -14.220 -42.331  1.00 49.54           C  
+ANISOU  146  C   ASP A  20     7175   5613   6035   -157   1166   -185       C  
+ATOM    147  O   ASP A  20      -4.027 -14.046 -41.400  1.00 46.05           O  
+ANISOU  147  O   ASP A  20     6690   5197   5609   -209   1069   -212       O  
+ATOM    148  CB  ASP A  20      -0.895 -14.979 -41.694  1.00 49.71           C  
+ANISOU  148  CB  ASP A  20     7224   5411   6252    141   1380     87       C  
+ATOM    149  CG  ASP A  20       0.620 -14.723 -41.766  1.00 64.33           C  
+ANISOU  149  CG  ASP A  20     8980   7254   8207    325   1414    288       C  
+ATOM    150  OD1 ASP A  20       1.122 -14.017 -42.690  1.00 70.08           O  
+ANISOU  150  OD1 ASP A  20     9639   8062   8926    341   1382    324       O  
+ATOM    151  OD2 ASP A  20       1.349 -15.271 -40.906  1.00 70.59           O  
+ANISOU  151  OD2 ASP A  20     9755   7971   9092    465   1489    432       O  
+ATOM    152  N   ASP A  21      -3.481 -14.806 -43.487  1.00 48.95           N  
+ANISOU  152  N   ASP A  21     7206   5550   5840   -269   1280   -332       N  
+ATOM    153  CA  ASP A  21      -4.886 -15.213 -43.792  1.00 52.07           C  
+ANISOU  153  CA  ASP A  21     7641   6029   6111   -489   1264   -547       C  
+ATOM    154  C   ASP A  21      -5.466 -16.364 -42.961  1.00 52.45           C  
+ANISOU  154  C   ASP A  21     7805   5898   6224   -586   1412   -652       C  
+ATOM    155  O   ASP A  21      -6.664 -16.621 -43.076  1.00 52.58           O  
+ANISOU  155  O   ASP A  21     7815   5996   6165   -785   1384   -823       O  
+ATOM    156  CB  ASP A  21      -5.080 -15.591 -45.251  1.00 52.20           C  
+ANISOU  156  CB  ASP A  21     7744   6142   5948   -620   1342   -716       C  
+ATOM    157  CG  ASP A  21      -4.192 -16.744 -45.675  1.00 54.79           C  
+ANISOU  157  CG  ASP A  21     8277   6240   6300   -591   1630   -772       C  
+ATOM    158  OD1 ASP A  21      -3.555 -17.399 -44.833  1.00 60.00           O  
+ANISOU  158  OD1 ASP A  21     9012   6660   7124   -475   1787   -678       O  
+ATOM    159  OD2 ASP A  21      -4.087 -16.978 -46.873  1.00 66.22           O  
+ANISOU  159  OD2 ASP A  21     9812   7754   7595   -664   1713   -895       O  
+ATOM    160  N   ASP A  22      -4.630 -17.059 -42.198  1.00 51.20           N  
+ANISOU  160  N   ASP A  22     7738   5513   6200   -447   1580   -538       N  
+ATOM    161  CA  ASP A  22      -5.098 -18.073 -41.256  1.00 62.00           C  
+ANISOU  161  CA  ASP A  22     9213   6691   7651   -492   1741   -573       C  
+ATOM    162  C   ASP A  22      -4.625 -17.744 -39.835  1.00 59.08           C  
+ANISOU  162  C   ASP A  22     8764   6294   7388   -296   1668   -346       C  
+ATOM    163  O   ASP A  22      -4.377 -18.629 -39.062  1.00 56.04           O  
+ANISOU  163  O   ASP A  22     8478   5724   7088   -214   1851   -264       O  
+ATOM    164  CB  ASP A  22      -4.624 -19.475 -41.714  1.00 71.33           C  
+ANISOU  164  CB  ASP A  22    10621   7602   8879   -509   2084   -652       C  
+ATOM    165  CG  ASP A  22      -3.093 -19.576 -41.838  1.00 78.00           C  
+ANISOU  165  CG  ASP A  22    11493   8340   9802   -254   2204   -448       C  
+ATOM    166  OD1 ASP A  22      -2.396 -18.536 -41.681  1.00 74.86           O  
+ANISOU  166  OD1 ASP A  22    10926   8102   9414   -102   2002   -272       O  
+ATOM    167  OD2 ASP A  22      -2.585 -20.697 -42.122  1.00 91.53           O  
+ANISOU  167  OD2 ASP A  22    13392   9804  11580   -212   2520   -469       O  
+ATOM    168  N   VAL A  23      -4.476 -16.441 -39.535  1.00 58.45           N  
+ANISOU  168  N   VAL A  23     8509   6406   7294   -222   1411   -246       N  
+ATOM    169  CA  VAL A  23      -4.282 -15.937 -38.191  1.00 51.17           C  
+ANISOU  169  CA  VAL A  23     7497   5521   6421    -98   1292    -96       C  
+ATOM    170  C   VAL A  23      -5.446 -15.027 -37.852  1.00 51.90           C  
+ANISOU  170  C   VAL A  23     7486   5767   6465   -205   1102   -177       C  
+ATOM    171  O   VAL A  23      -5.900 -14.243 -38.695  1.00 52.44           O  
+ANISOU  171  O   VAL A  23     7471   5973   6479   -287    979   -252       O  
+ATOM    172  CB  VAL A  23      -2.914 -15.295 -38.008  1.00 53.75           C  
+ANISOU  172  CB  VAL A  23     7721   5902   6798     84   1203     86       C  
+ATOM    173  CG1 VAL A  23      -2.782 -14.588 -36.643  1.00 51.64           C  
+ANISOU  173  CG1 VAL A  23     7345   5733   6542    168   1034    192       C  
+ATOM    174  CG2 VAL A  23      -1.872 -16.402 -38.166  1.00 52.89           C  
+ANISOU  174  CG2 VAL A  23     7712   5629   6754    219   1438    196       C  
+ATOM    175  N   TYR A  24      -6.020 -15.271 -36.669  1.00 49.92           N  
+ANISOU  175  N   TYR A  24     7252   5485   6229   -201   1118   -157       N  
+ATOM    176  CA  TYR A  24      -7.105 -14.448 -36.120  1.00 49.53           C  
+ANISOU  176  CA  TYR A  24     7107   5561   6149   -269    973   -210       C  
+ATOM    177  C   TYR A  24      -6.598 -13.062 -35.859  1.00 47.90           C  
+ANISOU  177  C   TYR A  24     6775   5477   5946   -174    771   -133       C  
+ATOM    178  O   TYR A  24      -5.530 -12.835 -35.330  1.00 50.41           O  
+ANISOU  178  O   TYR A  24     7074   5788   6289    -48    730    -19       O  
+ATOM    179  CB  TYR A  24      -7.664 -14.995 -34.792  1.00 53.15           C  
+ANISOU  179  CB  TYR A  24     7622   5953   6617   -252   1058   -177       C  
+ATOM    180  CG  TYR A  24      -8.368 -16.349 -34.905  1.00 54.34           C  
+ANISOU  180  CG  TYR A  24     7898   5950   6796   -378   1291   -263       C  
+ATOM    181  CD1 TYR A  24      -9.369 -16.572 -35.863  1.00 61.64           C  
+ANISOU  181  CD1 TYR A  24     8804   6911   7703   -595   1316   -450       C  
+ATOM    182  CD2 TYR A  24      -8.040 -17.385 -34.061  1.00 58.06           C  
+ANISOU  182  CD2 TYR A  24     8497   6251   7312   -288   1491   -157       C  
+ATOM    183  CE1 TYR A  24     -10.013 -17.808 -35.976  1.00 67.82           C  
+ANISOU  183  CE1 TYR A  24     9699   7540   8526   -757   1542   -565       C  
+ATOM    184  CE2 TYR A  24      -8.691 -18.634 -34.147  1.00 67.21           C  
+ANISOU  184  CE2 TYR A  24     9786   7220   8531   -418   1749   -239       C  
+ATOM    185  CZ  TYR A  24      -9.672 -18.847 -35.099  1.00 67.30           C  
+ANISOU  185  CZ  TYR A  24     9783   7246   8539   -670   1777   -462       C  
+ATOM    186  OH  TYR A  24     -10.304 -20.070 -35.167  1.00 63.80           O  
+ANISOU  186  OH  TYR A  24     9466   6605   8170   -838   2041   -574       O  
+ATOM    187  N   VAL A  25      -7.413 -12.132 -36.241  1.00 46.10           N  
+ANISOU  187  N   VAL A  25     6451   5365   5699   -242    655   -197       N  
+ATOM    188  CA  VAL A  25      -7.117 -10.720 -36.104  1.00 50.13           C  
+ANISOU  188  CA  VAL A  25     6853   5957   6237   -176    500   -147       C  
+ATOM    189  C   VAL A  25      -6.622 -10.284 -34.725  1.00 49.08           C  
+ANISOU  189  C   VAL A  25     6716   5810   6120    -88    441    -91       C  
+ATOM    190  O   VAL A  25      -5.602  -9.548 -34.601  1.00 48.80           O  
+ANISOU  190  O   VAL A  25     6626   5791   6124    -25    355    -35       O  
+ATOM    191  CB  VAL A  25      -8.407 -10.000 -36.511  1.00 51.95           C  
+ANISOU  191  CB  VAL A  25     6992   6297   6447   -248    438   -209       C  
+ATOM    192  CG1 VAL A  25      -8.482  -8.654 -35.889  1.00 65.37           C  
+ANISOU  192  CG1 VAL A  25     8616   8025   8196   -180    340   -173       C  
+ATOM    193  CG2 VAL A  25      -8.427  -9.935 -38.033  1.00 52.86           C  
+ANISOU  193  CG2 VAL A  25     7066   6496   6519   -296    427   -223       C  
+ATOM    194  N   LEU A  26      -7.301 -10.753 -33.681  1.00 50.45           N  
+ANISOU  194  N   LEU A  26     6944   5968   6256    -94    493   -112       N  
+ATOM    195  CA  LEU A  26      -6.902 -10.450 -32.290  1.00 47.33           C  
+ANISOU  195  CA  LEU A  26     6562   5597   5822    -14    443    -70       C  
+ATOM    196  C   LEU A  26      -5.498 -10.894 -32.035  1.00 47.06           C  
+ANISOU  196  C   LEU A  26     6537   5561   5781     82    436     39       C  
+ATOM    197  O   LEU A  26      -4.721 -10.194 -31.377  1.00 55.96           O  
+ANISOU  197  O   LEU A  26     7607   6768   6888    128    318     66       O  
+ATOM    198  CB  LEU A  26      -7.807 -11.200 -31.306  1.00 53.55           C  
+ANISOU  198  CB  LEU A  26     7430   6361   6552    -20    551    -79       C  
+ATOM    199  CG  LEU A  26      -7.510 -11.161 -29.801  1.00 55.13           C  
+ANISOU  199  CG  LEU A  26     7674   6613   6658     70    531    -26       C  
+ATOM    200  CD1 LEU A  26      -7.326  -9.720 -29.325  1.00 55.80           C  
+ANISOU  200  CD1 LEU A  26     7695   6785   6722     71    376    -95       C  
+ATOM    201  CD2 LEU A  26      -8.666 -11.852 -29.032  1.00 61.83           C  
+ANISOU  201  CD2 LEU A  26     8600   7428   7463     52    674    -33       C  
+ATOM    202  N   ASP A  27      -5.136 -12.069 -32.524  1.00 48.10           N  
+ANISOU  202  N   ASP A  27     6734   5609   5931    111    571    103       N  
+ATOM    203  CA  ASP A  27      -3.762 -12.587 -32.305  1.00 48.67           C  
+ANISOU  203  CA  ASP A  27     6797   5686   6010    241    594    251       C  
+ATOM    204  C   ASP A  27      -2.701 -11.814 -33.061  1.00 52.00           C  
+ANISOU  204  C   ASP A  27     7102   6157   6495    255    488    280       C  
+ATOM    205  O   ASP A  27      -1.619 -11.648 -32.546  1.00 53.84           O  
+ANISOU  205  O   ASP A  27     7252   6477   6725    342    414    382       O  
+ATOM    206  CB  ASP A  27      -3.670 -14.036 -32.649  1.00 52.61           C  
+ANISOU  206  CB  ASP A  27     7412   6040   6537    286    816    319       C  
+ATOM    207  CG  ASP A  27      -4.541 -14.887 -31.735  1.00 63.74           C  
+ANISOU  207  CG  ASP A  27     8934   7380   7902    289    954    332       C  
+ATOM    208  OD1 ASP A  27      -4.580 -14.630 -30.517  1.00 77.44           O  
+ANISOU  208  OD1 ASP A  27    10653   9216   9555    357    885    392       O  
+ATOM    209  OD2 ASP A  27      -5.228 -15.780 -32.232  1.00 66.17           O  
+ANISOU  209  OD2 ASP A  27     9347   7539   8253    206   1139    268       O  
+ATOM    210  N   ALA A  28      -3.001 -11.313 -34.254  1.00 45.81           N  
+ANISOU  210  N   ALA A  28     6297   5346   5763    172    479    200       N  
+ATOM    211  CA  ALA A  28      -2.052 -10.414 -34.921  1.00 45.27           C  
+ANISOU  211  CA  ALA A  28     6113   5321   5765    182    389    237       C  
+ATOM    212  C   ALA A  28      -1.874  -9.098 -34.113  1.00 46.76           C  
+ANISOU  212  C   ALA A  28     6200   5596   5968    152    219    201       C  
+ATOM    213  O   ALA A  28      -0.782  -8.604 -33.981  1.00 48.55           O  
+ANISOU  213  O   ALA A  28     6321   5880   6246    174    142    256       O  
+ATOM    214  CB  ALA A  28      -2.542 -10.096 -36.327  1.00 48.41           C  
+ANISOU  214  CB  ALA A  28     6517   5691   6185    111    423    175       C  
+ATOM    215  N   ALA A  29      -2.956  -8.547 -33.566  1.00 46.59           N  
+ANISOU  215  N   ALA A  29     6211   5582   5908     92    177     97       N  
+ATOM    216  CA  ALA A  29      -2.882  -7.367 -32.726  1.00 45.37           C  
+ANISOU  216  CA  ALA A  29     6001   5476   5760     54     58     28       C  
+ATOM    217  C   ALA A  29      -2.026  -7.590 -31.506  1.00 45.22           C  
+ANISOU  217  C   ALA A  29     5953   5566   5662     94    -18     63       C  
+ATOM    218  O   ALA A  29      -1.157  -6.760 -31.212  1.00 50.70           O  
+ANISOU  218  O   ALA A  29     6546   6327   6390     54   -129     39       O  
+ATOM    219  CB  ALA A  29      -4.268  -6.959 -32.299  1.00 45.68           C  
+ANISOU  219  CB  ALA A  29     6100   5492   5763     13     76    -74       C  
+ATOM    220  N   GLU A  30      -2.244  -8.704 -30.827  1.00 46.60           N  
+ANISOU  220  N   GLU A  30     6205   5769   5730    169     45    128       N  
+ATOM    221  CA  GLU A  30      -1.476  -9.042 -29.636  1.00 54.76           C  
+ANISOU  221  CA  GLU A  30     7206   6952   6646    243    -23    204       C  
+ATOM    222  C   GLU A  30      -0.050  -9.331 -29.955  1.00 54.21           C  
+ANISOU  222  C   GLU A  30     7010   6960   6626    313    -60    346       C  
+ATOM    223  O   GLU A  30       0.832  -8.823 -29.288  1.00 59.88           O  
+ANISOU  223  O   GLU A  30     7604   7849   7298    302   -205    354       O  
+ATOM    224  CB  GLU A  30      -2.122 -10.183 -28.852  1.00 62.74           C  
+ANISOU  224  CB  GLU A  30     8340   7960   7538    332     92    277       C  
+ATOM    225  CG  GLU A  30      -3.470  -9.711 -28.236  1.00 72.73           C  
+ANISOU  225  CG  GLU A  30     9697   9198   8739    260    106    134       C  
+ATOM    226  CD  GLU A  30      -4.256 -10.812 -27.471  1.00 84.77           C  
+ANISOU  226  CD  GLU A  30    11347  10699  10163    332    254    206       C  
+ATOM    227  OE1 GLU A  30      -4.358 -12.001 -27.950  1.00 80.99           O  
+ANISOU  227  OE1 GLU A  30    10932  10104   9737    384    419    311       O  
+ATOM    228  OE2 GLU A  30      -4.785 -10.463 -26.379  1.00 83.98           O  
+ANISOU  228  OE2 GLU A  30    11293  10681   9934    331    227    148       O  
+ATOM    229  N   GLU A  31       0.205 -10.073 -31.022  1.00 59.74           N  
+ANISOU  229  N   GLU A  31     7727   7548   7422    373     72    443       N  
+ATOM    230  CA  GLU A  31       1.574 -10.229 -31.535  1.00 56.65           C  
+ANISOU  230  CA  GLU A  31     7199   7210   7113    445     65    582       C  
+ATOM    231  C   GLU A  31       2.251  -8.862 -31.829  1.00 60.42           C  
+ANISOU  231  C   GLU A  31     7517   7754   7685    328    -85    505       C  
+ATOM    232  O   GLU A  31       3.462  -8.797 -31.776  1.00 66.23           O  
+ANISOU  232  O   GLU A  31     8087   8613   8463    365   -148    610       O  
+ATOM    233  CB  GLU A  31       1.536 -11.120 -32.771  1.00 64.23           C  
+ANISOU  233  CB  GLU A  31     8243   7999   8160    503    266    648       C  
+ATOM    234  CG  GLU A  31       2.861 -11.443 -33.471  1.00 83.36           C  
+ANISOU  234  CG  GLU A  31    10552  10436  10682    606    328    807       C  
+ATOM    235  CD  GLU A  31       3.790 -12.366 -32.669  1.00102.53           C  
+ANISOU  235  CD  GLU A  31    12908  12976  13073    795    367   1027       C  
+ATOM    236  OE1 GLU A  31       3.385 -12.888 -31.594  1.00113.60           O  
+ANISOU  236  OE1 GLU A  31    14374  14434  14355    859    366   1071       O  
+ATOM    237  OE2 GLU A  31       4.946 -12.569 -33.121  1.00102.50           O  
+ANISOU  237  OE2 GLU A  31    12770  13016  13158    898    412   1182       O  
+ATOM    238  N   ALA A  32       1.502  -7.778 -32.127  1.00 57.02           N  
+ANISOU  238  N   ALA A  32     7120   7240   7301    193   -126    339       N  
+ATOM    239  CA  ALA A  32       2.120  -6.424 -32.410  1.00 59.13           C  
+ANISOU  239  CA  ALA A  32     7255   7519   7692     72   -224    265       C  
+ATOM    240  C   ALA A  32       2.224  -5.516 -31.195  1.00 56.89           C  
+ANISOU  240  C   ALA A  32     6920   7349   7346    -41   -378    124       C  
+ATOM    241  O   ALA A  32       2.754  -4.418 -31.279  1.00 65.02           O  
+ANISOU  241  O   ALA A  32     7847   8373   8484   -167   -445     38       O  
+ATOM    242  CB  ALA A  32       1.377  -5.676 -33.506  1.00 53.40           C  
+ANISOU  242  CB  ALA A  32     6588   6626   7076     11   -149    198       C  
+ATOM    243  N   GLY A  33       1.719  -5.987 -30.077  1.00 55.56           N  
+ANISOU  243  N   GLY A  33     6832   7274   7000     -3   -414     93       N  
+ATOM    244  CA  GLY A  33       1.778  -5.257 -28.849  1.00 58.15           C  
+ANISOU  244  CA  GLY A  33     7139   7738   7215   -106   -550    -56       C  
+ATOM    245  C   GLY A  33       0.546  -4.451 -28.568  1.00 60.01           C  
+ANISOU  245  C   GLY A  33     7520   7837   7440   -190   -508   -246       C  
+ATOM    246  O   GLY A  33       0.620  -3.573 -27.741  1.00 69.82           O  
+ANISOU  246  O   GLY A  33     8758   9139   8629   -310   -593   -418       O  
+ATOM    247  N   ILE A  34      -0.586  -4.759 -29.224  1.00 61.19           N  
+ANISOU  247  N   ILE A  34     7792   7820   7635   -130   -370   -221       N  
+ATOM    248  CA  ILE A  34      -1.840  -4.017 -29.038  1.00 58.99           C  
+ANISOU  248  CA  ILE A  34     7627   7418   7368   -177   -306   -363       C  
+ATOM    249  C   ILE A  34      -2.765  -4.786 -28.094  1.00 62.15           C  
+ANISOU  249  C   ILE A  34     8149   7876   7589   -107   -261   -367       C  
+ATOM    250  O   ILE A  34      -3.056  -5.947 -28.343  1.00 63.50           O  
+ANISOU  250  O   ILE A  34     8359   8045   7721    -13   -186   -239       O  
+ATOM    251  CB  ILE A  34      -2.577  -3.788 -30.390  1.00 61.05           C  
+ANISOU  251  CB  ILE A  34     7905   7506   7785   -156   -191   -318       C  
+ATOM    252  CG1 ILE A  34      -1.701  -2.983 -31.385  1.00 65.68           C  
+ANISOU  252  CG1 ILE A  34     8381   8020   8552   -209   -201   -287       C  
+ATOM    253  CG2 ILE A  34      -3.890  -3.029 -30.184  1.00 63.01           C  
+ANISOU  253  CG2 ILE A  34     8237   7650   8053   -172   -114   -425       C  
+ATOM    254  CD1 ILE A  34      -2.244  -3.016 -32.822  1.00 63.49           C  
+ANISOU  254  CD1 ILE A  34     8108   7640   8375   -155    -98   -187       C  
+ATOM    255  N   ASP A  35      -3.244  -4.141 -27.030  1.00 63.70           N  
+ANISOU  255  N   ASP A  35     8416   8102   7682   -158   -279   -520       N  
+ATOM    256  CA  ASP A  35      -4.088  -4.826 -26.025  1.00 71.88           C  
+ANISOU  256  CA  ASP A  35     9569   9207   8534    -87   -222   -516       C  
+ATOM    257  C   ASP A  35      -5.567  -4.529 -26.307  1.00 64.56           C  
+ANISOU  257  C   ASP A  35     8725   8121   7682    -77    -78   -569       C  
+ATOM    258  O   ASP A  35      -6.130  -3.476 -25.980  1.00 66.69           O  
+ANISOU  258  O   ASP A  35     9040   8315   7983   -127    -40   -715       O  
+ATOM    259  CB  ASP A  35      -3.667  -4.503 -24.557  1.00 81.85           C  
+ANISOU  259  CB  ASP A  35    10862  10660   9575   -127   -325   -633       C  
+ATOM    260  CG  ASP A  35      -2.375  -5.278 -24.107  1.00 93.06           C  
+ANISOU  260  CG  ASP A  35    12178  12327  10851    -75   -463   -497       C  
+ATOM    261  OD1 ASP A  35      -2.081  -6.430 -24.567  1.00 87.57           O  
+ANISOU  261  OD1 ASP A  35    11448  11645  10179     50   -418   -279       O  
+ATOM    262  OD2 ASP A  35      -1.644  -4.712 -23.270  1.00100.07           O  
+ANISOU  262  OD2 ASP A  35    13013  13406  11600   -161   -608   -613       O  
+ATOM    263  N   LEU A  36      -6.157  -5.471 -27.010  1.00 61.66           N  
+ANISOU  263  N   LEU A  36     8363   7702   7362    -15     12   -444       N  
+ATOM    264  CA  LEU A  36      -7.533  -5.410 -27.353  1.00 56.54           C  
+ANISOU  264  CA  LEU A  36     7746   6959   6776     -6    133   -460       C  
+ATOM    265  C   LEU A  36      -8.274  -6.365 -26.418  1.00 58.83           C  
+ANISOU  265  C   LEU A  36     8125   7301   6925     45    227   -424       C  
+ATOM    266  O   LEU A  36      -7.725  -7.349 -25.938  1.00 59.91           O  
+ANISOU  266  O   LEU A  36     8295   7514   6951     96    222   -330       O  
+ATOM    267  CB  LEU A  36      -7.736  -5.829 -28.796  1.00 54.45           C  
+ANISOU  267  CB  LEU A  36     7414   6631   6642     -9    169   -370       C  
+ATOM    268  CG  LEU A  36      -7.150  -4.979 -29.924  1.00 47.43           C  
+ANISOU  268  CG  LEU A  36     6437   5685   5896    -39    115   -362       C  
+ATOM    269  CD1 LEU A  36      -7.661  -5.559 -31.235  1.00 51.84           C  
+ANISOU  269  CD1 LEU A  36     6953   6228   6515    -36    168   -282       C  
+ATOM    270  CD2 LEU A  36      -7.550  -3.538 -29.754  1.00 48.45           C  
+ANISOU  270  CD2 LEU A  36     6559   5739   6109    -60    130   -458       C  
+ATOM    271  N   PRO A  37      -9.531  -6.066 -26.158  1.00 52.73           N  
+ANISOU  271  N   PRO A  37     7383   6483   6166     48    333   -477       N  
+ATOM    272  CA  PRO A  37     -10.189  -6.803 -25.130  1.00 48.70           C  
+ANISOU  272  CA  PRO A  37     6960   6021   5521     94    437   -451       C  
+ATOM    273  C   PRO A  37     -10.579  -8.210 -25.630  1.00 53.45           C  
+ANISOU  273  C   PRO A  37     7558   6591   6158    100    538   -324       C  
+ATOM    274  O   PRO A  37     -10.785  -8.477 -26.837  1.00 49.32           O  
+ANISOU  274  O   PRO A  37     6961   6008   5767     49    548   -299       O  
+ATOM    275  CB  PRO A  37     -11.382  -5.907 -24.791  1.00 51.49           C  
+ANISOU  275  CB  PRO A  37     7326   6326   5910     95    535   -549       C  
+ATOM    276  CG  PRO A  37     -11.712  -5.243 -26.095  1.00 57.15           C  
+ANISOU  276  CG  PRO A  37     7927   6960   6825     64    521   -547       C  
+ATOM    277  CD  PRO A  37     -10.372  -5.018 -26.760  1.00 53.71           C  
+ANISOU  277  CD  PRO A  37     7454   6520   6432     30    379   -538       C  
+ATOM    278  N   TYR A  38     -10.593  -9.140 -24.698  1.00 52.25           N  
+ANISOU  278  N   TYR A  38     7494   6481   5875    159    621   -244       N  
+ATOM    279  CA  TYR A  38     -11.096 -10.464 -24.975  1.00 58.41           C  
+ANISOU  279  CA  TYR A  38     8299   7189   6702    152    774   -141       C  
+ATOM    280  C   TYR A  38     -11.545 -11.071 -23.664  1.00 53.69           C  
+ANISOU  280  C   TYR A  38     7809   6629   5962    227    913    -70       C  
+ATOM    281  O   TYR A  38     -11.164 -10.607 -22.581  1.00 52.44           O  
+ANISOU  281  O   TYR A  38     7709   6589   5625    301    856    -82       O  
+ATOM    282  CB  TYR A  38      -9.996 -11.359 -25.594  1.00 54.60           C  
+ANISOU  282  CB  TYR A  38     7821   6673   6252    178    755    -32       C  
+ATOM    283  CG  TYR A  38      -8.783 -11.536 -24.706  1.00 57.28           C  
+ANISOU  283  CG  TYR A  38     8199   7128   6435    299    678     70       C  
+ATOM    284  CD1 TYR A  38      -7.743 -10.603 -24.717  1.00 57.17           C  
+ANISOU  284  CD1 TYR A  38     8115   7222   6383    300    480     19       C  
+ATOM    285  CD2 TYR A  38      -8.679 -12.620 -23.830  1.00 58.87           C  
+ANISOU  285  CD2 TYR A  38     8492   7349   6525    412    809    232       C  
+ATOM    286  CE1 TYR A  38      -6.629 -10.752 -23.885  1.00 58.64           C  
+ANISOU  286  CE1 TYR A  38     8295   7574   6409    396    383    113       C  
+ATOM    287  CE2 TYR A  38      -7.565 -12.768 -22.985  1.00 62.26           C  
+ANISOU  287  CE2 TYR A  38     8928   7946   6781    545    722    358       C  
+ATOM    288  CZ  TYR A  38      -6.548 -11.837 -23.032  1.00 65.34           C  
+ANISOU  288  CZ  TYR A  38     9221   8482   7122    529    494    291       C  
+ATOM    289  OH  TYR A  38      -5.464 -11.963 -22.208  1.00 72.76           O  
+ANISOU  289  OH  TYR A  38    10126   9640   7877    642    383    410       O  
+ATOM    290  N   SER A  39     -12.264 -12.176 -23.765  1.00 51.04           N  
+ANISOU  290  N   SER A  39     7503   6198   5691    202   1102      6       N  
+ATOM    291  CA  SER A  39     -12.527 -12.977 -22.556  1.00 52.70           C  
+ANISOU  291  CA  SER A  39     7828   6422   5770    296   1272    129       C  
+ATOM    292  C   SER A  39     -12.586 -14.475 -22.933  1.00 54.61           C  
+ANISOU  292  C   SER A  39     8121   6509   6118    281   1473    260       C  
+ATOM    293  O   SER A  39     -11.697 -15.196 -22.537  1.00 53.05           O  
+ANISOU  293  O   SER A  39     8000   6314   5842    404   1512    419       O  
+ATOM    294  CB  SER A  39     -13.752 -12.480 -21.786  1.00 50.63           C  
+ANISOU  294  CB  SER A  39     7578   6193   5466    287   1376     64       C  
+ATOM    295  OG  SER A  39     -14.134 -13.447 -20.804  1.00 57.92           O  
+ANISOU  295  OG  SER A  39     8608   7099   6297    364   1590    207       O  
+ATOM    296  N   CYS A  40     -13.549 -14.932 -23.748  1.00 56.78           N  
+ANISOU  296  N   CYS A  40     8345   6653   6573    127   1599    192       N  
+ATOM    297  CA  CYS A  40     -13.634 -16.380 -24.053  1.00 57.29           C  
+ANISOU  297  CA  CYS A  40     8484   6532   6749     80   1830    281       C  
+ATOM    298  C   CYS A  40     -12.645 -16.882 -25.075  1.00 62.77           C  
+ANISOU  298  C   CYS A  40     9192   7133   7523     67   1795    293       C  
+ATOM    299  O   CYS A  40     -12.278 -18.037 -25.022  1.00 51.88           O  
+ANISOU  299  O   CYS A  40     7920   5599   6191    114   1993    416       O  
+ATOM    300  CB  CYS A  40     -15.040 -16.831 -24.469  1.00 62.61           C  
+ANISOU  300  CB  CYS A  40     9100   7107   7580   -117   2004    185       C  
+ATOM    301  SG  CYS A  40     -15.521 -16.463 -26.163  1.00 58.56           S  
+ANISOU  301  SG  CYS A  40     8420   6610   7220   -350   1871    -25       S  
+ATOM    302  N   ARG A  41     -12.233 -16.028 -26.016  1.00 58.00           N  
+ANISOU  302  N   ARG A  41     8486   6605   6943     14   1575    175       N  
+ATOM    303  CA  ARG A  41     -11.317 -16.429 -27.087  1.00 58.21           C  
+ANISOU  303  CA  ARG A  41     8521   6552   7044     -1   1548    175       C  
+ATOM    304  C   ARG A  41     -11.821 -17.585 -27.916  1.00 57.79           C  
+ANISOU  304  C   ARG A  41     8520   6302   7133   -157   1766    115       C  
+ATOM    305  O   ARG A  41     -10.986 -18.264 -28.554  1.00 62.26           O  
+ANISOU  305  O   ARG A  41     9155   6747   7753   -132   1841    152       O  
+ATOM    306  CB  ARG A  41      -9.896 -16.763 -26.569  1.00 63.76           C  
+ANISOU  306  CB  ARG A  41     9289   7271   7664    206   1533    363       C  
+ATOM    307  CG  ARG A  41      -9.186 -15.605 -25.856  1.00 71.18           C  
+ANISOU  307  CG  ARG A  41    10160   8433   8450    322   1286    387       C  
+ATOM    308  CD  ARG A  41      -7.710 -15.932 -25.568  1.00 80.04           C  
+ANISOU  308  CD  ARG A  41    11287   9622   9501    504   1238    570       C  
+ATOM    309  NE  ARG A  41      -6.866 -15.741 -26.762  1.00 74.82           N  
+ANISOU  309  NE  ARG A  41    10554   8929   8944    472   1145    532       N  
+ATOM    310  CZ  ARG A  41      -6.356 -14.582 -27.159  1.00 78.00           C  
+ANISOU  310  CZ  ARG A  41    10841   9455   9338    432    915    438       C  
+ATOM    311  NH1 ARG A  41      -6.552 -13.467 -26.465  1.00 81.90           N  
+ANISOU  311  NH1 ARG A  41    11285  10102   9732    410    749    350       N  
+ATOM    312  NH2 ARG A  41      -5.644 -14.532 -28.265  1.00 86.15           N  
+ANISOU  312  NH2 ARG A  41    11819  10442  10471    411    877    426       N  
+ATOM    313  N   ALA A  42     -13.150 -17.769 -27.971  1.00 49.81           N  
+ANISOU  313  N   ALA A  42     7468   5266   6189   -330   1868      7       N  
+ATOM    314  CA  ALA A  42     -13.768 -18.969 -28.558  1.00 57.96           C  
+ANISOU  314  CA  ALA A  42     8557   6104   7358   -524   2112    -75       C  
+ATOM    315  C   ALA A  42     -14.982 -18.639 -29.396  1.00 57.37           C  
+ANISOU  315  C   ALA A  42     8326   6129   7343   -777   2047   -282       C  
+ATOM    316  O   ALA A  42     -15.740 -19.526 -29.769  1.00 64.20           O  
+ANISOU  316  O   ALA A  42     9200   6877   8312   -990   2234   -389       O  
+ATOM    317  CB  ALA A  42     -14.200 -19.960 -27.458  1.00 56.47           C  
+ANISOU  317  CB  ALA A  42     8492   5754   7210   -482   2407     58       C  
+ATOM    318  N   GLY A  43     -15.231 -17.380 -29.635  1.00 52.54           N  
+ANISOU  318  N   GLY A  43     7557   5735   6668   -759   1799   -332       N  
+ATOM    319  CA  GLY A  43     -16.309 -17.040 -30.539  1.00 57.00           C  
+ANISOU  319  CA  GLY A  43     7941   6441   7273   -969   1718   -492       C  
+ATOM    320  C   GLY A  43     -17.684 -17.010 -29.934  1.00 58.47           C  
+ANISOU  320  C   GLY A  43     8011   6695   7510  -1063   1807   -511       C  
+ATOM    321  O   GLY A  43     -18.693 -16.858 -30.667  1.00 64.87           O  
+ANISOU  321  O   GLY A  43     8633   7651   8361  -1255   1754   -633       O  
+ATOM    322  N   SER A  44     -17.753 -17.088 -28.605  1.00 57.15           N  
+ANISOU  322  N   SER A  44     7930   6459   7323   -922   1931   -379       N  
+ATOM    323  CA  SER A  44     -19.057 -17.205 -27.927  1.00 59.58           C  
+ANISOU  323  CA  SER A  44     8144   6800   7692  -1003   2076   -376       C  
+ATOM    324  C   SER A  44     -19.354 -16.159 -26.851  1.00 51.98           C  
+ANISOU  324  C   SER A  44     7145   5959   6646   -810   2021   -281       C  
+ATOM    325  O   SER A  44     -20.144 -16.385 -25.952  1.00 58.60           O  
+ANISOU  325  O   SER A  44     7978   6778   7507   -803   2197   -224       O  
+ATOM    326  CB  SER A  44     -19.181 -18.630 -27.398  1.00 63.12           C  
+ANISOU  326  CB  SER A  44     8744   7008   8228  -1080   2393   -327       C  
+ATOM    327  OG  SER A  44     -17.989 -18.942 -26.691  1.00 75.47           O  
+ANISOU  327  OG  SER A  44    10522   8441   9708   -852   2451   -164       O  
+ATOM    328  N   CYS A  45     -18.785 -14.973 -26.981  1.00 51.65           N  
+ANISOU  328  N   CYS A  45     7072   6035   6515   -668   1796   -279       N  
+ATOM    329  CA  CYS A  45     -19.057 -13.886 -26.059  1.00 48.70           C  
+ANISOU  329  CA  CYS A  45     6677   5758   6066   -505   1753   -233       C  
+ATOM    330  C   CYS A  45     -18.893 -12.526 -26.780  1.00 51.12           C  
+ANISOU  330  C   CYS A  45     6860   6197   6366   -453   1521   -288       C  
+ATOM    331  O   CYS A  45     -18.542 -12.469 -27.980  1.00 50.80           O  
+ANISOU  331  O   CYS A  45     6749   6191   6360   -532   1388   -341       O  
+ATOM    332  CB  CYS A  45     -18.171 -13.989 -24.805  1.00 50.90           C  
+ANISOU  332  CB  CYS A  45     7166   5974   6198   -316   1803   -126       C  
+ATOM    333  SG  CYS A  45     -16.566 -13.180 -24.911  1.00 53.21           S  
+ANISOU  333  SG  CYS A  45     7542   6307   6366   -170   1556   -124       S  
+ATOM    334  N   SER A  46     -19.167 -11.439 -26.041  1.00 49.49           N  
+ANISOU  334  N   SER A  46     6640   6051   6114   -315   1503   -272       N  
+ATOM    335  CA  SER A  46     -19.261 -10.117 -26.614  1.00 49.31           C  
+ANISOU  335  CA  SER A  46     6494   6118   6122   -255   1356   -304       C  
+ATOM    336  C   SER A  46     -17.941  -9.369 -26.532  1.00 51.44           C  
+ANISOU  336  C   SER A  46     6888   6345   6311   -146   1208   -320       C  
+ATOM    337  O   SER A  46     -17.854  -8.268 -27.057  1.00 46.10           O  
+ANISOU  337  O   SER A  46     6135   5703   5678    -96   1103   -340       O  
+ATOM    338  CB  SER A  46     -20.298  -9.307 -25.871  1.00 50.14           C  
+ANISOU  338  CB  SER A  46     6525   6274   6249   -163   1462   -287       C  
+ATOM    339  OG  SER A  46     -19.973  -9.287 -24.483  1.00 49.91           O  
+ANISOU  339  OG  SER A  46     6691   6179   6094    -53   1563   -278       O  
+ATOM    340  N   SER A  47     -16.939  -9.950 -25.880  1.00 47.00           N  
+ANISOU  340  N   SER A  47     6498   5716   5641   -106   1210   -297       N  
+ATOM    341  CA  SER A  47     -15.839  -9.148 -25.341  1.00 48.40           C  
+ANISOU  341  CA  SER A  47     6782   5891   5713     -1   1093   -321       C  
+ATOM    342  C   SER A  47     -14.884  -8.548 -26.378  1.00 50.48           C  
+ANISOU  342  C   SER A  47     6998   6152   6030    -15    913   -348       C  
+ATOM    343  O   SER A  47     -14.433  -7.391 -26.220  1.00 42.86           O  
+ANISOU  343  O   SER A  47     6043   5186   5055     37    827   -403       O  
+ATOM    344  CB  SER A  47     -15.052  -9.964 -24.335  1.00 48.69           C  
+ANISOU  344  CB  SER A  47     6981   5919   5599     58   1134   -259       C  
+ATOM    345  OG  SER A  47     -14.042  -9.148 -23.822  1.00 52.57           O  
+ANISOU  345  OG  SER A  47     7538   6457   5976    130    999   -303       O  
+ATOM    346  N   CYS A  48     -14.586  -9.312 -27.432  1.00 46.30           N  
+ANISOU  346  N   CYS A  48     6426   5606   5560    -94    881   -320       N  
+ATOM    347  CA  CYS A  48     -13.615  -8.899 -28.445  1.00 43.55           C  
+ANISOU  347  CA  CYS A  48     6042   5255   5251   -102    736   -325       C  
+ATOM    348  C   CYS A  48     -14.282  -8.264 -29.667  1.00 48.19           C  
+ANISOU  348  C   CYS A  48     6472   5897   5938   -148    687   -339       C  
+ATOM    349  O   CYS A  48     -13.664  -8.163 -30.727  1.00 52.68           O  
+ANISOU  349  O   CYS A  48     7001   6475   6538   -171    598   -328       O  
+ATOM    350  CB  CYS A  48     -12.855 -10.140 -28.938  1.00 45.20           C  
+ANISOU  350  CB  CYS A  48     6309   5413   5450   -145    755   -280       C  
+ATOM    351  SG  CYS A  48     -13.936 -11.252 -29.869  1.00 49.74           S  
+ANISOU  351  SG  CYS A  48     6821   5979   6098   -304    875   -312       S  
+ATOM    352  N   ALA A  49     -15.536  -7.842 -29.543  1.00 49.80           N  
+ANISOU  352  N   ALA A  49     6575   6158   6186   -145    751   -344       N  
+ATOM    353  CA  ALA A  49     -16.294  -7.449 -30.708  1.00 48.00           C  
+ANISOU  353  CA  ALA A  49     6167   6039   6032   -181    710   -321       C  
+ATOM    354  C   ALA A  49     -15.698  -6.209 -31.365  1.00 47.62           C  
+ANISOU  354  C   ALA A  49     6079   5983   6030    -94    608   -287       C  
+ATOM    355  O   ALA A  49     -15.183  -5.313 -30.693  1.00 45.45           O  
+ANISOU  355  O   ALA A  49     5885   5614   5769     -6    608   -304       O  
+ATOM    356  CB  ALA A  49     -17.720  -7.170 -30.327  1.00 44.89           C  
+ANISOU  356  CB  ALA A  49     5646   5725   5684   -164    808   -302       C  
+ATOM    357  N   GLY A  50     -15.818  -6.151 -32.678  1.00 48.69           N  
+ANISOU  357  N   GLY A  50     6091   6224   6184   -128    536   -244       N  
+ATOM    358  CA  GLY A  50     -15.449  -4.952 -33.442  1.00 49.68           C  
+ANISOU  358  CA  GLY A  50     6154   6355   6367    -31    471   -170       C  
+ATOM    359  C   GLY A  50     -16.413  -4.732 -34.592  1.00 50.54           C  
+ANISOU  359  C   GLY A  50     6054   6667   6481    -27    438    -81       C  
+ATOM    360  O   GLY A  50     -17.402  -5.446 -34.760  1.00 51.08           O  
+ANISOU  360  O   GLY A  50     6008   6888   6512   -119    451    -99       O  
+ATOM    361  N   LYS A  51     -16.125  -3.748 -35.412  1.00 53.55           N  
+ANISOU  361  N   LYS A  51     6371   7070   6904     74    397     23       N  
+ATOM    362  CA  LYS A  51     -16.949  -3.502 -36.568  1.00 53.87           C  
+ANISOU  362  CA  LYS A  51     6201   7352   6915    104    349    142       C  
+ATOM    363  C   LYS A  51     -16.099  -3.324 -37.812  1.00 52.01           C  
+ANISOU  363  C   LYS A  51     5967   7166   6626    116    266    208       C  
+ATOM    364  O   LYS A  51     -15.259  -2.429 -37.884  1.00 49.83           O  
+ANISOU  364  O   LYS A  51     5763   6736   6431    222    288    275       O  
+ATOM    365  CB  LYS A  51     -17.819  -2.272 -36.354  1.00 55.11           C  
+ANISOU  365  CB  LYS A  51     6228   7529   7182    289    432    285       C  
+ATOM    366  CG  LYS A  51     -19.060  -2.302 -37.241  1.00 69.25           C  
+ANISOU  366  CG  LYS A  51     7743   9649   8918    311    386    414       C  
+ATOM    367  CD  LYS A  51     -19.994  -1.116 -37.036  1.00 78.14           C  
+ANISOU  367  CD  LYS A  51     8713  10807  10169    534    496    599       C  
+ATOM    368  CE  LYS A  51     -19.697  -0.098 -38.130  1.00 91.14           C  
+ANISOU  368  CE  LYS A  51    10279  12512  11835    709    478    813       C  
+ATOM    369  NZ  LYS A  51     -20.322   1.238 -37.924  1.00 98.12           N  
+ANISOU  369  NZ  LYS A  51    11067  13328  12884    974    639   1022       N  
+ATOM    370  N   VAL A  52     -16.371  -4.149 -38.805  1.00 56.50           N  
+ANISOU  370  N   VAL A  52     6451   7958   7057     -1    184    184       N  
+ATOM    371  CA  VAL A  52     -15.743  -4.044 -40.096  1.00 53.02           C  
+ANISOU  371  CA  VAL A  52     5995   7623   6524     10    113    251       C  
+ATOM    372  C   VAL A  52     -16.271  -2.823 -40.812  1.00 55.66           C  
+ANISOU  372  C   VAL A  52     6153   8116   6878    198    104    478       C  
+ATOM    373  O   VAL A  52     -17.446  -2.763 -41.107  1.00 57.54           O  
+ANISOU  373  O   VAL A  52     6184   8614   7061    218     71    553       O  
+ATOM    374  CB  VAL A  52     -16.050  -5.263 -40.957  1.00 52.46           C  
+ANISOU  374  CB  VAL A  52     5882   7779   6271   -184     41    135       C  
+ATOM    375  CG1 VAL A  52     -15.519  -5.033 -42.382  1.00 54.82           C  
+ANISOU  375  CG1 VAL A  52     6153   8239   6436   -149    -26    221       C  
+ATOM    376  CG2 VAL A  52     -15.383  -6.462 -40.332  1.00 50.36           C  
+ANISOU  376  CG2 VAL A  52     5817   7306   6010   -339     95    -55       C  
+ATOM    377  N   VAL A  53     -15.394  -1.848 -41.054  1.00 54.66           N  
+ANISOU  377  N   VAL A  53     6097   7828   6843    341    150    599       N  
+ATOM    378  CA  VAL A  53     -15.763  -0.629 -41.772  1.00 62.36           C  
+ANISOU  378  CA  VAL A  53     6930   8905   7859    554    184    855       C  
+ATOM    379  C   VAL A  53     -15.275  -0.645 -43.240  1.00 58.73           C  
+ANISOU  379  C   VAL A  53     6429   8643   7241    577    116    972       C  
+ATOM    380  O   VAL A  53     -15.785   0.085 -44.048  1.00 58.59           O  
+ANISOU  380  O   VAL A  53     6253   8828   7179    741    116   1203       O  
+ATOM    381  CB  VAL A  53     -15.351   0.680 -41.005  1.00 65.49           C  
+ANISOU  381  CB  VAL A  53     7415   8968   8500    719    338    940       C  
+ATOM    382  CG1 VAL A  53     -15.576   0.504 -39.522  1.00 72.86           C  
+ANISOU  382  CG1 VAL A  53     8446   9701   9533    655    398    764       C  
+ATOM    383  CG2 VAL A  53     -13.917   1.090 -41.248  1.00 68.64           C  
+ANISOU  383  CG2 VAL A  53     7968   9134   8977    721    376    943       C  
+ATOM    384  N   SER A  54     -14.348  -1.525 -43.591  1.00 53.74           N  
+ANISOU  384  N   SER A  54     5935   7975   6509    426     68    825       N  
+ATOM    385  CA  SER A  54     -13.884  -1.675 -44.972  1.00 55.87           C  
+ANISOU  385  CA  SER A  54     6190   8441   6597    431     18    904       C  
+ATOM    386  C   SER A  54     -13.173  -3.046 -45.081  1.00 56.25           C  
+ANISOU  386  C   SER A  54     6394   8450   6527    213    -15    660       C  
+ATOM    387  O   SER A  54     -12.486  -3.512 -44.129  1.00 49.66           O  
+ANISOU  387  O   SER A  54     5714   7335   5817    133     33    511       O  
+ATOM    388  CB  SER A  54     -12.991  -0.455 -45.367  1.00 56.61           C  
+ANISOU  388  CB  SER A  54     6326   8357   6824    625    121   1123       C  
+ATOM    389  OG  SER A  54     -12.138  -0.710 -46.445  1.00 63.73           O  
+ANISOU  389  OG  SER A  54     7288   9336   7589    611    112   1159       O  
+ATOM    390  N   GLY A  55     -13.389  -3.710 -46.217  1.00 53.71           N  
+ANISOU  390  N   GLY A  55     6028   8423   5956    121    -89    620       N  
+ATOM    391  CA  GLY A  55     -12.890  -5.055 -46.464  1.00 54.03           C  
+ANISOU  391  CA  GLY A  55     6214   8444   5869    -87    -89    381       C  
+ATOM    392  C   GLY A  55     -13.780  -6.179 -45.936  1.00 56.10           C  
+ANISOU  392  C   GLY A  55     6459   8774   6082   -306   -121    151       C  
+ATOM    393  O   GLY A  55     -14.982  -5.983 -45.681  1.00 60.55           O  
+ANISOU  393  O   GLY A  55     6840   9527   6637   -318   -183    180       O  
+ATOM    394  N   SER A  56     -13.181  -7.362 -45.781  1.00 52.30           N  
+ANISOU  394  N   SER A  56     6161   8128   5583   -472    -54    -60       N  
+ATOM    395  CA  SER A  56     -13.909  -8.555 -45.414  1.00 56.19           C  
+ANISOU  395  CA  SER A  56     6669   8647   6032   -704    -41   -291       C  
+ATOM    396  C   SER A  56     -13.104  -9.443 -44.480  1.00 53.82           C  
+ANISOU  396  C   SER A  56     6589   7981   5876   -764     94   -420       C  
+ATOM    397  O   SER A  56     -11.877  -9.347 -44.370  1.00 54.78           O  
+ANISOU  397  O   SER A  56     6852   7882   6078   -658    162   -364       O  
+ATOM    398  CB  SER A  56     -14.330  -9.330 -46.692  1.00 60.27           C  
+ANISOU  398  CB  SER A  56     7153   9473   6272   -904    -92   -449       C  
+ATOM    399  OG  SER A  56     -13.185  -9.640 -47.492  1.00 64.49           O  
+ANISOU  399  OG  SER A  56     7867   9923   6713   -890    -21   -484       O  
+ATOM    400  N   VAL A  57     -13.818 -10.317 -43.788  1.00 57.62           N  
+ANISOU  400  N   VAL A  57     7081   8413   6396   -929    142   -574       N  
+ATOM    401  CA  VAL A  57     -13.221 -11.309 -42.892  1.00 53.60           C  
+ANISOU  401  CA  VAL A  57     6773   7581   6008   -986    293   -680       C  
+ATOM    402  C   VAL A  57     -13.972 -12.587 -43.112  1.00 55.21           C  
+ANISOU  402  C   VAL A  57     7008   7830   6138  -1256    368   -912       C  
+ATOM    403  O   VAL A  57     -15.069 -12.588 -43.684  1.00 53.95           O  
+ANISOU  403  O   VAL A  57     6676   7964   5858  -1404    275   -989       O  
+ATOM    404  CB  VAL A  57     -13.373 -10.956 -41.381  1.00 57.18           C  
+ANISOU  404  CB  VAL A  57     7224   7857   6644   -880    320   -596       C  
+ATOM    405  CG1 VAL A  57     -12.463  -9.803 -41.015  1.00 52.19           C  
+ANISOU  405  CG1 VAL A  57     6602   7118   6107   -653    277   -417       C  
+ATOM    406  CG2 VAL A  57     -14.834 -10.627 -41.047  1.00 53.61           C  
+ANISOU  406  CG2 VAL A  57     6573   7599   6197   -936    258   -594       C  
+ATOM    407  N   ASP A  58     -13.366 -13.666 -42.641  1.00 55.82           N  
+ANISOU  407  N   ASP A  58     7294   7617   6296  -1318    547  -1013       N  
+ATOM    408  CA  ASP A  58     -13.996 -14.957 -42.561  1.00 58.46           C  
+ANISOU  408  CA  ASP A  58     7701   7878   6631  -1574    687  -1233       C  
+ATOM    409  C   ASP A  58     -14.136 -15.223 -41.085  1.00 55.65           C  
+ANISOU  409  C   ASP A  58     7395   7287   6461  -1515    794  -1166       C  
+ATOM    410  O   ASP A  58     -13.156 -15.457 -40.418  1.00 55.05           O  
+ANISOU  410  O   ASP A  58     7478   6953   6484  -1367    903  -1074       O  
+ATOM    411  CB  ASP A  58     -13.103 -16.016 -43.188  1.00 65.76           C  
+ANISOU  411  CB  ASP A  58     8862   8605   7518  -1656    869  -1374       C  
+ATOM    412  CG  ASP A  58     -13.609 -17.451 -42.970  1.00 72.39           C  
+ANISOU  412  CG  ASP A  58     9833   9259   8413  -1919   1088  -1606       C  
+ATOM    413  OD1 ASP A  58     -14.654 -17.719 -42.304  1.00 72.82           O  
+ANISOU  413  OD1 ASP A  58     9792   9332   8542  -2058   1107  -1663       O  
+ATOM    414  OD2 ASP A  58     -12.932 -18.339 -43.523  1.00 80.41           O  
+ANISOU  414  OD2 ASP A  58    11056  10090   9406  -1990   1274  -1737       O  
+ATOM    415  N   GLN A  59     -15.358 -15.182 -40.586  1.00 59.22           N  
+ANISOU  415  N   GLN A  59     7695   7858   6947  -1623    763  -1198       N  
+ATOM    416  CA  GLN A  59     -15.612 -15.422 -39.174  1.00 62.73           C  
+ANISOU  416  CA  GLN A  59     8181   8107   7544  -1569    878  -1130       C  
+ATOM    417  C   GLN A  59     -16.694 -16.457 -38.993  1.00 65.10           C  
+ANISOU  417  C   GLN A  59     8454   8390   7889  -1842   1015  -1303       C  
+ATOM    418  O   GLN A  59     -17.576 -16.295 -38.154  1.00 66.15           O  
+ANISOU  418  O   GLN A  59     8471   8562   8101  -1853   1028  -1256       O  
+ATOM    419  CB  GLN A  59     -16.011 -14.137 -38.463  1.00 57.25           C  
+ANISOU  419  CB  GLN A  59     7325   7538   6889  -1378    738   -953       C  
+ATOM    420  CG  GLN A  59     -17.246 -13.434 -39.016  1.00 55.97           C  
+ANISOU  420  CG  GLN A  59     6886   7717   6661  -1449    585   -955       C  
+ATOM    421  CD  GLN A  59     -17.652 -12.296 -38.096  1.00 62.04           C  
+ANISOU  421  CD  GLN A  59     7537   8522   7511  -1245    528   -780       C  
+ATOM    422  OE1 GLN A  59     -18.210 -11.269 -38.534  1.00 66.81           O  
+ANISOU  422  OE1 GLN A  59     7938   9366   8079  -1159    394   -682       O  
+ATOM    423  NE2 GLN A  59     -17.359 -12.461 -36.800  1.00 56.51           N  
+ANISOU  423  NE2 GLN A  59     6972   7583   6913  -1150    646   -731       N  
+ATOM    424  N   SER A  60     -16.585 -17.512 -39.786  1.00 64.01           N  
+ANISOU  424  N   SER A  60     8432   8180   7707  -2067   1137  -1510       N  
+ATOM    425  CA  SER A  60     -17.358 -18.738 -39.660  1.00 71.88           C  
+ANISOU  425  CA  SER A  60     9472   9060   8776  -2367   1339  -1715       C  
+ATOM    426  C   SER A  60     -17.042 -19.592 -38.399  1.00 69.57           C  
+ANISOU  426  C   SER A  60     9386   8371   8673  -2297   1620  -1638       C  
+ATOM    427  O   SER A  60     -17.817 -20.492 -38.073  1.00 71.72           O  
+ANISOU  427  O   SER A  60     9674   8528   9048  -2526   1809  -1764       O  
+ATOM    428  CB  SER A  60     -17.104 -19.609 -40.916  1.00 74.82           C  
+ANISOU  428  CB  SER A  60     9970   9417   9042  -2615   1424  -1979       C  
+ATOM    429  OG  SER A  60     -15.720 -19.921 -40.981  1.00 69.57           O  
+ANISOU  429  OG  SER A  60     9563   8467   8400  -2430   1560  -1910       O  
+ATOM    430  N   ASP A  61     -15.919 -19.358 -37.720  1.00 66.51           N  
+ANISOU  430  N   ASP A  61     9149   7790   8330  -1996   1657  -1429       N  
+ATOM    431  CA  ASP A  61     -15.690 -19.995 -36.412  1.00 71.53           C  
+ANISOU  431  CA  ASP A  61     9937   8131   9109  -1878   1886  -1294       C  
+ATOM    432  C   ASP A  61     -16.401 -19.243 -35.270  1.00 70.72           C  
+ANISOU  432  C   ASP A  61     9687   8145   9037  -1762   1797  -1144       C  
+ATOM    433  O   ASP A  61     -16.415 -19.738 -34.176  1.00 76.22           O  
+ANISOU  433  O   ASP A  61    10486   8652   9822  -1683   1980  -1036       O  
+ATOM    434  CB  ASP A  61     -14.215 -20.094 -36.049  1.00 71.97           C  
+ANISOU  434  CB  ASP A  61    10185   7978   9182  -1596   1957  -1113       C  
+ATOM    435  CG  ASP A  61     -13.386 -20.780 -37.089  1.00 73.12           C  
+ANISOU  435  CG  ASP A  61    10490   7983   9309  -1651   2076  -1223       C  
+ATOM    436  OD1 ASP A  61     -13.929 -21.468 -37.992  1.00 83.53           O  
+ANISOU  436  OD1 ASP A  61    11838   9288  10611  -1938   2174  -1474       O  
+ATOM    437  OD2 ASP A  61     -12.154 -20.643 -36.976  1.00 81.02           O  
+ANISOU  437  OD2 ASP A  61    11586   8889  10308  -1407   2081  -1062       O  
+ATOM    438  N   GLN A  62     -16.979 -18.058 -35.515  1.00 72.21           N  
+ANISOU  438  N   GLN A  62     9647   8639   9149  -1733   1544  -1123       N  
+ATOM    439  CA  GLN A  62     -17.756 -17.359 -34.476  1.00 67.75           C  
+ANISOU  439  CA  GLN A  62     8946   8173   8619  -1633   1498  -1004       C  
+ATOM    440  C   GLN A  62     -19.164 -17.942 -34.377  1.00 73.09           C  
+ANISOU  440  C   GLN A  62     9484   8914   9371  -1894   1611  -1123       C  
+ATOM    441  O   GLN A  62     -19.705 -18.425 -35.366  1.00 70.89           O  
+ANISOU  441  O   GLN A  62     9116   8743   9073  -2166   1602  -1315       O  
+ATOM    442  CB  GLN A  62     -17.867 -15.838 -34.717  1.00 66.80           C  
+ANISOU  442  CB  GLN A  62     8639   8319   8422  -1477   1233   -915       C  
+ATOM    443  CG  GLN A  62     -19.034 -15.338 -35.614  1.00 67.21           C  
+ANISOU  443  CG  GLN A  62     8412   8696   8428  -1635   1083   -998       C  
+ATOM    444  CD  GLN A  62     -20.329 -14.964 -34.874  1.00 64.99           C  
+ANISOU  444  CD  GLN A  62     7928   8548   8214  -1649   1104   -947       C  
+ATOM    445  OE1 GLN A  62     -20.304 -14.340 -33.806  1.00 71.34           O  
+ANISOU  445  OE1 GLN A  62     8759   9285   9059  -1444   1131   -808       O  
+ATOM    446  NE2 GLN A  62     -21.471 -15.281 -35.476  1.00 63.47           N  
+ANISOU  446  NE2 GLN A  62     7516   8576   8024  -1892   1085  -1063       N  
+ATOM    447  N   SER A  63     -19.742 -17.884 -33.174  1.00 72.66           N  
+ANISOU  447  N   SER A  63     9406   8808   9392  -1819   1719  -1013       N  
+ATOM    448  CA  SER A  63     -21.142 -18.244 -32.958  1.00 72.40           C  
+ANISOU  448  CA  SER A  63     9193   8866   9449  -2039   1821  -1088       C  
+ATOM    449  C   SER A  63     -21.984 -17.121 -32.341  1.00 66.69           C  
+ANISOU  449  C   SER A  63     8251   8365   8723  -1899   1711   -963       C  
+ATOM    450  O   SER A  63     -23.178 -17.067 -32.599  1.00 77.64           O  
+ANISOU  450  O   SER A  63     9385   9960  10154  -2074   1693  -1026       O  
+ATOM    451  CB  SER A  63     -21.277 -19.527 -32.118  1.00 70.17           C  
+ANISOU  451  CB  SER A  63     9088   8272   9301  -2138   2157  -1089       C  
+ATOM    452  OG  SER A  63     -20.352 -19.523 -31.051  1.00 76.40           O  
+ANISOU  452  OG  SER A  63    10101   8853  10075  -1847   2253   -890       O  
+ATOM    453  N   PHE A  64     -21.393 -16.238 -31.540  1.00 58.38           N  
+ANISOU  453  N   PHE A  64     7282   7276   7622  -1597   1650   -796       N  
+ATOM    454  CA  PHE A  64     -22.194 -15.361 -30.713  1.00 54.39           C  
+ANISOU  454  CA  PHE A  64     6633   6895   7136  -1461   1643   -686       C  
+ATOM    455  C   PHE A  64     -23.087 -14.381 -31.501  1.00 58.70           C  
+ANISOU  455  C   PHE A  64     6865   7765   7673  -1486   1455   -693       C  
+ATOM    456  O   PHE A  64     -24.182 -14.086 -31.039  1.00 57.54           O  
+ANISOU  456  O   PHE A  64     6527   7742   7591  -1488   1516   -644       O  
+ATOM    457  CB  PHE A  64     -21.262 -14.595 -29.755  1.00 55.93           C  
+ANISOU  457  CB  PHE A  64     7010   6983   7257  -1156   1611   -549       C  
+ATOM    458  CG  PHE A  64     -21.972 -13.594 -28.871  1.00 56.33           C  
+ANISOU  458  CG  PHE A  64     6960   7132   7311   -994   1624   -455       C  
+ATOM    459  CD1 PHE A  64     -22.478 -13.970 -27.623  1.00 51.74           C  
+ANISOU  459  CD1 PHE A  64     6445   6457   6754   -951   1839   -390       C  
+ATOM    460  CD2 PHE A  64     -22.149 -12.275 -29.292  1.00 59.73           C  
+ANISOU  460  CD2 PHE A  64     7237   7734   7723   -872   1452   -423       C  
+ATOM    461  CE1 PHE A  64     -23.090 -13.031 -26.810  1.00 57.92           C  
+ANISOU  461  CE1 PHE A  64     7159   7319   7527   -791   1874   -316       C  
+ATOM    462  CE2 PHE A  64     -22.810 -11.337 -28.484  1.00 60.57           C  
+ANISOU  462  CE2 PHE A  64     7268   7898   7848   -710   1505   -344       C  
+ATOM    463  CZ  PHE A  64     -23.289 -11.715 -27.242  1.00 54.75           C  
+ANISOU  463  CZ  PHE A  64     6607   7070   7122   -674   1715   -302       C  
+ATOM    464  N   LEU A  65     -22.629 -13.842 -32.646  1.00 59.31           N  
+ANISOU  464  N   LEU A  65     6879   7987   7667  -1474   1243   -724       N  
+ATOM    465  CA  LEU A  65     -23.379 -12.775 -33.374  1.00 63.23           C  
+ANISOU  465  CA  LEU A  65     7079   8808   8137  -1426   1064   -668       C  
+ATOM    466  C   LEU A  65     -24.616 -13.283 -34.188  1.00 65.16           C  
+ANISOU  466  C   LEU A  65     7020   9342   8393  -1708   1030   -770       C  
+ATOM    467  O   LEU A  65     -24.516 -14.329 -34.834  1.00 64.66           O  
+ANISOU  467  O   LEU A  65     7003   9259   8302  -1976   1052   -946       O  
+ATOM    468  CB  LEU A  65     -22.421 -11.920 -34.276  1.00 63.03           C  
+ANISOU  468  CB  LEU A  65     7095   8842   8010  -1279    864   -623       C  
+ATOM    469  CG  LEU A  65     -21.294 -11.083 -33.600  1.00 62.01           C  
+ANISOU  469  CG  LEU A  65     7179   8506   7874  -1004    852   -518       C  
+ATOM    470  CD1 LEU A  65     -20.274 -10.486 -34.575  1.00 60.47           C  
+ANISOU  470  CD1 LEU A  65     7035   8338   7604   -917    691   -493       C  
+ATOM    471  CD2 LEU A  65     -21.842  -9.965 -32.731  1.00 60.62           C  
+ANISOU  471  CD2 LEU A  65     6923   8352   7758   -795    888   -391       C  
+ATOM    472  N   ASP A  66     -25.764 -12.554 -34.051  1.00 69.31           N  
+ANISOU  472  N   ASP A  66     7243  10123   8968  -1644    999   -661       N  
+ATOM    473  CA  ASP A  66     -27.047 -12.615 -34.848  1.00 72.82           C  
+ANISOU  473  CA  ASP A  66     7291  10968   9408  -1843    905   -692       C  
+ATOM    474  C   ASP A  66     -26.639 -12.271 -36.317  1.00 77.46           C  
+ANISOU  474  C   ASP A  66     7794  11812   9825  -1871    656   -723       C  
+ATOM    475  O   ASP A  66     -25.716 -11.438 -36.541  1.00 65.69           O  
+ANISOU  475  O   ASP A  66     6447  10241   8269  -1624    566   -617       O  
+ATOM    476  CB  ASP A  66     -28.090 -11.511 -34.364  1.00 77.64           C  
+ANISOU  476  CB  ASP A  66     7609  11789  10101  -1619    914   -476       C  
+ATOM    477  CG  ASP A  66     -29.226 -12.030 -33.407  1.00 93.62           C  
+ANISOU  477  CG  ASP A  66     9481  13808  12279  -1733   1126   -471       C  
+ATOM    478  OD1 ASP A  66     -29.080 -13.108 -32.793  1.00106.10           O  
+ANISOU  478  OD1 ASP A  66    11255  15134  13923  -1914   1308   -596       O  
+ATOM    479  OD2 ASP A  66     -30.299 -11.343 -33.266  1.00 93.76           O  
+ANISOU  479  OD2 ASP A  66     9173  14081  12368  -1624   1134   -319       O  
+ATOM    480  N   ASP A  67     -27.292 -12.872 -37.323  1.00 79.83           N  
+ANISOU  480  N   ASP A  67     7864  12428  10039  -2173    549   -870       N  
+ATOM    481  CA  ASP A  67     -26.991 -12.525 -38.711  1.00 83.48           C  
+ANISOU  481  CA  ASP A  67     8232  13188  10299  -2190    314   -888       C  
+ATOM    482  C   ASP A  67     -27.305 -11.060 -38.958  1.00 81.61           C  
+ANISOU  482  C   ASP A  67     7760  13217  10028  -1857    174   -603       C  
+ATOM    483  O   ASP A  67     -26.634 -10.428 -39.777  1.00 81.48           O  
+ANISOU  483  O   ASP A  67     7791  13288   9878  -1705     34   -521       O  
+ATOM    484  CB  ASP A  67     -27.780 -13.356 -39.743  1.00 99.82           C  
+ANISOU  484  CB  ASP A  67    10050  15632  12243  -2589    204  -1107       C  
+ATOM    485  CG  ASP A  67     -27.453 -14.842 -39.701  1.00102.82           C  
+ANISOU  485  CG  ASP A  67    10677  15732  12655  -2952    367  -1422       C  
+ATOM    486  OD1 ASP A  67     -26.299 -15.215 -39.353  1.00102.93           O  
+ANISOU  486  OD1 ASP A  67    11082  15321  12706  -2872    499  -1465       O  
+ATOM    487  OD2 ASP A  67     -28.380 -15.622 -40.029  1.00 94.69           O  
+ANISOU  487  OD2 ASP A  67     9430  14922  11623  -3321    370  -1619       O  
+ATOM    488  N   GLU A  68     -28.313 -10.508 -38.287  1.00 76.33           N  
+ANISOU  488  N   GLU A  68     6842  12669   9490  -1729    236   -436       N  
+ATOM    489  CA  GLU A  68     -28.488  -9.053 -38.331  1.00 87.60           C  
+ANISOU  489  CA  GLU A  68     8114  14233  10935  -1350    179   -137       C  
+ATOM    490  C   GLU A  68     -27.140  -8.357 -37.951  1.00 89.20           C  
+ANISOU  490  C   GLU A  68     8695  14033  11161  -1073    233    -65       C  
+ATOM    491  O   GLU A  68     -26.669  -7.443 -38.628  1.00 88.13           O  
+ANISOU  491  O   GLU A  68     8554  13978  10954   -862    128     85       O  
+ATOM    492  CB  GLU A  68     -29.608  -8.602 -37.395  1.00 95.60           C  
+ANISOU  492  CB  GLU A  68     8892  15305  12126  -1215    319     23       C  
+ATOM    493  CG  GLU A  68     -29.735  -7.087 -37.282  1.00104.83           C  
+ANISOU  493  CG  GLU A  68     9958  16515  13354   -793    334    332       C  
+ATOM    494  CD  GLU A  68     -30.926  -6.636 -36.439  1.00115.12           C  
+ANISOU  494  CD  GLU A  68    11001  17904  14832   -647    495    501       C  
+ATOM    495  OE1 GLU A  68     -31.956  -7.348 -36.393  1.00115.20           O  
+ANISOU  495  OE1 GLU A  68    10727  18166  14878   -878    505    440       O  
+ATOM    496  OE2 GLU A  68     -30.828  -5.546 -35.827  1.00115.96           O  
+ANISOU  496  OE2 GLU A  68    11189  17821  15050   -303    630    690       O  
+ATOM    497  N   GLN A  69     -26.519  -8.821 -36.871  1.00 84.99           N  
+ANISOU  497  N   GLN A  69     8479  13084  10727  -1087    401   -169       N  
+ATOM    498  CA  GLN A  69     -25.278  -8.246 -36.383  1.00 74.41           C  
+ANISOU  498  CA  GLN A  69     7470  11389   9412   -867    450   -126       C  
+ATOM    499  C   GLN A  69     -24.113  -8.417 -37.351  1.00 73.31           C  
+ANISOU  499  C   GLN A  69     7506  11209   9139   -912    323   -199       C  
+ATOM    500  O   GLN A  69     -23.352  -7.485 -37.533  1.00 68.06           O  
+ANISOU  500  O   GLN A  69     6940  10450   8469   -692    283    -80       O  
+ATOM    501  CB  GLN A  69     -24.929  -8.815 -34.993  1.00 71.55           C  
+ANISOU  501  CB  GLN A  69     7378  10660   9148   -886    645   -216       C  
+ATOM    502  CG  GLN A  69     -25.865  -8.310 -33.913  1.00 72.95           C  
+ANISOU  502  CG  GLN A  69     7442  10823   9452   -746    799   -105       C  
+ATOM    503  CD  GLN A  69     -25.579  -8.891 -32.535  1.00 72.43           C  
+ANISOU  503  CD  GLN A  69     7636  10439   9442   -758    996   -178       C  
+ATOM    504  OE1 GLN A  69     -25.227 -10.079 -32.369  1.00 64.31           O  
+ANISOU  504  OE1 GLN A  69     6765   9275   8394   -961   1053   -316       O  
+ATOM    505  NE2 GLN A  69     -25.737  -8.050 -31.530  1.00 72.76           N  
+ANISOU  505  NE2 GLN A  69     7734  10360   9548   -530   1119    -79       N  
+ATOM    506  N   ILE A  70     -23.969  -9.589 -37.964  1.00 78.29           N  
+ANISOU  506  N   ILE A  70     8181  11890   9673  -1199    285   -397       N  
+ATOM    507  CA  ILE A  70     -22.959  -9.783 -39.017  1.00 75.81           C  
+ANISOU  507  CA  ILE A  70     8009  11579   9215  -1247    178   -469       C  
+ATOM    508  C   ILE A  70     -23.247  -8.950 -40.279  1.00 80.31           C  
+ANISOU  508  C   ILE A  70     8337  12536   9640  -1154     -9   -332       C  
+ATOM    509  O   ILE A  70     -22.334  -8.336 -40.829  1.00 79.77           O  
+ANISOU  509  O   ILE A  70     8384  12414   9511   -993    -67   -242       O  
+ATOM    510  CB  ILE A  70     -22.818 -11.250 -39.409  1.00 73.74           C  
+ANISOU  510  CB  ILE A  70     7859  11274   8884  -1583    218   -732       C  
+ATOM    511  CG1 ILE A  70     -22.302 -12.031 -38.193  1.00 70.17           C  
+ANISOU  511  CG1 ILE A  70     7687  10403   8571  -1612    426   -812       C  
+ATOM    512  CG2 ILE A  70     -21.890 -11.368 -40.629  1.00 69.89           C  
+ANISOU  512  CG2 ILE A  70     7488  10843   8223  -1620    113   -800       C  
+ATOM    513  CD1 ILE A  70     -22.178 -13.529 -38.421  1.00 73.86           C  
+ANISOU  513  CD1 ILE A  70     8298  10742   9024  -1925    541  -1055       C  
+ATOM    514  N   GLY A  71     -24.501  -8.934 -40.720  1.00 80.29           N  
+ANISOU  514  N   GLY A  71     7989  12934   9582  -1249    -96   -298       N  
+ATOM    515  CA  GLY A  71     -24.945  -8.065 -41.782  1.00 84.25           C  
+ANISOU  515  CA  GLY A  71     8211  13855   9944  -1113   -267   -106       C  
+ATOM    516  C   GLY A  71     -24.627  -6.587 -41.543  1.00 90.55           C  
+ANISOU  516  C   GLY A  71     9018  14542  10843   -709   -231    194       C  
+ATOM    517  O   GLY A  71     -24.287  -5.858 -42.499  1.00 81.85           O  
+ANISOU  517  O   GLY A  71     7861  13613   9622   -547   -332    358       O  
+ATOM    518  N   GLU A  72     -24.732  -6.122 -40.290  1.00 85.84           N  
+ANISOU  518  N   GLU A  72     8502  13652  10461   -547    -67    267       N  
+ATOM    519  CA  GLU A  72     -24.363  -4.720 -39.956  1.00 84.90           C  
+ANISOU  519  CA  GLU A  72     8443  13349  10466   -189     11    507       C  
+ATOM    520  C   GLU A  72     -22.834  -4.496 -39.871  1.00 78.13           C  
+ANISOU  520  C   GLU A  72     7942  12117   9627   -119     45    456       C  
+ATOM    521  O   GLU A  72     -22.372  -3.347 -39.798  1.00 75.56           O  
+ANISOU  521  O   GLU A  72     7680  11635   9392    134    104    627       O  
+ATOM    522  CB  GLU A  72     -25.091  -4.227 -38.684  1.00 90.04           C  
+ANISOU  522  CB  GLU A  72     9044  13847  11319    -41    188    589       C  
+ATOM    523  CG  GLU A  72     -26.571  -3.883 -38.951  1.00104.52           C  
+ANISOU  523  CG  GLU A  72    10456  16088  13168     32    166    776       C  
+ATOM    524  CD  GLU A  72     -27.484  -3.868 -37.708  1.00115.41           C  
+ANISOU  524  CD  GLU A  72    11754  17371  14724     75    350    790       C  
+ATOM    525  OE1 GLU A  72     -27.080  -4.369 -36.632  1.00119.16           O  
+ANISOU  525  OE1 GLU A  72    12496  17503  15276    -13    480    617       O  
+ATOM    526  OE2 GLU A  72     -28.638  -3.366 -37.807  1.00117.96           O  
+ANISOU  526  OE2 GLU A  72    11730  17986  15102    210    373    993       O  
+ATOM    527  N   GLY A  73     -22.058  -5.585 -39.875  1.00 67.33           N  
+ANISOU  527  N   GLY A  73     6793  10600   8189   -345     28    226       N  
+ATOM    528  CA  GLY A  73     -20.599  -5.498 -39.918  1.00 67.28           C  
+ANISOU  528  CA  GLY A  73     7077  10299   8185   -298     44    183       C  
+ATOM    529  C   GLY A  73     -19.821  -5.908 -38.667  1.00 62.16           C  
+ANISOU  529  C   GLY A  73     6708   9260   7648   -326    164     53       C  
+ATOM    530  O   GLY A  73     -18.585  -5.771 -38.638  1.00 63.30           O  
+ANISOU  530  O   GLY A  73     7062   9181   7806   -276    171     35       O  
+ATOM    531  N   PHE A  74     -20.506  -6.407 -37.635  1.00 54.21           N  
+ANISOU  531  N   PHE A  74     5695   8188   6714   -400    259    -21       N  
+ATOM    532  CA  PHE A  74     -19.813  -6.733 -36.379  1.00 50.47           C  
+ANISOU  532  CA  PHE A  74     5476   7384   6317   -394    373   -110       C  
+ATOM    533  C   PHE A  74     -19.017  -7.976 -36.610  1.00 48.37           C  
+ANISOU  533  C   PHE A  74     5385   7015   5978   -577    371   -263       C  
+ATOM    534  O   PHE A  74     -19.389  -8.786 -37.447  1.00 48.97           O  
+ANISOU  534  O   PHE A  74     5379   7259   5968   -764    327   -353       O  
+ATOM    535  CB  PHE A  74     -20.762  -6.849 -35.179  1.00 51.93           C  
+ANISOU  535  CB  PHE A  74     5616   7527   6587   -388    503   -119       C  
+ATOM    536  CG  PHE A  74     -21.382  -5.561 -34.825  1.00 54.65           C  
+ANISOU  536  CG  PHE A  74     5836   7903   7023   -170    553     31       C  
+ATOM    537  CD1 PHE A  74     -20.611  -4.556 -34.263  1.00 59.00           C  
+ANISOU  537  CD1 PHE A  74     6552   8226   7638     12    602     74       C  
+ATOM    538  CD2 PHE A  74     -22.705  -5.309 -35.097  1.00 62.03           C  
+ANISOU  538  CD2 PHE A  74     6482   9097   7988   -145    562    133       C  
+ATOM    539  CE1 PHE A  74     -21.141  -3.312 -33.970  1.00 61.30           C  
+ANISOU  539  CE1 PHE A  74     6759   8497   8034    221    689    205       C  
+ATOM    540  CE2 PHE A  74     -23.278  -4.074 -34.762  1.00 65.97           C  
+ANISOU  540  CE2 PHE A  74     6870   9599   8593     96    649    300       C  
+ATOM    541  CZ  PHE A  74     -22.481  -3.064 -34.213  1.00 69.36           C  
+ANISOU  541  CZ  PHE A  74     7503   9751   9097    283    726    331       C  
+ATOM    542  N   VAL A  75     -17.889  -8.074 -35.901  1.00 48.27           N  
+ANISOU  542  N   VAL A  75     5608   6737   5995   -517    420   -290       N  
+ATOM    543  CA  VAL A  75     -16.947  -9.203 -35.999  1.00 47.38           C  
+ANISOU  543  CA  VAL A  75     5685   6480   5837   -632    453   -395       C  
+ATOM    544  C   VAL A  75     -16.430  -9.520 -34.615  1.00 49.15           C  
+ANISOU  544  C   VAL A  75     6091   6473   6108   -575    558   -404       C  
+ATOM    545  O   VAL A  75     -16.248  -8.629 -33.798  1.00 47.13           O  
+ANISOU  545  O   VAL A  75     5866   6148   5892   -428    559   -343       O  
+ATOM    546  CB  VAL A  75     -15.775  -8.834 -36.956  1.00 48.33           C  
+ANISOU  546  CB  VAL A  75     5867   6583   5911   -571    365   -359       C  
+ATOM    547  CG1 VAL A  75     -15.079  -7.549 -36.507  1.00 49.98           C  
+ANISOU  547  CG1 VAL A  75     6112   6686   6192   -372    333   -248       C  
+ATOM    548  CG2 VAL A  75     -14.782  -9.929 -37.088  1.00 49.25           C  
+ANISOU  548  CG2 VAL A  75     6167   6549   5995   -653    422   -442       C  
+ATOM    549  N   LEU A  76     -16.184 -10.794 -34.355  1.00 49.57           N  
+ANISOU  549  N   LEU A  76     6273   6411   6149   -688    660   -479       N  
+ATOM    550  CA  LEU A  76     -15.521 -11.198 -33.153  1.00 47.93           C  
+ANISOU  550  CA  LEU A  76     6243   6013   5953   -611    755   -452       C  
+ATOM    551  C   LEU A  76     -14.098 -11.431 -33.507  1.00 45.41           C  
+ANISOU  551  C   LEU A  76     6054   5590   5609   -559    721   -432       C  
+ATOM    552  O   LEU A  76     -13.751 -12.422 -34.211  1.00 46.04           O  
+ANISOU  552  O   LEU A  76     6200   5615   5676   -659    783   -486       O  
+ATOM    553  CB  LEU A  76     -16.177 -12.472 -32.551  1.00 49.85           C  
+ANISOU  553  CB  LEU A  76     6545   6174   6220   -737    935   -501       C  
+ATOM    554  CG  LEU A  76     -17.619 -12.228 -32.105  1.00 52.45           C  
+ANISOU  554  CG  LEU A  76     6721   6616   6591   -787    985   -507       C  
+ATOM    555  CD1 LEU A  76     -18.258 -13.483 -31.561  1.00 56.78           C  
+ANISOU  555  CD1 LEU A  76     7318   7068   7185   -930   1185   -551       C  
+ATOM    556  CD2 LEU A  76     -17.640 -11.190 -31.039  1.00 52.12           C  
+ANISOU  556  CD2 LEU A  76     6691   6565   6545   -599    972   -426       C  
+ATOM    557  N   THR A  77     -13.248 -10.565 -32.967  1.00 46.19           N  
+ANISOU  557  N   THR A  77     6193   5650   5705   -409    646   -365       N  
+ATOM    558  CA  THR A  77     -11.875 -10.505 -33.442  1.00 42.15           C  
+ANISOU  558  CA  THR A  77     5743   5084   5186   -350    586   -327       C  
+ATOM    559  C   THR A  77     -11.098 -11.716 -33.100  1.00 45.26           C  
+ANISOU  559  C   THR A  77     6277   5354   5565   -343    692   -303       C  
+ATOM    560  O   THR A  77     -10.095 -11.997 -33.772  1.00 50.56           O  
+ANISOU  560  O   THR A  77     6987   5982   6241   -320    686   -277       O  
+ATOM    561  CB  THR A  77     -11.139  -9.239 -32.928  1.00 47.57           C  
+ANISOU  561  CB  THR A  77     6417   5768   5889   -225    479   -279       C  
+ATOM    562  OG1 THR A  77     -11.277  -9.041 -31.498  1.00 46.78           O  
+ANISOU  562  OG1 THR A  77     6374   5641   5757   -165    507   -279       O  
+ATOM    563  CG2 THR A  77     -11.677  -8.033 -33.637  1.00 44.69           C  
+ANISOU  563  CG2 THR A  77     5923   5486   5568   -210    405   -272       C  
+ATOM    564  N   CYS A  78     -11.496 -12.435 -32.033  1.00 50.28           N  
+ANISOU  564  N   CYS A  78     6990   5924   6188   -338    814   -286       N  
+ATOM    565  CA  CYS A  78     -10.739 -13.646 -31.587  1.00 46.96           C  
+ANISOU  565  CA  CYS A  78     6711   5367   5761   -289    955   -214       C  
+ATOM    566  C   CYS A  78     -11.053 -14.787 -32.559  1.00 49.46           C  
+ANISOU  566  C   CYS A  78     7079   5587   6126   -435   1104   -293       C  
+ATOM    567  O   CYS A  78     -10.411 -15.828 -32.559  1.00 51.48           O  
+ANISOU  567  O   CYS A  78     7458   5693   6407   -405   1257   -244       O  
+ATOM    568  CB  CYS A  78     -11.178 -14.092 -30.182  1.00 53.40           C  
+ANISOU  568  CB  CYS A  78     7600   6145   6541   -234   1071   -151       C  
+ATOM    569  SG  CYS A  78     -12.881 -14.758 -30.242  1.00 51.94           S  
+ANISOU  569  SG  CYS A  78     7387   5928   6417   -420   1238   -249       S  
+ATOM    570  N   ALA A  79     -12.058 -14.598 -33.405  1.00 53.34           N  
+ANISOU  570  N   ALA A  79     7469   6170   6626   -598   1068   -418       N  
+ATOM    571  CA  ALA A  79     -12.515 -15.685 -34.236  1.00 55.83           C  
+ANISOU  571  CA  ALA A  79     7829   6416   6967   -787   1210   -543       C  
+ATOM    572  C   ALA A  79     -12.550 -15.316 -35.744  1.00 59.35           C  
+ANISOU  572  C   ALA A  79     8188   7002   7360   -894   1092   -650       C  
+ATOM    573  O   ALA A  79     -13.139 -16.061 -36.535  1.00 55.41           O  
+ANISOU  573  O   ALA A  79     7693   6513   6847  -1099   1172   -802       O  
+ATOM    574  CB  ALA A  79     -13.912 -16.109 -33.759  1.00 59.21           C  
+ANISOU  574  CB  ALA A  79     8205   6858   7433   -940   1313   -616       C  
+ATOM    575  N   ALA A  80     -11.913 -14.208 -36.131  1.00 51.54           N  
+ANISOU  575  N   ALA A  80     7126   6121   6335   -767    917   -576       N  
+ATOM    576  CA  ALA A  80     -12.025 -13.689 -37.496  1.00 52.60           C  
+ANISOU  576  CA  ALA A  80     7161   6426   6398   -833    799   -635       C  
+ATOM    577  C   ALA A  80     -10.641 -13.659 -38.187  1.00 52.00           C  
+ANISOU  577  C   ALA A  80     7168   6282   6305   -734    797   -581       C  
+ATOM    578  O   ALA A  80      -9.618 -13.161 -37.620  1.00 49.22           O  
+ANISOU  578  O   ALA A  80     6840   5858   6002   -561    760   -450       O  
+ATOM    579  CB  ALA A  80     -12.672 -12.314 -37.499  1.00 53.13           C  
+ANISOU  579  CB  ALA A  80     7044   6690   6453   -769    627   -571       C  
+ATOM    580  N   TYR A  81     -10.617 -14.269 -39.382  1.00 50.25           N  
+ANISOU  580  N   TYR A  81     6991   6091   6011   -860    853   -698       N  
+ATOM    581  CA  TYR A  81      -9.473 -14.232 -40.301  1.00 47.17           C  
+ANISOU  581  CA  TYR A  81     6664   5673   5582   -786    868   -665       C  
+ATOM    582  C   TYR A  81      -9.695 -13.111 -41.287  1.00 50.29           C  
+ANISOU  582  C   TYR A  81     6913   6311   5882   -771    694   -634       C  
+ATOM    583  O   TYR A  81     -10.787 -12.986 -41.832  1.00 51.07           O  
+ANISOU  583  O   TYR A  81     6906   6613   5885   -903    620   -722       O  
+ATOM    584  CB  TYR A  81      -9.426 -15.493 -41.146  1.00 51.08           C  
+ANISOU  584  CB  TYR A  81     7306   6080   6021   -943   1048   -833       C  
+ATOM    585  CG  TYR A  81      -9.189 -16.735 -40.354  1.00 58.36           C  
+ANISOU  585  CG  TYR A  81     8398   6724   7050   -954   1282   -854       C  
+ATOM    586  CD1 TYR A  81      -8.089 -16.833 -39.537  1.00 55.81           C  
+ANISOU  586  CD1 TYR A  81     8143   6231   6829   -740   1353   -672       C  
+ATOM    587  CD2 TYR A  81     -10.088 -17.800 -40.389  1.00 63.94           C  
+ANISOU  587  CD2 TYR A  81     9183   7347   7763  -1179   1441  -1042       C  
+ATOM    588  CE1 TYR A  81      -7.853 -17.958 -38.801  1.00 58.92           C  
+ANISOU  588  CE1 TYR A  81     8687   6381   7317   -708   1583   -641       C  
+ATOM    589  CE2 TYR A  81      -9.854 -18.945 -39.642  1.00 66.35           C  
+ANISOU  589  CE2 TYR A  81     9659   7359   8190  -1168   1700  -1029       C  
+ATOM    590  CZ  TYR A  81      -8.713 -19.009 -38.869  1.00 60.72           C  
+ANISOU  590  CZ  TYR A  81     9018   6485   7566   -910   1772   -810       C  
+ATOM    591  OH  TYR A  81      -8.409 -20.106 -38.119  1.00 67.42           O  
+ANISOU  591  OH  TYR A  81    10026   7058   8530   -846   2038   -740       O  
+ATOM    592  N   PRO A  82      -8.665 -12.297 -41.547  1.00 49.08           N  
+ANISOU  592  N   PRO A  82     6740   6152   5756   -609    636   -494       N  
+ATOM    593  CA  PRO A  82      -8.810 -11.345 -42.651  1.00 47.57           C  
+ANISOU  593  CA  PRO A  82     6433   6172   5466   -585    518   -444       C  
+ATOM    594  C   PRO A  82      -8.910 -12.091 -43.994  1.00 51.19           C  
+ANISOU  594  C   PRO A  82     6956   6741   5751   -719    580   -581       C  
+ATOM    595  O   PRO A  82      -8.241 -13.098 -44.143  1.00 50.40           O  
+ANISOU  595  O   PRO A  82     7018   6474   5657   -755    739   -662       O  
+ATOM    596  CB  PRO A  82      -7.518 -10.563 -42.588  1.00 45.77           C  
+ANISOU  596  CB  PRO A  82     6205   5851   5335   -407    504   -279       C  
+ATOM    597  CG  PRO A  82      -6.511 -11.495 -41.974  1.00 47.02           C  
+ANISOU  597  CG  PRO A  82     6497   5788   5579   -368    638   -280       C  
+ATOM    598  CD  PRO A  82      -7.288 -12.383 -41.033  1.00 48.95           C  
+ANISOU  598  CD  PRO A  82     6803   5951   5844   -460    702   -380       C  
+ATOM    599  N   THR A  83      -9.757 -11.627 -44.924  1.00 48.49           N  
+ANISOU  599  N   THR A  83     6489   6685   5247   -788    466   -606       N  
+ATOM    600  CA  THR A  83      -9.762 -12.153 -46.307  1.00 51.50           C  
+ANISOU  600  CA  THR A  83     6925   7231   5411   -907    499   -734       C  
+ATOM    601  C   THR A  83      -9.429 -11.095 -47.342  1.00 49.00           C  
+ANISOU  601  C   THR A  83     6517   7124   4975   -771    406   -571       C  
+ATOM    602  O   THR A  83      -9.557 -11.323 -48.533  1.00 50.94           O  
+ANISOU  602  O   THR A  83     6777   7584   4992   -851    400   -650       O  
+ATOM    603  CB  THR A  83     -11.157 -12.749 -46.647  1.00 56.46           C  
+ANISOU  603  CB  THR A  83     7475   8093   5884  -1158    444   -940       C  
+ATOM    604  OG1 THR A  83     -12.178 -11.809 -46.291  1.00 57.08           O  
+ANISOU  604  OG1 THR A  83     7322   8384   5979  -1111    275   -821       O  
+ATOM    605  CG2 THR A  83     -11.377 -14.007 -45.846  1.00 57.41           C  
+ANISOU  605  CG2 THR A  83     7729   7967   6115  -1324    602  -1126       C  
+ATOM    606  N   SER A  84      -9.123  -9.892 -46.868  1.00 49.80           N  
+ANISOU  606  N   SER A  84     6516   7183   5220   -575    335   -345       N  
+ATOM    607  CA  SER A  84      -8.642  -8.809 -47.689  1.00 51.09           C  
+ANISOU  607  CA  SER A  84     6609   7464   5339   -413    295   -145       C  
+ATOM    608  C   SER A  84      -8.017  -7.870 -46.668  1.00 49.54           C  
+ANISOU  608  C   SER A  84     6376   7040   5405   -251    294     24       C  
+ATOM    609  O   SER A  84      -8.128  -8.093 -45.490  1.00 47.99           O  
+ANISOU  609  O   SER A  84     6201   6679   5354   -276    298    -28       O  
+ATOM    610  CB  SER A  84      -9.795  -8.069 -48.389  1.00 56.08           C  
+ANISOU  610  CB  SER A  84     7050   8454   5800   -400    154    -60       C  
+ATOM    611  OG  SER A  84     -10.589  -7.330 -47.413  1.00 52.75           O  
+ANISOU  611  OG  SER A  84     6487   8017   5536   -337     76     32       O  
+ATOM    612  N   ASP A  85      -7.382  -6.806 -47.126  1.00 46.38           N  
+ANISOU  612  N   ASP A  85     5924   6639   5057    -96    299    222       N  
+ATOM    613  CA  ASP A  85      -6.983  -5.755 -46.253  1.00 49.05           C  
+ANISOU  613  CA  ASP A  85     6206   6797   5631     21    291    358       C  
+ATOM    614  C   ASP A  85      -8.286  -5.304 -45.638  1.00 50.62           C  
+ANISOU  614  C   ASP A  85     6295   7088   5849     14    202    357       C  
+ATOM    615  O   ASP A  85      -9.334  -5.212 -46.322  1.00 54.44           O  
+ANISOU  615  O   ASP A  85     6675   7839   6170      0    136    381       O  
+ATOM    616  CB  ASP A  85      -6.341  -4.609 -47.058  1.00 47.60           C  
+ANISOU  616  CB  ASP A  85     5967   6628   5489    170    330    579       C  
+ATOM    617  CG  ASP A  85      -4.900  -4.925 -47.559  1.00 52.73           C  
+ANISOU  617  CG  ASP A  85     6707   7157   6169    197    442    609       C  
+ATOM    618  OD1 ASP A  85      -4.277  -5.936 -47.150  1.00 52.35           O  
+ANISOU  618  OD1 ASP A  85     6760   6983   6148    128    494    481       O  
+ATOM    619  OD2 ASP A  85      -4.368  -4.108 -48.369  1.00 56.01           O  
+ANISOU  619  OD2 ASP A  85     7082   7598   6598    307    501    791       O  
+ATOM    620  N   VAL A  86      -8.255  -4.968 -44.377  1.00 45.98           N  
+ANISOU  620  N   VAL A  86     5710   6311   5446     32    200    343       N  
+ATOM    621  CA  VAL A  86      -9.522  -4.737 -43.682  1.00 45.59           C  
+ANISOU  621  CA  VAL A  86     5573   6331   5415     18    147    316       C  
+ATOM    622  C   VAL A  86      -9.338  -3.725 -42.597  1.00 43.07           C  
+ANISOU  622  C   VAL A  86     5245   5812   5305    107    169    375       C  
+ATOM    623  O   VAL A  86      -8.275  -3.619 -42.020  1.00 44.22           O  
+ANISOU  623  O   VAL A  86     5470   5760   5572    112    201    355       O  
+ATOM    624  CB  VAL A  86     -10.078  -6.072 -43.148  1.00 48.01           C  
+ANISOU  624  CB  VAL A  86     5939   6654   5647   -143    144    117       C  
+ATOM    625  CG1 VAL A  86      -9.082  -6.710 -42.219  1.00 49.95           C  
+ANISOU  625  CG1 VAL A  86     6328   6652   5997   -170    206     38       C  
+ATOM    626  CG2 VAL A  86     -11.442  -5.939 -42.465  1.00 50.17           C  
+ANISOU  626  CG2 VAL A  86     6107   7020   5936   -171    105     89       C  
+ATOM    627  N   THR A  87     -10.371  -2.925 -42.367  1.00 45.19           N  
+ANISOU  627  N   THR A  87     5406   6148   5615    181    159    451       N  
+ATOM    628  CA  THR A  87     -10.350  -1.923 -41.352  1.00 45.50           C  
+ANISOU  628  CA  THR A  87     5452   5991   5845    259    209    481       C  
+ATOM    629  C   THR A  87     -11.421  -2.288 -40.351  1.00 47.74           C  
+ANISOU  629  C   THR A  87     5716   6302   6122    213    199    378       C  
+ATOM    630  O   THR A  87     -12.526  -2.582 -40.730  1.00 47.10           O  
+ANISOU  630  O   THR A  87     5523   6431   5940    201    166    397       O  
+ATOM    631  CB  THR A  87     -10.656  -0.544 -41.917  1.00 50.80           C  
+ANISOU  631  CB  THR A  87     6025   6671   6605    425    268    690       C  
+ATOM    632  OG1 THR A  87      -9.617  -0.209 -42.839  1.00 51.07           O  
+ANISOU  632  OG1 THR A  87     6082   6667   6653    467    301    799       O  
+ATOM    633  CG2 THR A  87     -10.675   0.502 -40.782  1.00 50.21           C  
+ANISOU  633  CG2 THR A  87     5986   6346   6744    486    359    679       C  
+ATOM    634  N   ILE A  88     -11.062  -2.273 -39.075  1.00 47.12           N  
+ANISOU  634  N   ILE A  88     5735   6028   6137    183    227    271       N  
+ATOM    635  CA  ILE A  88     -11.915  -2.724 -38.016  1.00 47.67           C  
+ANISOU  635  CA  ILE A  88     5818   6102   6192    138    240    168       C  
+ATOM    636  C   ILE A  88     -11.927  -1.719 -36.867  1.00 42.11           C  
+ANISOU  636  C   ILE A  88     5161   5214   5622    206    309    145       C  
+ATOM    637  O   ILE A  88     -10.907  -1.494 -36.286  1.00 43.07           O  
+ANISOU  637  O   ILE A  88     5383   5181   5801    181    309     80       O  
+ATOM    638  CB  ILE A  88     -11.395  -4.068 -37.512  1.00 49.90           C  
+ANISOU  638  CB  ILE A  88     6214   6349   6396     13    220     34       C  
+ATOM    639  CG1 ILE A  88     -11.587  -5.112 -38.628  1.00 49.65           C  
+ANISOU  639  CG1 ILE A  88     6153   6480   6231    -76    191     16       C  
+ATOM    640  CG2 ILE A  88     -12.108  -4.473 -36.212  1.00 51.61           C  
+ANISOU  640  CG2 ILE A  88     6470   6528   6608    -19    261    -58       C  
+ATOM    641  CD1 ILE A  88     -10.896  -6.432 -38.386  1.00 49.71           C  
+ANISOU  641  CD1 ILE A  88     6291   6410   6186   -176    218    -87       C  
+ATOM    642  N   GLU A  89     -13.099  -1.193 -36.519  1.00 43.90           N  
+ANISOU  642  N   GLU A  89     5312   5478   5890    282    372    184       N  
+ATOM    643  CA  GLU A  89     -13.278  -0.421 -35.290  1.00 47.43           C  
+ANISOU  643  CA  GLU A  89     5831   5749   6437    331    467    118       C  
+ATOM    644  C   GLU A  89     -13.369  -1.368 -34.118  1.00 48.10           C  
+ANISOU  644  C   GLU A  89     6014   5828   6434    235    456    -33       C  
+ATOM    645  O   GLU A  89     -14.208  -2.238 -34.131  1.00 46.09           O  
+ANISOU  645  O   GLU A  89     5699   5711   6099    192    450    -38       O  
+ATOM    646  CB  GLU A  89     -14.570   0.378 -35.316  1.00 52.55           C  
+ANISOU  646  CB  GLU A  89     6361   6442   7161    470    572    230       C  
+ATOM    647  CG  GLU A  89     -14.633   1.492 -36.347  1.00 63.41           C  
+ANISOU  647  CG  GLU A  89     7637   7813   8643    621    629    431       C  
+ATOM    648  CD  GLU A  89     -15.842   2.407 -36.147  1.00 73.77           C  
+ANISOU  648  CD  GLU A  89     8843   9123  10063    800    777    562       C  
+ATOM    649  OE1 GLU A  89     -16.733   2.070 -35.336  1.00 80.53           O  
+ANISOU  649  OE1 GLU A  89     9673  10027  10895    795    819    497       O  
+ATOM    650  OE2 GLU A  89     -15.887   3.472 -36.793  1.00 76.86           O  
+ANISOU  650  OE2 GLU A  89     9175   9453  10575    962    875    747       O  
+ATOM    651  N   THR A  90     -12.531  -1.166 -33.102  1.00 44.70           N  
+ANISOU  651  N   THR A  90     5722   5248   6013    199    462   -150       N  
+ATOM    652  CA  THR A  90     -12.427  -2.084 -32.002  1.00 43.34           C  
+ANISOU  652  CA  THR A  90     5650   5087   5727    129    448   -262       C  
+ATOM    653  C   THR A  90     -13.337  -1.652 -30.827  1.00 47.39           C  
+ANISOU  653  C   THR A  90     6210   5556   6240    178    561   -329       C  
+ATOM    654  O   THR A  90     -13.940  -0.586 -30.855  1.00 46.89           O  
+ANISOU  654  O   THR A  90     6109   5423   6283    268    659   -299       O  
+ATOM    655  CB  THR A  90     -10.937  -2.167 -31.551  1.00 42.54           C  
+ANISOU  655  CB  THR A  90     5651   4917   5596     69    368   -337       C  
+ATOM    656  OG1 THR A  90     -10.480  -0.884 -31.081  1.00 46.16           O  
+ANISOU  656  OG1 THR A  90     6155   5238   6145     78    400   -410       O  
+ATOM    657  CG2 THR A  90     -10.008  -2.601 -32.693  1.00 42.77           C  
+ANISOU  657  CG2 THR A  90     5633   4980   5636     38    287   -260       C  
+ATOM    658  N   HIS A  91     -13.394  -2.452 -29.757  1.00 47.53           N  
+ANISOU  658  N   HIS A  91     6321   5601   6138    136    573   -409       N  
+ATOM    659  CA  HIS A  91     -14.121  -2.027 -28.523  1.00 50.54           C  
+ANISOU  659  CA  HIS A  91     6775   5936   6488    184    696   -488       C  
+ATOM    660  C   HIS A  91     -15.577  -1.689 -28.823  1.00 48.63           C  
+ANISOU  660  C   HIS A  91     6407   5732   6336    269    818   -403       C  
+ATOM    661  O   HIS A  91     -16.087  -0.683 -28.370  1.00 47.55           O  
+ANISOU  661  O   HIS A  91     6289   5503   6273    358    945   -426       O  
+ATOM    662  CB  HIS A  91     -13.449  -0.819 -27.839  1.00 53.09           C  
+ANISOU  662  CB  HIS A  91     7212   6116   6841    192    723   -613       C  
+ATOM    663  CG  HIS A  91     -12.037  -1.062 -27.377  1.00 53.84           C  
+ANISOU  663  CG  HIS A  91     7400   6222   6835     99    592   -706       C  
+ATOM    664  ND1 HIS A  91     -10.998  -1.282 -28.245  1.00 52.95           N  
+ANISOU  664  ND1 HIS A  91     7230   6124   6762     50    467   -650       N  
+ATOM    665  CD2 HIS A  91     -11.494  -1.106 -26.132  1.00 56.62           C  
+ANISOU  665  CD2 HIS A  91     7877   6601   7033     54    565   -841       C  
+ATOM    666  CE1 HIS A  91      -9.869  -1.434 -27.568  1.00 52.38           C  
+ANISOU  666  CE1 HIS A  91     7223   6088   6589    -18    367   -735       C  
+ATOM    667  NE2 HIS A  91     -10.149  -1.348 -26.282  1.00 55.87           N  
+ANISOU  667  NE2 HIS A  91     7774   6556   6899    -20    411   -852       N  
+ATOM    668  N   LYS A  92     -16.228  -2.521 -29.624  1.00 49.04           N  
+ANISOU  668  N   LYS A  92     6318   5927   6386    236    787   -308       N  
+ATOM    669  CA  LYS A  92     -17.650  -2.390 -29.844  1.00 50.58           C  
+ANISOU  669  CA  LYS A  92     6348   6224   6644    297    884   -219       C  
+ATOM    670  C   LYS A  92     -18.507  -3.326 -28.988  1.00 47.54           C  
+ANISOU  670  C   LYS A  92     5964   5904   6193    250    976   -250       C  
+ATOM    671  O   LYS A  92     -19.654  -3.503 -29.328  1.00 46.77           O  
+ANISOU  671  O   LYS A  92     5686   5937   6144    257   1030   -172       O  
+ATOM    672  CB  LYS A  92     -17.947  -2.701 -31.306  1.00 53.04           C  
+ANISOU  672  CB  LYS A  92     6468   6705   6979    263    785   -107       C  
+ATOM    673  CG  LYS A  92     -17.183  -1.817 -32.271  1.00 56.58           C  
+ANISOU  673  CG  LYS A  92     6898   7107   7489    325    715    -36       C  
+ATOM    674  CD  LYS A  92     -17.486  -0.352 -32.061  1.00 58.78           C  
+ANISOU  674  CD  LYS A  92     7167   7262   7905    494    841     28       C  
+ATOM    675  CE  LYS A  92     -18.585   0.113 -32.975  1.00 67.73           C  
+ANISOU  675  CE  LYS A  92     8061   8564   9107    618    876    223       C  
+ATOM    676  NZ  LYS A  92     -18.423   1.591 -33.141  1.00 70.69           N  
+ANISOU  676  NZ  LYS A  92     8453   8766   9637    795    997    324       N  
+ATOM    677  N   GLU A  93     -17.977  -3.940 -27.920  1.00 48.14           N  
+ANISOU  677  N   GLU A  93     6220   5912   6155    204    998   -342       N  
+ATOM    678  CA  GLU A  93     -18.834  -4.744 -26.990  1.00 51.58           C  
+ANISOU  678  CA  GLU A  93     6674   6388   6535    183   1133   -348       C  
+ATOM    679  C   GLU A  93     -20.133  -4.043 -26.630  1.00 53.52           C  
+ANISOU  679  C   GLU A  93     6808   6660   6864    287   1296   -305       C  
+ATOM    680  O   GLU A  93     -21.165  -4.650 -26.637  1.00 58.70           O  
+ANISOU  680  O   GLU A  93     7330   7419   7554    250   1380   -249       O  
+ATOM    681  CB  GLU A  93     -18.112  -5.078 -25.684  1.00 53.98           C  
+ANISOU  681  CB  GLU A  93     7203   6619   6686    189   1166   -429       C  
+ATOM    682  CG  GLU A  93     -18.999  -5.656 -24.586  1.00 58.34           C  
+ANISOU  682  CG  GLU A  93     7798   7194   7171    208   1345   -418       C  
+ATOM    683  CD  GLU A  93     -18.194  -6.346 -23.498  1.00 59.78           C  
+ANISOU  683  CD  GLU A  93     8187   7360   7166    207   1352   -449       C  
+ATOM    684  OE1 GLU A  93     -17.556  -5.652 -22.668  1.00 64.37           O  
+ANISOU  684  OE1 GLU A  93     8915   7917   7626    267   1330   -538       O  
+ATOM    685  OE2 GLU A  93     -18.184  -7.597 -23.528  1.00 52.88           O  
+ANISOU  685  OE2 GLU A  93     7321   6504   6265    142   1381   -382       O  
+ATOM    686  N   GLU A  94     -20.084  -2.768 -26.311  1.00 59.72           N  
+ANISOU  686  N   GLU A  94     7647   7342   7699    413   1362   -331       N  
+ATOM    687  CA  GLU A  94     -21.265  -2.097 -25.790  1.00 66.89           C  
+ANISOU  687  CA  GLU A  94     8483   8242   8687    544   1565   -289       C  
+ATOM    688  C   GLU A  94     -22.389  -2.061 -26.822  1.00 70.98           C  
+ANISOU  688  C   GLU A  94     8697   8926   9344    581   1575   -124       C  
+ATOM    689  O   GLU A  94     -23.572  -2.215 -26.481  1.00 76.40           O  
+ANISOU  689  O   GLU A  94     9243   9704  10081    626   1721    -54       O  
+ATOM    690  CB  GLU A  94     -20.887  -0.700 -25.315  1.00 74.24           C  
+ANISOU  690  CB  GLU A  94     9560   8986   9660    665   1660   -370       C  
+ATOM    691  CG  GLU A  94     -20.302  -0.701 -23.891  1.00 88.53           C  
+ANISOU  691  CG  GLU A  94    11642  10696  11297    640   1723   -551       C  
+ATOM    692  CD  GLU A  94     -21.356  -0.505 -22.760  1.00104.83           C  
+ANISOU  692  CD  GLU A  94    13759  12740  13331    745   1979   -573       C  
+ATOM    693  OE1 GLU A  94     -20.982   0.024 -21.666  1.00102.89           O  
+ANISOU  693  OE1 GLU A  94    13744  12388  12962    767   2076   -740       O  
+ATOM    694  OE2 GLU A  94     -22.562  -0.867 -22.947  1.00106.29           O  
+ANISOU  694  OE2 GLU A  94    13749  13029  13605    797   2092   -434       O  
+ATOM    695  N   ALA A  95     -22.007  -1.909 -28.091  1.00 68.41           N  
+ANISOU  695  N   ALA A  95     8255   8672   9065    556   1414    -53       N  
+ATOM    696  CA  ALA A  95     -22.956  -1.709 -29.182  1.00 66.09           C  
+ANISOU  696  CA  ALA A  95     7658   8583   8869    608   1388    116       C  
+ATOM    697  C   ALA A  95     -23.662  -2.996 -29.528  1.00 66.44           C  
+ANISOU  697  C   ALA A  95     7523   8851   8867    435   1328    127       C  
+ATOM    698  O   ALA A  95     -24.779  -2.940 -30.017  1.00 70.13           O  
+ANISOU  698  O   ALA A  95     7710   9533   9402    465   1352    251       O  
+ATOM    699  CB  ALA A  95     -22.245  -1.163 -30.422  1.00 66.10           C  
+ANISOU  699  CB  ALA A  95     7613   8606   8896    636   1238    189       C  
+ATOM    700  N   ILE A  96     -23.008  -4.142 -29.324  1.00 64.35           N  
+ANISOU  700  N   ILE A  96     7407   8544   8498    253   1257      4       N  
+ATOM    701  CA  ILE A  96     -23.650  -5.436 -29.582  1.00 69.19           C  
+ANISOU  701  CA  ILE A  96     7886   9314   9088     56   1244    -17       C  
+ATOM    702  C   ILE A  96     -24.290  -6.032 -28.311  1.00 75.00           C  
+ANISOU  702  C   ILE A  96     8682   9987   9825     30   1440    -50       C  
+ATOM    703  O   ILE A  96     -24.815  -7.127 -28.332  1.00 68.54           O  
+ANISOU  703  O   ILE A  96     7782   9246   9011   -142   1479    -77       O  
+ATOM    704  CB  ILE A  96     -22.743  -6.492 -30.279  1.00 69.26           C  
+ANISOU  704  CB  ILE A  96     7991   9314   9009   -136   1099   -110       C  
+ATOM    705  CG1 ILE A  96     -21.443  -6.740 -29.528  1.00 72.23           C  
+ANISOU  705  CG1 ILE A  96     8677   9458   9305   -123   1101   -195       C  
+ATOM    706  CG2 ILE A  96     -22.463  -6.154 -31.716  1.00 67.05           C  
+ANISOU  706  CG2 ILE A  96     7581   9181   8715   -151    922    -66       C  
+ATOM    707  CD1 ILE A  96     -21.463  -8.031 -28.757  1.00 71.63           C  
+ANISOU  707  CD1 ILE A  96     8723   9309   9184   -245   1210   -253       C  
+ATOM    708  N   MET A  97     -24.258  -5.310 -27.201  1.00 82.61           N  
+ANISOU  708  N   MET A  97     9796  10805  10784    194   1584    -55       N  
+ATOM    709  CA  MET A  97     -25.147  -5.636 -26.099  1.00 87.11           C  
+ANISOU  709  CA  MET A  97    10364  11365  11366    215   1801    -42       C  
+ATOM    710  C   MET A  97     -26.325  -4.668 -26.205  1.00 95.61           C  
+ANISOU  710  C   MET A  97    11196  12551  12577    376   1919     80       C  
+ATOM    711  O   MET A  97     -27.410  -5.101 -26.587  1.00 94.92           O  
+ANISOU  711  O   MET A  97    10826  12664  12575    303   1954    163       O  
+ATOM    712  CB  MET A  97     -24.435  -5.516 -24.761  1.00 78.92           C  
+ANISOU  712  CB  MET A  97     9648  10134  10203    296   1904   -129       C  
+ATOM    713  CG  MET A  97     -23.264  -6.468 -24.547  1.00 69.75           C  
+ANISOU  713  CG  MET A  97     8709   8888   8904    181   1804   -207       C  
+ATOM    714  SD  MET A  97     -22.432  -5.914 -23.038  1.00 73.23           S  
+ANISOU  714  SD  MET A  97     9478   9183   9162    316   1882   -298       S  
+ATOM    715  CE  MET A  97     -21.484  -7.324 -22.587  1.00 70.81           C  
+ANISOU  715  CE  MET A  97     9359   8848   8698    213   1836   -305       C  
+ATOM    716  N   LEU A  98     -26.060  -3.361 -25.977  1.00101.64           N  
+ANISOU  716  N   LEU A  98    12055  13191  13371    583   1974     95       N  
+ATOM    717  CA  LEU A  98     -27.090  -2.306 -25.727  1.00104.44           C  
+ANISOU  717  CA  LEU A  98    12260  13563  13858    803   2175    216       C  
+ATOM    718  C   LEU A  98     -27.843  -1.768 -26.965  1.00 99.32           C  
+ANISOU  718  C   LEU A  98    11253  13135  13349    890   2107    411       C  
+ATOM    719  O   LEU A  98     -27.308  -1.714 -28.071  1.00 91.78           O  
+ANISOU  719  O   LEU A  98    10232  12263  12378    839   1898    441       O  
+ATOM    720  CB  LEU A  98     -26.469  -1.140 -24.941  1.00 98.56           C  
+ANISOU  720  CB  LEU A  98    11797  12554  13097    977   2303    129       C  
+TER     721      LEU A  98                                                      
+ATOM    722  N   ALA B   2     -32.420 -24.500 -28.343  1.00 55.40           N  
+ANISOU  722  N   ALA B   2     8165   6777   6106   -670  -1057   -125       N  
+ATOM    723  CA  ALA B   2     -32.280 -23.018 -28.309  1.00 49.74           C  
+ANISOU  723  CA  ALA B   2     7221   6215   5463   -512   -965    -28       C  
+ATOM    724  C   ALA B   2     -30.968 -22.594 -27.656  1.00 49.10           C  
+ANISOU  724  C   ALA B   2     7123   6073   5460   -324   -756    -44       C  
+ATOM    725  O   ALA B   2     -30.475 -23.240 -26.699  1.00 48.76           O  
+ANISOU  725  O   ALA B   2     7101   5932   5490   -308   -687    -81       O  
+ATOM    726  CB  ALA B   2     -33.452 -22.398 -27.562  1.00 46.79           C  
+ANISOU  726  CB  ALA B   2     6548   6005   5225   -580  -1026     92       C  
+ATOM    727  N   THR B   3     -30.475 -21.450 -28.119  1.00 46.70           N  
+ANISOU  727  N   THR B   3     6765   5842   5136   -203   -668      1       N  
+ATOM    728  CA  THR B   3     -29.337 -20.783 -27.525  1.00 45.09           C  
+ANISOU  728  CA  THR B   3     6493   5632   5006    -79   -488      8       C  
+ATOM    729  C   THR B   3     -29.711 -19.332 -27.227  1.00 42.65           C  
+ANISOU  729  C   THR B   3     6022   5395   4786    -46   -461    119       C  
+ATOM    730  O   THR B   3     -30.163 -18.603 -28.102  1.00 49.78           O  
+ANISOU  730  O   THR B   3     6948   6349   5617    -31   -515    186       O  
+ATOM    731  CB  THR B   3     -28.152 -20.886 -28.525  1.00 48.51           C  
+ANISOU  731  CB  THR B   3     7083   6061   5285     14   -383    -53       C  
+ATOM    732  OG1 THR B   3     -27.960 -22.272 -28.848  1.00 53.59           O  
+ANISOU  732  OG1 THR B   3     7925   6609   5827     21   -421   -170       O  
+ATOM    733  CG2 THR B   3     -26.865 -20.283 -27.959  1.00 46.16           C  
+ANISOU  733  CG2 THR B   3     6683   5804   5049    105   -200    -45       C  
+ATOM    734  N   TYR B   4     -29.515 -18.935 -25.980  1.00 41.02           N  
+ANISOU  734  N   TYR B   4     5685   5178   4720    -22   -381    136       N  
+ATOM    735  CA  TYR B   4     -29.849 -17.616 -25.484  1.00 41.59           C  
+ANISOU  735  CA  TYR B   4     5649   5273   4878     26   -345    216       C  
+ATOM    736  C   TYR B   4     -28.595 -16.874 -25.182  1.00 37.08           C  
+ANISOU  736  C   TYR B   4     5099   4664   4323     55   -200    205       C  
+ATOM    737  O   TYR B   4     -27.552 -17.444 -25.084  1.00 42.67           O  
+ANISOU  737  O   TYR B   4     5829   5376   5007     49   -128    142       O  
+ATOM    738  CB  TYR B   4     -30.686 -17.729 -24.208  1.00 37.93           C  
+ANISOU  738  CB  TYR B   4     5032   4839   4538     12   -366    231       C  
+ATOM    739  CG  TYR B   4     -31.986 -18.470 -24.482  1.00 40.65           C  
+ANISOU  739  CG  TYR B   4     5310   5268   4864    -68   -514    257       C  
+ATOM    740  CD1 TYR B   4     -32.953 -17.916 -25.304  1.00 41.09           C  
+ANISOU  740  CD1 TYR B   4     5314   5421   4875    -38   -621    333       C  
+ATOM    741  CD2 TYR B   4     -32.253 -19.691 -23.893  1.00 39.18           C  
+ANISOU  741  CD2 TYR B   4     5113   5074   4699   -188   -554    219       C  
+ATOM    742  CE1 TYR B   4     -34.139 -18.580 -25.546  1.00 44.88           C  
+ANISOU  742  CE1 TYR B   4     5690   6029   5332   -146   -773    362       C  
+ATOM    743  CE2 TYR B   4     -33.429 -20.383 -24.132  1.00 44.73           C  
+ANISOU  743  CE2 TYR B   4     5750   5865   5377   -328   -695    249       C  
+ATOM    744  CZ  TYR B   4     -34.369 -19.827 -24.951  1.00 49.31           C  
+ANISOU  744  CZ  TYR B   4     6235   6582   5915   -317   -807    316       C  
+ATOM    745  OH  TYR B   4     -35.506 -20.526 -25.188  1.00 53.31           O  
+ANISOU  745  OH  TYR B   4     6643   7219   6390   -490   -962    347       O  
+ATOM    746  N   LYS B   5     -28.735 -15.589 -25.005  1.00 42.61           N  
+ANISOU  746  N   LYS B   5     5794   5333   5063     87   -164    270       N  
+ATOM    747  CA  LYS B   5     -27.647 -14.685 -24.656  1.00 46.72           C  
+ANISOU  747  CA  LYS B   5     6346   5805   5598     57    -42    270       C  
+ATOM    748  C   LYS B   5     -27.871 -14.311 -23.218  1.00 44.65           C  
+ANISOU  748  C   LYS B   5     6004   5508   5453     71    -18    246       C  
+ATOM    749  O   LYS B   5     -28.949 -13.910 -22.869  1.00 45.52           O  
+ANISOU  749  O   LYS B   5     6085   5600   5610    146    -64    281       O  
+ATOM    750  CB  LYS B   5     -27.735 -13.411 -25.451  1.00 51.37           C  
+ANISOU  750  CB  LYS B   5     7063   6323   6130     68    -27    364       C  
+ATOM    751  CG  LYS B   5     -27.674 -13.596 -26.941  1.00 58.63           C  
+ANISOU  751  CG  LYS B   5     8089   7285   6902     63    -56    410       C  
+ATOM    752  CD  LYS B   5     -26.289 -13.986 -27.387  1.00 70.28           C  
+ANISOU  752  CD  LYS B   5     9582   8829   8291    -17     60    365       C  
+ATOM    753  CE  LYS B   5     -26.281 -14.277 -28.899  1.00 79.59           C  
+ANISOU  753  CE  LYS B   5    10887  10066   9288     -6     42    396       C  
+ATOM    754  NZ  LYS B   5     -26.107 -13.052 -29.737  1.00 78.80           N  
+ANISOU  754  NZ  LYS B   5    10935   9919   9086    -50     88    523       N  
+ATOM    755  N   VAL B   6     -26.848 -14.482 -22.400  1.00 44.73           N  
+ANISOU  755  N   VAL B   6     5968   5536   5490     12     52    185       N  
+ATOM    756  CA  VAL B   6     -26.898 -14.160 -21.017  1.00 43.45           C  
+ANISOU  756  CA  VAL B   6     5755   5350   5403      9     75    149       C  
+ATOM    757  C   VAL B   6     -25.875 -13.071 -20.817  1.00 41.71           C  
+ANISOU  757  C   VAL B   6     5600   5078   5168    -88    150    141       C  
+ATOM    758  O   VAL B   6     -24.703 -13.264 -21.100  1.00 40.03           O  
+ANISOU  758  O   VAL B   6     5351   4944   4913   -178    196    125       O  
+ATOM    759  CB  VAL B   6     -26.496 -15.357 -20.132  1.00 43.61           C  
+ANISOU  759  CB  VAL B   6     5675   5449   5445      0     68     89       C  
+ATOM    760  CG1 VAL B   6     -26.579 -14.975 -18.662  1.00 45.36           C  
+ANISOU  760  CG1 VAL B   6     5860   5659   5714     -5     90     55       C  
+ATOM    761  CG2 VAL B   6     -27.382 -16.547 -20.422  1.00 42.80           C  
+ANISOU  761  CG2 VAL B   6     5551   5370   5340     37     -7     99       C  
+ATOM    762  N   LYS B   7     -26.334 -11.940 -20.314  1.00 40.23           N  
+ANISOU  762  N   LYS B   7     5512   4765   5007    -71    162    150       N  
+ATOM    763  CA  LYS B   7     -25.489 -10.806 -20.042  1.00 45.32           C  
+ANISOU  763  CA  LYS B   7     6277   5311   5631   -205    217    138       C  
+ATOM    764  C   LYS B   7     -25.268 -10.732 -18.545  1.00 45.32           C  
+ANISOU  764  C   LYS B   7     6243   5315   5660   -241    224     48       C  
+ATOM    765  O   LYS B   7     -26.230 -10.649 -17.762  1.00 42.09           O  
+ANISOU  765  O   LYS B   7     5848   4860   5282   -112    213     21       O  
+ATOM    766  CB  LYS B   7     -26.179  -9.567 -20.522  1.00 45.61           C  
+ANISOU  766  CB  LYS B   7     6527   5150   5652   -141    218    203       C  
+ATOM    767  CG  LYS B   7     -25.518  -8.255 -20.177  1.00 54.48           C  
+ANISOU  767  CG  LYS B   7     7863   6090   6747   -291    265    191       C  
+ATOM    768  CD  LYS B   7     -26.348  -7.148 -20.886  1.00 64.52           C  
+ANISOU  768  CD  LYS B   7     9392   7132   7990   -162    255    284       C  
+ATOM    769  CE  LYS B   7     -25.977  -5.744 -20.437  1.00 79.55           C  
+ANISOU  769  CE  LYS B   7    11603   8759   9861   -274    292    266       C  
+ATOM    770  NZ  LYS B   7     -26.138  -4.714 -21.507  1.00 86.71           N  
+ANISOU  770  NZ  LYS B   7    12802   9439  10702   -264    295    396       N  
+ATOM    771  N   PHE B   8     -24.001 -10.796 -18.153  1.00 44.26           N  
+ANISOU  771  N   PHE B   8     6044   5273   5500   -411    242      4       N  
+ATOM    772  CA  PHE B   8     -23.636 -10.739 -16.757  1.00 48.27           C  
+ANISOU  772  CA  PHE B   8     6521   5811   6006   -472    227    -80       C  
+ATOM    773  C   PHE B   8     -23.203  -9.366 -16.431  1.00 50.61           C  
+ANISOU  773  C   PHE B   8     7012   5952   6265   -641    245   -114       C  
+ATOM    774  O   PHE B   8     -22.332  -8.836 -17.115  1.00 51.85           O  
+ANISOU  774  O   PHE B   8     7205   6107   6388   -833    269    -77       O  
+ATOM    775  CB  PHE B   8     -22.495 -11.714 -16.458  1.00 46.54           C  
+ANISOU  775  CB  PHE B   8     6090   5823   5767   -542    208   -104       C  
+ATOM    776  CG  PHE B   8     -22.919 -13.133 -16.573  1.00 43.20           C  
+ANISOU  776  CG  PHE B   8     5543   5501   5370   -371    182    -85       C  
+ATOM    777  CD1 PHE B   8     -23.753 -13.684 -15.611  1.00 45.99           C  
+ANISOU  777  CD1 PHE B   8     5889   5839   5743   -264    152   -107       C  
+ATOM    778  CD2 PHE B   8     -22.561 -13.888 -17.670  1.00 43.53           C  
+ANISOU  778  CD2 PHE B   8     5511   5629   5397   -326    196    -45       C  
+ATOM    779  CE1 PHE B   8     -24.172 -15.008 -15.719  1.00 45.51           C  
+ANISOU  779  CE1 PHE B   8     5756   5836   5697   -151    123    -79       C  
+ATOM    780  CE2 PHE B   8     -22.977 -15.203 -17.786  1.00 47.96           C  
+ANISOU  780  CE2 PHE B   8     6024   6230   5968   -183    163    -39       C  
+ATOM    781  CZ  PHE B   8     -23.809 -15.751 -16.817  1.00 42.32           C  
+ANISOU  781  CZ  PHE B   8     5316   5479   5284   -113    120    -50       C  
+ATOM    782  N   ILE B   9     -23.818  -8.777 -15.407  1.00 50.12           N  
+ANISOU  782  N   ILE B   9     7095   5752   6194   -582    242   -186       N  
+ATOM    783  CA  ILE B   9     -23.293  -7.532 -14.841  1.00 52.49           C  
+ANISOU  783  CA  ILE B   9     7627   5881   6436   -773    243   -255       C  
+ATOM    784  C   ILE B   9     -22.569  -7.908 -13.581  1.00 53.51           C  
+ANISOU  784  C   ILE B   9     7645   6168   6518   -894    195   -352       C  
+ATOM    785  O   ILE B   9     -23.199  -8.276 -12.624  1.00 53.62           O  
+ANISOU  785  O   ILE B   9     7645   6206   6520   -744    190   -409       O  
+ATOM    786  CB  ILE B   9     -24.432  -6.583 -14.587  1.00 57.52           C  
+ANISOU  786  CB  ILE B   9     8544   6244   7065   -590    275   -283       C  
+ATOM    787  CG1 ILE B   9     -25.188  -6.426 -15.903  1.00 59.96           C  
+ANISOU  787  CG1 ILE B   9     8902   6461   7416   -426    296   -159       C  
+ATOM    788  CG2 ILE B   9     -23.932  -5.267 -13.966  1.00 60.99           C  
+ANISOU  788  CG2 ILE B   9     9309   6437   7425   -785    272   -375       C  
+ATOM    789  CD1 ILE B   9     -26.172  -5.282 -15.905  1.00 63.59           C  
+ANISOU  789  CD1 ILE B   9     9670   6630   7859   -227    324   -158       C  
+ATOM    790  N   THR B  10     -21.243  -7.928 -13.622  1.00 58.51           N  
+ANISOU  790  N   THR B  10     8157   6958   7116  -1157    160   -355       N  
+ATOM    791  CA  THR B  10     -20.440  -8.325 -12.483  1.00 58.08           C  
+ANISOU  791  CA  THR B  10     7958   7106   7001  -1275     86   -431       C  
+ATOM    792  C   THR B  10     -19.973  -7.044 -11.813  1.00 70.23           C  
+ANISOU  792  C   THR B  10     9748   8484   8451  -1550     48   -529       C  
+ATOM    793  O   THR B  10     -20.144  -5.958 -12.368  1.00 71.35           O  
+ANISOU  793  O   THR B  10    10161   8360   8588  -1655     87   -519       O  
+ATOM    794  CB  THR B  10     -19.189  -9.135 -12.876  1.00 58.06           C  
+ANISOU  794  CB  THR B  10     7627   7434   6998  -1387     54   -376       C  
+ATOM    795  OG1 THR B  10     -18.233  -8.281 -13.503  1.00 67.21           O  
+ANISOU  795  OG1 THR B  10     8801   8610   8125  -1697     67   -349       O  
+ATOM    796  CG2 THR B  10     -19.528 -10.276 -13.818  1.00 59.69           C  
+ANISOU  796  CG2 THR B  10     7661   7742   7275  -1147    101   -286       C  
+ATOM    797  N   PRO B  11     -19.376  -7.160 -10.620  1.00 72.85           N  
+ANISOU  797  N   PRO B  11    10022   8962   8697  -1676    -39   -622       N  
+ATOM    798  CA  PRO B  11     -18.837  -5.933 -10.022  1.00 82.51           C  
+ANISOU  798  CA  PRO B  11    11506  10030   9814  -1997    -96   -729       C  
+ATOM    799  C   PRO B  11     -17.740  -5.357 -10.913  1.00 82.13           C  
+ANISOU  799  C   PRO B  11    11402  10036   9765  -2359   -105   -662       C  
+ATOM    800  O   PRO B  11     -17.601  -4.166 -11.043  1.00 81.04           O  
+ANISOU  800  O   PRO B  11    11578   9634   9578  -2614   -104   -697       O  
+ATOM    801  CB  PRO B  11     -18.263  -6.417  -8.685  1.00 79.55           C  
+ANISOU  801  CB  PRO B  11    10990   9902   9333  -2070   -213   -819       C  
+ATOM    802  CG  PRO B  11     -19.024  -7.665  -8.373  1.00 77.41           C  
+ANISOU  802  CG  PRO B  11    10544   9762   9107  -1699   -182   -776       C  
+ATOM    803  CD  PRO B  11     -19.316  -8.308  -9.700  1.00 70.96           C  
+ANISOU  803  CD  PRO B  11     9555   8976   8429  -1526    -97   -639       C  
+ATOM    804  N   GLU B  12     -17.008  -6.230 -11.575  1.00 88.50           N  
+ANISOU  804  N   GLU B  12    11824  11180  10621  -2365    -98   -558       N  
+ATOM    805  CA  GLU B  12     -15.873  -5.804 -12.367  1.00 99.06           C  
+ANISOU  805  CA  GLU B  12    13027  12669  11940  -2715    -89   -484       C  
+ATOM    806  C   GLU B  12     -16.299  -5.351 -13.766  1.00 93.10           C  
+ANISOU  806  C   GLU B  12    12432  11697  11242  -2687     35   -371       C  
+ATOM    807  O   GLU B  12     -15.498  -4.776 -14.476  1.00 97.19           O  
+ANISOU  807  O   GLU B  12    12930  12268  11727  -3008     67   -299       O  
+ATOM    808  CB  GLU B  12     -14.832  -6.940 -12.446  1.00112.44           C  
+ANISOU  808  CB  GLU B  12    14216  14866  13638  -2694   -123   -425       C  
+ATOM    809  CG  GLU B  12     -14.678  -7.719 -11.130  1.00124.13           C  
+ANISOU  809  CG  GLU B  12    15529  16561  15070  -2562   -247   -503       C  
+ATOM    810  CD  GLU B  12     -13.339  -8.441 -10.973  1.00129.71           C  
+ANISOU  810  CD  GLU B  12    15773  17775  15735  -2649   -327   -459       C  
+ATOM    811  OE1 GLU B  12     -12.591  -8.120 -10.015  1.00122.40           O  
+ANISOU  811  OE1 GLU B  12    14767  17032  14705  -2906   -469   -526       O  
+ATOM    812  OE2 GLU B  12     -13.042  -9.343 -11.788  1.00121.66           O  
+ANISOU  812  OE2 GLU B  12    14470  16981  14772  -2441   -255   -362       O  
+ATOM    813  N   GLY B  13     -17.543  -5.608 -14.166  1.00 81.49           N  
+ANISOU  813  N   GLY B  13    11109  10009   9842  -2323     99   -344       N  
+ATOM    814  CA  GLY B  13     -18.037  -5.147 -15.451  1.00 73.28           C  
+ANISOU  814  CA  GLY B  13    10250   8758   8835  -2271    193   -234       C  
+ATOM    815  C   GLY B  13     -18.997  -6.122 -16.098  1.00 65.13           C  
+ANISOU  815  C   GLY B  13     9104   7761   7882  -1867    241   -172       C  
+ATOM    816  O   GLY B  13     -19.539  -6.995 -15.467  1.00 67.40           O  
+ANISOU  816  O   GLY B  13     9263   8138   8207  -1617    210   -222       O  
+ATOM    817  N   GLU B  14     -19.185  -5.957 -17.384  1.00 61.80           N  
+ANISOU  817  N   GLU B  14     8743   7270   7466  -1838    311    -57       N  
+ATOM    818  CA  GLU B  14     -20.256  -6.598 -18.125  1.00 63.32           C  
+ANISOU  818  CA  GLU B  14     8924   7423   7712  -1496    338      2       C  
+ATOM    819  C   GLU B  14     -19.671  -7.497 -19.189  1.00 56.31           C  
+ANISOU  819  C   GLU B  14     7774   6809   6810  -1480    388     83       C  
+ATOM    820  O   GLU B  14     -18.634  -7.194 -19.774  1.00 62.63           O  
+ANISOU  820  O   GLU B  14     8502   7742   7551  -1731    443    140       O  
+ATOM    821  CB  GLU B  14     -21.129  -5.544 -18.792  1.00 71.21           C  
+ANISOU  821  CB  GLU B  14    10284   8074   8699  -1428    362     71       C  
+ATOM    822  CG  GLU B  14     -22.416  -5.260 -18.043  1.00 86.78           C  
+ANISOU  822  CG  GLU B  14    12442   9819  10709  -1152    330      6       C  
+ATOM    823  CD  GLU B  14     -22.957  -3.842 -18.244  1.00101.03           C  
+ANISOU  823  CD  GLU B  14    14681  11229  12476  -1139    343     36       C  
+ATOM    824  OE1 GLU B  14     -22.373  -2.917 -17.631  1.00107.53           O  
+ANISOU  824  OE1 GLU B  14    15741  11869  13245  -1388    335    -29       O  
+ATOM    825  OE2 GLU B  14     -23.973  -3.658 -18.983  1.00100.60           O  
+ANISOU  825  OE2 GLU B  14    14745  11041  12435   -874    348    124       O  
+ATOM    826  N   LEU B  15     -20.365  -8.587 -19.454  1.00 56.17           N  
+ANISOU  826  N   LEU B  15     7631   6874   6835  -1191    377     86       N  
+ATOM    827  CA  LEU B  15     -20.023  -9.507 -20.528  1.00 55.27           C  
+ANISOU  827  CA  LEU B  15     7340   6967   6692  -1109    425    143       C  
+ATOM    828  C   LEU B  15     -21.290 -10.308 -20.860  1.00 53.66           C  
+ANISOU  828  C   LEU B  15     7166   6692   6529   -812    384    148       C  
+ATOM    829  O   LEU B  15     -22.039 -10.720 -19.940  1.00 50.69           O  
+ANISOU  829  O   LEU B  15     6774   6269   6216   -671    324     88       O  
+ATOM    830  CB  LEU B  15     -18.948 -10.454 -20.044  1.00 57.82           C  
+ANISOU  830  CB  LEU B  15     7356   7605   7007  -1128    426     93       C  
+ATOM    831  CG  LEU B  15     -18.440 -11.420 -21.099  1.00 63.30           C  
+ANISOU  831  CG  LEU B  15     7878   8524   7650  -1015    495    130       C  
+ATOM    832  CD1 LEU B  15     -17.756 -10.666 -22.216  1.00 63.58           C  
+ANISOU  832  CD1 LEU B  15     7943   8622   7594  -1222    602    221       C  
+ATOM    833  CD2 LEU B  15     -17.443 -12.372 -20.490  1.00 65.97           C  
+ANISOU  833  CD2 LEU B  15     7922   9161   7982   -958    487     79       C  
+ATOM    834  N   GLU B  16     -21.532 -10.510 -22.157  1.00 47.63           N  
+ANISOU  834  N   GLU B  16     6447   5937   5713   -745    415    221       N  
+ATOM    835  CA  GLU B  16     -22.664 -11.304 -22.654  1.00 45.67           C  
+ANISOU  835  CA  GLU B  16     6218   5656   5479   -515    358    230       C  
+ATOM    836  C   GLU B  16     -22.099 -12.584 -23.254  1.00 48.61           C  
+ANISOU  836  C   GLU B  16     6436   6232   5799   -443    384    205       C  
+ATOM    837  O   GLU B  16     -21.055 -12.553 -23.930  1.00 46.18           O  
+ANISOU  837  O   GLU B  16     6067   6070   5407   -536    474    229       O  
+ATOM    838  CB  GLU B  16     -23.451 -10.553 -23.698  1.00 48.11           C  
+ANISOU  838  CB  GLU B  16     6731   5809   5738   -476    346    328       C  
+ATOM    839  CG  GLU B  16     -24.832 -11.113 -24.032  1.00 53.37           C  
+ANISOU  839  CG  GLU B  16     7413   6439   6424   -265    251    343       C  
+ATOM    840  CD  GLU B  16     -25.747 -10.126 -24.829  1.00 57.72           C  
+ANISOU  840  CD  GLU B  16     8169   6829   6932   -188    209    453       C  
+ATOM    841  OE1 GLU B  16     -26.936  -9.937 -24.446  1.00 69.98           O  
+ANISOU  841  OE1 GLU B  16     9737   8308   8544    -25    134    464       O  
+ATOM    842  OE2 GLU B  16     -25.279  -9.506 -25.809  1.00 71.51           O  
+ANISOU  842  OE2 GLU B  16    10058   8536   8577   -278    255    541       O  
+ATOM    843  N   VAL B  17     -22.770 -13.706 -22.978  1.00 44.60           N  
+ANISOU  843  N   VAL B  17     5878   5736   5329   -281    315    156       N  
+ATOM    844  CA  VAL B  17     -22.304 -14.976 -23.436  1.00 46.75           C  
+ANISOU  844  CA  VAL B  17     6070   6143   5548   -182    330    114       C  
+ATOM    845  C   VAL B  17     -23.463 -15.746 -23.995  1.00 45.90           C  
+ANISOU  845  C   VAL B  17     6061   5952   5427    -66    243    110       C  
+ATOM    846  O   VAL B  17     -24.614 -15.485 -23.683  1.00 43.95           O  
+ANISOU  846  O   VAL B  17     5860   5598   5240    -49    164    134       O  
+ATOM    847  CB  VAL B  17     -21.605 -15.796 -22.302  1.00 47.60           C  
+ANISOU  847  CB  VAL B  17     6021   6360   5702   -132    323     46       C  
+ATOM    848  CG1 VAL B  17     -20.698 -14.897 -21.492  1.00 51.12           C  
+ANISOU  848  CG1 VAL B  17     6363   6888   6173   -288    361     47       C  
+ATOM    849  CG2 VAL B  17     -22.618 -16.422 -21.353  1.00 49.09           C  
+ANISOU  849  CG2 VAL B  17     6236   6442   5970    -53    227     18       C  
+ATOM    850  N   GLU B  18     -23.109 -16.745 -24.776  1.00 46.07           N  
+ANISOU  850  N   GLU B  18     6102   6043   5357     16    260     72       N  
+ATOM    851  CA  GLU B  18     -24.020 -17.745 -25.270  1.00 49.63           C  
+ANISOU  851  CA  GLU B  18     6660   6424   5772     95    165     41       C  
+ATOM    852  C   GLU B  18     -24.215 -18.851 -24.218  1.00 44.35           C  
+ANISOU  852  C   GLU B  18     5956   5714   5177    154    106    -15       C  
+ATOM    853  O   GLU B  18     -23.267 -19.284 -23.560  1.00 51.13           O  
+ANISOU  853  O   GLU B  18     6734   6641   6052    214    157    -53       O  
+ATOM    854  CB  GLU B  18     -23.469 -18.355 -26.555  1.00 50.78           C  
+ANISOU  854  CB  GLU B  18     6901   6634   5756    163    218      4       C  
+ATOM    855  CG  GLU B  18     -23.996 -17.686 -27.802  1.00 68.77           C  
+ANISOU  855  CG  GLU B  18     9305   8898   7926    113    200     68       C  
+ATOM    856  CD  GLU B  18     -23.835 -18.551 -29.075  1.00 88.05           C  
+ANISOU  856  CD  GLU B  18    11901  11375  10178    191    210      8       C  
+ATOM    857  OE1 GLU B  18     -22.896 -19.410 -29.126  1.00 81.31           O  
+ANISOU  857  OE1 GLU B  18    11040  10592   9262    305    301    -77       O  
+ATOM    858  OE2 GLU B  18     -24.664 -18.355 -30.020  1.00 95.29           O  
+ANISOU  858  OE2 GLU B  18    12955  12254  10995    158    122     45       O  
+ATOM    859  N   CYS B  19     -25.445 -19.302 -24.075  1.00 41.64           N  
+ANISOU  859  N   CYS B  19     5672   5279   4869    130     -6     -8       N  
+ATOM    860  CA  CYS B  19     -25.763 -20.344 -23.133  1.00 45.79           C  
+ANISOU  860  CA  CYS B  19     6202   5746   5449    144    -63    -39       C  
+ATOM    861  C   CYS B  19     -26.882 -21.159 -23.766  1.00 46.84           C  
+ANISOU  861  C   CYS B  19     6462   5798   5535     89   -183    -46       C  
+ATOM    862  O   CYS B  19     -27.984 -20.631 -23.986  1.00 45.31           O  
+ANISOU  862  O   CYS B  19     6227   5622   5365     10   -256      7       O  
+ATOM    863  CB  CYS B  19     -26.236 -19.742 -21.802  1.00 44.81           C  
+ANISOU  863  CB  CYS B  19     5955   5628   5442     93    -68      2       C  
+ATOM    864  SG  CYS B  19     -26.514 -21.029 -20.541  1.00 44.98           S  
+ANISOU  864  SG  CYS B  19     5996   5591   5503     93   -120     -8       S  
+ATOM    865  N   ASP B  20     -26.627 -22.429 -24.061  1.00 46.35           N  
+ANISOU  865  N   ASP B  20     6561   5651   5398    132   -213   -114       N  
+ATOM    866  CA  ASP B  20     -27.710 -23.303 -24.531  1.00 48.89           C  
+ANISOU  866  CA  ASP B  20     7032   5874   5668     19   -349   -130       C  
+ATOM    867  C   ASP B  20     -28.747 -23.474 -23.465  1.00 47.10           C  
+ANISOU  867  C   ASP B  20     6711   5635   5547   -116   -420    -68       C  
+ATOM    868  O   ASP B  20     -28.472 -23.214 -22.296  1.00 47.38           O  
+ANISOU  868  O   ASP B  20     6628   5699   5673    -83   -357    -35       O  
+ATOM    869  CB  ASP B  20     -27.195 -24.650 -25.016  1.00 49.61           C  
+ANISOU  869  CB  ASP B  20     7385   5821   5642     92   -366   -229       C  
+ATOM    870  CG  ASP B  20     -26.366 -24.538 -26.278  1.00 56.74           C  
+ANISOU  870  CG  ASP B  20     8391   6766   6398    224   -291   -299       C  
+ATOM    871  OD1 ASP B  20     -26.482 -23.537 -27.079  1.00 61.65           O  
+ANISOU  871  OD1 ASP B  20     8935   7508   6978    194   -270   -261       O  
+ATOM    872  OD2 ASP B  20     -25.587 -25.487 -26.498  1.00 64.45           O  
+ANISOU  872  OD2 ASP B  20     9551   7653   7283    379   -246   -389       O  
+ATOM    873  N   ASP B  21     -29.960 -23.866 -23.882  1.00 51.35           N  
+ANISOU  873  N   ASP B  21     7285   6165   6058   -283   -552    -48       N  
+ATOM    874  CA  ASP B  21     -31.093 -24.022 -22.958  1.00 47.36           C  
+ANISOU  874  CA  ASP B  21     6647   5708   5637   -444   -612     27       C  
+ATOM    875  C   ASP B  21     -30.985 -25.153 -21.926  1.00 47.41           C  
+ANISOU  875  C   ASP B  21     6767   5582   5664   -512   -610     28       C  
+ATOM    876  O   ASP B  21     -31.828 -25.247 -21.025  1.00 45.27           O  
+ANISOU  876  O   ASP B  21     6372   5373   5454   -650   -626    105       O  
+ATOM    877  CB  ASP B  21     -32.458 -24.057 -23.695  1.00 51.66           C  
+ANISOU  877  CB  ASP B  21     7134   6351   6143   -629   -764     67       C  
+ATOM    878  CG  ASP B  21     -32.657 -25.259 -24.632  1.00 61.29           C  
+ANISOU  878  CG  ASP B  21     8623   7436   7229   -779   -899     -7       C  
+ATOM    879  OD1 ASP B  21     -31.869 -26.233 -24.690  1.00 60.99           O  
+ANISOU  879  OD1 ASP B  21     8860   7188   7124   -738   -879    -93       O  
+ATOM    880  OD2 ASP B  21     -33.671 -25.198 -25.362  1.00 76.44           O  
+ANISOU  880  OD2 ASP B  21    10482   9466   9093   -935  -1042     19       O  
+ATOM    881  N   ASP B  22     -29.986 -26.019 -22.094  1.00 45.76           N  
+ANISOU  881  N   ASP B  22     6801   5197   5386   -402   -588    -47       N  
+ATOM    882  CA  ASP B  22     -29.698 -27.103 -21.184  1.00 49.94           C  
+ANISOU  882  CA  ASP B  22     7499   5560   5913   -408   -587    -39       C  
+ATOM    883  C   ASP B  22     -28.367 -26.870 -20.478  1.00 51.81           C  
+ANISOU  883  C   ASP B  22     7692   5815   6179   -156   -468    -51       C  
+ATOM    884  O   ASP B  22     -27.852 -27.770 -19.904  1.00 47.85           O  
+ANISOU  884  O   ASP B  22     7360   5171   5649    -76   -467    -52       O  
+ATOM    885  CB  ASP B  22     -29.681 -28.442 -21.915  1.00 54.09           C  
+ANISOU  885  CB  ASP B  22     8395   5838   6318   -461   -680   -116       C  
+ATOM    886  CG  ASP B  22     -28.657 -28.512 -23.084  1.00 63.91           C  
+ANISOU  886  CG  ASP B  22     9803   7027   7451   -230   -637   -239       C  
+ATOM    887  OD1 ASP B  22     -28.251 -27.429 -23.607  1.00 56.77           O  
+ANISOU  887  OD1 ASP B  22     8699   6311   6557   -118   -561   -247       O  
+ATOM    888  OD2 ASP B  22     -28.283 -29.685 -23.476  1.00 60.29           O  
+ANISOU  888  OD2 ASP B  22     9703   6324   6879   -163   -672   -325       O  
+ATOM    889  N   VAL B  23     -27.833 -25.644 -20.499  1.00 44.07           N  
+ANISOU  889  N   VAL B  23     6485   5013   5246    -45   -380    -50       N  
+ATOM    890  CA  VAL B  23     -26.611 -25.310 -19.757  1.00 47.28           C  
+ANISOU  890  CA  VAL B  23     6793   5491   5677    138   -286    -54       C  
+ATOM    891  C   VAL B  23     -26.941 -24.305 -18.663  1.00 46.19           C  
+ANISOU  891  C   VAL B  23     6430   5488   5630     71   -247      9       C  
+ATOM    892  O   VAL B  23     -27.644 -23.285 -18.881  1.00 39.27           O  
+ANISOU  892  O   VAL B  23     5415   4705   4798    -11   -238     32       O  
+ATOM    893  CB  VAL B  23     -25.523 -24.795 -20.707  1.00 50.38           C  
+ANISOU  893  CB  VAL B  23     7146   5975   6020    297   -208   -117       C  
+ATOM    894  CG1 VAL B  23     -24.342 -24.208 -19.967  1.00 46.76           C  
+ANISOU  894  CG1 VAL B  23     6507   5665   5593    423   -122   -109       C  
+ATOM    895  CG2 VAL B  23     -25.132 -25.955 -21.653  1.00 50.32           C  
+ANISOU  895  CG2 VAL B  23     7401   5823   5893    416   -228   -198       C  
+ATOM    896  N   TYR B  24     -26.524 -24.659 -17.451  1.00 44.23           N  
+ANISOU  896  N   TYR B  24     6178   5236   5390    119   -233     41       N  
+ATOM    897  CA  TYR B  24     -26.635 -23.726 -16.362  1.00 42.32           C  
+ANISOU  897  CA  TYR B  24     5758   5120   5199     83   -187     78       C  
+ATOM    898  C   TYR B  24     -25.823 -22.471 -16.675  1.00 41.44           C  
+ANISOU  898  C   TYR B  24     5501   5135   5109    147   -123     34       C  
+ATOM    899  O   TYR B  24     -24.735 -22.521 -17.198  1.00 44.52           O  
+ANISOU  899  O   TYR B  24     5887   5563   5465    255    -98     -5       O  
+ATOM    900  CB  TYR B  24     -26.178 -24.336 -15.052  1.00 44.23           C  
+ANISOU  900  CB  TYR B  24     6041   5350   5413    135   -194    117       C  
+ATOM    901  CG  TYR B  24     -27.087 -25.431 -14.497  1.00 46.96           C  
+ANISOU  901  CG  TYR B  24     6541   5568   5732     16   -244    192       C  
+ATOM    902  CD1 TYR B  24     -28.468 -25.241 -14.364  1.00 44.56           C  
+ANISOU  902  CD1 TYR B  24     6173   5299   5459   -181   -243    243       C  
+ATOM    903  CD2 TYR B  24     -26.538 -26.649 -14.079  1.00 51.68           C  
+ANISOU  903  CD2 TYR B  24     7347   6023   6266    106   -288    224       C  
+ATOM    904  CE1 TYR B  24     -29.285 -26.242 -13.871  1.00 49.24           C  
+ANISOU  904  CE1 TYR B  24     6889   5801   6017   -343   -281    325       C  
+ATOM    905  CE2 TYR B  24     -27.342 -27.663 -13.564  1.00 49.44           C  
+ANISOU  905  CE2 TYR B  24     7249   5591   5944    -40   -332    309       C  
+ATOM    906  CZ  TYR B  24     -28.691 -27.448 -13.430  1.00 51.30           C  
+ANISOU  906  CZ  TYR B  24     7400   5881   6209   -289   -324    362       C  
+ATOM    907  OH  TYR B  24     -29.429 -28.459 -12.887  1.00 52.12           O  
+ANISOU  907  OH  TYR B  24     7677   5862   6265   -476   -359    461       O  
+ATOM    908  N   VAL B  25     -26.397 -21.353 -16.303  1.00 42.58           N  
+ANISOU  908  N   VAL B  25     5534   5344   5298     74    -91     48       N  
+ATOM    909  CA  VAL B  25     -25.870 -19.977 -16.515  1.00 43.51           C  
+ANISOU  909  CA  VAL B  25     5561   5536   5435     77    -35     18       C  
+ATOM    910  C   VAL B  25     -24.479 -19.883 -15.914  1.00 45.47           C  
+ANISOU  910  C   VAL B  25     5750   5871   5655    125    -13    -11       C  
+ATOM    911  O   VAL B  25     -23.583 -19.234 -16.483  1.00 42.02           O  
+ANISOU  911  O   VAL B  25     5255   5505   5205    121     24    -37       O  
+ATOM    912  CB  VAL B  25     -27.014 -19.053 -15.974  1.00 47.07           C  
+ANISOU  912  CB  VAL B  25     5964   5993   5928     22    -14     42       C  
+ATOM    913  CG1 VAL B  25     -26.658 -17.985 -14.979  1.00 51.34           C  
+ANISOU  913  CG1 VAL B  25     6467   6569   6468     14     35     10       C  
+ATOM    914  CG2 VAL B  25     -27.951 -18.612 -17.124  1.00 50.07           C  
+ANISOU  914  CG2 VAL B  25     6347   6348   6328     11    -32     68       C  
+ATOM    915  N   LEU B  26     -24.262 -20.562 -14.781  1.00 42.91           N  
+ANISOU  915  N   LEU B  26     5432   5567   5305    159    -44      3       N  
+ATOM    916  CA  LEU B  26     -22.937 -20.508 -14.120  1.00 44.21           C  
+ANISOU  916  CA  LEU B  26     5508   5860   5427    214    -52    -16       C  
+ATOM    917  C   LEU B  26     -21.873 -21.156 -15.044  1.00 43.16           C  
+ANISOU  917  C   LEU B  26     5348   5786   5263    347    -46    -31       C  
+ATOM    918  O   LEU B  26     -20.782 -20.644 -15.204  1.00 41.77           O  
+ANISOU  918  O   LEU B  26     5032   5771   5066    355    -19    -54       O  
+ATOM    919  CB  LEU B  26     -22.973 -21.227 -12.757  1.00 43.87           C  
+ANISOU  919  CB  LEU B  26     5503   5824   5338    252   -103     21       C  
+ATOM    920  CG  LEU B  26     -21.633 -21.303 -12.009  1.00 51.12           C  
+ANISOU  920  CG  LEU B  26     6319   6909   6194    330   -147     15       C  
+ATOM    921  CD1 LEU B  26     -20.989 -19.928 -11.865  1.00 47.47           C  
+ANISOU  921  CD1 LEU B  26     5717   6587   5732    203   -127    -41       C  
+ATOM    922  CD2 LEU B  26     -21.802 -21.989 -10.650  1.00 53.81           C  
+ANISOU  922  CD2 LEU B  26     6734   7244   6465    368   -206     70       C  
+ATOM    923  N   ASP B  27     -22.221 -22.283 -15.633  1.00 44.33           N  
+ANISOU  923  N   ASP B  27     5638   5809   5394    443    -66    -21       N  
+ATOM    924  CA  ASP B  27     -21.332 -23.028 -16.510  1.00 46.01           C  
+ANISOU  924  CA  ASP B  27     5876   6049   5554    621    -47    -49       C  
+ATOM    925  C   ASP B  27     -20.956 -22.226 -17.766  1.00 49.22           C  
+ANISOU  925  C   ASP B  27     6200   6549   5953    583     32    -86       C  
+ATOM    926  O   ASP B  27     -19.802 -22.149 -18.106  1.00 50.44           O  
+ANISOU  926  O   ASP B  27     6223   6878   6063    680     86   -104       O  
+ATOM    927  CB  ASP B  27     -21.974 -24.337 -16.843  1.00 48.69           C  
+ANISOU  927  CB  ASP B  27     6461   6173   5864    695    -93    -45       C  
+ATOM    928  CG  ASP B  27     -22.021 -25.293 -15.605  1.00 61.91           C  
+ANISOU  928  CG  ASP B  27     8247   7761   7515    764   -163     12       C  
+ATOM    929  OD1 ASP B  27     -21.027 -25.395 -14.870  1.00 72.00           O  
+ANISOU  929  OD1 ASP B  27     9424   9171   8760    907   -177     30       O  
+ATOM    930  OD2 ASP B  27     -23.034 -25.974 -15.376  1.00 65.76           O  
+ANISOU  930  OD2 ASP B  27     8922   8059   8004    668   -211     51       O  
+ATOM    931  N   ALA B  28     -21.929 -21.573 -18.394  1.00 48.34           N  
+ANISOU  931  N   ALA B  28     6146   6346   5874    438     40    -82       N  
+ATOM    932  CA  ALA B  28     -21.708 -20.761 -19.556  1.00 48.04           C  
+ANISOU  932  CA  ALA B  28     6072   6368   5811    384    108    -93       C  
+ATOM    933  C   ALA B  28     -20.772 -19.641 -19.163  1.00 49.27           C  
+ANISOU  933  C   ALA B  28     6043   6698   5979    293    162    -84       C  
+ATOM    934  O   ALA B  28     -19.857 -19.319 -19.908  1.00 48.19           O  
+ANISOU  934  O   ALA B  28     5813   6707   5789    295    240    -88       O  
+ATOM    935  CB  ALA B  28     -23.039 -20.183 -20.056  1.00 52.12           C  
+ANISOU  935  CB  ALA B  28     6682   6758   6362    258     77    -68       C  
+ATOM    936  N   ALA B  29     -20.986 -19.058 -17.979  1.00 46.38           N  
+ANISOU  936  N   ALA B  29     5631   6327   5664    194    124    -74       N  
+ATOM    937  CA  ALA B  29     -20.076 -18.026 -17.489  1.00 48.20           C  
+ANISOU  937  CA  ALA B  29     5715   6706   5890     65    150    -79       C  
+ATOM    938  C   ALA B  29     -18.630 -18.516 -17.392  1.00 50.01           C  
+ANISOU  938  C   ALA B  29     5752   7184   6064    154    166    -86       C  
+ATOM    939  O   ALA B  29     -17.691 -17.860 -17.830  1.00 49.30           O  
+ANISOU  939  O   ALA B  29     5514   7276   5941     54    227    -80       O  
+ATOM    940  CB  ALA B  29     -20.518 -17.475 -16.122  1.00 48.24           C  
+ANISOU  940  CB  ALA B  29     5740   6659   5928    -29     96    -90       C  
+ATOM    941  N   GLU B  30     -18.455 -19.668 -16.787  1.00 50.74           N  
+ANISOU  941  N   GLU B  30     5842   7297   6140    344    111    -87       N  
+ATOM    942  CA  GLU B  30     -17.125 -20.211 -16.573  1.00 53.52           C  
+ANISOU  942  CA  GLU B  30     5996   7905   6434    495    109    -84       C  
+ATOM    943  C   GLU B  30     -16.487 -20.582 -17.882  1.00 55.58           C  
+ANISOU  943  C   GLU B  30     6200   8279   6639    634    213    -95       C  
+ATOM    944  O   GLU B  30     -15.310 -20.345 -18.062  1.00 53.30           O  
+ANISOU  944  O   GLU B  30     5662   8284   6302    650    267    -87       O  
+ATOM    945  CB  GLU B  30     -17.245 -21.433 -15.659  1.00 55.76           C  
+ANISOU  945  CB  GLU B  30     6364   8121   6701    708     18    -66       C  
+ATOM    946  CG  GLU B  30     -17.599 -20.966 -14.244  1.00 60.54           C  
+ANISOU  946  CG  GLU B  30     6973   8707   7322    564    -69    -52       C  
+ATOM    947  CD  GLU B  30     -17.847 -22.100 -13.266  1.00 67.44           C  
+ANISOU  947  CD  GLU B  30     7969   9491   8161    734   -157     -9       C  
+ATOM    948  OE1 GLU B  30     -18.209 -23.248 -13.665  1.00 66.53           O  
+ANISOU  948  OE1 GLU B  30     8035   9206   8037    915   -159      8       O  
+ATOM    949  OE2 GLU B  30     -17.669 -21.807 -12.069  1.00 71.86           O  
+ANISOU  949  OE2 GLU B  30     8471  10144   8689    666   -231      5       O  
+ATOM    950  N   GLU B  31     -17.279 -21.156 -18.796  1.00 55.65           N  
+ANISOU  950  N   GLU B  31     6432   8073   6637    723    241   -115       N  
+ATOM    951  CA  GLU B  31     -16.811 -21.477 -20.146  1.00 59.63           C  
+ANISOU  951  CA  GLU B  31     6942   8655   7058    853    350   -141       C  
+ATOM    952  C   GLU B  31     -16.291 -20.194 -20.865  1.00 55.72           C  
+ANISOU  952  C   GLU B  31     6283   8348   6540    632    457   -115       C  
+ATOM    953  O   GLU B  31     -15.355 -20.248 -21.599  1.00 58.65           O  
+ANISOU  953  O   GLU B  31     6505   8951   6826    714    569   -117       O  
+ATOM    954  CB  GLU B  31     -17.945 -22.145 -20.947  1.00 70.50           C  
+ANISOU  954  CB  GLU B  31     8629   9739   8417    905    329   -174       C  
+ATOM    955  CG  GLU B  31     -17.483 -23.157 -21.980  1.00 89.10           C  
+ANISOU  955  CG  GLU B  31    11093  12105  10654   1169    401   -233       C  
+ATOM    956  CD  GLU B  31     -16.540 -24.196 -21.373  1.00108.88           C  
+ANISOU  956  CD  GLU B  31    13534  14713  13120   1482    395   -248       C  
+ATOM    957  OE1 GLU B  31     -16.868 -24.715 -20.275  1.00122.63           O  
+ANISOU  957  OE1 GLU B  31    15352  16322  14918   1517    281   -221       O  
+ATOM    958  OE2 GLU B  31     -15.461 -24.475 -21.968  1.00106.25           O  
+ANISOU  958  OE2 GLU B  31    13067  14613  12689   1708    509   -276       O  
+ATOM    959  N   ALA B  32     -16.902 -19.037 -20.607  1.00 54.88           N  
+ANISOU  959  N   ALA B  32     6217   8134   6498    356    427    -85       N  
+ATOM    960  CA  ALA B  32     -16.492 -17.745 -21.179  1.00 59.73           C  
+ANISOU  960  CA  ALA B  32     6747   8857   7089    106    511    -44       C  
+ATOM    961  C   ALA B  32     -15.343 -17.016 -20.467  1.00 63.02           C  
+ANISOU  961  C   ALA B  32     6892   9546   7504    -75    520    -22       C  
+ATOM    962  O   ALA B  32     -14.998 -15.927 -20.905  1.00 63.67           O  
+ANISOU  962  O   ALA B  32     6934   9694   7561   -332    587     19       O  
+ATOM    963  CB  ALA B  32     -17.705 -16.806 -21.263  1.00 56.13           C  
+ANISOU  963  CB  ALA B  32     6511   8123   6691    -78    469    -20       C  
+ATOM    964  N   GLY B  33     -14.771 -17.574 -19.387  1.00 61.55           N  
+ANISOU  964  N   GLY B  33     6539   9514   7332     31    442    -42       N  
+ATOM    965  CA  GLY B  33     -13.600 -16.980 -18.707  1.00 63.73           C  
+ANISOU  965  CA  GLY B  33     6518  10110   7586   -146    423    -23       C  
+ATOM    966  C   GLY B  33     -13.895 -16.058 -17.525  1.00 66.50           C  
+ANISOU  966  C   GLY B  33     6927  10352   7985   -414    308    -38       C  
+ATOM    967  O   GLY B  33     -13.016 -15.338 -17.073  1.00 78.05           O  
+ANISOU  967  O   GLY B  33     8192  12044   9417   -656    282    -30       O  
+ATOM    968  N   ILE B  34     -15.129 -16.076 -17.028  1.00 67.68           N  
+ANISOU  968  N   ILE B  34     7348  10169   8195   -380    241    -66       N  
+ATOM    969  CA  ILE B  34     -15.538 -15.285 -15.866  1.00 69.74           C  
+ANISOU  969  CA  ILE B  34     7714  10302   8481   -573    147    -99       C  
+ATOM    970  C   ILE B  34     -15.518 -16.184 -14.636  1.00 64.98           C  
+ANISOU  970  C   ILE B  34     7061   9759   7866   -396     34   -118       C  
+ATOM    971  O   ILE B  34     -15.991 -17.333 -14.669  1.00 60.93           O  
+ANISOU  971  O   ILE B  34     6620   9161   7366   -127     24   -105       O  
+ATOM    972  CB  ILE B  34     -17.001 -14.806 -15.975  1.00 71.51           C  
+ANISOU  972  CB  ILE B  34     8249  10155   8763   -593    156   -112       C  
+ATOM    973  CG1 ILE B  34     -17.270 -14.074 -17.281  1.00 80.00           C  
+ANISOU  973  CG1 ILE B  34     9439  11114   9843   -696    254    -73       C  
+ATOM    974  CG2 ILE B  34     -17.370 -13.876 -14.827  1.00 74.50           C  
+ANISOU  974  CG2 ILE B  34     8756  10405   9144   -770     88   -161       C  
+ATOM    975  CD1 ILE B  34     -18.763 -13.913 -17.553  1.00 74.26           C  
+ANISOU  975  CD1 ILE B  34     8970  10076   9167   -610    251    -68       C  
+ATOM    976  N   ASP B  35     -15.021 -15.649 -13.537  1.00 61.68           N  
+ANISOU  976  N   ASP B  35     6562   9464   7408   -566    -59   -146       N  
+ATOM    977  CA  ASP B  35     -15.019 -16.414 -12.290  1.00 75.53           C  
+ANISOU  977  CA  ASP B  35     8296  11276   9122   -414   -177   -152       C  
+ATOM    978  C   ASP B  35     -16.098 -15.810 -11.437  1.00 67.96           C  
+ANISOU  978  C   ASP B  35     7599  10051   8171   -525   -210   -201       C  
+ATOM    979  O   ASP B  35     -16.004 -14.664 -11.028  1.00 71.36           O  
+ANISOU  979  O   ASP B  35     8091  10448   8572   -786   -232   -255       O  
+ATOM    980  CB  ASP B  35     -13.639 -16.434 -11.587  1.00 82.96           C  
+ANISOU  980  CB  ASP B  35     8931  12609   9978   -475   -282   -144       C  
+ATOM    981  CG  ASP B  35     -12.583 -17.205 -12.400  1.00 94.51           C  
+ANISOU  981  CG  ASP B  35    10096  14387  11425   -270   -232    -89       C  
+ATOM    982  OD1 ASP B  35     -12.806 -18.403 -12.759  1.00 96.53           O  
+ANISOU  982  OD1 ASP B  35    10403  14579  11694     79   -201    -57       O  
+ATOM    983  OD2 ASP B  35     -11.544 -16.586 -12.714  1.00 99.59           O  
+ANISOU  983  OD2 ASP B  35    10468  15337  12033   -469   -215    -78       O  
+ATOM    984  N   LEU B  36     -17.154 -16.583 -11.237  1.00 61.63           N  
+ANISOU  984  N   LEU B  36     6966   9051   7399   -328   -200   -183       N  
+ATOM    985  CA  LEU B  36     -18.261 -16.213 -10.383  1.00 56.78           C  
+ANISOU  985  CA  LEU B  36     6568   8229   6777   -371   -208   -218       C  
+ATOM    986  C   LEU B  36     -18.245 -17.129  -9.153  1.00 52.62           C  
+ANISOU  986  C   LEU B  36     6041   7780   6171   -238   -300   -192       C  
+ATOM    987  O   LEU B  36     -17.788 -18.261  -9.215  1.00 51.28           O  
+ANISOU  987  O   LEU B  36     5782   7714   5987    -45   -339   -130       O  
+ATOM    988  CB  LEU B  36     -19.602 -16.402 -11.121  1.00 60.32           C  
+ANISOU  988  CB  LEU B  36     7183   8425   7309   -276   -120   -195       C  
+ATOM    989  CG  LEU B  36     -19.852 -15.603 -12.408  1.00 57.54           C  
+ANISOU  989  CG  LEU B  36     6878   7960   7022   -360    -36   -197       C  
+ATOM    990  CD1 LEU B  36     -21.213 -16.023 -13.015  1.00 57.02           C  
+ANISOU  990  CD1 LEU B  36     6941   7702   7020   -235     11   -162       C  
+ATOM    991  CD2 LEU B  36     -19.778 -14.120 -12.106  1.00 59.01           C  
+ANISOU  991  CD2 LEU B  36     7159   8073   7187   -580    -26   -256       C  
+ATOM    992  N   PRO B  37     -18.782 -16.647  -8.045  1.00 49.77           N  
+ANISOU  992  N   PRO B  37     5814   7352   5742   -321   -326   -238       N  
+ATOM    993  CA  PRO B  37     -18.701 -17.404  -6.819  1.00 52.69           C  
+ANISOU  993  CA  PRO B  37     6200   7815   6003   -225   -415   -204       C  
+ATOM    994  C   PRO B  37     -19.633 -18.609  -6.877  1.00 53.75           C  
+ANISOU  994  C   PRO B  37     6445   7807   6169    -30   -373   -116       C  
+ATOM    995  O   PRO B  37     -20.659 -18.578  -7.575  1.00 52.82           O  
+ANISOU  995  O   PRO B  37     6420   7504   6142    -22   -275   -109       O  
+ATOM    996  CB  PRO B  37     -19.214 -16.410  -5.772  1.00 55.23           C  
+ANISOU  996  CB  PRO B  37     6682   8069   6232   -380   -415   -294       C  
+ATOM    997  CG  PRO B  37     -20.178 -15.544  -6.538  1.00 56.99           C  
+ANISOU  997  CG  PRO B  37     7035   8061   6556   -438   -287   -341       C  
+ATOM    998  CD  PRO B  37     -19.480 -15.359  -7.863  1.00 55.95           C  
+ANISOU  998  CD  PRO B  37     6768   7965   6526   -486   -269   -323       C  
+ATOM    999  N   TYR B  38     -19.264 -19.661  -6.164  1.00 51.10           N  
+ANISOU  999  N   TYR B  38     6105   7561   5748    112   -458    -39       N  
+ATOM   1000  CA  TYR B  38     -20.137 -20.809  -5.974  1.00 53.20           C  
+ANISOU 1000  CA  TYR B  38     6529   7673   6010    243   -434     55       C  
+ATOM   1001  C   TYR B  38     -19.656 -21.588  -4.747  1.00 54.18           C  
+ANISOU 1001  C   TYR B  38     6693   7909   5982    349   -548    134       C  
+ATOM   1002  O   TYR B  38     -18.582 -21.362  -4.322  1.00 49.82           O  
+ANISOU 1002  O   TYR B  38     6003   7570   5353    362   -654    116       O  
+ATOM   1003  CB  TYR B  38     -20.086 -21.734  -7.179  1.00 49.99           C  
+ANISOU 1003  CB  TYR B  38     6125   7167   5701    391   -407    104       C  
+ATOM   1004  CG  TYR B  38     -18.715 -22.275  -7.467  1.00 51.86           C  
+ANISOU 1004  CG  TYR B  38     6213   7579   5912    569   -486    127       C  
+ATOM   1005  CD1 TYR B  38     -17.850 -21.585  -8.312  1.00 55.55           C  
+ANISOU 1005  CD1 TYR B  38     6471   8204   6429    528   -464     64       C  
+ATOM   1006  CD2 TYR B  38     -18.269 -23.505  -6.904  1.00 61.75           C  
+ANISOU 1006  CD2 TYR B  38     7532   8850   7078    796   -577    225       C  
+ATOM   1007  CE1 TYR B  38     -16.594 -22.086  -8.614  1.00 58.14           C  
+ANISOU 1007  CE1 TYR B  38     6611   8752   6728    714   -515     89       C  
+ATOM   1008  CE2 TYR B  38     -16.989 -24.012  -7.186  1.00 59.46           C  
+ANISOU 1008  CE2 TYR B  38     7079   8753   6757   1025   -645    249       C  
+ATOM   1009  CZ  TYR B  38     -16.163 -23.294  -8.041  1.00 63.46           C  
+ANISOU 1009  CZ  TYR B  38     7329   9462   7319    985   -607    177       C  
+ATOM   1010  OH  TYR B  38     -14.891 -23.736  -8.343  1.00 72.30           O  
+ANISOU 1010  OH  TYR B  38     8229  10837   8402   1217   -652    201       O  
+ATOM   1011  N   SER B  39     -20.429 -22.569  -4.273  1.00 53.67           N  
+ANISOU 1011  N   SER B  39     6815   7706   5871    420   -534    237       N  
+ATOM   1012  CA  SER B  39     -19.980 -23.484  -3.243  1.00 53.24           C  
+ANISOU 1012  CA  SER B  39     6845   7718   5665    553   -645    347       C  
+ATOM   1013  C   SER B  39     -20.482 -24.911  -3.576  1.00 53.99           C  
+ANISOU 1013  C   SER B  39     7143   7585   5782    691   -631    477       C  
+ATOM   1014  O   SER B  39     -19.746 -25.715  -4.122  1.00 60.00           O  
+ANISOU 1014  O   SER B  39     7905   8323   6567    901   -692    520       O  
+ATOM   1015  CB  SER B  39     -20.438 -22.974  -1.880  1.00 54.47           C  
+ANISOU 1015  CB  SER B  39     7088   7945   5660    422   -647    339       C  
+ATOM   1016  OG  SER B  39     -19.961 -23.809  -0.856  1.00 58.10           O  
+ANISOU 1016  OG  SER B  39     7640   8485   5947    547   -768    457       O  
+ATOM   1017  N   CYS B  40     -21.745 -25.188  -3.303  1.00 53.52           N  
+ANISOU 1017  N   CYS B  40     7259   7360   5714    563   -542    532       N  
+ATOM   1018  CA  CYS B  40     -22.341 -26.502  -3.494  1.00 52.54           C  
+ANISOU 1018  CA  CYS B  40     7371   6999   5591    607   -533    661       C  
+ATOM   1019  C   CYS B  40     -22.554 -26.894  -4.990  1.00 54.29           C  
+ANISOU 1019  C   CYS B  40     7615   7039   5972    636   -494    621       C  
+ATOM   1020  O   CYS B  40     -22.599 -28.079  -5.354  1.00 56.18           O  
+ANISOU 1020  O   CYS B  40     8070   7067   6206    733   -528    702       O  
+ATOM   1021  CB  CYS B  40     -23.706 -26.493  -2.758  1.00 53.96           C  
+ANISOU 1021  CB  CYS B  40     7670   7122   5708    390   -431    727       C  
+ATOM   1022  SG  CYS B  40     -25.014 -25.506  -3.570  1.00 54.05           S  
+ANISOU 1022  SG  CYS B  40     7540   7122   5874    175   -270    621       S  
+ATOM   1023  N   ARG B  41     -22.745 -25.881  -5.830  1.00 55.21           N  
+ANISOU 1023  N   ARG B  41     7548   7218   6210    539   -423    498       N  
+ATOM   1024  CA  ARG B  41     -23.100 -26.036  -7.243  1.00 57.45           C  
+ANISOU 1024  CA  ARG B  41     7842   7361   6623    525   -377    449       C  
+ATOM   1025  C   ARG B  41     -24.289 -26.944  -7.426  1.00 53.39           C  
+ANISOU 1025  C   ARG B  41     7544   6624   6118    400   -351    531       C  
+ATOM   1026  O   ARG B  41     -24.325 -27.732  -8.363  1.00 57.10           O  
+ANISOU 1026  O   ARG B  41     8153   6914   6628    449   -373    531       O  
+ATOM   1027  CB  ARG B  41     -21.926 -26.568  -8.054  1.00 57.25           C  
+ANISOU 1027  CB  ARG B  41     7805   7331   6614    765   -433    421       C  
+ATOM   1028  CG  ARG B  41     -20.730 -25.656  -8.128  1.00 63.18           C  
+ANISOU 1028  CG  ARG B  41     8288   8346   7369    845   -452    341       C  
+ATOM   1029  CD  ARG B  41     -19.678 -26.364  -8.976  1.00 72.83           C  
+ANISOU 1029  CD  ARG B  41     9491   9584   8595   1112   -481    331       C  
+ATOM   1030  NE  ARG B  41     -20.005 -26.268 -10.416  1.00 66.24           N  
+ANISOU 1030  NE  ARG B  41     8672   8642   7853   1080   -399    256       N  
+ATOM   1031  CZ  ARG B  41     -19.501 -25.337 -11.206  1.00 60.12           C  
+ANISOU 1031  CZ  ARG B  41     7679   8033   7131   1040   -342    173       C  
+ATOM   1032  NH1 ARG B  41     -18.683 -24.440 -10.693  1.00 58.08           N  
+ANISOU 1032  NH1 ARG B  41     7175   8038   6852    995   -361    150       N  
+ATOM   1033  NH2 ARG B  41     -19.820 -25.271 -12.489  1.00 59.63           N  
+ANISOU 1033  NH2 ARG B  41     7655   7877   7124   1014   -273    118       N  
+ATOM   1034  N   ALA B  42     -25.254 -26.842  -6.528  1.00 50.75           N  
+ANISOU 1034  N   ALA B  42     7240   6313   5731    225   -301    598       N  
+ATOM   1035  CA  ALA B  42     -26.322 -27.854  -6.466  1.00 55.82           C  
+ANISOU 1035  CA  ALA B  42     8087   6773   6348     63   -286    714       C  
+ATOM   1036  C   ALA B  42     -27.689 -27.296  -6.092  1.00 53.85           C  
+ANISOU 1036  C   ALA B  42     7723   6630   6105   -178   -176    738       C  
+ATOM   1037  O   ALA B  42     -28.646 -28.060  -5.838  1.00 55.78           O  
+ANISOU 1037  O   ALA B  42     8091   6791   6308   -370   -150    854       O  
+ATOM   1038  CB  ALA B  42     -25.919 -28.950  -5.457  1.00 58.15           C  
+ANISOU 1038  CB  ALA B  42     8635   6960   6496    141   -354    856       C  
+ATOM   1039  N   GLY B  43     -27.766 -25.976  -6.007  1.00 52.94           N  
+ANISOU 1039  N   GLY B  43     7380   6706   6028   -168   -108    636       N  
+ATOM   1040  CA  GLY B  43     -29.019 -25.311  -5.704  1.00 57.58           C  
+ANISOU 1040  CA  GLY B  43     7828   7426   6621   -321      9    641       C  
+ATOM   1041  C   GLY B  43     -29.393 -25.157  -4.247  1.00 56.47           C  
+ANISOU 1041  C   GLY B  43     7699   7426   6330   -371     90    705       C  
+ATOM   1042  O   GLY B  43     -30.490 -24.676  -3.919  1.00 61.40           O  
+ANISOU 1042  O   GLY B  43     8200   8190   6936   -473    214    718       O  
+ATOM   1043  N   SER B  44     -28.507 -25.552  -3.359  1.00 57.15           N  
+ANISOU 1043  N   SER B  44     7922   7502   6288   -282     25    748       N  
+ATOM   1044  CA  SER B  44     -28.864 -25.573  -1.947  1.00 60.58           C  
+ANISOU 1044  CA  SER B  44     8415   8061   6539   -340     95    828       C  
+ATOM   1045  C   SER B  44     -28.070 -24.590  -1.083  1.00 55.02           C  
+ANISOU 1045  C   SER B  44     7667   7512   5724   -218     81    721       C  
+ATOM   1046  O   SER B  44     -27.939 -24.770   0.117  1.00 56.43           O  
+ANISOU 1046  O   SER B  44     7952   7776   5712   -219     84    785       O  
+ATOM   1047  CB  SER B  44     -28.724 -26.997  -1.455  1.00 64.11           C  
+ANISOU 1047  CB  SER B  44     9118   8363   6875   -384     27   1009       C  
+ATOM   1048  OG  SER B  44     -27.369 -27.403  -1.576  1.00 72.90           O  
+ANISOU 1048  OG  SER B  44    10341   9378   7978   -177   -128    997       O  
+ATOM   1049  N   CYS B  45     -27.538 -23.526  -1.680  1.00 50.30           N  
+ANISOU 1049  N   CYS B  45     6932   6949   5228   -137     60    559       N  
+ATOM   1050  CA  CYS B  45     -26.911 -22.490  -0.877  1.00 49.85           C  
+ANISOU 1050  CA  CYS B  45     6852   7028   5060    -85     47    441       C  
+ATOM   1051  C   CYS B  45     -27.172 -21.136  -1.520  1.00 51.33           C  
+ANISOU 1051  C   CYS B  45     6911   7228   5361    -83    119    279       C  
+ATOM   1052  O   CYS B  45     -27.967 -21.005  -2.440  1.00 55.12           O  
+ANISOU 1052  O   CYS B  45     7307   7650   5984   -106    191    278       O  
+ATOM   1053  CB  CYS B  45     -25.390 -22.791  -0.653  1.00 48.86           C  
+ANISOU 1053  CB  CYS B  45     6762   6928   4874     20   -134    439       C  
+ATOM   1054  SG  CYS B  45     -24.160 -22.368  -1.931  1.00 53.35           S  
+ANISOU 1054  SG  CYS B  45     7176   7476   5616    105   -244    330       S  
+ATOM   1055  N   SER B  46     -26.488 -20.125  -1.034  1.00 50.33           N  
+ANISOU 1055  N   SER B  46     6793   7170   5158    -63     86    148       N  
+ATOM   1056  CA  SER B  46     -26.687 -18.804  -1.525  1.00 53.49           C  
+ANISOU 1056  CA  SER B  46     7146   7540   5638    -62    151      0       C  
+ATOM   1057  C   SER B  46     -25.436 -18.250  -2.234  1.00 51.56           C  
+ANISOU 1057  C   SER B  46     6851   7262   5478    -77     28    -89       C  
+ATOM   1058  O   SER B  46     -25.397 -17.049  -2.580  1.00 51.57           O  
+ANISOU 1058  O   SER B  46     6861   7210   5522   -105     60   -215       O  
+ATOM   1059  CB  SER B  46     -27.077 -17.908  -0.321  1.00 54.94           C  
+ANISOU 1059  CB  SER B  46     7434   7804   5634    -60    242   -100       C  
+ATOM   1060  OG  SER B  46     -25.958 -17.693   0.515  1.00 59.45           O  
+ANISOU 1060  OG  SER B  46     8091   8449   6049    -97    114   -164       O  
+ATOM   1061  N   SER B  47     -24.417 -19.082  -2.465  1.00 50.22           N  
+ANISOU 1061  N   SER B  47     6631   7124   5322    -52   -104    -22       N  
+ATOM   1062  CA  SER B  47     -23.104 -18.545  -2.910  1.00 53.80           C  
+ANISOU 1062  CA  SER B  47     6993   7634   5811    -82   -216   -100       C  
+ATOM   1063  C   SER B  47     -23.102 -17.974  -4.299  1.00 49.57           C  
+ANISOU 1063  C   SER B  47     6378   7002   5454   -106   -173   -147       C  
+ATOM   1064  O   SER B  47     -22.409 -17.004  -4.556  1.00 47.90           O  
+ANISOU 1064  O   SER B  47     6131   6812   5254   -198   -204   -242       O  
+ATOM   1065  CB  SER B  47     -21.985 -19.587  -2.863  1.00 55.00           C  
+ANISOU 1065  CB  SER B  47     7072   7893   5931      4   -358    -10       C  
+ATOM   1066  OG  SER B  47     -21.799 -19.986  -1.539  1.00 67.17           O  
+ANISOU 1066  OG  SER B  47     8697   9542   7281     24   -428     35       O  
+ATOM   1067  N   CYS B  48     -23.836 -18.597  -5.210  1.00 47.83           N  
+ANISOU 1067  N   CYS B  48     6142   6677   5355    -48   -112    -75       N  
+ATOM   1068  CA  CYS B  48     -23.825 -18.180  -6.626  1.00 44.63           C  
+ANISOU 1068  CA  CYS B  48     5671   6187   5098    -57    -82   -102       C  
+ATOM   1069  C   CYS B  48     -25.021 -17.249  -6.959  1.00 50.82           C  
+ANISOU 1069  C   CYS B  48     6502   6866   5939    -77     30   -146       C  
+ATOM   1070  O   CYS B  48     -25.428 -17.128  -8.127  1.00 51.63           O  
+ANISOU 1070  O   CYS B  48     6573   6888   6154    -61     61   -130       O  
+ATOM   1071  CB  CYS B  48     -23.944 -19.426  -7.480  1.00 41.68           C  
+ANISOU 1071  CB  CYS B  48     5279   5758   4800     24   -100     -7       C  
+ATOM   1072  SG  CYS B  48     -25.514 -20.250  -7.174  1.00 47.58           S  
+ANISOU 1072  SG  CYS B  48     6110   6423   5543     17    -32     87       S  
+ATOM   1073  N   ALA B  49     -25.604 -16.621  -5.943  1.00 49.42           N  
+ANISOU 1073  N   ALA B  49     6406   6702   5668    -83     90   -197       N  
+ATOM   1074  CA  ALA B  49     -26.803 -15.852  -6.145  1.00 47.37           C  
+ANISOU 1074  CA  ALA B  49     6180   6371   5446    -34    205   -227       C  
+ATOM   1075  C   ALA B  49     -26.501 -14.656  -7.025  1.00 46.14           C  
+ANISOU 1075  C   ALA B  49     6068   6096   5364    -55    208   -304       C  
+ATOM   1076  O   ALA B  49     -25.486 -13.952  -6.837  1.00 47.19           O  
+ANISOU 1076  O   ALA B  49     6266   6210   5452   -154    153   -386       O  
+ATOM   1077  CB  ALA B  49     -27.369 -15.388  -4.811  1.00 48.90           C  
+ANISOU 1077  CB  ALA B  49     6469   6616   5493     -1    286   -284       C  
+ATOM   1078  N   GLY B  50     -27.414 -14.412  -7.959  1.00 46.35           N  
+ANISOU 1078  N   GLY B  50     6066   6052   5491     20    266   -269       N  
+ATOM   1079  CA  GLY B  50     -27.486 -13.112  -8.644  1.00 47.72           C  
+ANISOU 1079  CA  GLY B  50     6340   6080   5709     42    296   -328       C  
+ATOM   1080  C   GLY B  50     -28.933 -12.665  -8.683  1.00 52.51           C  
+ANISOU 1080  C   GLY B  50     6951   6666   6333    212    397   -314       C  
+ATOM   1081  O   GLY B  50     -29.796 -13.183  -7.949  1.00 51.98           O  
+ANISOU 1081  O   GLY B  50     6805   6723   6220    284    461   -282       O  
+ATOM   1082  N   LYS B  51     -29.194 -11.690  -9.542  1.00 56.73           N  
+ANISOU 1082  N   LYS B  51     7570   7061   6924    284    413   -325       N  
+ATOM   1083  CA  LYS B  51     -30.512 -11.104  -9.670  1.00 58.00           C  
+ANISOU 1083  CA  LYS B  51     7730   7209   7099    498    500   -309       C  
+ATOM   1084  C   LYS B  51     -30.801 -10.888 -11.160  1.00 51.95           C  
+ANISOU 1084  C   LYS B  51     6929   6374   6435    547    454   -226       C  
+ATOM   1085  O   LYS B  51     -30.004 -10.302 -11.879  1.00 52.34           O  
+ANISOU 1085  O   LYS B  51     7113   6268   6503    462    408   -238       O  
+ATOM   1086  CB  LYS B  51     -30.496  -9.813  -8.890  1.00 62.10           C  
+ANISOU 1086  CB  LYS B  51     8498   7573   7524    591    570   -437       C  
+ATOM   1087  CG  LYS B  51     -31.824  -9.123  -8.706  1.00 71.92           C  
+ANISOU 1087  CG  LYS B  51     9764   8816   8744    885    686   -447       C  
+ATOM   1088  CD  LYS B  51     -31.618  -8.015  -7.667  1.00 84.43           C  
+ANISOU 1088  CD  LYS B  51    11656  10230  10191    959    758   -609       C  
+ATOM   1089  CE  LYS B  51     -32.841  -7.727  -6.792  1.00 94.36           C  
+ANISOU 1089  CE  LYS B  51    12897  11600  11354   1256    917   -653       C  
+ATOM   1090  NZ  LYS B  51     -33.550  -6.484  -7.218  1.00 96.90           N  
+ANISOU 1090  NZ  LYS B  51    13408  11730  11677   1566    985   -687       N  
+ATOM   1091  N   VAL B  52     -31.914 -11.423 -11.620  1.00 50.92           N  
+ANISOU 1091  N   VAL B  52     6608   6386   6353    653    460   -133       N  
+ATOM   1092  CA  VAL B  52     -32.367 -11.250 -12.978  1.00 51.68           C  
+ANISOU 1092  CA  VAL B  52     6658   6458   6518    721    403    -47       C  
+ATOM   1093  C   VAL B  52     -32.869  -9.817 -13.125  1.00 55.73           C  
+ANISOU 1093  C   VAL B  52     7340   6819   7014    951    455    -71       C  
+ATOM   1094  O   VAL B  52     -33.750  -9.385 -12.411  1.00 58.68           O  
+ANISOU 1094  O   VAL B  52     7692   7253   7351   1160    545    -97       O  
+ATOM   1095  CB  VAL B  52     -33.490 -12.237 -13.328  1.00 54.96           C  
+ANISOU 1095  CB  VAL B  52     6806   7103   6972    744    375     55       C  
+ATOM   1096  CG1 VAL B  52     -34.003 -12.011 -14.775  1.00 52.68           C  
+ANISOU 1096  CG1 VAL B  52     6472   6811   6730    819    290    142       C  
+ATOM   1097  CG2 VAL B  52     -33.016 -13.669 -13.135  1.00 50.23           C  
+ANISOU 1097  CG2 VAL B  52     6115   6592   6375    524    325     77       C  
+ATOM   1098  N   VAL B  53     -32.237  -9.071 -14.009  1.00 58.89           N  
+ANISOU 1098  N   VAL B  53     7936   7012   7426    915    408    -62       N  
+ATOM   1099  CA  VAL B  53     -32.688  -7.736 -14.395  1.00 62.70           C  
+ANISOU 1099  CA  VAL B  53     8634   7295   7891   1137    434    -54       C  
+ATOM   1100  C   VAL B  53     -33.762  -7.758 -15.502  1.00 61.16           C  
+ANISOU 1100  C   VAL B  53     8295   7206   7737   1336    378     79       C  
+ATOM   1101  O   VAL B  53     -34.656  -6.932 -15.513  1.00 69.08           O  
+ANISOU 1101  O   VAL B  53     9355   8170   8723   1636    413    102       O  
+ATOM   1102  CB  VAL B  53     -31.492  -6.995 -14.961  1.00 67.39           C  
+ANISOU 1102  CB  VAL B  53     9512   7623   8467    954    398    -72       C  
+ATOM   1103  CG1 VAL B  53     -31.901  -5.593 -15.416  1.00 75.74           C  
+ANISOU 1103  CG1 VAL B  53    10871   8410   9495   1168    416    -46       C  
+ATOM   1104  CG2 VAL B  53     -30.375  -6.982 -13.920  1.00 67.44           C  
+ANISOU 1104  CG2 VAL B  53     9626   7572   8426    722    423   -198       C  
+ATOM   1105  N   SER B  54     -33.617  -8.645 -16.484  1.00 54.51           N  
+ANISOU 1105  N   SER B  54     7294   6485   6931   1182    280    163       N  
+ATOM   1106  CA  SER B  54     -34.604  -8.785 -17.528  1.00 57.42           C  
+ANISOU 1106  CA  SER B  54     7508   6990   7316   1325    196    285       C  
+ATOM   1107  C   SER B  54     -34.480 -10.128 -18.163  1.00 49.00           C  
+ANISOU 1107  C   SER B  54     6241   6104   6271   1103    103    329       C  
+ATOM   1108  O   SER B  54     -33.420 -10.732 -18.126  1.00 48.61           O  
+ANISOU 1108  O   SER B  54     6245   6001   6221    872    101    279       O  
+ATOM   1109  CB  SER B  54     -34.457  -7.676 -18.615  1.00 67.92           C  
+ANISOU 1109  CB  SER B  54     9097   8093   8615   1441    149    357       C  
+ATOM   1110  OG  SER B  54     -33.212  -7.732 -19.310  1.00 68.84           O  
+ANISOU 1110  OG  SER B  54     9383   8061   8712   1181    119    358       O  
+ATOM   1111  N   GLY B  55     -35.568 -10.582 -18.760  1.00 49.29           N  
+ANISOU 1111  N   GLY B  55     6054   6357   6315   1182     19    420       N  
+ATOM   1112  CA  GLY B  55     -35.709 -11.925 -19.288  1.00 50.10           C  
+ANISOU 1112  CA  GLY B  55     5974   6638   6423    971    -80    453       C  
+ATOM   1113  C   GLY B  55     -36.015 -12.954 -18.190  1.00 54.88           C  
+ANISOU 1113  C   GLY B  55     6384   7412   7052    843    -31    418       C  
+ATOM   1114  O   GLY B  55     -36.357 -12.606 -17.055  1.00 51.85           O  
+ANISOU 1114  O   GLY B  55     5948   7078   6673    954     81    384       O  
+ATOM   1115  N   SER B  56     -35.910 -14.235 -18.540  1.00 52.05           N  
+ANISOU 1115  N   SER B  56     5952   7132   6691    610   -114    429       N  
+ATOM   1116  CA  SER B  56     -36.309 -15.287 -17.628  1.00 53.81           C  
+ANISOU 1116  CA  SER B  56     6017   7504   6924    461    -86    428       C  
+ATOM   1117  C   SER B  56     -35.313 -16.444 -17.548  1.00 48.18           C  
+ANISOU 1117  C   SER B  56     5432   6672   6199    230   -112    379       C  
+ATOM   1118  O   SER B  56     -34.496 -16.695 -18.438  1.00 51.01           O  
+ANISOU 1118  O   SER B  56     5943   6898   6540    166   -175    352       O  
+ATOM   1119  CB  SER B  56     -37.721 -15.783 -17.988  1.00 54.48           C  
+ANISOU 1119  CB  SER B  56     5818   7872   7006    422   -173    525       C  
+ATOM   1120  OG  SER B  56     -37.776 -16.208 -19.330  1.00 61.00           O  
+ANISOU 1120  OG  SER B  56     6673   8698   7805    316   -335    559       O  
+ATOM   1121  N   VAL B  57     -35.377 -17.125 -16.430  1.00 52.89           N  
+ANISOU 1121  N   VAL B  57     5977   7324   6794    133    -50    372       N  
+ATOM   1122  CA  VAL B  57     -34.589 -18.335 -16.192  1.00 52.55           C  
+ANISOU 1122  CA  VAL B  57     6056   7179   6732    -51    -77    346       C  
+ATOM   1123  C   VAL B  57     -35.550 -19.389 -15.674  1.00 55.14           C  
+ANISOU 1123  C   VAL B  57     6247   7658   7045   -229    -95    418       C  
+ATOM   1124  O   VAL B  57     -36.603 -19.052 -15.133  1.00 53.51           O  
+ANISOU 1124  O   VAL B  57     5830   7660   6841   -192    -35    473       O  
+ATOM   1125  CB  VAL B  57     -33.487 -18.134 -15.162  1.00 52.68           C  
+ANISOU 1125  CB  VAL B  57     6199   7082   6734     -9     13    278       C  
+ATOM   1126  CG1 VAL B  57     -32.429 -17.171 -15.703  1.00 51.20           C  
+ANISOU 1126  CG1 VAL B  57     6147   6751   6553     92     24    213       C  
+ATOM   1127  CG2 VAL B  57     -34.091 -17.723 -13.810  1.00 53.07           C  
+ANISOU 1127  CG2 VAL B  57     6147   7252   6763     53    130    286       C  
+ATOM   1128  N   ASP B  58     -35.198 -20.651 -15.913  1.00 54.40           N  
+ANISOU 1128  N   ASP B  58     6283   7459   6926   -420   -174    422       N  
+ATOM   1129  CA  ASP B  58     -35.847 -21.810 -15.319  1.00 52.99           C  
+ANISOU 1129  CA  ASP B  58     6069   7345   6718   -650   -190    493       C  
+ATOM   1130  C   ASP B  58     -34.894 -22.245 -14.184  1.00 50.66           C  
+ANISOU 1130  C   ASP B  58     5942   6913   6390   -637   -113    472       C  
+ATOM   1131  O   ASP B  58     -33.754 -22.666 -14.424  1.00 47.29           O  
+ANISOU 1131  O   ASP B  58     5729   6287   5951   -598   -150    415       O  
+ATOM   1132  CB  ASP B  58     -36.003 -22.916 -16.375  1.00 56.52           C  
+ANISOU 1132  CB  ASP B  58     6638   7699   7137   -861   -345    501       C  
+ATOM   1133  CG  ASP B  58     -36.384 -24.259 -15.771  1.00 65.37           C  
+ANISOU 1133  CG  ASP B  58     7842   8781   8214  -1139   -373    568       C  
+ATOM   1134  OD1 ASP B  58     -36.846 -24.290 -14.603  1.00 66.65           O  
+ANISOU 1134  OD1 ASP B  58     7883   9073   8365  -1194   -270    640       O  
+ATOM   1135  OD2 ASP B  58     -36.259 -25.280 -16.483  1.00 70.20           O  
+ANISOU 1135  OD2 ASP B  58     8664   9224   8785  -1310   -494    551       O  
+ATOM   1136  N   GLN B  59     -35.324 -22.089 -12.942  1.00 54.05           N  
+ANISOU 1136  N   GLN B  59     6267   7475   6792   -640     -1    517       N  
+ATOM   1137  CA  GLN B  59     -34.515 -22.555 -11.829  1.00 56.53           C  
+ANISOU 1137  CA  GLN B  59     6742   7686   7049   -640     51    516       C  
+ATOM   1138  C   GLN B  59     -35.268 -23.486 -10.889  1.00 59.14           C  
+ANISOU 1138  C   GLN B  59     7050   8107   7313   -851     94    632       C  
+ATOM   1139  O   GLN B  59     -35.188 -23.390  -9.661  1.00 65.41           O  
+ANISOU 1139  O   GLN B  59     7848   8966   8035   -822    198    658       O  
+ATOM   1140  CB  GLN B  59     -33.848 -21.372 -11.086  1.00 52.52           C  
+ANISOU 1140  CB  GLN B  59     6227   7196   6529   -422    151    436       C  
+ATOM   1141  CG  GLN B  59     -34.803 -20.396 -10.480  1.00 52.17           C  
+ANISOU 1141  CG  GLN B  59     5988   7361   6471   -326    274    442       C  
+ATOM   1142  CD  GLN B  59     -34.109 -19.292  -9.719  1.00 53.41           C  
+ANISOU 1142  CD  GLN B  59     6218   7483   6592   -139    359    343       C  
+ATOM   1143  OE1 GLN B  59     -33.067 -19.483  -9.016  1.00 53.43           O  
+ANISOU 1143  OE1 GLN B  59     6375   7387   6535   -140    352    304       O  
+ATOM   1144  NE2 GLN B  59     -34.697 -18.133  -9.795  1.00 52.42           N  
+ANISOU 1144  NE2 GLN B  59     5992   7441   6483     26    432    304       N  
+ATOM   1145  N   SER B  60     -35.956 -24.435 -11.476  1.00 61.58           N  
+ANISOU 1145  N   SER B  60     7365   8407   7623  -1092      6    703       N  
+ATOM   1146  CA  SER B  60     -36.740 -25.363 -10.706  1.00 72.30           C  
+ANISOU 1146  CA  SER B  60     8709   9847   8911  -1360     39    834       C  
+ATOM   1147  C   SER B  60     -35.825 -26.402 -10.017  1.00 74.05           C  
+ANISOU 1147  C   SER B  60     9269   9808   9057  -1412     17    868       C  
+ATOM   1148  O   SER B  60     -36.221 -27.047  -9.059  1.00 79.84           O  
+ANISOU 1148  O   SER B  60    10049  10581   9705  -1588     76    985       O  
+ATOM   1149  CB  SER B  60     -37.836 -25.999 -11.579  1.00 70.49           C  
+ANISOU 1149  CB  SER B  60     8372   9709   8700  -1655    -64    902       C  
+ATOM   1150  OG  SER B  60     -37.380 -26.382 -12.862  1.00 66.63           O  
+ANISOU 1150  OG  SER B  60     8069   9001   8245  -1672   -224    825       O  
+ATOM   1151  N   ASP B  61     -34.586 -26.497 -10.469  1.00 73.32           N  
+ANISOU 1151  N   ASP B  61     9398   9476   8984  -1231    -57    775       N  
+ATOM   1152  CA  ASP B  61     -33.572 -27.336  -9.811  1.00 80.97           C  
+ANISOU 1152  CA  ASP B  61    10666  10220   9879  -1177    -83    802       C  
+ATOM   1153  C   ASP B  61     -32.954 -26.686  -8.541  1.00 78.11           C  
+ANISOU 1153  C   ASP B  61    10262   9960   9455   -993     15    795       C  
+ATOM   1154  O   ASP B  61     -32.067 -27.263  -7.927  1.00 77.14           O  
+ANISOU 1154  O   ASP B  61    10352   9697   9259   -910    -16    823       O  
+ATOM   1155  CB  ASP B  61     -32.442 -27.683 -10.824  1.00 80.12           C  
+ANISOU 1155  CB  ASP B  61    10771   9865   9803  -1012   -194    700       C  
+ATOM   1156  CG  ASP B  61     -32.957 -28.443 -12.071  1.00 80.27           C  
+ANISOU 1156  CG  ASP B  61    10914   9741   9842  -1194   -306    687       C  
+ATOM   1157  OD1 ASP B  61     -34.142 -28.856 -12.081  1.00 92.44           O  
+ANISOU 1157  OD1 ASP B  61    12396  11355  11371  -1491   -323    772       O  
+ATOM   1158  OD2 ASP B  61     -32.182 -28.623 -13.047  1.00 76.90           O  
+ANISOU 1158  OD2 ASP B  61    10638   9149   9429  -1052   -377    588       O  
+ATOM   1159  N   GLN B  62     -33.390 -25.476  -8.178  1.00 79.30           N  
+ANISOU 1159  N   GLN B  62    10161  10347   9623   -909    123    751       N  
+ATOM   1160  CA  GLN B  62     -32.867 -24.772  -7.012  1.00 68.13           C  
+ANISOU 1160  CA  GLN B  62     8730   9027   8129   -755    210    719       C  
+ATOM   1161  C   GLN B  62     -33.726 -25.126  -5.824  1.00 70.39           C  
+ANISOU 1161  C   GLN B  62     8983   9467   8294   -906    322    843       C  
+ATOM   1162  O   GLN B  62     -34.807 -25.613  -6.012  1.00 70.22           O  
+ANISOU 1162  O   GLN B  62     8868   9532   8279  -1119    351    940       O  
+ATOM   1163  CB  GLN B  62     -32.896 -23.248  -7.236  1.00 67.38           C  
+ANISOU 1163  CB  GLN B  62     8445   9063   8093   -577    276    593       C  
+ATOM   1164  CG  GLN B  62     -34.246 -22.561  -6.972  1.00 69.55           C  
+ANISOU 1164  CG  GLN B  62     8479   9582   8363   -600    408    617       C  
+ATOM   1165  CD  GLN B  62     -34.463 -22.057  -5.534  1.00 62.14           C  
+ANISOU 1165  CD  GLN B  62     7513   8808   7289   -536    557    618       C  
+ATOM   1166  OE1 GLN B  62     -33.521 -21.731  -4.823  1.00 54.90           O  
+ANISOU 1166  OE1 GLN B  62     6736   7827   6294   -429    553    551       O  
+ATOM   1167  NE2 GLN B  62     -35.725 -21.958  -5.128  1.00 63.75           N  
+ANISOU 1167  NE2 GLN B  62     7519   9253   7449   -599    689    690       N  
+ATOM   1168  N   SER B  63     -33.270 -24.812  -4.615  1.00 70.67           N  
+ANISOU 1168  N   SER B  63     9076   9569   8205   -808    389    839       N  
+ATOM   1169  CA  SER B  63     -34.035 -25.114  -3.403  1.00 75.26           C  
+ANISOU 1169  CA  SER B  63     9643  10319   8633   -940    519    961       C  
+ATOM   1170  C   SER B  63     -33.862 -24.131  -2.248  1.00 70.31           C  
+ANISOU 1170  C   SER B  63     8977   9863   7874   -780    639    888       C  
+ATOM   1171  O   SER B  63     -34.598 -24.210  -1.268  1.00 78.27           O  
+ANISOU 1171  O   SER B  63     9939  11060   8737   -863    783    973       O  
+ATOM   1172  CB  SER B  63     -33.622 -26.492  -2.865  1.00 78.05           C  
+ANISOU 1172  CB  SER B  63    10283  10493   8878  -1077    445   1110       C  
+ATOM   1173  OG  SER B  63     -32.682 -26.352  -1.790  1.00 90.02           O  
+ANISOU 1173  OG  SER B  63    11950  12004  10249   -923    435   1099       O  
+ATOM   1174  N   PHE B  64     -32.871 -23.255  -2.316  1.00 65.41           N  
+ANISOU 1174  N   PHE B  64     8394   9180   7277   -573    583    734       N  
+ATOM   1175  CA  PHE B  64     -32.513 -22.466  -1.160  1.00 66.55           C  
+ANISOU 1175  CA  PHE B  64     8587   9435   7264   -453    656    655       C  
+ATOM   1176  C   PHE B  64     -33.316 -21.199  -1.122  1.00 68.15           C  
+ANISOU 1176  C   PHE B  64     8618   9800   7476   -340    809    543       C  
+ATOM   1177  O   PHE B  64     -33.653 -20.718  -0.042  1.00 74.02           O  
+ANISOU 1177  O   PHE B  64     9375  10700   8049   -284    945    514       O  
+ATOM   1178  CB  PHE B  64     -31.016 -22.107  -1.193  1.00 68.74           C  
+ANISOU 1178  CB  PHE B  64     8990   9582   7543   -323    508    542       C  
+ATOM   1179  CG  PHE B  64     -30.513 -21.437   0.066  1.00 66.26           C  
+ANISOU 1179  CG  PHE B  64     8771   9370   7032   -245    537    461       C  
+ATOM   1180  CD1 PHE B  64     -30.170 -22.204   1.194  1.00 72.14           C  
+ANISOU 1180  CD1 PHE B  64     9675  10158   7576   -288    506    569       C  
+ATOM   1181  CD2 PHE B  64     -30.367 -20.052   0.126  1.00 68.69           C  
+ANISOU 1181  CD2 PHE B  64     9050   9711   7337   -137    581    279       C  
+ATOM   1182  CE1 PHE B  64     -29.710 -21.588   2.347  1.00 71.63           C  
+ANISOU 1182  CE1 PHE B  64     9709  10202   7302   -228    515    487       C  
+ATOM   1183  CE2 PHE B  64     -29.898 -19.430   1.275  1.00 69.45           C  
+ANISOU 1183  CE2 PHE B  64     9273   9885   7231    -91    592    184       C  
+ATOM   1184  CZ  PHE B  64     -29.571 -20.192   2.381  1.00 72.58           C  
+ANISOU 1184  CZ  PHE B  64     9799  10357   7420   -139    555    283       C  
+ATOM   1185  N   LEU B  65     -33.564 -20.610  -2.286  1.00 63.83           N  
+ANISOU 1185  N   LEU B  65     7937   9206   7106   -274    785    473       N  
+ATOM   1186  CA  LEU B  65     -34.355 -19.387  -2.350  1.00 66.16           C  
+ANISOU 1186  CA  LEU B  65     8089   9630   7419   -115    921    375       C  
+ATOM   1187  C   LEU B  65     -35.835 -19.702  -2.078  1.00 70.47           C  
+ANISOU 1187  C   LEU B  65     8411  10440   7921   -180   1083    494       C  
+ATOM   1188  O   LEU B  65     -36.350 -20.690  -2.636  1.00 62.63           O  
+ANISOU 1188  O   LEU B  65     7317   9477   7002   -379   1035    634       O  
+ATOM   1189  CB  LEU B  65     -34.235 -18.733  -3.729  1.00 59.60           C  
+ANISOU 1189  CB  LEU B  65     7191   8674   6779    -25    837    299       C  
+ATOM   1190  CG  LEU B  65     -32.861 -18.246  -4.188  1.00 57.76           C  
+ANISOU 1190  CG  LEU B  65     7120   8221   6602     27    700    183       C  
+ATOM   1191  CD1 LEU B  65     -32.917 -17.582  -5.576  1.00 57.81           C  
+ANISOU 1191  CD1 LEU B  65     7061   8127   6775    102    646    135       C  
+ATOM   1192  CD2 LEU B  65     -32.327 -17.275  -3.169  1.00 54.39           C  
+ANISOU 1192  CD2 LEU B  65     6839   7788   6036    131    752     49       C  
+ATOM   1193  N   ASP B  66     -36.487 -18.860  -1.244  1.00 74.89           N  
+ANISOU 1193  N   ASP B  66     8903  11198   8353    -19   1274    433       N  
+ATOM   1194  CA  ASP B  66     -37.951 -18.868  -1.031  1.00 83.97           C  
+ANISOU 1194  CA  ASP B  66     9768  12675   9461    -11   1464    525       C  
+ATOM   1195  C   ASP B  66     -38.674 -18.077  -2.147  1.00 89.05           C  
+ANISOU 1195  C   ASP B  66    10182  13382  10272    163   1465    482       C  
+ATOM   1196  O   ASP B  66     -38.020 -17.512  -3.028  1.00 82.08           O  
+ANISOU 1196  O   ASP B  66     9403  12261   9520    266   1329    383       O  
+ATOM   1197  CB  ASP B  66     -38.358 -18.435   0.427  1.00 96.74           C  
+ANISOU 1197  CB  ASP B  66    11411  14515  10828    108   1694    490       C  
+ATOM   1198  CG  ASP B  66     -37.950 -16.966   0.829  1.00101.11           C  
+ANISOU 1198  CG  ASP B  66    12141  14972  11304    434   1759    259       C  
+ATOM   1199  OD1 ASP B  66     -37.822 -16.062  -0.014  1.00103.38           O  
+ANISOU 1199  OD1 ASP B  66    12437  15111  11732    617   1698    145       O  
+ATOM   1200  OD2 ASP B  66     -37.801 -16.698   2.048  1.00107.31           O  
+ANISOU 1200  OD2 ASP B  66    13086  15829  11857    502   1880    192       O  
+ATOM   1201  N   ASP B  67     -40.005 -18.040  -2.158  1.00 86.95           N  
+ANISOU 1201  N   ASP B  67     9589  13450   9995    197   1609    569       N  
+ATOM   1202  CA  ASP B  67     -40.677 -17.431  -3.312  1.00 90.53           C  
+ANISOU 1202  CA  ASP B  67     9811  13977  10608    358   1567    559       C  
+ATOM   1203  C   ASP B  67     -40.717 -15.891  -3.254  1.00 86.24           C  
+ANISOU 1203  C   ASP B  67     9333  13386  10046    784   1662    390       C  
+ATOM   1204  O   ASP B  67     -40.642 -15.233  -4.281  1.00 81.53           O  
+ANISOU 1204  O   ASP B  67     8742  12650   9585    940   1557    340       O  
+ATOM   1205  CB  ASP B  67     -42.050 -18.071  -3.552  1.00102.24           C  
+ANISOU 1205  CB  ASP B  67    10878  15858  12109    201   1632    735       C  
+ATOM   1206  CG  ASP B  67     -41.961 -19.594  -3.777  1.00110.12           C  
+ANISOU 1206  CG  ASP B  67    11890  16814  13135   -260   1502    895       C  
+ATOM   1207  OD1 ASP B  67     -41.291 -20.027  -4.737  1.00 98.84           O  
+ANISOU 1207  OD1 ASP B  67    10618  15101  11835   -390   1286    884       O  
+ATOM   1208  OD2 ASP B  67     -42.569 -20.364  -2.992  1.00123.31           O  
+ANISOU 1208  OD2 ASP B  67    13438  18728  14684   -494   1624   1033       O  
+ATOM   1209  N   GLU B  68     -40.822 -15.309  -2.066  1.00 91.45           N  
+ANISOU 1209  N   GLU B  68    10086  14137  10521    974   1859    301       N  
+ATOM   1210  CA  GLU B  68     -40.562 -13.877  -1.887  1.00 92.79           C  
+ANISOU 1210  CA  GLU B  68    10476  14135  10644   1350   1926    105       C  
+ATOM   1211  C   GLU B  68     -39.357 -13.490  -2.763  1.00 88.95           C  
+ANISOU 1211  C   GLU B  68    10277  13221  10296   1308   1700     12       C  
+ATOM   1212  O   GLU B  68     -39.451 -12.586  -3.592  1.00 86.77           O  
+ANISOU 1212  O   GLU B  68    10035  12811  10122   1529   1655    -46       O  
+ATOM   1213  CB  GLU B  68     -40.252 -13.581  -0.409  1.00103.28           C  
+ANISOU 1213  CB  GLU B  68    12042  15476  11725   1425   2090     -9       C  
+ATOM   1214  CG  GLU B  68     -39.658 -12.210  -0.110  1.00110.78           C  
+ANISOU 1214  CG  GLU B  68    13356  16139  12594   1717   2114   -242       C  
+ATOM   1215  CD  GLU B  68     -40.711 -11.114  -0.156  1.00127.27           C  
+ANISOU 1215  CD  GLU B  68    15344  18364  14648   2164   2303   -314       C  
+ATOM   1216  OE1 GLU B  68     -41.594 -11.091   0.736  1.00128.34           O  
+ANISOU 1216  OE1 GLU B  68    15322  18834  14608   2332   2547   -306       O  
+ATOM   1217  OE2 GLU B  68     -40.658 -10.276  -1.086  1.00135.31           O  
+ANISOU 1217  OE2 GLU B  68    16444  19164  15804   2367   2214   -371       O  
+ATOM   1218  N   GLN B  69     -38.246 -14.206  -2.567  1.00 72.49           N  
+ANISOU 1218  N   GLN B  69     8389  10950   8204   1029   1565     15       N  
+ATOM   1219  CA  GLN B  69     -36.950 -13.911  -3.200  1.00 73.21           C  
+ANISOU 1219  CA  GLN B  69     8740  10685   8389    958   1373    -72       C  
+ATOM   1220  C   GLN B  69     -36.830 -14.132  -4.727  1.00 65.48           C  
+ANISOU 1220  C   GLN B  69     7666   9593   7621    882   1203     -2       C  
+ATOM   1221  O   GLN B  69     -36.176 -13.383  -5.439  1.00 63.34           O  
+ANISOU 1221  O   GLN B  69     7557   9080   7428    951   1109    -83       O  
+ATOM   1222  CB  GLN B  69     -35.889 -14.744  -2.517  1.00 75.50           C  
+ANISOU 1222  CB  GLN B  69     9196  10892   8596    710   1285    -63       C  
+ATOM   1223  CG  GLN B  69     -35.602 -14.267  -1.103  1.00 78.45           C  
+ANISOU 1223  CG  GLN B  69     9777  11287   8741    786   1396   -180       C  
+ATOM   1224  CD  GLN B  69     -34.680 -15.215  -0.356  1.00 80.20           C  
+ANISOU 1224  CD  GLN B  69    10125  11490   8855    555   1301   -133       C  
+ATOM   1225  OE1 GLN B  69     -34.695 -16.439  -0.569  1.00 76.67           O  
+ANISOU 1225  OE1 GLN B  69     9569  11099   8460    360   1233     26       O  
+ATOM   1226  NE2 GLN B  69     -33.869 -14.655   0.531  1.00 79.87           N  
+ANISOU 1226  NE2 GLN B  69    10337  11359   8650    578   1283   -273       N  
+ATOM   1227  N   ILE B  70     -37.462 -15.169  -5.220  1.00 61.39           N  
+ANISOU 1227  N   ILE B  70     6901   9247   7174    715   1163    150       N  
+ATOM   1228  CA  ILE B  70     -37.598 -15.345  -6.623  1.00 62.15           C  
+ANISOU 1228  CA  ILE B  70     6889   9291   7432    668   1022    213       C  
+ATOM   1229  C   ILE B  70     -38.394 -14.186  -7.226  1.00 73.71           C  
+ANISOU 1229  C   ILE B  70     8249  10811   8945    974   1070    184       C  
+ATOM   1230  O   ILE B  70     -37.990 -13.601  -8.254  1.00 73.86           O  
+ANISOU 1230  O   ILE B  70     8375  10629   9058   1051    959    151       O  
+ATOM   1231  CB  ILE B  70     -38.275 -16.689  -6.902  1.00 63.18           C  
+ANISOU 1231  CB  ILE B  70     6790   9618   7596    406    976    375       C  
+ATOM   1232  CG1 ILE B  70     -37.310 -17.798  -6.413  1.00 66.78           C  
+ANISOU 1232  CG1 ILE B  70     7440   9927   8005    147    902    402       C  
+ATOM   1233  CG2 ILE B  70     -38.638 -16.782  -8.387  1.00 57.25           C  
+ANISOU 1233  CG2 ILE B  70     5905   8860   6984    377    830    431       C  
+ATOM   1234  CD1 ILE B  70     -37.651 -19.227  -6.791  1.00 70.61           C  
+ANISOU 1234  CD1 ILE B  70     7840  10465   8522   -155    814    547       C  
+ATOM   1235  N   GLY B  71     -39.521 -13.852  -6.588  1.00 75.76           N  
+ANISOU 1235  N   GLY B  71     8304  11355   9126   1166   1244    206       N  
+ATOM   1236  CA  GLY B  71     -40.376 -12.761  -7.036  1.00 72.95           C  
+ANISOU 1236  CA  GLY B  71     7834  11088   8796   1528   1307    188       C  
+ATOM   1237  C   GLY B  71     -39.624 -11.447  -7.147  1.00 76.38           C  
+ANISOU 1237  C   GLY B  71     8635  11163   9222   1770   1297     33       C  
+ATOM   1238  O   GLY B  71     -39.883 -10.642  -8.039  1.00 79.14           O  
+ANISOU 1238  O   GLY B  71     9008  11417   9641   1993   1244     36       O  
+ATOM   1239  N   GLU B  72     -38.660 -11.230  -6.267  1.00 77.34           N  
+ANISOU 1239  N   GLU B  72     9065  11073   9246   1700   1332    -94       N  
+ATOM   1240  CA  GLU B  72     -37.776 -10.071  -6.409  1.00 82.72           C  
+ANISOU 1240  CA  GLU B  72    10132  11378   9918   1818   1290   -240       C  
+ATOM   1241  C   GLU B  72     -36.680 -10.326  -7.459  1.00 73.65           C  
+ANISOU 1241  C   GLU B  72     9106   9983   8893   1567   1086   -215       C  
+ATOM   1242  O   GLU B  72     -35.725  -9.562  -7.537  1.00 70.35           O  
+ANISOU 1242  O   GLU B  72     8999   9266   8462   1543   1035   -321       O  
+ATOM   1243  CB  GLU B  72     -37.154  -9.705  -5.053  1.00 94.77           C  
+ANISOU 1243  CB  GLU B  72    11940  12798  11270   1810   1386   -397       C  
+ATOM   1244  CG  GLU B  72     -38.118  -9.911  -3.884  1.00110.12           C  
+ANISOU 1244  CG  GLU B  72    13720  15060  13058   1957   1598   -397       C  
+ATOM   1245  CD  GLU B  72     -37.932  -8.936  -2.746  1.00125.11           C  
+ANISOU 1245  CD  GLU B  72    15946  16831  14757   2160   1737   -591       C  
+ATOM   1246  OE1 GLU B  72     -38.918  -8.231  -2.409  1.00134.61           O  
+ANISOU 1246  OE1 GLU B  72    17112  18158  15874   2531   1921   -638       O  
+ATOM   1247  OE2 GLU B  72     -36.813  -8.896  -2.185  1.00125.40           O  
+ANISOU 1247  OE2 GLU B  72    16271  16663  14709   1956   1661   -698       O  
+ATOM   1248  N   GLY B  73     -36.799 -11.410  -8.233  1.00 65.49           N  
+ANISOU 1248  N   GLY B  73     7839   9083   7962   1362    977    -81       N  
+ATOM   1249  CA  GLY B  73     -35.815 -11.747  -9.280  1.00 62.64           C  
+ANISOU 1249  CA  GLY B  73     7570   8528   7700   1152    805    -56       C  
+ATOM   1250  C   GLY B  73     -34.511 -12.432  -8.913  1.00 64.69           C  
+ANISOU 1250  C   GLY B  73     7964   8676   7938    882    732    -95       C  
+ATOM   1251  O   GLY B  73     -33.621 -12.552  -9.759  1.00 61.13           O  
+ANISOU 1251  O   GLY B  73     7598   8075   7554    753    615    -91       O  
+ATOM   1252  N   PHE B  74     -34.380 -12.937  -7.687  1.00 63.75           N  
+ANISOU 1252  N   PHE B  74     7851   8656   7716    805    798   -121       N  
+ATOM   1253  CA  PHE B  74     -33.146 -13.627  -7.305  1.00 53.01           C  
+ANISOU 1253  CA  PHE B  74     6598   7217   6322    587    711   -142       C  
+ATOM   1254  C   PHE B  74     -32.988 -14.972  -7.975  1.00 47.15           C  
+ANISOU 1254  C   PHE B  74     5729   6525   5658    405    605    -25       C  
+ATOM   1255  O   PHE B  74     -33.925 -15.691  -8.210  1.00 47.19           O  
+ANISOU 1255  O   PHE B  74     5555   6679   5693    369    619     77       O  
+ATOM   1256  CB  PHE B  74     -33.033 -13.757  -5.799  1.00 57.58           C  
+ANISOU 1256  CB  PHE B  74     7252   7883   6742    571    795   -193       C  
+ATOM   1257  CG  PHE B  74     -32.721 -12.455  -5.116  1.00 62.39           C  
+ANISOU 1257  CG  PHE B  74     8094   8364   7244    697    859   -353       C  
+ATOM   1258  CD1 PHE B  74     -31.451 -11.886  -5.228  1.00 64.10           C  
+ANISOU 1258  CD1 PHE B  74     8521   8379   7455    591    755   -452       C  
+ATOM   1259  CD2 PHE B  74     -33.694 -11.780  -4.360  1.00 68.55           C  
+ANISOU 1259  CD2 PHE B  74     8895   9233   7917    917   1026   -411       C  
+ATOM   1260  CE1 PHE B  74     -31.155 -10.674  -4.606  1.00 68.98           C  
+ANISOU 1260  CE1 PHE B  74     9402   8846   7962    659    796   -610       C  
+ATOM   1261  CE2 PHE B  74     -33.400 -10.578  -3.721  1.00 69.76           C  
+ANISOU 1261  CE2 PHE B  74     9331   9224   7950   1040   1082   -581       C  
+ATOM   1262  CZ  PHE B  74     -32.132 -10.015  -3.852  1.00 73.33           C  
+ANISOU 1262  CZ  PHE B  74    10030   9434   8398    891    957   -683       C  
+ATOM   1263  N   VAL B  75     -31.754 -15.313  -8.293  1.00 47.62           N  
+ANISOU 1263  N   VAL B  75     5890   6460   5740    284    497    -46       N  
+ATOM   1264  CA  VAL B  75     -31.467 -16.527  -9.040  1.00 46.67           C  
+ANISOU 1264  CA  VAL B  75     5712   6336   5685    155    395     39       C  
+ATOM   1265  C   VAL B  75     -30.245 -17.245  -8.471  1.00 44.46           C  
+ANISOU 1265  C   VAL B  75     5526   6018   5347     63    327     28       C  
+ATOM   1266  O   VAL B  75     -29.283 -16.611  -8.088  1.00 45.81           O  
+ANISOU 1266  O   VAL B  75     5789   6141   5474     68    309    -55       O  
+ATOM   1267  CB  VAL B  75     -31.345 -16.225 -10.573  1.00 48.40           C  
+ANISOU 1267  CB  VAL B  75     5921   6459   6009    172    323     45       C  
+ATOM   1268  CG1 VAL B  75     -30.229 -15.219 -10.891  1.00 49.82           C  
+ANISOU 1268  CG1 VAL B  75     6236   6497   6194    190    302    -42       C  
+ATOM   1269  CG2 VAL B  75     -31.192 -17.513 -11.372  1.00 52.32           C  
+ANISOU 1269  CG2 VAL B  75     6386   6945   6547     57    228    117       C  
+ATOM   1270  N   LEU B  76     -30.313 -18.572  -8.356  1.00 47.02           N  
+ANISOU 1270  N   LEU B  76     5836   6371   5658    -24    284    120       N  
+ATOM   1271  CA  LEU B  76     -29.121 -19.328  -8.132  1.00 46.01           C  
+ANISOU 1271  CA  LEU B  76     5797   6192   5492    -56    197    127       C  
+ATOM   1272  C   LEU B  76     -28.512 -19.742  -9.476  1.00 40.71           C  
+ANISOU 1272  C   LEU B  76     5131   5424   4910    -54    113    128       C  
+ATOM   1273  O   LEU B  76     -29.048 -20.600 -10.147  1.00 46.45           O  
+ANISOU 1273  O   LEU B  76     5862   6109   5676   -101     81    191       O  
+ATOM   1274  CB  LEU B  76     -29.384 -20.509  -7.221  1.00 47.02           C  
+ANISOU 1274  CB  LEU B  76     5975   6357   5531   -120    195    227       C  
+ATOM   1275  CG  LEU B  76     -30.003 -20.207  -5.836  1.00 48.21           C  
+ANISOU 1275  CG  LEU B  76     6126   6635   5556   -128    299    239       C  
+ATOM   1276  CD1 LEU B  76     -30.102 -21.517  -5.059  1.00 49.81           C  
+ANISOU 1276  CD1 LEU B  76     6416   6849   5659   -217    281    368       C  
+ATOM   1277  CD2 LEU B  76     -29.254 -19.158  -5.030  1.00 49.77           C  
+ANISOU 1277  CD2 LEU B  76     6382   6864   5663    -57    312    121       C  
+ATOM   1278  N   THR B  77     -27.369 -19.136  -9.812  1.00 41.43           N  
+ANISOU 1278  N   THR B  77     5232   5494   5012    -17     79     55       N  
+ATOM   1279  CA  THR B  77     -26.727 -19.284 -11.128  1.00 40.67           C  
+ANISOU 1279  CA  THR B  77     5128   5343   4981      1     33     43       C  
+ATOM   1280  C   THR B  77     -26.271 -20.689 -11.435  1.00 44.46           C  
+ANISOU 1280  C   THR B  77     5663   5781   5447     32    -31     92       C  
+ATOM   1281  O   THR B  77     -26.273 -21.097 -12.621  1.00 45.14           O  
+ANISOU 1281  O   THR B  77     5776   5802   5573     48    -54     93       O  
+ATOM   1282  CB  THR B  77     -25.599 -18.252 -11.327  1.00 45.56           C  
+ANISOU 1282  CB  THR B  77     5722   5989   5598     -3     32    -32       C  
+ATOM   1283  OG1 THR B  77     -24.616 -18.284 -10.248  1.00 43.74           O  
+ANISOU 1283  OG1 THR B  77     5481   5849   5287    -11     -5    -59       O  
+ATOM   1284  CG2 THR B  77     -26.220 -16.863 -11.400  1.00 41.29           C  
+ANISOU 1284  CG2 THR B  77     5203   5403   5080    -24     94    -78       C  
+ATOM   1285  N   CYS B  78     -25.967 -21.485 -10.393  1.00 45.27           N  
+ANISOU 1285  N   CYS B  78     5822   5901   5476     54    -62    136       N  
+ATOM   1286  CA  CYS B  78     -25.538 -22.878 -10.640  1.00 40.58           C  
+ANISOU 1286  CA  CYS B  78     5341   5220   4856    123   -127    189       C  
+ATOM   1287  C   CYS B  78     -26.708 -23.807 -10.971  1.00 41.12           C  
+ANISOU 1287  C   CYS B  78     5522   5162   4939     26   -135    258       C  
+ATOM   1288  O   CYS B  78     -26.504 -24.884 -11.423  1.00 43.05           O  
+ANISOU 1288  O   CYS B  78     5913   5275   5166     63   -188    285       O  
+ATOM   1289  CB  CYS B  78     -24.806 -23.414  -9.415  1.00 46.97           C  
+ANISOU 1289  CB  CYS B  78     6199   6078   5566    196   -175    233       C  
+ATOM   1290  SG  CYS B  78     -25.953 -23.849  -8.071  1.00 49.40           S  
+ANISOU 1290  SG  CYS B  78     6603   6375   5792     77   -143    334       S  
+ATOM   1291  N   ALA B  79     -27.946 -23.393 -10.780  1.00 44.78           N  
+ANISOU 1291  N   ALA B  79     5918   5669   5425   -101    -84    285       N  
+ATOM   1292  CA  ALA B  79     -29.075 -24.281 -11.005  1.00 47.60           C  
+ANISOU 1292  CA  ALA B  79     6343   5956   5784   -250   -101    364       C  
+ATOM   1293  C   ALA B  79     -30.185 -23.636 -11.871  1.00 49.22           C  
+ANISOU 1293  C   ALA B  79     6406   6233   6061   -337    -80    350       C  
+ATOM   1294  O   ALA B  79     -31.381 -24.008 -11.779  1.00 50.46           O  
+ANISOU 1294  O   ALA B  79     6514   6441   6216   -497    -73    423       O  
+ATOM   1295  CB  ALA B  79     -29.642 -24.689  -9.663  1.00 51.35           C  
+ANISOU 1295  CB  ALA B  79     6846   6483   6181   -345    -59    459       C  
+ATOM   1296  N   ALA B  80     -29.766 -22.691 -12.710  1.00 47.73           N  
+ANISOU 1296  N   ALA B  80     6146   6065   5924   -239    -75    270       N  
+ATOM   1297  CA  ALA B  80     -30.656 -21.820 -13.483  1.00 47.79           C  
+ANISOU 1297  CA  ALA B  80     6019   6149   5986   -255    -60    260       C  
+ATOM   1298  C   ALA B  80     -30.291 -21.968 -14.960  1.00 45.38           C  
+ANISOU 1298  C   ALA B  80     5782   5759   5701   -235   -129    220       C  
+ATOM   1299  O   ALA B  80     -29.179 -21.667 -15.325  1.00 42.72           O  
+ANISOU 1299  O   ALA B  80     5492   5381   5359   -133   -121    162       O  
+ATOM   1300  CB  ALA B  80     -30.480 -20.367 -13.042  1.00 44.94           C  
+ANISOU 1300  CB  ALA B  80     5565   5866   5641   -146     20    209       C  
+ATOM   1301  N   TYR B  81     -31.241 -22.453 -15.772  1.00 45.42           N  
+ANISOU 1301  N   TYR B  81     5782   5764   5709   -349   -198    253       N  
+ATOM   1302  CA  TYR B  81     -31.175 -22.431 -17.214  1.00 43.03           C  
+ANISOU 1302  CA  TYR B  81     5534   5415   5397   -340   -266    217       C  
+ATOM   1303  C   TYR B  81     -31.816 -21.180 -17.774  1.00 44.38           C  
+ANISOU 1303  C   TYR B  81     5553   5705   5604   -286   -255    229       C  
+ATOM   1304  O   TYR B  81     -32.869 -20.780 -17.277  1.00 44.18           O  
+ANISOU 1304  O   TYR B  81     5365   5812   5607   -312   -236    284       O  
+ATOM   1305  CB  TYR B  81     -31.969 -23.595 -17.812  1.00 42.89           C  
+ANISOU 1305  CB  TYR B  81     5610   5343   5342   -522   -376    244       C  
+ATOM   1306  CG  TYR B  81     -31.528 -24.936 -17.365  1.00 42.13           C  
+ANISOU 1306  CG  TYR B  81     5733   5076   5198   -585   -404    245       C  
+ATOM   1307  CD1 TYR B  81     -30.314 -25.445 -17.790  1.00 44.66           C  
+ANISOU 1307  CD1 TYR B  81     6255   5239   5473   -442   -407    173       C  
+ATOM   1308  CD2 TYR B  81     -32.339 -25.734 -16.540  1.00 48.75           C  
+ANISOU 1308  CD2 TYR B  81     6588   5909   6023   -781   -423    329       C  
+ATOM   1309  CE1 TYR B  81     -29.894 -26.718 -17.417  1.00 43.35           C  
+ANISOU 1309  CE1 TYR B  81     6335   4883   5252   -447   -440    177       C  
+ATOM   1310  CE2 TYR B  81     -31.956 -27.041 -16.215  1.00 49.19           C  
+ANISOU 1310  CE2 TYR B  81     6919   5750   6019   -844   -464    343       C  
+ATOM   1311  CZ  TYR B  81     -30.702 -27.516 -16.640  1.00 48.71           C  
+ANISOU 1311  CZ  TYR B  81     7087   5502   5916   -648   -475    264       C  
+ATOM   1312  OH  TYR B  81     -30.184 -28.775 -16.265  1.00 52.95           O  
+ANISOU 1312  OH  TYR B  81     7931   5800   6387   -626   -510    278       O  
+ATOM   1313  N   PRO B  82     -31.222 -20.582 -18.851  1.00 42.24           N  
+ANISOU 1313  N   PRO B  82     5338   5394   5317   -199   -264    189       N  
+ATOM   1314  CA  PRO B  82     -31.916 -19.427 -19.426  1.00 43.63           C  
+ANISOU 1314  CA  PRO B  82     5410   5655   5512   -135   -271    223       C  
+ATOM   1315  C   PRO B  82     -33.193 -19.929 -20.110  1.00 42.47           C  
+ANISOU 1315  C   PRO B  82     5186   5607   5344   -243   -390    277       C  
+ATOM   1316  O   PRO B  82     -33.182 -21.018 -20.729  1.00 43.18           O  
+ANISOU 1316  O   PRO B  82     5392   5637   5378   -371   -481    256       O  
+ATOM   1317  CB  PRO B  82     -30.922 -18.885 -20.482  1.00 44.26           C  
+ANISOU 1317  CB  PRO B  82     5608   5658   5550    -62   -258    186       C  
+ATOM   1318  CG  PRO B  82     -29.978 -19.997 -20.799  1.00 44.00           C  
+ANISOU 1318  CG  PRO B  82     5714   5541   5462    -94   -268    127       C  
+ATOM   1319  CD  PRO B  82     -30.010 -20.955 -19.610  1.00 43.22           C  
+ANISOU 1319  CD  PRO B  82     5616   5415   5388   -148   -264    126       C  
+ATOM   1320  N   THR B  83     -34.264 -19.156 -20.049  1.00 43.35           N  
+ANISOU 1320  N   THR B  83     5114   5871   5484   -188   -400    342       N  
+ATOM   1321  CA  THR B  83     -35.417 -19.426 -20.932  1.00 48.62           C  
+ANISOU 1321  CA  THR B  83     5669   6684   6117   -276   -540    401       C  
+ATOM   1322  C   THR B  83     -35.748 -18.249 -21.876  1.00 46.65           C  
+ANISOU 1322  C   THR B  83     5378   6497   5848   -103   -583    444       C  
+ATOM   1323  O   THR B  83     -36.739 -18.269 -22.580  1.00 52.22           O  
+ANISOU 1323  O   THR B  83     5955   7366   6519   -132   -710    507       O  
+ATOM   1324  CB  THR B  83     -36.648 -19.794 -20.074  1.00 51.91           C  
+ANISOU 1324  CB  THR B  83     5841   7312   6568   -382   -542    472       C  
+ATOM   1325  OG1 THR B  83     -36.869 -18.747 -19.119  1.00 51.49           O  
+ANISOU 1325  OG1 THR B  83     5651   7343   6568   -184   -407    493       O  
+ATOM   1326  CG2 THR B  83     -36.374 -21.131 -19.339  1.00 48.25           C  
+ANISOU 1326  CG2 THR B  83     5482   6756   6094   -602   -531    453       C  
+ATOM   1327  N   SER B  84     -34.922 -17.220 -21.872  1.00 49.15           N  
+ANISOU 1327  N   SER B  84     5810   6685   6177     63   -486    421       N  
+ATOM   1328  CA  SER B  84     -34.968 -16.169 -22.893  1.00 47.46           C  
+ANISOU 1328  CA  SER B  84     5660   6452   5920    210   -525    469       C  
+ATOM   1329  C   SER B  84     -33.560 -15.596 -22.849  1.00 44.74           C  
+ANISOU 1329  C   SER B  84     5522   5905   5571    249   -408    413       C  
+ATOM   1330  O   SER B  84     -32.757 -15.991 -21.985  1.00 42.92           O  
+ANISOU 1330  O   SER B  84     5325   5604   5377    186   -319    344       O  
+ATOM   1331  CB  SER B  84     -35.999 -15.072 -22.468  1.00 52.98           C  
+ANISOU 1331  CB  SER B  84     6195   7271   6664    424   -504    546       C  
+ATOM   1332  OG  SER B  84     -35.478 -14.259 -21.372  1.00 56.24           O  
+ANISOU 1332  OG  SER B  84     6673   7559   7133    543   -346    502       O  
+ATOM   1333  N   ASP B  85     -33.263 -14.621 -23.694  1.00 42.09           N  
+ANISOU 1333  N   ASP B  85     5315   5491   5184    344   -407    456       N  
+ATOM   1334  CA  ASP B  85     -32.096 -13.779 -23.472  1.00 44.39           C  
+ANISOU 1334  CA  ASP B  85     5767   5616   5481    363   -284    426       C  
+ATOM   1335  C   ASP B  85     -32.367 -13.068 -22.174  1.00 44.33           C  
+ANISOU 1335  C   ASP B  85     5710   5574   5557    461   -200    408       C  
+ATOM   1336  O   ASP B  85     -33.473 -12.654 -21.914  1.00 44.74           O  
+ANISOU 1336  O   ASP B  85     5658   5704   5635    607   -226    457       O  
+ATOM   1337  CB  ASP B  85     -31.910 -12.741 -24.580  1.00 50.33           C  
+ANISOU 1337  CB  ASP B  85     6690   6279   6153    434   -297    505       C  
+ATOM   1338  CG  ASP B  85     -31.692 -13.380 -25.963  1.00 53.63           C  
+ANISOU 1338  CG  ASP B  85     7180   6749   6447    352   -376    524       C  
+ATOM   1339  OD1 ASP B  85     -31.204 -14.529 -26.022  1.00 47.82           O  
+ANISOU 1339  OD1 ASP B  85     6425   6053   5688    233   -377    444       O  
+ATOM   1340  OD2 ASP B  85     -32.060 -12.736 -26.984  1.00 54.69           O  
+ANISOU 1340  OD2 ASP B  85     7411   6877   6491    428   -443    619       O  
+ATOM   1341  N   VAL B  86     -31.347 -12.922 -21.361  1.00 40.17           N  
+ANISOU 1341  N   VAL B  86     5253   4953   5056    392   -100    335       N  
+ATOM   1342  CA  VAL B  86     -31.545 -12.627 -20.001  1.00 40.95           C  
+ANISOU 1342  CA  VAL B  86     5304   5044   5208    443    -28    288       C  
+ATOM   1343  C   VAL B  86     -30.338 -11.876 -19.512  1.00 40.30           C  
+ANISOU 1343  C   VAL B  86     5380   4811   5117    374     55    227       C  
+ATOM   1344  O   VAL B  86     -29.225 -12.135 -19.945  1.00 43.19           O  
+ANISOU 1344  O   VAL B  86     5796   5158   5455    238     66    208       O  
+ATOM   1345  CB  VAL B  86     -31.795 -13.954 -19.226  1.00 47.21           C  
+ANISOU 1345  CB  VAL B  86     5933   5975   6029    355    -41    254       C  
+ATOM   1346  CG1 VAL B  86     -30.588 -14.864 -19.281  1.00 53.03           C  
+ANISOU 1346  CG1 VAL B  86     6718   6685   6745    209    -38    201       C  
+ATOM   1347  CG2 VAL B  86     -32.149 -13.690 -17.770  1.00 54.18           C  
+ANISOU 1347  CG2 VAL B  86     6757   6889   6939    415     40    215       C  
+ATOM   1348  N   THR B  87     -30.567 -10.923 -18.633  1.00 40.44           N  
+ANISOU 1348  N   THR B  87     5478   4736   5147    466    116    194       N  
+ATOM   1349  CA  THR B  87     -29.526 -10.137 -17.998  1.00 44.61           C  
+ANISOU 1349  CA  THR B  87     6175   5117   5656    367    180    122       C  
+ATOM   1350  C   THR B  87     -29.555 -10.459 -16.518  1.00 45.83           C  
+ANISOU 1350  C   THR B  87     6259   5336   5818    364    223     34       C  
+ATOM   1351  O   THR B  87     -30.627 -10.480 -15.920  1.00 50.57           O  
+ANISOU 1351  O   THR B  87     6782   6002   6428    521    248     34       O  
+ATOM   1352  CB  THR B  87     -29.811  -8.653 -18.240  1.00 48.52           C  
+ANISOU 1352  CB  THR B  87     6913   5392   6126    483    206    148       C  
+ATOM   1353  OG1 THR B  87     -29.766  -8.415 -19.647  1.00 50.14           O  
+ANISOU 1353  OG1 THR B  87     7196   5550   6304    478    160    251       O  
+ATOM   1354  CG2 THR B  87     -28.775  -7.748 -17.578  1.00 51.37           C  
+ANISOU 1354  CG2 THR B  87     7497   5567   6453    330    258     66       C  
+ATOM   1355  N   ILE B  88     -28.383 -10.757 -15.950  1.00 46.15           N  
+ANISOU 1355  N   ILE B  88     6302   5394   5839    188    231    -31       N  
+ATOM   1356  CA  ILE B  88     -28.248 -11.202 -14.561  1.00 48.38           C  
+ANISOU 1356  CA  ILE B  88     6524   5758   6100    163    252   -105       C  
+ATOM   1357  C   ILE B  88     -27.148 -10.356 -13.868  1.00 49.51           C  
+ANISOU 1357  C   ILE B  88     6821   5800   6187     17    267   -195       C  
+ATOM   1358  O   ILE B  88     -26.006 -10.333 -14.336  1.00 50.31           O  
+ANISOU 1358  O   ILE B  88     6922   5912   6279   -155    240   -192       O  
+ATOM   1359  CB  ILE B  88     -27.854 -12.697 -14.495  1.00 47.16           C  
+ANISOU 1359  CB  ILE B  88     6193   5770   5956     88    209    -84       C  
+ATOM   1360  CG1 ILE B  88     -29.028 -13.550 -14.899  1.00 45.49           C  
+ANISOU 1360  CG1 ILE B  88     5857   5647   5780    178    185    -12       C  
+ATOM   1361  CG2 ILE B  88     -27.449 -13.095 -13.075  1.00 50.06           C  
+ANISOU 1361  CG2 ILE B  88     6533   6211   6275     43    217   -145       C  
+ATOM   1362  CD1 ILE B  88     -28.663 -14.921 -15.331  1.00 44.53           C  
+ANISOU 1362  CD1 ILE B  88     5652   5603   5664    105    128     18       C  
+ATOM   1363  N   GLU B  89     -27.473  -9.703 -12.758  1.00 53.87           N  
+ANISOU 1363  N   GLU B  89     7496   6283   6689     72    309   -278       N  
+ATOM   1364  CA  GLU B  89     -26.437  -9.087 -11.897  1.00 55.06           C  
+ANISOU 1364  CA  GLU B  89     7788   6369   6762   -106    296   -383       C  
+ATOM   1365  C   GLU B  89     -25.875 -10.173 -10.991  1.00 50.59           C  
+ANISOU 1365  C   GLU B  89     7047   6015   6159   -183    255   -407       C  
+ATOM   1366  O   GLU B  89     -26.632 -10.952 -10.372  1.00 53.81           O  
+ANISOU 1366  O   GLU B  89     7350   6536   6558    -58    279   -389       O  
+ATOM   1367  CB  GLU B  89     -26.978  -7.962 -10.983  1.00 64.61           C  
+ANISOU 1367  CB  GLU B  89     9255   7398   7894     -8    353   -489       C  
+ATOM   1368  CG  GLU B  89     -27.906  -6.925 -11.597  1.00 75.35           C  
+ANISOU 1368  CG  GLU B  89    10817   8537   9275    195    408   -466       C  
+ATOM   1369  CD  GLU B  89     -28.548  -5.964 -10.562  1.00 82.40           C  
+ANISOU 1369  CD  GLU B  89    11968   9264  10073    373    485   -588       C  
+ATOM   1370  OE1 GLU B  89     -29.068  -6.404  -9.504  1.00 81.15           O  
+ANISOU 1370  OE1 GLU B  89    11720   9254   9857    488    538   -645       O  
+ATOM   1371  OE2 GLU B  89     -28.574  -4.751 -10.836  1.00 78.16           O  
+ANISOU 1371  OE2 GLU B  89    11754   8436   9505    415    502   -623       O  
+ATOM   1372  N   THR B  90     -24.559 -10.217 -10.878  1.00 51.58           N  
+ANISOU 1372  N   THR B  90     7135   6210   6250   -392    193   -434       N  
+ATOM   1373  CA  THR B  90     -23.886 -11.258 -10.104  1.00 50.76           C  
+ANISOU 1373  CA  THR B  90     6862   6321   6103   -439    132   -438       C  
+ATOM   1374  C   THR B  90     -23.428 -10.795  -8.698  1.00 54.51           C  
+ANISOU 1374  C   THR B  90     7442   6820   6450   -544     93   -549       C  
+ATOM   1375  O   THR B  90     -23.592  -9.656  -8.337  1.00 56.29           O  
+ANISOU 1375  O   THR B  90     7894   6876   6618   -596    118   -640       O  
+ATOM   1376  CB  THR B  90     -22.680 -11.779 -10.880  1.00 51.05           C  
+ANISOU 1376  CB  THR B  90     6727   6500   6170   -558     78   -385       C  
+ATOM   1377  OG1 THR B  90     -21.707 -10.743 -11.014  1.00 54.51           O  
+ANISOU 1377  OG1 THR B  90     7235   6905   6570   -789     55   -432       O  
+ATOM   1378  CG2 THR B  90     -23.111 -12.265 -12.264  1.00 50.66           C  
+ANISOU 1378  CG2 THR B  90     6610   6424   6212   -455    115   -293       C  
+ATOM   1379  N   HIS B  91     -22.903 -11.730  -7.904  1.00 57.41           N  
+ANISOU 1379  N   HIS B  91     7670   7387   6755   -556     26   -538       N  
+ATOM   1380  CA  HIS B  91     -22.350 -11.451  -6.562  1.00 53.16           C  
+ANISOU 1380  CA  HIS B  91     7206   6925   6066   -666    -42   -633       C  
+ATOM   1381  C   HIS B  91     -23.379 -10.865  -5.635  1.00 52.82           C  
+ANISOU 1381  C   HIS B  91     7383   6755   5930   -566     36   -720       C  
+ATOM   1382  O   HIS B  91     -23.140  -9.839  -5.055  1.00 57.23           O  
+ANISOU 1382  O   HIS B  91     8152   7207   6385   -681     21   -845       O  
+ATOM   1383  CB  HIS B  91     -21.120 -10.564  -6.646  1.00 52.81           C  
+ANISOU 1383  CB  HIS B  91     7193   6894   5976   -939   -126   -702       C  
+ATOM   1384  CG  HIS B  91     -19.981 -11.185  -7.409  1.00 56.19           C  
+ANISOU 1384  CG  HIS B  91     7349   7534   6464  -1027   -193   -617       C  
+ATOM   1385  ND1 HIS B  91     -20.029 -11.423  -8.768  1.00 58.74           N  
+ANISOU 1385  ND1 HIS B  91     7574   7829   6914   -972   -131   -528       N  
+ATOM   1386  CD2 HIS B  91     -18.759 -11.596  -7.006  1.00 57.11           C  
+ANISOU 1386  CD2 HIS B  91     7263   7919   6514  -1145   -312   -608       C  
+ATOM   1387  CE1 HIS B  91     -18.883 -11.936  -9.173  1.00 56.30           C  
+ANISOU 1387  CE1 HIS B  91     7023   7755   6612  -1044   -186   -477       C  
+ATOM   1388  NE2 HIS B  91     -18.094 -12.046  -8.123  1.00 61.01           N  
+ANISOU 1388  NE2 HIS B  91     7535   8546   7100  -1142   -299   -520       N  
+ATOM   1389  N   LYS B  92     -24.532 -11.528  -5.533  1.00 51.89           N  
+ANISOU 1389  N   LYS B  92     7218   6656   5842   -359    126   -655       N  
+ATOM   1390  CA  LYS B  92     -25.686 -11.045  -4.764  1.00 55.84           C  
+ANISOU 1390  CA  LYS B  92     7874   7083   6259   -208    243   -719       C  
+ATOM   1391  C   LYS B  92     -25.938 -11.835  -3.472  1.00 58.34           C  
+ANISOU 1391  C   LYS B  92     8162   7572   6430   -160    255   -708       C  
+ATOM   1392  O   LYS B  92     -26.959 -11.637  -2.823  1.00 58.98           O  
+ANISOU 1392  O   LYS B  92     8322   7655   6430    -18    378   -739       O  
+ATOM   1393  CB  LYS B  92     -26.964 -11.067  -5.656  1.00 58.46           C  
+ANISOU 1393  CB  LYS B  92     8144   7346   6720    -12    355   -640       C  
+ATOM   1394  CG  LYS B  92     -26.997  -9.991  -6.743  1.00 61.75           C  
+ANISOU 1394  CG  LYS B  92     8682   7550   7230     -3    369   -662       C  
+ATOM   1395  CD  LYS B  92     -26.411  -8.664  -6.237  1.00 67.88           C  
+ANISOU 1395  CD  LYS B  92     9757   8134   7899   -115    352   -813       C  
+ATOM   1396  CE  LYS B  92     -26.933  -7.436  -6.946  1.00 70.75           C  
+ANISOU 1396  CE  LYS B  92    10351   8225   8305    -10    414   -844       C  
+ATOM   1397  NZ  LYS B  92     -28.067  -6.929  -6.119  1.00 81.14           N  
+ANISOU 1397  NZ  LYS B  92    11822   9481   9525    253    540   -928       N  
+ATOM   1398  N   GLU B  93     -25.011 -12.712  -3.102  1.00 57.86           N  
+ANISOU 1398  N   GLU B  93     7989   7669   6322   -262    135   -655       N  
+ATOM   1399  CA  GLU B  93     -25.136 -13.464  -1.883  1.00 65.82           C  
+ANISOU 1399  CA  GLU B  93     9004   8833   7172   -230    128   -625       C  
+ATOM   1400  C   GLU B  93     -25.494 -12.505  -0.726  1.00 69.11           C  
+ANISOU 1400  C   GLU B  93     9655   9212   7388   -218    194   -777       C  
+ATOM   1401  O   GLU B  93     -26.466 -12.735  -0.015  1.00 63.37           O  
+ANISOU 1401  O   GLU B  93     8970   8546   6562    -93    320   -761       O  
+ATOM   1402  CB  GLU B  93     -23.834 -14.222  -1.595  1.00 65.64           C  
+ANISOU 1402  CB  GLU B  93     8876   8968   7096   -333    -46   -573       C  
+ATOM   1403  CG  GLU B  93     -23.788 -14.779  -0.175  1.00 79.13           C  
+ANISOU 1403  CG  GLU B  93    10652  10826   8585   -318    -84   -555       C  
+ATOM   1404  CD  GLU B  93     -22.987 -16.055  -0.039  1.00 76.96           C  
+ANISOU 1404  CD  GLU B  93    10243  10705   8292   -298   -222   -415       C  
+ATOM   1405  OE1 GLU B  93     -21.780 -16.023  -0.371  1.00 79.66           O  
+ANISOU 1405  OE1 GLU B  93    10469  11135   8663   -371   -367   -426       O  
+ATOM   1406  OE2 GLU B  93     -23.601 -17.080   0.373  1.00 67.00           O  
+ANISOU 1406  OE2 GLU B  93     8994   9478   6985   -203   -176   -287       O  
+ATOM   1407  N   GLU B  94     -24.725 -11.432  -0.566  1.00 69.03           N  
+ANISOU 1407  N   GLU B  94     9811   9108   7309   -360    117   -926       N  
+ATOM   1408  CA  GLU B  94     -24.928 -10.483   0.552  1.00 77.95           C  
+ANISOU 1408  CA  GLU B  94    11229  10170   8216   -366    159  -1102       C  
+ATOM   1409  C   GLU B  94     -26.428 -10.119   0.763  1.00 81.07           C  
+ANISOU 1409  C   GLU B  94    11734  10485   8584   -108    385  -1134       C  
+ATOM   1410  O   GLU B  94     -26.904 -10.013   1.908  1.00 85.54           O  
+ANISOU 1410  O   GLU B  94    12445  11120   8935    -23    471  -1211       O  
+ATOM   1411  CB  GLU B  94     -24.036  -9.252   0.313  1.00 87.19           C  
+ANISOU 1411  CB  GLU B  94    12594  11164   9368   -580     56  -1254       C  
+ATOM   1412  CG  GLU B  94     -24.432  -7.953   1.012  1.00106.45           C  
+ANISOU 1412  CG  GLU B  94    15427  13387  11633   -560    129  -1466       C  
+ATOM   1413  CD  GLU B  94     -23.735  -7.773   2.341  1.00110.68           C  
+ANISOU 1413  CD  GLU B  94    16139  14025  11889   -732      8  -1602       C  
+ATOM   1414  OE1 GLU B  94     -22.649  -8.362   2.521  1.00117.37           O  
+ANISOU 1414  OE1 GLU B  94    16802  15085  12706   -936   -180  -1543       O  
+ATOM   1415  OE2 GLU B  94     -24.273  -7.047   3.196  1.00115.53           O  
+ANISOU 1415  OE2 GLU B  94    17075  14520  12301   -644     98  -1769       O  
+ATOM   1416  N   ALA B  95     -27.170  -9.967  -0.337  1.00 78.47           N  
+ANISOU 1416  N   ALA B  95    11311  10048   8454     26    480  -1068       N  
+ATOM   1417  CA  ALA B  95     -28.574  -9.572  -0.290  1.00 77.65           C  
+ANISOU 1417  CA  ALA B  95    11250   9907   8344    296    683  -1084       C  
+ATOM   1418  C   ALA B  95     -29.499 -10.685   0.182  1.00 81.60           C  
+ANISOU 1418  C   ALA B  95    11531  10661   8809    409    797   -947       C  
+ATOM   1419  O   ALA B  95     -30.560 -10.398   0.728  1.00 86.75           O  
+ANISOU 1419  O   ALA B  95    12220  11380   9361    612    978   -982       O  
+ATOM   1420  CB  ALA B  95     -29.025  -9.052  -1.654  1.00 73.64           C  
+ANISOU 1420  CB  ALA B  95    10698   9230   8050    400    717  -1040       C  
+ATOM   1421  N   ILE B  96     -29.102 -11.941  -0.054  1.00 86.66           N  
+ANISOU 1421  N   ILE B  96    11957  11441   9527    280    698   -787       N  
+ATOM   1422  CA  ILE B  96     -29.871 -13.143   0.330  1.00 86.56           C  
+ANISOU 1422  CA  ILE B  96    11762  11641   9485    314    780   -627       C  
+ATOM   1423  C   ILE B  96     -29.477 -13.617   1.721  1.00 84.77           C  
+ANISOU 1423  C   ILE B  96    11641  11556   9011    242    759   -633       C  
+ATOM   1424  O   ILE B  96     -29.685 -14.761   2.056  1.00 83.12           O  
+ANISOU 1424  O   ILE B  96    11324  11491   8764    195    764   -478       O  
+ATOM   1425  CB  ILE B  96     -29.623 -14.323  -0.663  1.00 90.19           C  
+ANISOU 1425  CB  ILE B  96    12007  12122  10139    213    673   -451       C  
+ATOM   1426  CG1 ILE B  96     -30.006 -13.941  -2.082  1.00 90.30           C  
+ANISOU 1426  CG1 ILE B  96    11917  12018  10374    272    679   -433       C  
+ATOM   1427  CG2 ILE B  96     -30.408 -15.587  -0.291  1.00 96.15           C  
+ANISOU 1427  CG2 ILE B  96    12623  13049  10858    195    743   -278       C  
+ATOM   1428  CD1 ILE B  96     -31.453 -13.513  -2.211  1.00 91.73           C  
+ANISOU 1428  CD1 ILE B  96    12017  12259  10577    456    854   -422       C  
+ATOM   1429  N   MET B  97     -28.867 -12.760   2.519  1.00 96.04           N  
+ANISOU 1429  N   MET B  97    13305  12930  10255    212    718   -806       N  
+ATOM   1430  CA  MET B  97     -28.752 -13.030   3.959  1.00108.10           C  
+ANISOU 1430  CA  MET B  97    14968  14612  11491    186    733   -832       C  
+ATOM   1431  C   MET B  97     -29.333 -11.852   4.784  1.00112.54           C  
+ANISOU 1431  C   MET B  97    15786  15132  11839    327    893  -1037       C  
+ATOM   1432  O   MET B  97     -30.047 -12.090   5.759  1.00113.07           O  
+ANISOU 1432  O   MET B  97    15896  15370  11696    422   1051  -1023       O  
+ATOM   1433  CB  MET B  97     -27.301 -13.410   4.291  1.00103.41           C  
+ANISOU 1433  CB  MET B  97    14414  14054  10823     -6    483   -827       C  
+ATOM   1434  CG  MET B  97     -26.722 -14.327   3.206  1.00102.45           C  
+ANISOU 1434  CG  MET B  97    14057  13920  10946    -76    347   -664       C  
+ATOM   1435  SD  MET B  97     -25.246 -15.322   3.559  1.00117.34           S  
+ANISOU 1435  SD  MET B  97    15877  15943  12762   -203     84   -561       S  
+ATOM   1436  CE  MET B  97     -24.043 -14.027   3.907  1.00103.96           C  
+ANISOU 1436  CE  MET B  97    14327  14224  10946   -367    -80   -786       C  
+ATOM   1437  N   LEU B  98     -29.087 -10.604   4.344  1.00119.26           N  
+ANISOU 1437  N   LEU B  98    16822  15748  12742    354    871  -1217       N  
+ATOM   1438  CA  LEU B  98     -29.639  -9.356   4.952  1.00123.66           C  
+ANISOU 1438  CA  LEU B  98    17689  16180  13115    531   1024  -1437       C  
+ATOM   1439  C   LEU B  98     -30.866  -9.535   5.875  1.00124.53           C  
+ANISOU 1439  C   LEU B  98    17792  16500  13023    769   1286  -1430       C  
+ATOM   1440  O   LEU B  98     -32.001  -9.708   5.420  1.00113.44           O  
+ANISOU 1440  O   LEU B  98    16173  15192  11737    973   1471  -1329       O  
+ATOM   1441  CB  LEU B  98     -29.945  -8.322   3.862  1.00113.25           C  
+ANISOU 1441  CB  LEU B  98    16446  14588  11996    658   1065  -1512       C  
+TER    1442      LEU B  98                                                      
+HETATM 1443 FE1  FES A 201     -15.271 -14.172 -26.506  1.00 44.84          FE  
+ANISOU 1443 FE1  FES A 201     6513   5131   5392   -266   1479   -131      FE  
+HETATM 1444 FE2  FES A 201     -13.886 -13.246 -28.774  1.00 43.12          FE  
+ANISOU 1444 FE2  FES A 201     6178   4976   5230   -316   1145   -235      FE  
+HETATM 1445  S1  FES A 201     -13.297 -13.107 -26.664  1.00 54.52           S  
+ANISOU 1445  S1  FES A 201     7773   6436   6506    -96   1188   -102       S  
+HETATM 1446  S2  FES A 201     -15.933 -14.020 -28.613  1.00 59.15           S  
+ANISOU 1446  S2  FES A 201     8119   7001   7354   -510   1368   -300       S  
+HETATM 1447 CL    CL A 202      -7.271  -6.453 -50.333  1.00 66.75          CL  
+ANISOU 1447 CL    CL A 202     8476   9756   7127    -15    320    384      CL  
+HETATM 1448 FE1  FES B 201     -24.764 -23.326  -3.870  1.00 41.78          FE  
+ANISOU 1448 FE1  FES B 201     5702   5769   4400    126   -205    391      FE  
+HETATM 1449 FE2  FES B 201     -25.332 -22.283  -6.421  1.00 37.10          FE  
+ANISOU 1449 FE2  FES B 201     4925   5056   4113     76   -116    248      FE  
+HETATM 1450  S1  FES B 201     -23.427 -22.583  -5.443  1.00 50.74           S  
+ANISOU 1450  S1  FES B 201     6641   6936   5699    218   -266    253       S  
+HETATM 1451  S2  FES B 201     -26.631 -22.784  -4.792  1.00 52.36           S  
+ANISOU 1451  S2  FES B 201     6974   7030   5890    -29    -34    366       S  
+HETATM 1452 CL    CL B 202     -35.655 -14.036 -25.892  1.00 63.11          CL  
+ANISOU 1452 CL    CL B 202     7828   8449   7699    533   -737    677      CL  
+HETATM 1453  O   HOH A 301      -5.604   9.621 -30.726  1.00 71.67           O  
+ANISOU 1453  O   HOH A 301     9741   6570  10921   -503   1286  -1155       O  
+HETATM 1454  O   HOH A 302      -9.976  -2.053 -46.606  1.00 60.80           O  
+ANISOU 1454  O   HOH A 302     7211   8696   7192    404    154    904       O  
+HETATM 1455  O   HOH A 303     -25.242 -15.543 -37.047  1.00 85.19           O  
+ANISOU 1455  O   HOH A 303     9346  12270  10750  -2562    894  -1311       O  
+HETATM 1456  O   HOH A 304      -8.615  -1.310 -44.946  1.00 51.68           O  
+ANISOU 1456  O   HOH A 304     6188   7004   6442    421    264    856       O  
+HETATM 1457  O   HOH A 305      -6.495 -20.103 -36.227  1.00 66.33           O  
+ANISOU 1457  O   HOH A 305     9914   6783   8504   -320   2093   -239       O  
+HETATM 1458  O   HOH A 306     -15.756 -22.203 -33.220  1.00 75.41           O  
+ANISOU 1458  O   HOH A 306    10813   7895   9944  -1695   2618   -965       O  
+HETATM 1459  O   HOH A 307     -29.136  -5.336 -24.489  1.00 68.01           O  
+ANISOU 1459  O   HOH A 307     7381   9218   9240    419   2448    230       O  
+HETATM 1460  O   HOH A 308     -31.273  -2.568 -37.684  1.00 80.28           O  
+ANISOU 1460  O   HOH A 308     6166  13841  10493    472    476   1418       O  
+HETATM 1461  O   HOH A 309     -12.248  -4.951 -30.108  1.00 43.84           O  
+ANISOU 1461  O   HOH A 309     5908   5247   5501      5    448   -372       O  
+HETATM 1462  O   HOH A 310       1.254  -8.613 -35.928  1.00 55.59           O  
+ANISOU 1462  O   HOH A 310     7059   6744   7319    249    231    444       O  
+HETATM 1463  O   HOH A 311     -14.907  -5.701 -24.000  1.00 59.32           O  
+ANISOU 1463  O   HOH A 311     8290   7265   6981    168    940   -557       O  
+HETATM 1464  O   HOH A 312     -10.817  -7.018 -29.284  1.00 41.62           O  
+ANISOU 1464  O   HOH A 312     5784   4984   5045    -27    423   -341       O  
+HETATM 1465  O   HOH A 313       2.334  -6.045 -37.941  1.00 48.64           O  
+ANISOU 1465  O   HOH A 313     5952   5811   6716    139    202    488       O  
+HETATM 1466  O   HOH A 314     -15.179  -4.981 -27.668  1.00 53.48           O  
+ANISOU 1466  O   HOH A 314     7171   6503   6643     87    768   -437       O  
+HETATM 1467  O   HOH A 315      -0.012 -13.050 -38.710  1.00 53.80           O  
+ANISOU 1467  O   HOH A 315     7374   6131   6936    327    934    389       O  
+HETATM 1468  O   HOH A 316      -4.588   7.953 -34.979  1.00 55.02           O  
+ANISOU 1468  O   HOH A 316     7176   4879   8848   -164   1054   -241       O  
+HETATM 1469  O   HOH A 317      -7.401   1.030 -46.099  1.00 71.07           O  
+ANISOU 1469  O   HOH A 317     8592   9256   9152    695    478   1286       O  
+HETATM 1470  O   HOH B 301     -17.227  -5.370 -20.274  1.00 67.76           O  
+ANISOU 1470  O   HOH B 301     9358   8307   8078  -2373    512    237       O  
+HETATM 1471  O   HOH B 302     -16.773 -19.622 -11.270  1.00 63.07           O  
+ANISOU 1471  O   HOH B 302     7080   9333   7548    271   -261    -62       O  
+HETATM 1472  O   HOH B 303     -33.187 -25.431 -12.986  1.00 63.64           O  
+ANISOU 1472  O   HOH B 303     8214   8071   7895   -876   -197    526       O  
+HETATM 1473  O   HOH B 304     -33.633 -10.672 -27.113  1.00 59.09           O  
+ANISOU 1473  O   HOH B 304     8015   7375   7058    804   -526    819       O  
+HETATM 1474  O   HOH B 305     -29.987  -6.037  -4.511  1.00 66.34           O  
+ANISOU 1474  O   HOH B 305    10207   7566   7433    765    801  -1097       O  
+HETATM 1475  O   HOH B 306     -13.899 -20.518 -11.569  1.00 81.57           O  
+ANISOU 1475  O   HOH B 306     8854  12357   9778    587   -327     -7       O  
+HETATM 1476  O   HOH B 307     -22.312 -17.195  -9.418  1.00 47.15           O  
+ANISOU 1476  O   HOH B 307     5812   6486   5616   -124    -89   -163       O  
+HETATM 1477  O   HOH B 308     -34.144 -23.559 -20.590  1.00 51.64           O  
+ANISOU 1477  O   HOH B 308     6612   6627   6378   -778   -651    271       O  
+HETATM 1478  O   HOH B 309     -24.795 -27.082 -17.164  1.00 48.12           O  
+ANISOU 1478  O   HOH B 309     7064   5473   5745    466   -276     10       O  
+HETATM 1479  O   HOH B 310     -32.217 -13.054   4.356  1.00 88.41           O  
+ANISOU 1479  O   HOH B 310    12239  12424   8929    628   1337   -721       O  
+HETATM 1480  O   HOH B 311     -23.165 -14.400  -5.121  1.00 48.01           O  
+ANISOU 1480  O   HOH B 311     6333   6580   5329   -351    -63   -448       O  
+HETATM 1481  O   HOH B 312     -20.108 -19.770 -22.685  1.00 54.43           O  
+ANISOU 1481  O   HOH B 312     6815   7445   6419    397    344   -109       O  
+HETATM 1482  O   HOH B 313     -29.379 -10.101 -22.915  1.00 62.52           O  
+ANISOU 1482  O   HOH B 313     8624   7375   7757    322     17    426       O  
+HETATM 1483  O   HOH B 314     -23.815 -23.224 -24.077  1.00 46.69           O  
+ANISOU 1483  O   HOH B 314     6623   5782   5333    479     -2   -224       O  
+HETATM 1484  O   HOH B 315     -31.917  -9.402 -21.408  1.00 63.89           O  
+ANISOU 1484  O   HOH B 315     8654   7581   8038    758    -17    436       O  
+HETATM 1485  O   HOH B 316     -43.034 -22.451  -5.025  1.00 82.32           O  
+ANISOU 1485  O   HOH B 316     8131  13423   9723  -1213   1232   1263       O  
+HETATM 1486  O   HOH B 317     -13.942 -19.466 -15.267  1.00 82.69           O  
+ANISOU 1486  O   HOH B 317     8966  12435  10017    416     49    -55       O  
+HETATM 1487  O   HOH B 318     -23.364 -14.585  -8.816  1.00 50.88           O  
+ANISOU 1487  O   HOH B 318     6512   6761   6060   -288     14   -313       O  
+HETATM 1488  O   HOH B 319     -41.794 -18.985   0.026  0.50 60.91           O  
+ANISOU 1488  O   HOH B 319     5945  10874   6323     -9   2050    811       O  
+HETATM 1489  O   HOH B 320     -29.672 -24.828 -29.742  1.00 55.49           O  
+ANISOU 1489  O   HOH B 320     8618   6605   5859   -263   -781   -333       O  
+HETATM 1490  O   HOH B 321     -38.215 -20.833 -12.380  1.00 58.66           O  
+ANISOU 1490  O   HOH B 321     6197   8706   7384   -584    159    654       O  
+CONECT  301 1443                                                                
+CONECT  333 1443                                                                
+CONECT  351 1444                                                                
+CONECT  569 1444                                                                
+CONECT 1022 1448                                                                
+CONECT 1054 1448                                                                
+CONECT 1072 1449                                                                
+CONECT 1290 1449                                                                
+CONECT 1443  301  333 1445 1446                                                 
+CONECT 1444  351  569 1445 1446                                                 
+CONECT 1445 1443 1444                                                           
+CONECT 1446 1443 1444                                                           
+CONECT 1448 1022 1054 1450 1451                                                 
+CONECT 1449 1072 1290 1450 1451                                                 
+CONECT 1450 1448 1449                                                           
+CONECT 1451 1448 1449                                                           
+MASTER      410    0    4    7   14    0    6    6 1488    2   16   18          
+END                                                                             
diff --git a/test/jalview/ext/rbvi/chimera/4zho.xml.gz b/test/jalview/ext/rbvi/chimera/4zho.xml.gz
new file mode 100644 (file)
index 0000000..c7e6bd2
Binary files /dev/null and b/test/jalview/ext/rbvi/chimera/4zho.xml.gz differ
diff --git a/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java b/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java
new file mode 100644 (file)
index 0000000..c9e1cad
--- /dev/null
@@ -0,0 +1,34 @@
+package jalview.ext.rbvi.chimera;
+
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+public class AtomSpecModelTest
+{
+  @Test(groups = "Functional")
+  public void testGetAtomSpec()
+  {
+    AtomSpecModel model = new AtomSpecModel();
+    assertEquals(model.getAtomSpec(), "");
+    model.addRange(1, 2, 4, "A");
+    assertEquals(model.getAtomSpec(), "#1:2-4.A");
+    model.addRange(1, 8, 8, "A");
+    assertEquals(model.getAtomSpec(), "#1:2-4.A,8.A");
+    model.addRange(1, 5, 7, "B");
+    assertEquals(model.getAtomSpec(), "#1:2-4.A,8.A,5-7.B");
+    model.addRange(1, 3, 5, "A");
+    assertEquals(model.getAtomSpec(), "#1:2-5.A,8.A,5-7.B");
+    model.addRange(0, 1, 4, "B");
+    assertEquals(model.getAtomSpec(), "#0:1-4.B|#1:2-5.A,8.A,5-7.B");
+    model.addRange(0, 5, 9, "C");
+    assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-7.B");
+    model.addRange(1, 8, 10, "B");
+    assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B");
+    model.addRange(1, 8, 9, "B");
+    assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B");
+    model.addRange(0, 3, 10, "C"); // subsumes 5-9
+    assertEquals(model.getAtomSpec(), "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B");
+  }
+
+}
index ffb886c..49a951e 100644 (file)
  */
 package jalview.ext.rbvi.chimera;
 
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertTrue;
-
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
 import jalview.gui.JvOptionPane;
+import jalview.gui.SequenceRenderer;
+import jalview.schemes.JalviewColourScheme;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureMappingcommandSet;
+import jalview.structure.StructureSelectionManager;
 
 import java.awt.Color;
-import java.util.Arrays;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.SortedMap;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -46,58 +56,10 @@ public class ChimeraCommandsTest
   }
 
   @Test(groups = { "Functional" })
-  public void testAddColourRange()
-  {
-    Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> map = new LinkedHashMap<Color, SortedMap<Integer, Map<String, List<int[]>>>>();
-    ChimeraCommands.addColourRange(map, Color.pink, 1, 2, 4, "A");
-    ChimeraCommands.addColourRange(map, Color.pink, 1, 8, 8, "A");
-    ChimeraCommands.addColourRange(map, Color.pink, 1, 5, 7, "B");
-    ChimeraCommands.addColourRange(map, Color.red, 1, 3, 5, "A");
-    ChimeraCommands.addColourRange(map, Color.red, 0, 1, 4, "B");
-    ChimeraCommands.addColourRange(map, Color.orange, 0, 5, 9, "C");
-
-    // three colours mapped
-    assertEquals(3, map.keySet().size());
-
-    // Red has two models, Pink and Orange one each
-    assertEquals(2, map.get(Color.red).keySet().size());
-    assertEquals(1, map.get(Color.orange).keySet().size());
-    assertEquals(1, map.get(Color.pink).keySet().size());
-
-    // pink model 1 has two chains, red.0 / red.1 / orange.0 one each
-    assertEquals(2, map.get(Color.pink).get(1).keySet().size());
-    assertEquals(1, map.get(Color.red).get(0).keySet().size());
-    assertEquals(1, map.get(Color.red).get(1).keySet().size());
-    assertEquals(1, map.get(Color.orange).get(0).keySet().size());
-
-    // inspect positions
-    List<int[]> posList = map.get(Color.pink).get(1).get("A");
-    assertEquals(2, posList.size());
-    assertTrue(Arrays.equals(new int[] { 2, 4 }, posList.get(0)));
-    assertTrue(Arrays.equals(new int[] { 8, 8 }, posList.get(1)));
-
-    posList = map.get(Color.pink).get(1).get("B");
-    assertEquals(1, posList.size());
-    assertTrue(Arrays.equals(new int[] { 5, 7 }, posList.get(0)));
-
-    posList = map.get(Color.red).get(0).get("B");
-    assertEquals(1, posList.size());
-    assertTrue(Arrays.equals(new int[] { 1, 4 }, posList.get(0)));
-
-    posList = map.get(Color.red).get(1).get("A");
-    assertEquals(1, posList.size());
-    assertTrue(Arrays.equals(new int[] { 3, 5 }, posList.get(0)));
-
-    posList = map.get(Color.orange).get(0).get("C");
-    assertEquals(1, posList.size());
-    assertTrue(Arrays.equals(new int[] { 5, 9 }, posList.get(0)));
-  }
-
-  @Test(groups = { "Functional" })
   public void testBuildColourCommands()
   {
 
-    Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> map = new LinkedHashMap<Color, SortedMap<Integer, Map<String, List<int[]>>>>();
+    Map<Object, AtomSpecModel> map = new LinkedHashMap<Object, AtomSpecModel>();
     ChimeraCommands.addColourRange(map, Color.blue, 0, 2, 5, "A");
     ChimeraCommands.addColourRange(map, Color.blue, 0, 7, 7, "B");
     ChimeraCommands.addColourRange(map, Color.blue, 0, 9, 23, "A");
@@ -106,13 +68,153 @@ public class ChimeraCommandsTest
     ChimeraCommands.addColourRange(map, Color.yellow, 1, 8, 8, "A");
     ChimeraCommands.addColourRange(map, Color.yellow, 1, 3, 5, "A");
     ChimeraCommands.addColourRange(map, Color.red, 0, 3, 5, "A");
+    ChimeraCommands.addColourRange(map, Color.red, 0, 6, 9, "A");
 
     // Colours should appear in the Chimera command in the order in which
-    // they were added; within colour, by model, by chain, and positions as
-    // added
+    // they were added; within colour, by model, by chain, ranges in start order
     String command = ChimeraCommands.buildColourCommands(map).get(0);
     assertEquals(
-            "color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B; color #ffff00 #1:8.A,3-5.A; color #ff0000 #0:3-5.A",
-            command);
+            command,
+            "color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B; color #ffff00 #1:3-5.A,8.A; color #ff0000 #0:3-9.A");
+  }
+
+  @Test(groups = { "Functional" })
+  public void testBuildSetAttributeCommands()
+  {
+    /*
+     * make a map of { featureType, {featureValue, {residue range specification } } }
+     */
+    Map<String, Map<Object, AtomSpecModel>> featuresMap = new LinkedHashMap<String, Map<Object, AtomSpecModel>>();
+    Map<Object, AtomSpecModel> featureValues = new HashMap<Object, AtomSpecModel>();
+    
+    /*
+     * start with just one feature/value...
+     */
+    featuresMap.put("chain", featureValues);
+    ChimeraCommands.addColourRange(featureValues, "X", 0, 8, 20, "A");
+  
+    List<String> commands = ChimeraCommands
+            .buildSetAttributeCommands(featuresMap);
+    assertEquals(1, commands.size());
+
+    /*
+     * feature name gets a jv_ namespace prefix
+     * feature value is quoted in case it contains spaces
+     */
+    assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:8-20.A");
+
+    // add same feature value, overlapping range
+    ChimeraCommands.addColourRange(featureValues, "X", 0, 3, 9, "A");
+    // same feature value, contiguous range
+    ChimeraCommands.addColourRange(featureValues, "X", 0, 21, 25, "A");
+    commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+    assertEquals(1, commands.size());
+    assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:3-25.A");
+
+    // same feature value and model, different chain
+    ChimeraCommands.addColourRange(featureValues, "X", 0, 21, 25, "B");
+    // same feature value and chain, different model
+    ChimeraCommands.addColourRange(featureValues, "X", 1, 26, 30, "A");
+    commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+    assertEquals(1, commands.size());
+    assertEquals(commands.get(0),
+            "setattr r jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A");
+
+    // same feature, different value
+    ChimeraCommands.addColourRange(featureValues, "Y", 0, 40, 50, "A");
+    commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+    assertEquals(2, commands.size());
+    // commands are ordered by feature type but not by value
+    // so use contains to test for the expected command:
+    assertTrue(commands
+            .contains("setattr r jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A"));
+    assertTrue(commands.contains("setattr r jv_chain 'Y' #0:40-50.A"));
+
+    featuresMap.clear();
+    featureValues.clear();
+    featuresMap.put("side-chain binding!", featureValues);
+    ChimeraCommands.addColourRange(featureValues,
+            "<html>metal <a href=\"http:a.b.c/x\"> 'ion!", 0, 7, 15,
+            "A");
+    // feature names are sanitised to change non-alphanumeric to underscore
+    // feature values are sanitised to encode single quote characters
+    commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+    assertTrue(commands
+            .contains("setattr r jv_side_chain_binding_ '<html>metal <a href=\"http:a.b.c/x\"> &#39;ion!' #0:7-15.A"));
+  }
+
+  /**
+   * Tests for the method that prefixes and sanitises a feature name so it can
+   * be used as a valid, namespaced attribute name in Chimera
+   */
+  @Test(groups = { "Functional" })
+  public void testMakeAttributeName()
+  {
+    assertEquals(ChimeraCommands.makeAttributeName(null), "jv_");
+    assertEquals(ChimeraCommands.makeAttributeName(""), "jv_");
+    assertEquals(ChimeraCommands.makeAttributeName("helix"), "jv_helix");
+    assertEquals(ChimeraCommands.makeAttributeName("Hello World 24"),
+            "jv_Hello_World_24");
+    assertEquals(
+            ChimeraCommands.makeAttributeName("!this is-a_very*{odd(name"),
+            "jv__this_is_a_very__odd_name");
+    // name ending in color gets underscore appended
+    assertEquals(ChimeraCommands.makeAttributeName("helixColor"),
+            "jv_helixColor_");
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetColourBySequenceCommands_hiddenColumns()
+  {
+    /*
+     * load these sequences, coloured by Strand propensity,
+     * with columns 2-4 hidden
+     */
+    SequenceI seq1 = new Sequence("seq1", "MHRSQSSSGG");
+    SequenceI seq2 = new Sequence("seq2", "MVRSNGGSSS");
+    AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
+    AlignFrame af = new AlignFrame(al, 800, 500);
+    af.changeColour_actionPerformed(JalviewColourScheme.Strand.toString());
+    ColumnSelection cs = new ColumnSelection();
+    cs.addElement(2);
+    cs.addElement(3);
+    cs.addElement(4);
+    af.getViewport().setColumnSelection(cs);
+    af.hideSelColumns_actionPerformed(null);
+    SequenceRenderer sr = new SequenceRenderer(af.getViewport());
+    SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
+    String[] files = new String[] { "seq1.pdb", "seq2.pdb" };
+    StructureSelectionManager ssm = new StructureSelectionManager();
+
+    /*
+     * map residues 1-10 to residues 21-30 (atoms 105-150) in structures
+     */
+    HashMap<Integer, int[]> map = new HashMap<Integer, int[]>();
+    for (int pos = 1; pos <= seq1.getLength(); pos++)
+    {
+      map.put(pos, new int[] { 20 + pos, 5 * (20 + pos) });
+    }
+    StructureMapping sm1 = new StructureMapping(seq1, "seq1.pdb", "pdb1",
+            "A", map, null);
+    ssm.addStructureMapping(sm1);
+    StructureMapping sm2 = new StructureMapping(seq2, "seq2.pdb", "pdb2",
+            "B", map, null);
+    ssm.addStructureMapping(sm2);
+
+    StructureMappingcommandSet[] commands = ChimeraCommands
+            .getColourBySequenceCommand(ssm, files, seqs, sr, af.alignPanel);
+    assertEquals(1, commands.length);
+    assertEquals(1, commands[0].commands.length);
+    String theCommand = commands[0].commands[0];
+    // M colour is #82827d (see strand.html help page)
+    assertTrue(theCommand.contains("color #82827d #0:21.A|#1:21.B"));
+    // H colour is #60609f
+    assertTrue(theCommand.contains("color #60609f #0:22.A"));
+    // V colour is ##ffff00
+    assertTrue(theCommand.contains("color #ffff00 #1:22.B"));
+    // hidden columns are Gray (128, 128, 128)
+    assertTrue(theCommand.contains("color #808080 #0:23-25.A|#1:23-25.B"));
+    // S and G are both coloured #4949b6
+    assertTrue(theCommand.contains("color #4949b6 #0:26-30.A|#1:26-30.B"));
   }
 }
index f4fb40b..d85bb10 100644 (file)
  */
 package jalview.ext.rbvi.chimera;
 
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
 
+import jalview.api.FeatureRenderer;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
+import jalview.bin.Jalview;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
 import jalview.gui.JvOptionPane;
 import jalview.gui.Preferences;
 import jalview.gui.StructureViewer;
 import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.FileLoader;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
+import jalview.ws.sifts.SiftsClient;
+import jalview.ws.sifts.SiftsException;
+import jalview.ws.sifts.SiftsSettings;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Vector;
 import jalview.io.DataSourceType;
 
 import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -48,15 +68,25 @@ public class JalviewChimeraView
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
+  private JalviewStructureDisplayI chimeraViewer;
+
   /**
    * @throws java.lang.Exception
    */
   @BeforeClass(alwaysRun = true)
   public static void setUpBeforeClass() throws Exception
   {
-    jalview.bin.Jalview.main(new String[] {
-        "-noquestionnaire -nonews -props",
+    Jalview.main(new String[] { "-noquestionnaire", "-nonews", "-props",
         "test/jalview/ext/rbvi/chimera/testProps.jvprops" });
+    Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
+            ViewerType.CHIMERA.name());
+    Cache.setProperty("SHOW_ANNOTATIONS", "false");
+    Cache.setProperty(Preferences.STRUCT_FROM_PDB, "false");
+    Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
+            ViewerType.CHIMERA.name());
+    Cache.setProperty("MAP_WITH_SIFTS", "true");
+    // TODO this should not be necessary!
+    SiftsSettings.setMapWithSifts(true);
   }
 
   /**
@@ -65,57 +95,388 @@ public class JalviewChimeraView
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.instance.closeAll_actionPerformed(null);
+  }
+
+  @AfterMethod(alwaysRun = true)
+  public void tearDownAfterTest() throws Exception
+  {
+    SiftsClient.setMockSiftsFile(null);
+    if (chimeraViewer != null)
+    {
+      chimeraViewer.closeViewer(true);
+    }
   }
 
-  @Test(groups = { "Functional" })
+  /**
+   * Load 1GAQ and view the first structure for which a PDB id is found. Note no
+   * network connection is needed - PDB file is read locally, SIFTS fetch fails
+   * so mapping falls back to Needleman-Wunsch - ok for this test.
+   */
+  // External as local install of Chimera required
+  @Test(groups = { "External" })
   public void testSingleSeqViewChimera()
   {
-    Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
-            ViewerType.CHIMERA.name());
     String inFile = "examples/1gaq.txt";
-    AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
-            inFile, DataSourceType.FILE);
-    assertTrue("Didn't read input file " + inFile, af != null);
-    for (SequenceI sq : af.getViewport().getAlignment().getSequences())
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
+            DataSourceType.FILE);
+    assertNotNull(af, "Failed to create AlignFrame");
+    SequenceI sq = af.getViewport().getAlignment().getSequenceAt(0);
+    assertEquals(sq.getName(), "1GAQ|A");
+    SequenceI dsq = sq.getDatasetSequence();
+    Vector<PDBEntry> pdbIds = dsq.getAllPDBEntries();
+    assertEquals(pdbIds.size(), 1);
+    PDBEntry pdbEntry = pdbIds.get(0);
+    assertEquals(pdbEntry.getId(), "1GAQ");
+    StructureViewer structureViewer = new StructureViewer(af.getViewport()
+            .getStructureSelectionManager());
+    chimeraViewer = structureViewer.viewStructures(pdbEntry,
+            new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
+    JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
+            .getBinding();
+
+    /*
+     * Wait for viewer load thread to complete
+     */
+    while (!binding.isFinishedInit())
     {
-      System.out.println("** sq=" + sq.getName());
-      SequenceI dsq = sq.getDatasetSequence();
-      while (dsq.getDatasetSequence() != null)
+      try
+      {
+        Thread.sleep(500);
+      } catch (InterruptedException e)
       {
-        dsq = dsq.getDatasetSequence();
       }
-      if (dsq.getAllPDBEntries() != null
-              && dsq.getAllPDBEntries().size() > 0)
+    }
+
+    assertTrue(binding.isChimeraRunning(), "Failed to start Chimera");
+
+    assertEquals(chimeraViewer.getBinding().getPdbCount(), 1);
+    chimeraViewer.closeViewer(true);
+    chimeraViewer = null;
+    return;
+  }
+
+  /**
+   * Test for writing Jalview features as attributes on mapped residues in
+   * Chimera. Note this uses local copies of PDB and SIFTS file, no network
+   * connection required.
+   * 
+   * @throws IOException
+   * @throws SiftsException
+   */
+  // External as this requires a local install of Chimera
+  @Test(groups = { "External" })
+  public void testTransferFeatures() throws IOException, SiftsException
+  {
+    String inFile = "examples/uniref50.fa";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
+            DataSourceType.FILE);
+    assertNotNull(af, "Failed to create AlignFrame");
+    SequenceI sq = af.getViewport().getAlignment().findName("FER2_ARATH");
+    assertNotNull(sq, "Didn't find FER2_ARATH");
+
+    /*
+     * need a Uniprot dbref for SIFTS mapping to work!!
+     */
+    sq.addDBRef(new DBRefEntry("UNIPROT", "0", "P16972", null));
+
+    /*
+     * use local test PDB and SIFTS files
+     */
+    String pdbFilePath = new File(
+            "test/jalview/ext/rbvi/chimera/4zho.pdb").getPath();
+    PDBEntry pdbEntry = new PDBEntry("4ZHO", null, null, pdbFilePath);
+    String siftsFilePath = new File(
+            "test/jalview/ext/rbvi/chimera/4zho.xml.gz")
+            .getPath();
+    SiftsClient.setMockSiftsFile(new File(siftsFilePath));
+
+    StructureViewer structureViewer = new StructureViewer(af.getViewport()
+            .getStructureSelectionManager());
+    chimeraViewer = structureViewer.viewStructures(pdbEntry,
+            new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
+
+    JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
+            .getBinding();
+    do
+    {
+      try
+      {
+        Thread.sleep(500);
+      } catch (InterruptedException e)
+      {
+      }
+    } while (!binding.isFinishedInit());
+
+    assertTrue(binding.isChimeraRunning(), "Failed to launch Chimera");
+
+    assertEquals(binding.getPdbCount(), 1);
+
+    /*
+     * check mapping is (sequence) 53-145 to (structure) 2-94 A/B
+     * (or possibly 52-145 to 1-94 - see JAL-2319)
+     */
+    StructureSelectionManager ssm = binding.getSsm();
+    String pdbFile = binding.getPdbFile()[0];
+    StructureMapping[] mappings = ssm.getMapping(pdbFile);
+    assertTrue(mappings[0].getMappingDetailsOutput().contains("SIFTS"),
+            "Failed to perform SIFTS mapping");
+    assertEquals(mappings.length, 2);
+    assertEquals(mappings[0].getChain(), "A");
+    assertEquals(mappings[0].getPDBResNum(53), 2);
+    assertEquals(mappings[0].getPDBResNum(145), 94);
+    assertEquals(mappings[1].getChain(), "B");
+    assertEquals(mappings[1].getPDBResNum(53), 2);
+    assertEquals(mappings[1].getPDBResNum(145), 94);
+
+    /*
+     * now add some features to FER2_ARATH 
+     */
+    // feature on a sequence region not mapped to structure:
+    sq.addSequenceFeature(new SequenceFeature("transit peptide",
+            "chloroplast", 1, 51, Float.NaN, null));
+    // feature on a region mapped to structure:
+    sq.addSequenceFeature(new SequenceFeature("domain",
+            "2Fe-2S ferredoxin-type", 55, 145, Float.NaN, null));
+    // on sparse positions of the sequence
+    sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
+            "Iron-Sulfur (2Fe-2S)", 91, 91, Float.NaN, null));
+    sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
+            "Iron-Sulfur (2Fe-2S)", 96, 96, Float.NaN, null));
+    // on a sequence region that is partially mapped to structure:
+    sq.addSequenceFeature(new SequenceFeature("helix", null, 50, 60,
+            Float.NaN, null));
+    // and again:
+    sq.addSequenceFeature(new SequenceFeature("chain", null, 50, 70,
+            Float.NaN, null));
+    // add numeric valued features - score is set as attribute value
+    sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 62,
+            62, -2.1f, null));
+    sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 65,
+            65, 3.6f, null));
+    sq.addSequenceFeature(new SequenceFeature("RESNUM", "ALA:   2  4zhoA",
+            53, 53, Float.NaN, null));
+
+    /*
+     * set all features visible except for chain
+     */
+    af.setShowSeqFeatures(true);
+    FeatureRenderer fr = af.getFeatureRenderer();
+    fr.setVisible("transit peptide");
+    fr.setVisible("domain");
+    fr.setVisible("metal ion-binding site");
+    fr.setVisible("helix");
+    fr.setVisible("kd");
+    fr.setVisible("RESNUM");
+
+    /*
+     * 'perform' menu action to copy visible features to
+     * attributes in Chimera
+     */
+    // TODO rename and pull up method to binding interface
+    // once functionality is added for Jmol as well
+    binding.sendFeaturesToViewer(af.getViewport().getAlignPanel());
+
+    /*
+     * give Chimera time to open the commands file and execute it
+     */
+    try
+    {
+      Thread.sleep(1000);
+    } catch (InterruptedException e)
+    {
+    }
+
+    /*
+     * ask Chimera for its residue attribute names
+     */
+    List<String> reply = binding.sendChimeraCommand("list resattr", true);
+    // prefixed and sanitised attribute names for Jalview features:
+    assertTrue(reply.contains("resattr jv_domain"));
+    assertTrue(reply.contains("resattr jv_metal_ion_binding_site"));
+    assertTrue(reply.contains("resattr jv_helix"));
+    assertTrue(reply.contains("resattr jv_kd"));
+    assertTrue(reply.contains("resattr jv_RESNUM"));
+    // feature is not on a mapped region - no attribute created
+    assertFalse(reply.contains("resattr jv_transit_peptide"));
+    // feature is not visible - no attribute created
+    assertFalse(reply.contains("resattr jv_chain"));
+
+    /*
+     * ask Chimera for residues with an attribute
+     * 91 and 96 on sequence --> residues 40 and 45 on chains A and B
+     */
+    reply = binding.sendChimeraCommand(
+            "list resi att jv_metal_ion_binding_site", true);
+    assertEquals(reply.size(), 4);
+    assertTrue(reply
+            .contains("residue id #0:40.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
+    assertTrue(reply
+            .contains("residue id #0:45.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
+    assertTrue(reply
+            .contains("residue id #0:40.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
+    assertTrue(reply
+            .contains("residue id #0:45.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
+
+    /*
+     * check attributes with score values
+     * sequence positions 62 and 65 --> residues 11 and 14 on chains A and B
+     */
+    reply = binding.sendChimeraCommand("list resi att jv_kd", true);
+    assertEquals(reply.size(), 4);
+    assertTrue(reply.contains("residue id #0:11.A jv_kd -2.1 index 11"));
+    assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
+    assertTrue(reply.contains("residue id #0:11.B jv_kd -2.1 index 11"));
+    assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
+
+    /*
+     * list residues with positive kd score 
+     */
+    reply = binding.sendChimeraCommand(
+            "list resi spec :*/jv_kd>0 attr jv_kd", true);
+    assertEquals(reply.size(), 2);
+    assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
+    assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
+
+    SiftsClient.setMockSiftsFile(null);
+    chimeraViewer.closeViewer(true);
+    chimeraViewer = null;
+  }
+
+  /**
+   * Test for creating Jalview features from attributes on mapped residues in
+   * Chimera. Note this uses local copies of PDB and SIFTS file, no network
+   * connection required.
+   * 
+   * @throws IOException
+   * @throws SiftsException
+   */
+  // External as this requires a local install of Chimera
+  @Test(groups = { "External" })
+  public void testGetAttributes() throws IOException, SiftsException
+  {
+    String inFile = "examples/uniref50.fa";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
+            DataSourceType.FILE);
+    assertNotNull(af, "Failed to create AlignFrame");
+    SequenceI fer2Arath = af.getViewport().getAlignment()
+            .findName("FER2_ARATH");
+    assertNotNull(fer2Arath, "Didn't find FER2_ARATH");
+  
+    /*
+     * need a Uniprot dbref for SIFTS mapping to work!!
+     */
+    fer2Arath.addDBRef(new DBRefEntry("UNIPROT", "0", "P16972", null));
+  
+    /*
+     * use local test PDB and SIFTS files
+     */
+    String pdbFilePath = new File(
+            "test/jalview/ext/rbvi/chimera/4zho.pdb").getPath();
+    PDBEntry pdbEntry = new PDBEntry("4ZHO", null, null, pdbFilePath);
+    String siftsFilePath = new File(
+            "test/jalview/ext/rbvi/chimera/4zho.xml.gz")
+            .getPath();
+    SiftsClient.setMockSiftsFile(new File(siftsFilePath));
+  
+    StructureViewer structureViewer = new StructureViewer(af.getViewport()
+            .getStructureSelectionManager());
+    chimeraViewer = structureViewer.viewStructures(pdbEntry,
+            new SequenceI[] { fer2Arath }, af.getCurrentView()
+                    .getAlignPanel());
+  
+    JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
+            .getBinding();
+    do
+    {
+      try
+      {
+        Thread.sleep(500);
+      } catch (InterruptedException e)
       {
-        for (int q = 0; q < dsq.getAllPDBEntries().size(); q++)
-        {
-          final StructureViewer structureViewer = new StructureViewer(af
-                  .getViewport().getStructureSelectionManager());
-          structureViewer.setViewerType(ViewerType.CHIMERA);
-          JalviewStructureDisplayI chimeraViewer = structureViewer
-                  .viewStructures(dsq.getAllPDBEntries().elementAt(q),
-                          new SequenceI[] { sq }, af.getCurrentView()
-                                  .getAlignPanel());
-          /*
-           * Wait for viewer load thread to complete
-           */
-          while (!chimeraViewer.getBinding().isFinishedInit())
-          {
-            try
-            {
-              Thread.sleep(500);
-            } catch (InterruptedException e)
-            {
-            }
-          }
-          assertEquals(1, chimeraViewer.getBinding().getPdbCount());
-          chimeraViewer.closeViewer(true);
-          // todo: break here means only once through this loop?
-          break;
-        }
-        break;
       }
+    } while (!binding.isFinishedInit());
+  
+    assertTrue(binding.isChimeraRunning(), "Failed to launch Chimera");
+  
+    assertEquals(binding.getPdbCount(), 1);
+  
+    /*
+     * 'perform' menu action to copy visible features to
+     * attributes in Chimera
+     */
+    // TODO rename and pull up method to binding interface
+    // once functionality is added for Jmol as well
+    binding.copyStructureAttributesToFeatures("isHelix", af.getViewport()
+            .getAlignPanel());
+
+    /*
+     * verify 22 residues have isHelix feature
+     * (may merge into ranges in future)
+     */
+    af.setShowSeqFeatures(true);
+    FeatureRenderer fr = af.getFeatureRenderer();
+    fr.setVisible("isHelix");
+    for (int res = 75; res <= 83; res++)
+    {
+      checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
     }
+    for (int res = 117; res <= 123; res++)
+    {
+      checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
+    }
+    for (int res = 129; res <= 131; res++)
+    {
+      checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
+    }
+    for (int res = 143; res <= 145; res++)
+    {
+      checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
+    }
+
+    /*
+     * fetch a numeric valued attribute
+     */
+    binding.copyStructureAttributesToFeatures("phi", af.getViewport()
+            .getAlignPanel());
+    fr.setVisible("phi");
+    List<SequenceFeature> fs = fr.findFeaturesAtRes(fer2Arath, 54);
+    assertEquals(fs.size(), 3);
+    assertEquals(fs.get(0).getType(), "RESNUM");
+    assertEquals(fs.get(1).getType(), "phi");
+    assertEquals(fs.get(2).getType(), "phi");
+    assertEquals(fs.get(1).getDescription(), "A"); // chain
+    assertEquals(fs.get(2).getDescription(), "B");
+    assertEquals(fs.get(1).getScore(), -131.0713f, 0.001f);
+    assertEquals(fs.get(2).getScore(), -127.39512, 0.001f);
+
+    /*
+     * tear down - also in AfterMethod
+     */
+    SiftsClient.setMockSiftsFile(null);
+    chimeraViewer.closeViewer(true);
+    chimeraViewer = null;
+  }
+
+  /**
+   * Helper method to verify new feature at a sequence position
+   * 
+   * @param seq
+   * @param fr
+   * @param res
+   * @param featureType
+   */
+  protected void checkFeaturesAtRes(SequenceI seq, FeatureRenderer fr,
+          int res, String featureType)
+  {
+    String where = "at position " + res;
+    List<SequenceFeature> fs = fr.findFeaturesAtRes(seq, res);
+    assertEquals(fs.size(), 2, where);
+    assertEquals(fs.get(0).getType(), "RESNUM", where);
+    SequenceFeature sf = fs.get(1);
+    assertEquals(sf.getType(), featureType, where);
+    assertEquals(sf.getFeatureGroup(), "Chimera", where);
+    assertEquals(sf.getDescription(), "True", where);
+    assertEquals(sf.getScore(), Float.NaN, where);
   }
 }
index 95da22e..5c08d8e 100644 (file)
@@ -1,85 +1,82 @@
-#---JalviewX Properties File---
-#Fri Apr 25 09:54:25 BST 2014
-SCREEN_Y=768
-SCREEN_X=936
-SHOW_WSDISCOVERY_ERRORS=true
-LATEST_VERSION=2.8.0b1
-SHOW_CONSERVATION=true
+ANNOTATIONCOLOUR_MAX=ff0000
+ANNOTATIONCOLOUR_MIN=ffc800
+ANTI_ALIAS=false
+AUTHORFNAMES=Jim Procter, Andrew Waterhouse, Jan Engelhardt, Lauren Lui, Michele Clamp, James Cuff, Steve Searle, David Martin & Geoff Barton
+AUTHORS=J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
+AUTO_CALC_CONSENSUS=true
+BLC_JVSUFFIX=true
+BUILD_DATE=01 November 2013
+CLUSTAL_JVSUFFIX=true
+DAS_ACTIVE_SOURCE=uniprot\t
+DAS_LOCAL_SOURCE=
+DAS_REGISTRY_URL=http\://www.ebi.ac.uk/das-srv/registry/das/
+DEFAULT_COLOUR=None
+DEFAULT_FILE_FORMAT=FASTA
+FASTA_JVSUFFIX=true
+FIGURE_AUTOIDWIDTH=false
+FIGURE_USERIDWIDTH=
+FONT_NAME=SansSerif
+FONT_SIZE=10
+FONT_STYLE=plain
+GAP_SYMBOL=-
+ID_ITALICS=true
+JALVIEW_NEWS_RSS_LASTMODIFIED=Apr 23, 2014 2\:53\:26 PM
+JALVIEW_RSS_WINDOW_SCREEN_HEIGHT=328
 JALVIEW_RSS_WINDOW_SCREEN_WIDTH=550
+JALVIEW_RSS_WINDOW_SCREEN_X=0
+JALVIEW_RSS_WINDOW_SCREEN_Y=0
+JAVA_CONSOLE_SCREEN_HEIGHT=162
 JAVA_CONSOLE_SCREEN_WIDTH=450
+JAVA_CONSOLE_SCREEN_X=830
+JAVA_CONSOLE_SCREEN_Y=475
+JWS2HOSTURLS=http\://www.compbio.dundee.ac.uk/jabaws
 LAST_DIRECTORY=/Volumes/Data/Users/jimp/Documents/testing/Jalview/examples
-ID_ITALICS=true
-SORT_ALIGNMENT=No sort
-SHOW_IDENTITY=true
-WSMENU_BYHOST=false
-SEQUENCE_LINKS=EMBL-EBI Search|http\://www.ebi.ac.uk/ebisearch/search.ebi?db\=allebi&query\=$SEQUENCE_ID$
-SHOW_FULLSCREEN=false
-RECENT_URL=http\://www.jalview.org/examples/exampleFile_2_7.jar
-FONT_NAME=SansSerif
-BLC_JVSUFFIX=true
-VERSION_CHECK=false
-YEAR=2011
-SHOW_DBREFS_TOOLTIP=true
+LATEST_VERSION=2.8.0b1
 MSF_JVSUFFIX=true
-SCREENGEOMETRY_HEIGHT=1600
-JAVA_CONSOLE_SCREEN_Y=475
-JAVA_CONSOLE_SCREEN_X=830
+NOQUESTIONNAIRES=true
+PAD_GAPS=false
 PFAM_JVSUFFIX=true
+PILEUP_JVSUFFIX=true
 PIR_JVSUFFIX=true
-STARTUP_FILE=http\://www.jalview.org/examples/exampleFile_2_3.jar
-JAVA_CONSOLE_SCREEN_HEIGHT=162
 PIR_MODELLER=false
-GAP_SYMBOL=-
-SHOW_QUALITY=true
+RECENT_FILE=examples/uniref50.fa\t/Volumes/Data/Users/jimp/Documents/testing/Jalview/examples/RF00031_folded.stk\t/Volumes/Data/Users/jimp/bs_ig_mult.out
+RECENT_URL=http\://www.jalview.org/examples/exampleFile_2_7.jar
+RIGHT_ALIGN_IDS=false
+RSBS_SERVICES=|Multi-Harmony|Analysis|Sequence Harmony and Multi-Relief (Brandt et al. 2010)|hseparable,gapCharacter\='-',returns\='ANNOTATION'|?tool\=jalview|http\://zeus.few.vu.nl/programs/shmrwww/index.php?tool\=jalview&groups\=$PARTITION\:min\='2',minsize\='2',sep\=' '$&ali_file\=$ALIGNMENT\:format\='FASTA',writeasfile$
+SCREEN_HEIGHT=650
+SCREEN_WIDTH=900
+SCREEN_X=936
+SCREEN_Y=768
+SCREENGEOMETRY_HEIGHT=1600
+SCREENGEOMETRY_WIDTH=2560
+SEQUENCE_LINKS=EMBL-EBI Search|http\://www.ebi.ac.uk/ebisearch/search.ebi?db\=allebi&query\=$SEQUENCE_ID$
+SHOW_ANNOTATIONS=true
+SHOW_CONSENSUS_HISTOGRAM=true
+SHOW_CONSENSUS_LOGO=false
+SHOW_CONSERVATION=true
+SHOW_DBREFS_TOOLTIP=true
+SHOW_ENFIN_SERVICES=true
+SHOW_FULLSCREEN=false
+SHOW_GROUP_CONSENSUS=false
 SHOW_GROUP_CONSERVATION=false
+SHOW_IDENTITY=true
+SHOW_JAVA_CONSOLE=false
+SHOW_JVSUFFIX=true
 SHOW_JWS2_SERVICES=true
 SHOW_NPFEATS_TOOLTIP=true
-FONT_STYLE=plain
-ANTI_ALIAS=false
-SORT_BY_TREE=false
-RSBS_SERVICES=|Multi-Harmony|Analysis|Sequence Harmony and Multi-Relief (Brandt et al. 2010)|hseparable,gapCharacter\='-',returns\='ANNOTATION'|?tool\=jalview|http\://zeus.few.vu.nl/programs/shmrwww/index.php?tool\=jalview&groups\=$PARTITION\:min\='2',minsize\='2',sep\=' '$&ali_file\=$ALIGNMENT\:format\='FASTA',writeasfile$
-AUTHORFNAMES=Jim Procter, Andrew Waterhouse, Jan Engelhardt, Lauren Lui, Michele Clamp, James Cuff, Steve Searle, David Martin & Geoff Barton
-JALVIEW_RSS_WINDOW_SCREEN_HEIGHT=328
-SHOW_GROUP_CONSENSUS=false
-SHOW_CONSENSUS_HISTOGRAM=true
 SHOW_OVERVIEW=false
-AUTHORS=J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
-FIGURE_AUTOIDWIDTH=false
-SCREEN_WIDTH=900
-ANNOTATIONCOLOUR_MIN=ffc800
+SHOW_QUALITY=true
 SHOW_STARTUP_FILE=false
-RECENT_FILE=examples/uniref50.fa\t/Volumes/Data/Users/jimp/Documents/testing/Jalview/examples/RF00031_folded.stk\t/Volumes/Data/Users/jimp/bs_ig_mult.out
-DEFAULT_FILE_FORMAT=FASTA
-SHOW_JAVA_CONSOLE=false
-VERSION=2.8b1
-FIGURE_USERIDWIDTH=
-WSMENU_BYTYPE=false
-DEFAULT_COLOUR=None
-NOQUESTIONNAIRES=true
-JALVIEW_NEWS_RSS_LASTMODIFIED=Apr 23, 2014 2\:53\:26 PM
-BUILD_DATE=01 November 2013
-PILEUP_JVSUFFIX=true
-SHOW_CONSENSUS_LOGO=false
-SCREENGEOMETRY_WIDTH=2560
-SHOW_ANNOTATIONS=true
-JALVIEW_RSS_WINDOW_SCREEN_Y=0
-USAGESTATS=false
-JALVIEW_RSS_WINDOW_SCREEN_X=0
 SHOW_UNCONSERVED=false
-SHOW_JVSUFFIX=true
-DAS_LOCAL_SOURCE=
-SCREEN_HEIGHT=650
-ANNOTATIONCOLOUR_MAX=ff0000
-AUTO_CALC_CONSENSUS=true
-FASTA_JVSUFFIX=true
-DAS_ACTIVE_SOURCE=uniprot\t
-JWS2HOSTURLS=http\://www.compbio.dundee.ac.uk/jabaws
-PAD_GAPS=false
-CLUSTAL_JVSUFFIX=true
-SHOW_ENFIN_SERVICES=true
-FONT_SIZE=10
-RIGHT_ALIGN_IDS=false
+SHOW_WSDISCOVERY_ERRORS=true
+SORT_ALIGNMENT=No sort
+SORT_BY_TREE=false
+STARTUP_FILE=
+USAGESTATS=false
 USE_PROXY=false
+VERSION_CHECK=false
+VERSION=2.8b1
 WRAP_ALIGNMENT=false
-#DAS_REGISTRY_URL=http\://www.dasregistry.org/das/ # retired 01/05/2015
-DAS_REGISTRY_URL=http\://www.ebi.ac.uk/das-srv/registry/das/
+WSMENU_BYHOST=false
+WSMENU_BYTYPE=false
+YEAR=2011
index 1cdcdb7..06df70a 100644 (file)
@@ -38,6 +38,7 @@ import jalview.datamodel.PDBEntry.Type;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
@@ -382,4 +383,25 @@ public class AlignViewportTest
     af.getViewport().setSearchResults(null);
     assertFalse(af.getViewport().hasSearchResults());
   }
+
+  /**
+   * Verify that setting the selection group has the side-effect of setting the
+   * context on the group, unless it already has one
+   */
+  @Test(groups = { "Functional" })
+  public void testSetSelectionGroup()
+  {
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", DataSourceType.FILE);
+    AlignViewport av = af.getViewport();
+    SequenceGroup sg1 = new SequenceGroup();
+    SequenceGroup sg2 = new SequenceGroup();
+
+    av.setSelectionGroup(sg1);
+    assertSame(sg1.getContext(), av.getAlignment()); // context set
+
+    sg2.setContext(sg1);
+    av.setSelectionGroup(sg2);
+    assertSame(sg2.getContext(), sg1); // unchanged
+  }
 }
diff --git a/test/jalview/gui/AlignmentPanelTest.java b/test/jalview/gui/AlignmentPanelTest.java
new file mode 100644 (file)
index 0000000..b228ba1
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.gui;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.bin.Cache;
+import jalview.bin.Jalview;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+import jalview.viewmodel.ViewportRanges;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class AlignmentPanelTest
+{
+  SequenceI seq1 = new Sequence(
+          "Seq1",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq2 = new Sequence(
+          "Seq2",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq3 = new Sequence(
+          "Seq3",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq4 = new Sequence(
+          "Seq4",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq5 = new Sequence(
+          "Seq5",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq6 = new Sequence(
+          "Seq6",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq7 = new Sequence(
+          "Seq7",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq8 = new Sequence(
+          "Seq8",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq9 = new Sequence(
+          "Seq9",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq10 = new Sequence(
+          "Seq10",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq11 = new Sequence(
+          "Seq11",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq12 = new Sequence(
+          "Seq12",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq13 = new Sequence(
+          "Seq13",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq14 = new Sequence(
+          "Seq14",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq15 = new Sequence(
+          "Seq15",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq16 = new Sequence(
+          "Seq16",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq17 = new Sequence(
+          "Seq17",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq18 = new Sequence(
+          "Seq18",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq19 = new Sequence(
+          "Seq19",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq20 = new Sequence(
+          "Seq20",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq21 = new Sequence(
+          "Seq21",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq22 = new Sequence(
+          "Seq22",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  SequenceI seq23 = new Sequence(
+          "Seq23",
+          "ABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBACABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+  AlignFrame af;
+
+  @BeforeMethod(alwaysRun = true)
+  public void setUp()
+  {
+    Jalview.main(new String[] { "-nonews", "-props",
+        "test/jalview/testProps.jvprops" });
+
+    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+            Boolean.TRUE.toString());
+    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
+            DataSourceType.FILE);
+
+    /*
+     * wait for Consensus thread to complete
+     */
+    synchronized (this)
+    {
+      while (af.getViewport().getConsensusSeq() == null)
+      {
+        try
+        {
+          wait(50);
+        } catch (InterruptedException e)
+        {
+        }
+      }
+    }
+  }
+
+
+  /**
+   * Test side effect that end residue is set correctly by setScrollValues, with
+   * or without hidden columns
+   */
+  @Test(groups = "Functional")
+  public void TestSetScrollValues()
+  {
+    ViewportRanges ranges = af.getViewport().getRanges();
+    af.alignPanel.setScrollValues(0, 0);
+
+    int oldres = ranges.getEndRes();
+    af.alignPanel.setScrollValues(-1, 5);
+
+    // setting -ve x value does not change residue
+    assertEquals(ranges.getEndRes(), oldres);
+
+    af.alignPanel.setScrollValues(0, 5);
+
+    // setting 0 as x value does not change residue
+    assertEquals(ranges.getEndRes(), oldres);
+
+    af.alignPanel.setScrollValues(5, 5);
+    // setting x value to 5 extends endRes by 5 residues
+    assertEquals(ranges.getEndRes(), oldres + 5);
+
+    // scroll to position after hidden columns sets endres to oldres (width) +
+    // position
+    int scrollpos = 60;
+    af.getViewport().hideColumns(30, 50);
+    af.alignPanel.setScrollValues(scrollpos, 5);
+    assertEquals(ranges.getEndRes(), oldres + scrollpos);
+
+    // scroll to position within hidden columns, still sets endres to oldres +
+    // position
+    // not sure if this is actually correct behaviour but this is what Jalview
+    // currently does
+    scrollpos = 40;
+    af.getViewport().showAllHiddenColumns();
+    af.getViewport().hideColumns(30, 50);
+    af.alignPanel.setScrollValues(scrollpos, 5);
+    assertEquals(ranges.getEndRes(), oldres + scrollpos);
+
+    // scroll to position within <width> distance of the end of the alignment
+    // endRes should be set to width of alignment - 1
+    scrollpos = 130;
+    af.getViewport().showAllHiddenColumns();
+    af.alignPanel.setScrollValues(scrollpos, 5);
+    assertEquals(ranges.getEndRes(), af.getViewport()
+            .getAlignment().getWidth() - 1);
+
+    // now hide some columns, and scroll to position within <width>
+    // distance of the end of the alignment
+    // endRes should be set to width of alignment - 1 - the number of hidden
+    // columns
+    af.getViewport().hideColumns(30, 50);
+    af.alignPanel.setScrollValues(scrollpos, 5);
+    assertEquals(ranges.getEndRes(), af.getViewport()
+            .getAlignment().getWidth() - 1 - 21); // 21 is the number of hidden
+                                                  // columns
+  }
+}
index 38c1855..fbdda09 100644 (file)
@@ -326,7 +326,7 @@ public class AnnotationChooserTest
 
     types = AnnotationChooser.getAnnotationTypes(
             parentPanel.getAlignment(), false);
-    assertEquals("Not six annotation types", 6, types.size());
+    assertEquals("Not six annotation types", 7, types.size());
     assertTrue("IUPRED missing", types.contains("IUPRED"));
     assertTrue("JMol missing", types.contains("JMol"));
     assertTrue("Beauty missing", types.contains("Beauty"));
@@ -334,6 +334,7 @@ public class AnnotationChooserTest
     assertTrue("Consensus missing", types.contains("Consensus"));
     assertTrue("Quality missing", types.contains("Quality"));
     assertTrue("Conservation missing", types.contains("Conservation"));
+    assertTrue("Occupancy missing", types.contains("Occupancy"));
   }
 
   /**
@@ -357,18 +358,20 @@ public class AnnotationChooserTest
     AlignmentAnnotation[] anns = parentPanel.getAlignment()
             .getAlignmentAnnotation();
 
-    assertTrue(anns[5].visible); // JMol for seq3
-    assertTrue(anns[7].visible); // JMol for seq1
+    int autocalc = countAutocalc(anns);
+    assertTrue(anns[autocalc + 2].visible); // JMol for seq3
+    assertTrue(anns[autocalc + 4].visible); // JMol for seq1
 
     setSelected(getTypeCheckbox("JMol"), true);
     assertTrue(anns[0].visible); // Conservation
     assertTrue(anns[1].visible); // Quality
     assertTrue(anns[2].visible); // Consensus
-    assertTrue(anns[3].visible); // IUPred for seq0
-    assertTrue(anns[4].visible); // Beauty
-    assertFalse(anns[5].visible); // JMol for seq3 - not selected but hidden
-    assertTrue(anns[6].visible); // IUPRED for seq2
-    assertFalse(anns[7].visible); // JMol for seq1 - selected and hidden
+    assertTrue(anns[3].visible); // Occupancy
+    assertTrue(anns[4].visible); // IUPred for seq0
+    assertTrue(anns[5].visible); // Beauty
+    assertFalse(anns[6].visible); // JMol for seq3 - not selected but hidden
+    assertTrue(anns[7].visible); // IUPRED for seq2
+    assertFalse(anns[8].visible); // JMol for seq1 - selected and hidden
   }
 
   /**
@@ -395,17 +398,19 @@ public class AnnotationChooserTest
     AlignmentAnnotation[] anns = parentPanel.getAlignment()
             .getAlignmentAnnotation();
 
-    assertTrue(anns[7].visible); // JMol for seq1
+    int autocalc = countAutocalc(anns);
+    assertTrue(anns[autocalc + 4].visible); // JMol for seq1
 
     setSelected(getTypeCheckbox("JMol"), true);
     assertTrue(anns[0].visible); // Conservation
     assertTrue(anns[1].visible); // Quality
     assertTrue(anns[2].visible); // Consensus
-    assertTrue(anns[3].visible); // IUPred for seq0
-    assertTrue(anns[4].visible); // Beauty
-    assertTrue(anns[5].visible); // JMol for seq3 not in selection group
-    assertTrue(anns[6].visible); // IUPRED for seq2
-    assertFalse(anns[7].visible); // JMol for seq1 in selection group
+    assertTrue(anns[3].visible); // Occupancy
+    assertTrue(anns[4].visible); // IUPred for seq0
+    assertTrue(anns[5].visible); // Beauty
+    assertTrue(anns[6].visible); // JMol for seq3 not in selection group
+    assertTrue(anns[7].visible); // IUPRED for seq2
+    assertFalse(anns[8].visible); // JMol for seq1 in selection group
   }
 
   /**
@@ -435,19 +440,35 @@ public class AnnotationChooserTest
 
     // select JMol - all hidden
     setSelected(typeCheckbox, true);
-    assertFalse(anns[5].visible); // JMol for seq3
-    assertFalse(anns[7].visible); // JMol for seq1
+    int autocalc = countAutocalc(anns);
+    assertFalse(anns[autocalc + 2].visible); // JMol for seq3
+    assertFalse(anns[autocalc + 4].visible); // JMol for seq1
 
     // deselect JMol - all unhidden
     setSelected(typeCheckbox, false);
-    assertTrue(anns[0].visible); // Conservation
-    assertTrue(anns[1].visible); // Quality
-    assertTrue(anns[2].visible); // Consensus
-    assertTrue(anns[3].visible); // IUPred for seq0
-    assertTrue(anns[4].visible); // Beauty
-    assertTrue(anns[5].visible); // JMol for seq3
-    assertTrue(anns[6].visible); // IUPRED for seq2
-    assertTrue(anns[7].visible); // JMol for seq1
+    for (AlignmentAnnotation ann : anns)
+    {
+      assertTrue(ann.visible);
+    }
+  }
+
+  /**
+   * Returns a count of autocalculated annotations in the set provided
+   * 
+   * @param anns
+   * @return
+   */
+  private int countAutocalc(AlignmentAnnotation[] anns)
+  {
+    int count = 0;
+    for (AlignmentAnnotation ann : anns)
+    {
+      if (ann.autoCalculated)
+      {
+        count++;
+      }
+    }
+    return count;
   }
 
   /**
@@ -510,18 +531,15 @@ public class AnnotationChooserTest
     setSelected(allSequencesCheckbox, true);
     setSelected(hideCheckbox, true);
     setSelected(getTypeCheckbox("JMol"), true);
-    assertFalse(anns[5].visible); // JMol for seq3
-    assertFalse(anns[7].visible); // JMol for seq1
+    int autocalc = countAutocalc(anns);
+    assertFalse(anns[autocalc + 2].visible); // JMol for seq3
+    assertFalse(anns[autocalc + 4].visible); // JMol for seq1
     // ...now show them...
     setSelected(showCheckbox, true);
-    assertTrue(anns[0].visible); // Conservation
-    assertTrue(anns[1].visible); // Quality
-    assertTrue(anns[2].visible); // Consensus
-    assertTrue(anns[3].visible); // IUPred for seq0
-    assertTrue(anns[4].visible); // Beauty
-    assertTrue(anns[5].visible); // JMol for seq3
-    assertTrue(anns[6].visible); // IUPRED for seq2
-    assertTrue(anns[7].visible); // JMol for seq1
+    for (AlignmentAnnotation ann : anns)
+    {
+      assertTrue(ann.visible);
+    }
   }
 
   /**
@@ -551,19 +569,16 @@ public class AnnotationChooserTest
     setSelected(hideCheckbox, true);
     setSelected(getTypeCheckbox("JMol"), true);
 
-    assertTrue(anns[5].visible); // JMol for seq3
-    assertFalse(anns[7].visible); // JMol for seq1
+    int autocalc = countAutocalc(anns);
+    assertTrue(anns[autocalc + 2].visible); // JMol for seq3
+    assertFalse(anns[autocalc + 4].visible); // JMol for seq1
     // ...now show them...
     setSelected(showCheckbox, true);
 
-    assertTrue(anns[0].visible); // Conservation
-    assertTrue(anns[1].visible); // Quality
-    assertTrue(anns[2].visible); // Consensus
-    assertTrue(anns[3].visible); // IUPred for seq0
-    assertTrue(anns[4].visible); // Beauty
-    assertTrue(anns[5].visible); // JMol for seq3
-    assertTrue(anns[6].visible); // IUPRED for seq2
-    assertTrue(anns[7].visible); // JMol for seq1
+    for (AlignmentAnnotation ann : anns)
+    {
+      assertTrue(ann.visible);
+    }
   }
 
   /**
@@ -592,19 +607,21 @@ public class AnnotationChooserTest
     final Checkbox typeCheckbox = getTypeCheckbox("JMol");
     // select JMol - all shown
     setSelected(typeCheckbox, true);
-    assertTrue(anns[5].visible); // JMol for seq3
-    assertTrue(anns[7].visible); // JMol for seq1
+    int autocalc = countAutocalc(anns);
+    assertTrue(anns[autocalc + 2].visible); // JMol for seq3
+    assertTrue(anns[autocalc + 4].visible); // JMol for seq1
 
     // deselect JMol - all hidden
     setSelected(typeCheckbox, false);
     assertTrue(anns[0].visible); // Conservation
     assertTrue(anns[1].visible); // Quality
     assertTrue(anns[2].visible); // Consensus
-    assertTrue(anns[3].visible); // IUPred for seq0
-    assertTrue(anns[4].visible); // Beauty
-    assertFalse(anns[5].visible); // JMol for seq3
-    assertTrue(anns[6].visible); // IUPRED for seq2
-    assertFalse(anns[7].visible); // JMol for seq1
+    assertTrue(anns[3].visible); // Occupancy
+    assertTrue(anns[4].visible); // IUPred for seq0
+    assertTrue(anns[5].visible); // Beauty
+    assertFalse(anns[6].visible); // JMol for seq3
+    assertTrue(anns[7].visible); // IUPRED for seq2
+    assertFalse(anns[8].visible); // JMol for seq1
   }
 
   /**
@@ -633,19 +650,21 @@ public class AnnotationChooserTest
 
     // select JMol - should remain visible
     setSelected(getTypeCheckbox("JMol"), true);
-    assertTrue(anns[5].visible); // JMol for seq3
-    assertTrue(anns[7].visible); // JMol for seq1
+    int autocalc = countAutocalc(anns);
+    assertTrue(anns[autocalc + 2].visible); // JMol for seq3
+    assertTrue(anns[autocalc + 4].visible); // JMol for seq1
 
     // deselect JMol - should be hidden for selected sequences only
     setSelected(getTypeCheckbox("JMol"), false);
     assertTrue(anns[0].visible); // Conservation
     assertTrue(anns[1].visible); // Quality
     assertTrue(anns[2].visible); // Consensus
-    assertTrue(anns[3].visible); // IUPred for seq0
-    assertTrue(anns[4].visible); // Beauty
-    assertTrue(anns[5].visible); // JMol for seq3 not in selection group
-    assertTrue(anns[6].visible); // IUPRED for seq2
-    assertFalse(anns[7].visible); // JMol for seq1 in selection group
+    assertTrue(anns[3].visible); // Occupancy
+    assertTrue(anns[4].visible); // IUPred for seq0
+    assertTrue(anns[5].visible); // Beauty
+    assertTrue(anns[6].visible); // JMol for seq3 not in selection group
+    assertTrue(anns[7].visible); // IUPRED for seq2
+    assertFalse(anns[8].visible); // JMol for seq1 in selection group
   }
 
   /**
@@ -721,12 +740,12 @@ public class AnnotationChooserTest
 
     AlignmentAnnotation[] anns = parentPanel.getAlignment()
             .getAlignmentAnnotation();
-    // remember 3 annotations to skip (Conservation/Quality/Consensus)
-    assertFalse(testee.isInActionScope(anns[3]));
-    assertFalse(testee.isInActionScope(anns[4]));
-    assertFalse(testee.isInActionScope(anns[5]));
-    assertTrue(testee.isInActionScope(anns[6]));
-    assertTrue(testee.isInActionScope(anns[7]));
+    int autocalc = countAutocalc(anns);
+    assertFalse(testee.isInActionScope(anns[autocalc]));
+    assertFalse(testee.isInActionScope(anns[autocalc + 1]));
+    assertFalse(testee.isInActionScope(anns[autocalc + 2]));
+    assertTrue(testee.isInActionScope(anns[autocalc + 3]));
+    assertTrue(testee.isInActionScope(anns[autocalc + 4]));
   }
 
   /**
@@ -747,12 +766,12 @@ public class AnnotationChooserTest
 
     AlignmentAnnotation[] anns = parentPanel.getAlignment()
             .getAlignmentAnnotation();
-    // remember 3 annotations to skip (Conservation/Quality/Consensus)
-    assertTrue(testee.isInActionScope(anns[3]));
-    assertTrue(testee.isInActionScope(anns[4]));
-    assertTrue(testee.isInActionScope(anns[5]));
-    assertFalse(testee.isInActionScope(anns[6]));
-    assertFalse(testee.isInActionScope(anns[7]));
+    int autocalc = countAutocalc(anns);
+    assertTrue(testee.isInActionScope(anns[autocalc]));
+    assertTrue(testee.isInActionScope(anns[autocalc + 1]));
+    assertTrue(testee.isInActionScope(anns[autocalc + 2]));
+    assertFalse(testee.isInActionScope(anns[autocalc + 3]));
+    assertFalse(testee.isInActionScope(anns[autocalc + 4]));
   }
 
   /**
@@ -787,11 +806,12 @@ public class AnnotationChooserTest
     assertTrue(anns[0].visible); // Conservation
     assertTrue(anns[1].visible); // Quality
     assertTrue(anns[2].visible); // Consensus
-    assertFalse(anns[3].visible); // IUPRED
-    assertTrue(anns[4].visible); // Beauty (not seq-related)
-    assertFalse(anns[5].visible); // JMol
-    assertFalse(anns[6].visible); // IUPRED
-    assertFalse(anns[7].visible); // JMol
+    assertTrue(anns[3].visible); // Occupancy
+    assertFalse(anns[4].visible); // IUPRED
+    assertTrue(anns[5].visible); // Beauty (not seq-related)
+    assertFalse(anns[6].visible); // JMol
+    assertFalse(anns[7].visible); // IUPRED
+    assertFalse(anns[8].visible); // JMol
 
     // reset - should all be visible
     testee.resetOriginalState();
diff --git a/test/jalview/gui/AnnotationRowFilterTest.java b/test/jalview/gui/AnnotationRowFilterTest.java
new file mode 100644 (file)
index 0000000..69a41c5
--- /dev/null
@@ -0,0 +1,121 @@
+package jalview.gui;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+
+import java.util.Vector;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for methods of base class of annotation column or colour chooser
+ */
+public class AnnotationRowFilterTest
+{
+  AlignFrame af;
+
+  private AnnotationRowFilter testee;
+
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
+            Boolean.TRUE.toString());
+    Cache.applicationProperties.setProperty(
+            Preferences.SHOW_AUTOCALC_ABOVE, Boolean.TRUE.toString());
+    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
+            DataSourceType.FILE);
+    testee = new AnnotationRowFilter(af.viewport, af.alignPanel)
+    {
+      @Override
+      public void valueChanged(boolean updateAllAnnotation)
+      {
+      }
+
+      @Override
+      public void updateView()
+      {
+      }
+
+      @Override
+      public void reset()
+      {
+      }
+    };
+  }
+
+  /**
+   * Test the method that builds the drop-down list of annotations to choose
+   * from for colour by annotation or select columns by annotation
+   */
+  @Test(groups = "Functional")
+  public void testGetAnnotationItems()
+  {
+    AlignmentI al = af.getViewport().getAlignment();
+    SequenceI seq1 = al.findSequenceMatch("FER_CAPAA")[0];
+    SequenceI seq2 = al.findSequenceMatch("FER_BRANA")[0];
+
+    AlignmentAnnotation ann1 = new AlignmentAnnotation("ann1Label", "ann1",
+            null);
+    al.addAnnotation(ann1);
+    AlignmentAnnotation ann2 = new AlignmentAnnotation("Significance",
+            "ann2", null);
+    al.addAnnotation(ann2);
+    /*
+     * a second Significance alignment annotation
+     */
+    AlignmentAnnotation ann2a = new AlignmentAnnotation("Significance",
+            "ann2", null);
+    al.addAnnotation(ann2a);
+
+    AlignmentAnnotation ann3 = new AlignmentAnnotation("Jronn", "Jronn",
+            null);
+    ann3.setSequenceRef(seq1);
+    al.addAnnotation(ann3);
+    AlignmentAnnotation ann4 = new AlignmentAnnotation("Jronn", "Jronn",
+            null);
+    ann4.setSequenceRef(seq2);
+    al.addAnnotation(ann4);
+    AlignmentAnnotation ann5 = new AlignmentAnnotation("Jnet", "Jnet", null);
+    ann5.setSequenceRef(seq2);
+    al.addAnnotation(ann5);
+    /*
+     * a second Jnet annotation for FER_BRANA
+     */
+    AlignmentAnnotation ann6 = new AlignmentAnnotation("Jnet", "Jnet", null);
+    ann6.setSequenceRef(seq2);
+    al.addAnnotation(ann6);
+
+    /*
+     * drop-down items with 'Per-sequence only' not checked
+     */
+    Vector<String> items = testee.getAnnotationItems(false);
+    assertEquals(
+            items.toString(),
+            "[Conservation, Quality, Consensus, Occupancy, ann1Label, Significance, Significance_1, Jronn_FER_CAPAA, Jronn_FER_BRANA, Jnet_FER_BRANA, Jnet_FER_BRANA_2]");
+    assertEquals(testee.getAnnotationMenuLabel(ann1), "ann1Label");
+    assertEquals(testee.getAnnotationMenuLabel(ann2), "Significance");
+    assertEquals(testee.getAnnotationMenuLabel(ann2a), "Significance_1");
+    assertEquals(testee.getAnnotationMenuLabel(ann3), "Jronn_FER_CAPAA");
+    assertEquals(testee.getAnnotationMenuLabel(ann4), "Jronn_FER_BRANA");
+    assertEquals(testee.getAnnotationMenuLabel(ann5), "Jnet_FER_BRANA");
+    assertEquals(testee.getAnnotationMenuLabel(ann6), "Jnet_FER_BRANA_2");
+
+    /*
+     * drop-down items with 'Per-sequence only' checked
+     */
+    items = testee.getAnnotationItems(true);
+    assertEquals(items.toString(), "[Jronn, Jnet]");
+    // the first annotation of the type is associated with the menu item
+    assertEquals(testee.getAnnotationMenuLabel(ann3), "Jronn");
+    assertEquals(testee.getAnnotationMenuLabel(ann5), "Jnet");
+  }
+}
index 81289b0..29a9a52 100644 (file)
@@ -53,10 +53,10 @@ public class SequenceRendererTest
     av.setGlobalColourScheme(new ZappoColourScheme());
 
     // @see ResidueProperties.zappo
-    assertEquals(Color.pink, sr.getResidueColour(seq, 0, null)); // M
-    assertEquals(Color.green, sr.getResidueColour(seq, 2, null)); // T
-    assertEquals(Color.magenta, sr.getResidueColour(seq, 5, null)); // G
-    assertEquals(Color.orange, sr.getResidueColour(seq, 12, null)); // F
+    assertEquals(Color.pink, sr.getResidueBoxColour(seq, 0)); // M
+    assertEquals(Color.green, sr.getResidueBoxColour(seq, 2)); // T
+    assertEquals(Color.magenta, sr.getResidueBoxColour(seq, 5)); // G
+    assertEquals(Color.orange, sr.getResidueBoxColour(seq, 12)); // F
   }
   // TODO more tests for getResidueBoxColour covering groups, feature rendering,
   // gaps, overview...
index a4acbd0..961602d 100644 (file)
@@ -8,9 +8,12 @@ import java.util.Arrays;
 import java.util.Random;
 
 import org.testng.annotations.Test;
+import org.testng.internal.junit.ArrayAsserts;
 
 public class MatrixTest
 {
+  final static double DELTA = 0.0001d;
+
   @Test(groups = "Timing")
   public void testPreMultiply_timing()
   {
@@ -37,20 +40,20 @@ public class MatrixTest
      * 1x3 times 3x1 is 1x1
      * 2x5 + 3x6 + 4*7 =  56
      */
-    Matrix m3 = m2.preMultiply(m1);
-    assertEquals(m3.rows, 1);
-    assertEquals(m3.cols, 1);
-    assertEquals(m3.value[0][0], 56d);
+    MatrixI m3 = m2.preMultiply(m1);
+    assertEquals(m3.height(), 1);
+    assertEquals(m3.width(), 1);
+    assertEquals(m3.getValue(0, 0), 56d);
 
     /*
      * 3x1 times 1x3 is 3x3
      */
     m3 = m1.preMultiply(m2);
-    assertEquals(m3.rows, 3);
-    assertEquals(m3.cols, 3);
-    assertEquals(Arrays.toString(m3.value[0]), "[10.0, 15.0, 20.0]");
-    assertEquals(Arrays.toString(m3.value[1]), "[12.0, 18.0, 24.0]");
-    assertEquals(Arrays.toString(m3.value[2]), "[14.0, 21.0, 28.0]");
+    assertEquals(m3.height(), 3);
+    assertEquals(m3.width(), 3);
+    assertEquals(Arrays.toString(m3.getRow(0)), "[10.0, 15.0, 20.0]");
+    assertEquals(Arrays.toString(m3.getRow(1)), "[12.0, 18.0, 24.0]");
+    assertEquals(Arrays.toString(m3.getRow(2)), "[14.0, 21.0, 28.0]");
   }
 
   @Test(
@@ -85,7 +88,18 @@ public class MatrixTest
   
   
   private boolean matrixEquals(Matrix m1, Matrix m2) {
-    return Arrays.deepEquals(m1.value, m2.value);
+    if (m1.width() != m2.width() || m1.height() != m2.height())
+    {
+      return false;
+    }
+    for (int i = 0; i < m1.height(); i++)
+    {
+      if (!Arrays.equals(m1.getRow(i), m2.getRow(i)))
+      {
+        return false;
+      }
+    }
+    return true;
   }
 
   @Test(groups = "Functional")
@@ -99,18 +113,18 @@ public class MatrixTest
      * (3020 30200)
      * (5040 50400)
      */
-    Matrix m1 = new Matrix(new double[][] { { 2, 3 }, { 4, 5 } });
-    Matrix m2 = new Matrix(new double[][] { { 10, 100 }, { 1000, 10000 } });
-    Matrix m3 = m1.postMultiply(m2);
-    assertEquals(Arrays.toString(m3.value[0]), "[3020.0, 30200.0]");
-    assertEquals(Arrays.toString(m3.value[1]), "[5040.0, 50400.0]");
+    MatrixI m1 = new Matrix(new double[][] { { 2, 3 }, { 4, 5 } });
+    MatrixI m2 = new Matrix(new double[][] { { 10, 100 }, { 1000, 10000 } });
+    MatrixI m3 = m1.postMultiply(m2);
+    assertEquals(Arrays.toString(m3.getRow(0)), "[3020.0, 30200.0]");
+    assertEquals(Arrays.toString(m3.getRow(1)), "[5040.0, 50400.0]");
 
     /*
      * also check m2.preMultiply(m1) - should be same as m1.postMultiply(m2) 
      */
     m3 = m2.preMultiply(m1);
-    assertEquals(Arrays.toString(m3.value[0]), "[3020.0, 30200.0]");
-    assertEquals(Arrays.toString(m3.value[1]), "[5040.0, 50400.0]");
+    assertEquals(Arrays.toString(m3.getRow(0)), "[3020.0, 30200.0]");
+    assertEquals(Arrays.toString(m3.getRow(1)), "[5040.0, 50400.0]");
 
     /*
      * m1 has more rows than columns
@@ -120,15 +134,15 @@ public class MatrixTest
     m1 = new Matrix(new double[][] { { 2 }, { 3 } });
     m2 = new Matrix(new double[][] { { 10, 100, 1000 } });
     m3 = m1.postMultiply(m2);
-    assertEquals(m3.rows, 2);
-    assertEquals(m3.cols, 3);
-    assertEquals(Arrays.toString(m3.value[0]), "[20.0, 200.0, 2000.0]");
-    assertEquals(Arrays.toString(m3.value[1]), "[30.0, 300.0, 3000.0]");
+    assertEquals(m3.height(), 2);
+    assertEquals(m3.width(), 3);
+    assertEquals(Arrays.toString(m3.getRow(0)), "[20.0, 200.0, 2000.0]");
+    assertEquals(Arrays.toString(m3.getRow(1)), "[30.0, 300.0, 3000.0]");
     m3 = m2.preMultiply(m1);
-    assertEquals(m3.rows, 2);
-    assertEquals(m3.cols, 3);
-    assertEquals(Arrays.toString(m3.value[0]), "[20.0, 200.0, 2000.0]");
-    assertEquals(Arrays.toString(m3.value[1]), "[30.0, 300.0, 3000.0]");
+    assertEquals(m3.height(), 2);
+    assertEquals(m3.width(), 3);
+    assertEquals(Arrays.toString(m3.getRow(0)), "[20.0, 200.0, 2000.0]");
+    assertEquals(Arrays.toString(m3.getRow(1)), "[30.0, 300.0, 3000.0]");
 
     /*
      * m1 has more columns than rows
@@ -141,19 +155,19 @@ public class MatrixTest
     m1 = new Matrix(new double[][] { { 2, 3, 4 } });
     m2 = new Matrix(new double[][] { { 5, 4 }, { 6, 3 }, { 7, 2 } });
     m3 = m1.postMultiply(m2);
-    assertEquals(m3.rows, 1);
-    assertEquals(m3.cols, 2);
-    assertEquals(m3.value[0][0], 56d);
-    assertEquals(m3.value[0][1], 25d);
+    assertEquals(m3.height(), 1);
+    assertEquals(m3.width(), 2);
+    assertEquals(m3.getRow(0)[0], 56d);
+    assertEquals(m3.getRow(0)[1], 25d);
 
     /*
      * and check premultiply equivalent
      */
     m3 = m2.preMultiply(m1);
-    assertEquals(m3.rows, 1);
-    assertEquals(m3.cols, 2);
-    assertEquals(m3.value[0][0], 56d);
-    assertEquals(m3.value[0][1], 25d);
+    assertEquals(m3.height(), 1);
+    assertEquals(m3.width(), 2);
+    assertEquals(m3.getRow(0)[0], 56d);
+    assertEquals(m3.getRow(0)[1], 25d);
   }
 
   @Test(groups = "Functional")
@@ -172,7 +186,7 @@ public class MatrixTest
       }
     }
     Matrix m1 = new Matrix(in);
-    Matrix m2 = m1.copy();
+    Matrix m2 = (Matrix) m1.copy();
     assertTrue(matrixEquals(m1, m2));
   }
 
@@ -200,12 +214,12 @@ public class MatrixTest
     // / origmat.print(System.out);
     // System.out.println();
     // System.out.println(" --- transpose matrix ---- ");
-    Matrix trans = origmat.transpose();
+    MatrixI trans = origmat.transpose();
   
     // trans.print(System.out);
     // System.out.println();
     // System.out.println(" --- OrigT * Orig ---- ");
-    Matrix symm = trans.postMultiply(origmat);
+    MatrixI symm = trans.postMultiply(origmat);
   
     // symm.print(System.out);
     // System.out.println();
@@ -255,4 +269,113 @@ public class MatrixTest
     // }
     // System.out.println();
   }
+
+  @Test(groups = "Timing")
+  public void testSign()
+  {
+    assertEquals(Matrix.sign(-1, -2), -1d);
+    assertEquals(Matrix.sign(-1, 2), 1d);
+    assertEquals(Matrix.sign(-1, 0), 1d);
+    assertEquals(Matrix.sign(1, -2), -1d);
+    assertEquals(Matrix.sign(1, 2), 1d);
+    assertEquals(Matrix.sign(1, 0), 1d);
+  }
+
+  /**
+   * Helper method to make values for a sparse, pseudo-random symmetric matrix
+   * 
+   * @param rows
+   * @param cols
+   * @param occupancy
+   *          one in 'occupancy' entries will be non-zero
+   * @return
+   */
+  public double[][] getSparseValues(int rows, int cols, int occupancy)
+  {
+    Random r = new Random(1729);
+
+    /*
+     * generate whole number values between -12 and +12
+     * (to mimic score matrices used in Jalview)
+     */
+    double[][] d = new double[rows][cols];
+    int m = 0;
+    for (int i = 0; i < rows; i++)
+    {
+      if (++m % occupancy == 0)
+      {
+        d[i][i] = r.nextInt() % 13; // diagonal
+      }
+      for (int j = 0; j < i; j++)
+      {
+        if (++m % occupancy == 0)
+        {
+          d[i][j] = r.nextInt() % 13;
+          d[j][i] = d[i][j];
+        }
+      }
+    }
+    return d;
+  
+  }
+
+  /**
+   * Verify that the results of method tred() are the same if the calculation is
+   * redone
+   */
+  @Test(groups = "Functional")
+  public void testTred_reproducible()
+  {
+    /*
+     * make a pseudo-random symmetric matrix as required for tred/tqli
+     */
+    int rows = 10;
+    int cols = rows;
+    double[][] d = getSparseValues(rows, cols, 3);
+  
+    /*
+     * make a copy of the values so m1, m2 are not
+     * sharing arrays!
+     */
+    double[][] d1 = new double[rows][cols];
+    for (int row = 0; row < rows; row++)
+    {
+      for (int col = 0; col < cols; col++)
+      {
+        d1[row][col] = d[row][col];
+      }
+    }
+    Matrix m1 = new Matrix(d);
+    Matrix m2 = new Matrix(d1);
+    assertMatricesMatch(m1, m2); // sanity check
+    m1.tred();
+    m2.tred();
+    assertMatricesMatch(m1, m2);
+  }
+
+  private void assertMatricesMatch(MatrixI m1, MatrixI m2)
+  {
+    if (m1.height() != m2.height())
+    {
+      fail("height mismatch");
+    }
+    if (m1.width() != m2.width())
+    {
+      fail("width mismatch");
+    }
+    for (int row = 0; row < m1.height(); row++)
+    {
+      for (int col = 0; col < m1.width(); col++)
+      {
+        double v2 = m2.getValue(row, col);
+        double v1 = m1.getValue(row, col);
+        if (Math.abs(v1 - v2) > DELTA)
+        {
+          fail(String.format("At [%d, %d] %f != %f", row, col, v1, v2));
+        }
+      }
+    }
+    ArrayAsserts.assertArrayEquals(m1.getD(), m2.getD(), 0.00001d);
+    ArrayAsserts.assertArrayEquals(m1.getE(), m2.getE(), 0.00001d);
+  }
 }
diff --git a/test/jalview/math/SparseMatrixTest.java b/test/jalview/math/SparseMatrixTest.java
new file mode 100644 (file)
index 0000000..3c2ccaa
--- /dev/null
@@ -0,0 +1,416 @@
+package jalview.math;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.Random;
+
+import org.testng.annotations.Test;
+import org.testng.internal.junit.ArrayAsserts;
+
+public class SparseMatrixTest
+{
+  final static double DELTA = 0.0001d;
+
+  Random r = new Random(1729);
+
+  @Test(groups = "Functional")
+  public void testConstructor()
+  {
+    MatrixI m1 = new SparseMatrix(
+            new double[][] { { 2, 0, 4 }, { 0, 6, 0 } });
+    assertEquals(m1.getValue(0, 0), 2d);
+    assertEquals(m1.getValue(0, 1), 0d);
+    assertEquals(m1.getValue(0, 2), 4d);
+    assertEquals(m1.getValue(1, 0), 0d);
+    assertEquals(m1.getValue(1, 1), 6d);
+    assertEquals(m1.getValue(1, 2), 0d);
+  }
+
+  @Test(groups = "Functional")
+  public void testTranspose()
+  {
+    MatrixI m1 = new SparseMatrix(
+            new double[][] { { 2, 0, 4 }, { 5, 6, 0 } });
+    MatrixI m2 = m1.transpose();
+    assertTrue(m2 instanceof SparseMatrix);
+    assertEquals(m2.height(), 3);
+    assertEquals(m2.width(), 2);
+    assertEquals(m2.getValue(0, 0), 2d);
+    assertEquals(m2.getValue(0, 1), 5d);
+    assertEquals(m2.getValue(1, 0), 0d);
+    assertEquals(m2.getValue(1, 1), 6d);
+    assertEquals(m2.getValue(2, 0), 4d);
+    assertEquals(m2.getValue(2, 1), 0d);
+  }
+  @Test(groups = "Functional")
+  public void testPreMultiply()
+  {
+    MatrixI m1 = new SparseMatrix(new double[][] { { 2, 3, 4 } }); // 1x3
+    MatrixI m2 = new SparseMatrix(new double[][] { { 5 }, { 6 }, { 7 } }); // 3x1
+
+    /*
+     * 1x3 times 3x1 is 1x1
+     * 2x5 + 3x6 + 4*7 =  56
+     */
+    MatrixI m3 = m2.preMultiply(m1);
+    assertFalse(m3 instanceof SparseMatrix);
+    assertEquals(m3.height(), 1);
+    assertEquals(m3.width(), 1);
+    assertEquals(m3.getValue(0, 0), 56d);
+
+    /*
+     * 3x1 times 1x3 is 3x3
+     */
+    m3 = m1.preMultiply(m2);
+    assertEquals(m3.height(), 3);
+    assertEquals(m3.width(), 3);
+    assertEquals(m3.getValue(0, 0), 10d);
+    assertEquals(m3.getValue(0, 1), 15d);
+    assertEquals(m3.getValue(0, 2), 20d);
+    assertEquals(m3.getValue(1, 0), 12d);
+    assertEquals(m3.getValue(1, 1), 18d);
+    assertEquals(m3.getValue(1, 2), 24d);
+    assertEquals(m3.getValue(2, 0), 14d);
+    assertEquals(m3.getValue(2, 1), 21d);
+    assertEquals(m3.getValue(2, 2), 28d);
+  }
+
+  @Test(
+    groups = "Functional",
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testPreMultiply_tooManyColumns()
+  {
+    Matrix m1 = new SparseMatrix(
+            new double[][] { { 2, 3, 4 }, { 3, 4, 5 } }); // 2x3
+
+    /*
+     * 2x3 times 2x3 invalid operation - 
+     * multiplier has more columns than multiplicand has rows
+     */
+    m1.preMultiply(m1);
+    fail("Expected exception");
+  }
+
+  @Test(
+    groups = "Functional",
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testPreMultiply_tooFewColumns()
+  {
+    Matrix m1 = new SparseMatrix(
+            new double[][] { { 2, 3, 4 }, { 3, 4, 5 } }); // 2x3
+
+    /*
+     * 3x2 times 3x2 invalid operation - 
+     * multiplier has more columns than multiplicand has row
+     */
+    m1.preMultiply(m1);
+    fail("Expected exception");
+  }
+  
+  @Test(groups = "Functional")
+  public void testPostMultiply()
+  {
+    /*
+     * Square matrices
+     * (2 3) . (10   100)
+     * (4 5)   (1000 10000)
+     * =
+     * (3020 30200)
+     * (5040 50400)
+     */
+    MatrixI m1 = new SparseMatrix(new double[][] { { 2, 3 }, { 4, 5 } });
+    MatrixI m2 = new SparseMatrix(new double[][] { { 10, 100 },
+        { 1000, 10000 } });
+    MatrixI m3 = m1.postMultiply(m2);
+    assertEquals(m3.getValue(0, 0), 3020d);
+    assertEquals(m3.getValue(0, 1), 30200d);
+    assertEquals(m3.getValue(1, 0), 5040d);
+    assertEquals(m3.getValue(1, 1), 50400d);
+
+    /*
+     * also check m2.preMultiply(m1) - should be same as m1.postMultiply(m2) 
+     */
+    MatrixI m4 = m2.preMultiply(m1);
+    assertMatricesMatch(m3, m4, 0.00001d);
+
+    /*
+     * m1 has more rows than columns
+     * (2).(10 100 1000) = (20 200 2000)
+     * (3)                 (30 300 3000)
+     */
+    m1 = new SparseMatrix(new double[][] { { 2 }, { 3 } });
+    m2 = new SparseMatrix(new double[][] { { 10, 100, 1000 } });
+    m3 = m1.postMultiply(m2);
+    assertEquals(m3.height(), 2);
+    assertEquals(m3.width(), 3);
+    assertEquals(m3.getValue(0, 0), 20d);
+    assertEquals(m3.getValue(0, 1), 200d);
+    assertEquals(m3.getValue(0, 2), 2000d);
+    assertEquals(m3.getValue(1, 0), 30d);
+    assertEquals(m3.getValue(1, 1), 300d);
+    assertEquals(m3.getValue(1, 2), 3000d);
+
+    m4 = m2.preMultiply(m1);
+    assertMatricesMatch(m3, m4, 0.00001d);
+
+    /*
+     * m1 has more columns than rows
+     * (2 3 4) . (5 4) = (56 25)
+     *           (6 3) 
+     *           (7 2)
+     * [0, 0] = 2*5 + 3*6 + 4*7 = 56
+     * [0, 1] = 2*4 + 3*3 + 4*2 = 25  
+     */
+    m1 = new SparseMatrix(new double[][] { { 2, 3, 4 } });
+    m2 = new SparseMatrix(new double[][] { { 5, 4 }, { 6, 3 }, { 7, 2 } });
+    m3 = m1.postMultiply(m2);
+    assertEquals(m3.height(), 1);
+    assertEquals(m3.width(), 2);
+    assertEquals(m3.getValue(0, 0), 56d);
+    assertEquals(m3.getValue(0, 1), 25d);
+
+    /*
+     * and check premultiply equivalent
+     */
+    m4 = m2.preMultiply(m1);
+    assertMatricesMatch(m3, m4, 0.00001d);
+  }
+
+  @Test(groups = "Timing")
+  public void testSign()
+  {
+    assertEquals(Matrix.sign(-1, -2), -1d);
+    assertEquals(Matrix.sign(-1, 2), 1d);
+    assertEquals(Matrix.sign(-1, 0), 1d);
+    assertEquals(Matrix.sign(1, -2), -1d);
+    assertEquals(Matrix.sign(1, 2), 1d);
+    assertEquals(Matrix.sign(1, 0), 1d);
+  }
+
+  /**
+   * Verify that the results of method tred() are the same for SparseMatrix as
+   * they are for Matrix (i.e. a regression test rather than an absolute test of
+   * correctness of results)
+   */
+  @Test(groups = "Functional")
+  public void testTred_matchesMatrix()
+  {
+    /*
+     * make a pseudo-random symmetric matrix as required for tred/tqli
+     */
+    int rows = 10;
+    int cols = rows;
+    double[][] d = getSparseValues(rows, cols, 3);
+
+    /*
+     * make a copy of the values so m1, m2 are not
+     * sharing arrays!
+     */
+    double[][] d1 = new double[rows][cols];
+    for (int row = 0; row < rows; row++)
+    {
+      for (int col = 0; col < cols; col++)
+      {
+        d1[row][col] = d[row][col];
+      }
+    }
+    Matrix m1 = new Matrix(d);
+    Matrix m2 = new SparseMatrix(d1);
+    assertMatricesMatch(m1, m2, 0.00001d); // sanity check
+    m1.tred();
+    m2.tred();
+    assertMatricesMatch(m1, m2, 0.00001d);
+  }
+
+  private void assertMatricesMatch(MatrixI m1, MatrixI m2, double delta)
+  {
+    if (m1.height() != m2.height())
+    {
+      fail("height mismatch");
+    }
+    if (m1.width() != m2.width())
+    {
+      fail("width mismatch");
+    }
+    for (int row = 0; row < m1.height(); row++)
+    {
+      for (int col = 0; col < m1.width(); col++)
+      {
+        double v2 = m2.getValue(row, col);
+        double v1 = m1.getValue(row, col);
+        if (Math.abs(v1 - v2) > DELTA)
+        {
+          fail(String.format("At [%d, %d] %f != %f", row, col, v1, v2));
+        }
+      }
+    }
+    ArrayAsserts.assertArrayEquals(m1.getD(), m2.getD(), delta);
+    ArrayAsserts.assertArrayEquals(m1.getE(), m2.getE(), 0.00001d);
+  }
+
+  @Test
+  public void testGetValue()
+  {
+    double[][] d = new double[][] { { 0, 0, 1, 0, 0 }, { 2, 3, 0, 0, 0 },
+        { 4, 0, 0, 0, 5 } };
+    MatrixI m = new SparseMatrix(d);
+    for (int row = 0; row < 3; row++)
+    {
+      for (int col = 0; col < 5; col++)
+      {
+        assertEquals(m.getValue(row, col), d[row][col],
+                String.format("At [%d, %d]", row, col));
+      }
+    }
+  }
+
+  /**
+   * Verify that the results of method tqli() are the same for SparseMatrix as
+   * they are for Matrix (i.e. a regression test rather than an absolute test of
+   * correctness of results)
+   * 
+   * @throws Exception
+   */
+  @Test(groups = "Functional")
+  public void testTqli_matchesMatrix() throws Exception
+  {
+    /*
+     * make a pseudo-random symmetric matrix as required for tred
+     */
+    int rows = 6;
+    int cols = rows;
+    double[][] d = getSparseValues(rows, cols, 3);
+  
+    /*
+     * make a copy of the values so m1, m2 are not
+     * sharing arrays!
+     */
+    double[][] d1 = new double[rows][cols];
+    for (int row = 0; row < rows; row++)
+    {
+      for (int col = 0; col < cols; col++)
+      {
+        d1[row][col] = d[row][col];
+      }
+    }
+    Matrix m1 = new Matrix(d);
+    Matrix m2 = new SparseMatrix(d1);
+
+    // have to do tred() before doing tqli()
+    m1.tred();
+    m2.tred();
+    assertMatricesMatch(m1, m2, 0.00001d);
+
+    m1.tqli();
+    m2.tqli();
+    assertMatricesMatch(m1, m2, 0.00001d);
+  }
+
+  /**
+   * Helper method to make values for a sparse, pseudo-random symmetric matrix
+   * 
+   * @param rows
+   * @param cols
+   * @param occupancy
+   *          one in 'occupancy' entries will be non-zero
+   * @return
+   */
+  public double[][] getSparseValues(int rows, int cols, int occupancy)
+  {
+    /*
+     * generate whole number values between -12 and +12
+     * (to mimic score matrices used in Jalview)
+     */
+    double[][] d = new double[rows][cols];
+    int m = 0;
+    for (int i = 0; i < rows; i++)
+    {
+      if (++m % occupancy == 0)
+      {
+        d[i][i] = r.nextInt() % 13; // diagonal
+      }
+      for (int j = 0; j < i; j++)
+      {
+        if (++m % occupancy == 0)
+        {
+          d[i][j] = r.nextInt() % 13;
+          d[j][i] = d[i][j];
+        }
+      }
+    }
+    return d;
+
+  }
+
+  /**
+   * Test that verifies that the result of preMultiply is a SparseMatrix if more
+   * than 80% zeroes, else a Matrix
+   */
+  @Test(groups = "Functional")
+  public void testPreMultiply_sparseProduct()
+  {
+    MatrixI m1 = new SparseMatrix(new double[][] { { 1 }, { 0 }, { 0 },
+        { 0 }, { 0 } }); // 5x1
+    MatrixI m2 = new SparseMatrix(new double[][] { { 1, 1, 1, 1 } }); // 1x4
+  
+    /*
+     * m1.m2 makes a row of 4 1's, and 4 rows of zeros
+     * 20% non-zero so not 'sparse'
+     */
+    MatrixI m3 = m2.preMultiply(m1);
+    assertFalse(m3 instanceof SparseMatrix);
+
+    /*
+     * replace a 1 with a 0 in the product:
+     * it is now > 80% zero so 'sparse'
+     */
+    m2 = new SparseMatrix(new double[][] { { 1, 1, 1, 0 } });
+    m3 = m2.preMultiply(m1);
+    assertTrue(m3 instanceof SparseMatrix);
+  }
+
+  @Test(groups = "Functional")
+  public void testFillRatio()
+  {
+    SparseMatrix m1 = new SparseMatrix(new double[][] { { 2, 0, 4, 1, 0 },
+    { 0, 6, 0, 0, 0 } });
+    assertEquals(m1.getFillRatio(), 0.4f);
+  }
+
+  /**
+   * Verify that the results of method tred() are the same if the calculation is
+   * redone
+   */
+  @Test(groups = "Functional")
+  public void testTred_reproducible()
+  {
+    /*
+     * make a pseudo-random symmetric matrix as required for tred/tqli
+     */
+    int rows = 10;
+    int cols = rows;
+    double[][] d = getSparseValues(rows, cols, 3);
+  
+    /*
+     * make a copy of the values so m1, m2 are not
+     * sharing arrays!
+     */
+    double[][] d1 = new double[rows][cols];
+    for (int row = 0; row < rows; row++)
+    {
+      for (int col = 0; col < cols; col++)
+      {
+        d1[row][col] = d[row][col];
+      }
+    }
+    Matrix m1 = new SparseMatrix(d);
+    Matrix m2 = new SparseMatrix(d1);
+    assertMatricesMatch(m1, m2, 1.0e16); // sanity check
+    m1.tred();
+    m2.tred();
+    assertMatricesMatch(m1, m2, 0.00001d);
+  }
+}
\ No newline at end of file
diff --git a/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java
new file mode 100644 (file)
index 0000000..59566ed
--- /dev/null
@@ -0,0 +1,448 @@
+package jalview.renderer.seqfeatures;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import jalview.api.FeatureColourI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
+import jalview.gui.FeatureRenderer;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+import jalview.schemes.FeatureColour;
+
+import java.awt.Color;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for feature colour determination, including but not limited to
+ * <ul>
+ * <li>gap position</li>
+ * <li>no features present</li>
+ * <li>features present but show features turned off</li>
+ * <li>features displayed but selected feature turned off</li>
+ * <li>features displayed but feature group turned off</li>
+ * <li>feature displayed but none at the specified position</li>
+ * <li>multiple features at position, with no transparency</li>
+ * <li>multiple features at position, with transparency</li>
+ * <li>score graduated feature colour</li>
+ * <li>contact feature start at the selected position</li>
+ * <li>contact feature end at the selected position</li>
+ * <li>contact feature straddling the selected position (not shown)</li>
+ * </ul>
+ */
+public class FeatureColourFinderTest
+{
+  private AlignViewport av;
+
+  private SequenceI seq;
+
+  private FeatureColourFinder finder;
+
+  private AlignFrame af;
+
+  private FeatureRenderer fr;
+
+  @BeforeTest(alwaysRun = true)
+  public void setUp()
+  {
+    // aligned column 8 is sequence position 6
+    String s = ">s1\nABCDE---FGHIJKLMNOPQRSTUVWXYZ\n";
+    af = new FileLoader().LoadFileWaitTillLoaded(s,
+            DataSourceType.PASTE);
+    av = af.getViewport();
+    seq = av.getAlignment().getSequenceAt(0);
+    fr = af.getFeatureRenderer();
+    finder = new FeatureColourFinder(fr);
+  }
+
+  /**
+   * Clear down any sequence features before each test (not as easy as it
+   * sounds...)
+   */
+  @BeforeMethod(alwaysRun = true)
+  public void setUpBeforeTest()
+  {
+    SequenceFeature[] sfs = seq.getSequenceFeatures();
+    if (sfs != null)
+    {
+      for (SequenceFeature sf : sfs)
+      {
+        seq.deleteFeature(sf);
+      }
+    }
+    fr.findAllFeatures(true);
+
+    /*
+     * reset all feature groups to visible
+     */
+    for (String group : fr.getGroups(false))
+    {
+      fr.setGroupVisibility(group, true);
+    }
+
+    fr.clearRenderOrder();
+    av.setShowSequenceFeatures(true);
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_noFeatures()
+  {
+    av.setShowSequenceFeatures(false);
+    Color c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.blue);
+
+    av.setShowSequenceFeatures(true);
+    c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.blue);
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_noFeaturesShown()
+  {
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
+            Float.NaN, "MetalGroup"));
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(false);
+    Color c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.blue);
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_singleFeatureAtPosition()
+  {
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
+            Float.NaN, "MetalGroup"));
+    fr.setColour("Metal", new FeatureColour(Color.red));
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+    Color c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.red);
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_gapPosition()
+  {
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, 0f,
+            null));
+    fr.setColour("Metal", new FeatureColour(Color.red));
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+    Color c = finder.findFeatureColour(null, seq, 6);
+    assertEquals(c, Color.white);
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_multipleFeaturesAtPositionNoTransparency()
+  {
+    /*
+     * featuresAdded -> FeatureRendererModel.updateRenderOrder which adds any
+     * new features 'on top' (but reverses the order of any added features)
+     */
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
+            Float.NaN, "MetalGroup"));
+    FeatureColour red = new FeatureColour(Color.red);
+    fr.setColour("Metal", red);
+    fr.featuresAdded();
+    seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15,
+            Float.NaN, "DomainGroup"));
+    FeatureColour green = new FeatureColour(Color.green);
+    fr.setColour("Domain", green);
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+
+    /*
+     * expect Domain (green) to be rendered above Metal (red)
+     */
+    Color c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.green);
+
+    /*
+     * now promote Metal above Domain
+     * - currently no way other than mimicking reordering of
+     * table in Feature Settings
+     */
+    Object[][] data = new Object[2][];
+    data[0] = new Object[] { "Metal", red, true };
+    data[1] = new Object[] { "Domain", green, true };
+    fr.setFeaturePriority(data);
+    c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.red);
+
+    /*
+     * ..and turn off display of Metal
+     */
+    data[0][2] = false;
+    fr.setFeaturePriority(data);
+    c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.green);
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_singleFeatureNotAtPosition()
+  {
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 8, 12,
+            Float.NaN, "MetalGroup"));
+    fr.setColour("Metal", new FeatureColour(Color.red));
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+    // column 2 = sequence position 3
+    Color c = finder.findFeatureColour(Color.blue, seq, 2);
+    assertEquals(c, Color.blue);
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_featureTypeNotDisplayed()
+  {
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
+            Float.NaN, "MetalGroup"));
+    FeatureColour red = new FeatureColour(Color.red);
+    fr.setColour("Metal", red);
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+    Color c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.red);
+
+    /*
+     * turn off display of Metal - is this the easiest way to do it??
+     */
+    Object[][] data = new Object[1][];
+    data[0] = new Object[] { "Metal", red, false };
+    fr.setFeaturePriority(data);
+    c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.blue);
+
+    /*
+     * turn display of Metal back on
+     */
+    data[0] = new Object[] { "Metal", red, true };
+    fr.setFeaturePriority(data);
+    c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.red);
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_featureGroupNotDisplayed()
+  {
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
+            Float.NaN, "MetalGroup"));
+    FeatureColour red = new FeatureColour(Color.red);
+    fr.setColour("Metal", red);
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+    Color c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.red);
+
+    /*
+     * turn off display of MetalGroup
+     */
+    fr.setGroupVisibility("MetalGroup", false);
+    c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.blue);
+
+    /*
+     * turn display of MetalGroup back on
+     */
+    fr.setGroupVisibility("MetalGroup", true);
+    c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.red);
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_contactFeature()
+  {
+    /*
+     * currently contact feature == type "Disulphide Bond" or "Disulfide Bond" !!
+     */
+    seq.addSequenceFeature(new SequenceFeature("Disulphide Bond",
+            "Contact", 2, 12, Float.NaN, "Disulphide"));
+    fr.setColour("Disulphide Bond", new FeatureColour(Color.red));
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+
+    /*
+     * Contact positions are residues 2 and 12
+     * which are columns 1 and 14
+     * positions in between don't count for a contact feature!
+     */
+    Color c = finder.findFeatureColour(Color.blue, seq, 10);
+    assertEquals(c, Color.blue);
+    c = finder.findFeatureColour(Color.blue, seq, 8);
+    assertEquals(c, Color.blue);
+    c = finder.findFeatureColour(Color.blue, seq, 1);
+    assertEquals(c, Color.red);
+    c = finder.findFeatureColour(Color.blue, seq, 14);
+    assertEquals(c, Color.red);
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_graduatedFeatureColour()
+  {
+    seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2,
+            2, 0f, "KdGroup"));
+    seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 4,
+            4, 5f, "KdGroup"));
+    seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 7,
+            7, 10f, "KdGroup"));
+
+    /*
+     * graduated colour from 0 to 10
+     */
+    Color min = new Color(100, 50, 150);
+    Color max = new Color(200, 0, 100);
+    FeatureColourI fc = new FeatureColour(min, max, 0, 10);
+    fr.setColour("kd", fc);
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+
+    /*
+     * position 2, column 1, score 0 - minimum colour in range
+     */
+    Color c = finder.findFeatureColour(Color.blue, seq, 1);
+    assertEquals(c, min);
+
+    /*
+     * position 7, column 9, score 10 - maximum colour in range
+     */
+    c = finder.findFeatureColour(Color.blue, seq, 9);
+    assertEquals(c, max);
+
+    /*
+     * position 4, column 3, score 5 - half way from min to max
+     */
+    c = finder.findFeatureColour(Color.blue, seq, 3);
+    assertEquals(c, new Color(150, 25, 125));
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_transparencySingleFeature()
+  {
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
+            Float.NaN, "MetalGroup"));
+    FeatureColour red = new FeatureColour(Color.red);
+    fr.setColour("Metal", red);
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+  
+    /*
+     * the FeatureSettings transparency slider has range 0-70 which
+     * corresponds to a transparency value of 1 - 0.3
+     * A value of 0.4 gives a combination of
+     * 0.4 * red(255, 0, 0) + 0.6 * cyan(0, 255, 255) = (102, 153, 153)
+     */
+    fr.setTransparency(0.4f);
+    Color c = finder.findFeatureColour(Color.cyan, seq, 10);
+    assertEquals(c, new Color(102, 153, 153));
+  }
+
+  @Test(groups = "Functional")
+  public void testFindFeatureColour_transparencyTwoFeatures()
+  {
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
+            Float.NaN, "MetalGroup"));
+    FeatureColour red = new FeatureColour(Color.red);
+    fr.setColour("Metal", red);
+    fr.featuresAdded();
+    seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15,
+            Float.NaN, "DomainGroup"));
+    FeatureColour green = new FeatureColour(Color.green);
+    fr.setColour("Domain", green);
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+  
+    /*
+     * Domain (green) rendered above Metal (red) above background (cyan)
+     * 1) 0.6 * red(255, 0, 0) + 0.4 * cyan(0, 255, 255) = (153, 102, 102)
+     * 2) 0.6* green(0, 255, 0) + 0.4 * (153, 102, 102) = (61, 194, 41) rounded
+     */
+    fr.setTransparency(0.6f);
+    Color c = finder.findFeatureColour(Color.cyan, seq, 10);
+    assertEquals(c, new Color(61, 194, 41));
+  
+    /*
+     * now promote Metal above Domain
+     * - currently no way other than mimicking reordering of
+     * table in Feature Settings
+     * Metal (red) rendered above Domain (green) above background (cyan)
+     * 1) 0.6 * green(0, 255, 0) + 0.4 * cyan(0, 255, 255) = (0, 255, 102)
+     * 2) 0.6* red(255, 0, 0) + 0.4 * (0, 255, 102) = (153, 102, 41) rounded
+     */
+    Object[][] data = new Object[2][];
+    data[0] = new Object[] { "Metal", red, true };
+    data[1] = new Object[] { "Domain", green, true };
+    fr.setFeaturePriority(data);
+    c = finder.findFeatureColour(Color.cyan, seq, 10);
+    assertEquals(c, new Color(153, 102, 41));
+  
+    /*
+     * ..and turn off display of Metal
+     * Domain (green) above background (pink)
+     * 0.6 * green(0, 255, 0) + 0.4 * pink(255, 175, 175) = (102, 223, 70)
+     */
+    data[0][2] = false;
+    fr.setFeaturePriority(data);
+    c = finder.findFeatureColour(Color.pink, seq, 10);
+    assertEquals(c, new Color(102, 223, 70));
+  }
+
+  @Test(groups = "Functional")
+  public void testNoFeaturesDisplayed()
+  {
+    /*
+     * no features on alignment to render
+     */
+    assertTrue(finder.noFeaturesDisplayed());
+
+    /*
+     * add a feature
+     * it will be automatically set visible but we leave
+     * the viewport configured not to show features
+     */
+    av.setShowSequenceFeatures(false);
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
+            Float.NaN, "MetalGroup"));
+    FeatureColour red = new FeatureColour(Color.red);
+    fr.setColour("Metal", red);
+    fr.featuresAdded();
+    assertTrue(finder.noFeaturesDisplayed());
+
+    /*
+     * turn on feature display
+     */
+    av.setShowSequenceFeatures(true);
+    assertFalse(finder.noFeaturesDisplayed());
+
+    /*
+     * turn off display of Metal
+     */
+    Object[][] data = new Object[1][];
+    data[0] = new Object[] { "Metal", red, false };
+    fr.setFeaturePriority(data);
+    assertTrue(finder.noFeaturesDisplayed());
+
+    /*
+     * turn display of Metal back on
+     */
+    fr.setVisible("Metal");
+    assertFalse(finder.noFeaturesDisplayed());
+
+    /*
+     * turn off MetalGroup - has no effect here since the group of a
+     * sequence feature instance is independent of its type
+     */
+    fr.setGroupVisibility("MetalGroup", false);
+    assertFalse(finder.noFeaturesDisplayed());
+
+    /*
+     * a finder with no feature renderer
+     */
+    FeatureColourFinder finder2 = new FeatureColourFinder(null);
+    assertTrue(finder2.noFeaturesDisplayed());
+  }
+}
diff --git a/test/jalview/schemes/AnnotationColourGradientTest.java b/test/jalview/schemes/AnnotationColourGradientTest.java
new file mode 100644 (file)
index 0000000..1c93856
--- /dev/null
@@ -0,0 +1,300 @@
+package jalview.schemes;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.GraphLine;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import java.awt.Color;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class AnnotationColourGradientTest
+{
+  final static int WIDTH = 11;
+
+  final static int THRESHOLD_FIVE = 5;
+
+  private AlignmentAnnotation ann;
+
+  private SequenceI seq;
+
+  private AlignmentI al;
+
+  Color minColour = new Color(50, 200, 150);
+
+  Color maxColour = new Color(150, 100, 250);
+
+  /**
+   * Setup creates an annotation over 11 columns with values 0-10 and threshold
+   * 5
+   */
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Annotation[] anns = new Annotation[WIDTH];
+    /*
+     * set annotations with values 0-10, graded colours
+     */
+    for (int col = 0; col < WIDTH; col++)
+    {
+      int hue = col * 20;
+      Color colour = new Color(hue, hue, hue);
+      anns[col] = new Annotation("a", "a", 'a', col, colour);
+    }
+
+    seq = new Sequence("", "");
+    al = new Alignment(new SequenceI[]{ seq});
+    
+    /*
+     * AlignmentAnnotation constructor works out min-max range
+     */
+    ann = new AlignmentAnnotation("", "", anns);
+    ann.setThreshold(new GraphLine(THRESHOLD_FIVE, "", Color.RED));
+    seq.addAlignmentAnnotation(ann);
+  }
+
+  @Test(groups = "Functional")
+  public void testShadeCalculation_noThreshold()
+  {
+    AnnotationColourGradient testee = new AnnotationColourGradient(ann,
+            minColour, maxColour, AnnotationColourGradient.NO_THRESHOLD);
+    for (int col = 0; col < WIDTH; col++)
+    {
+      Color result = testee.shadeCalculation(ann, col);
+      /*
+       * column <n> is n/10 of the way from minCol to maxCol
+       */
+      Color expected = new Color(50 + 10 * col, 200 - 10 * col,
+              150 + 10 * col);
+      assertEquals(result, expected, "for column " + col);
+    }
+  }
+
+  /**
+   * Test the 'colour above threshold' case
+   */
+  @Test(groups = "Functional")
+  public void testShadeCalculation_aboveThreshold()
+  {
+    AnnotationColourGradient testee = new AnnotationColourGradient(ann,
+            minColour, maxColour, AnnotationColourGradient.ABOVE_THRESHOLD);
+    for (int col = 0; col < WIDTH; col++)
+    {
+      Color result = testee.shadeCalculation(ann, col);
+      /*
+       * colour is derived regardless of the threshold value 
+       * (the renderer will suppress colouring if above/below threshold)
+       */
+      Color expected = new Color(50 + 10 * col, 200 - 10 * col,
+              150 + 10 * col);
+      assertEquals(result, expected, "for column " + col);
+    }
+
+    /*
+     * now make 6-10 the span of the colour range
+     * (annotation value == column number in this test)
+     */
+    testee.setThresholdIsMinMax(true);
+    for (int col = 0; col < THRESHOLD_FIVE; col++)
+    {
+      /*
+       * colours below the threshold are computed as before
+       */
+      Color expected = new Color(50 + 10 * col, 200 - 10 * col,
+              150 + 10 * col);
+      Color result = testee.shadeCalculation(ann, col);
+      assertEquals(result, expected, "for column " + col);
+    }
+    for (int col = THRESHOLD_FIVE; col < WIDTH; col++)
+    {
+      /*
+       * colours for values >= threshold are graduated
+       * range is 6-10 so steps of 100/5 = 20
+       */
+      int factor = col - THRESHOLD_FIVE;
+      Color expected = new Color(50 + 20 * factor, 200 - 20 * factor,
+              150 + 20 * factor);
+      Color result = testee.shadeCalculation(ann, col);
+      assertEquals(result, expected, "for column " + col);
+    }
+  }
+
+  /**
+   * Test the 'colour below threshold' case
+   */
+  @Test(groups = "Functional")
+  public void testShadeCalculation_belowThreshold()
+  {
+    AnnotationColourGradient testee = new AnnotationColourGradient(ann,
+            minColour, maxColour, AnnotationColourGradient.BELOW_THRESHOLD);
+
+    for (int col = 0; col < WIDTH; col++)
+    {
+      Color result = testee.shadeCalculation(ann, col);
+      /*
+       * colour is derived regardless of the threshold value 
+       * (the renderer will suppress colouring if above/below threshold)
+       */
+      Color expected = new Color(50 + 10 * col, 200 - 10 * col,
+              150 + 10 * col);
+      assertEquals(result, expected, "for column " + col);
+    }
+
+    /*
+     * now make 0-5 the span of the colour range
+     * (annotation value == column number in this test)
+     */
+    testee.setThresholdIsMinMax(true);
+    for (int col = THRESHOLD_FIVE + 1; col < WIDTH; col++)
+    {
+      /*
+       * colours above the threshold are computed as before
+       */
+      Color expected = new Color(50 + 10 * col, 200 - 10 * col,
+              150 + 10 * col);
+      Color result = testee.shadeCalculation(ann, col);
+      assertEquals(result, expected, "for column " + col);
+    }
+
+    for (int col = 0; col <= THRESHOLD_FIVE; col++)
+    {
+      /*
+       * colours for values <= threshold are graduated
+       * range is 0-5 so steps of 100/5 = 20
+       */
+      Color expected = new Color(50 + 20 * col, 200 - 20 * col,
+              150 + 20 * col);
+      Color result = testee.shadeCalculation(ann, col);
+      assertEquals(result, expected, "for column " + col);
+    }
+  }
+
+  /**
+   * Test the 'colour above threshold' case
+   */
+  @Test(groups = "Functional")
+  public void testFindColour_aboveThreshold()
+  {
+    AnnotationColourGradient testee = new AnnotationColourGradient(ann,
+            minColour, maxColour, AnnotationColourGradient.ABOVE_THRESHOLD);
+    testee = (AnnotationColourGradient) testee.getInstance(al, null);
+
+    for (int col = 0; col < WIDTH; col++)
+    {
+      Color result = testee.findColour('a', col, seq);
+      /*
+       * expect white below threshold of 5
+       */
+      Color expected = col < 5 ? Color.white : new Color(50 + 10 * col,
+              200 - 10 * col,
+              150 + 10 * col);
+      assertEquals(result, expected, "for column " + col);
+    }
+  
+    /*
+     * now make 6-10 the span of the colour range
+     * (annotation value == column number in this test)
+     */
+    testee.setThresholdIsMinMax(true);
+    for (int col = 0; col < WIDTH; col++)
+    {
+      /*
+       * colours for values >= threshold are graduated
+       * range is 6-10 so steps of 100/5 = 20
+       */
+      int factor = col - THRESHOLD_FIVE;
+      Color expected = col < 5 ? Color.white : new Color(50 + 20 * factor,
+              200 - 20 * factor,
+              150 + 20 * factor);
+      Color result = testee.findColour('a', col, seq);
+      assertEquals(result, expected, "for column " + col);
+    }
+  }
+
+  /**
+   * Test the 'colour below threshold' case
+   */
+  @Test(groups = "Functional")
+  public void testFindColour_belowThreshold()
+  {
+    AnnotationColourGradient testee = new AnnotationColourGradient(ann,
+            minColour, maxColour, AnnotationColourGradient.BELOW_THRESHOLD);
+    testee = (AnnotationColourGradient) testee.getInstance(al, null);
+  
+    for (int col = 0; col < WIDTH; col++)
+    {
+      Color result = testee.findColour('a', col, seq);
+      Color expected = col > 5 ? Color.white : new Color(50 + 10 * col,
+              200 - 10 * col, 150 + 10 * col);
+      assertEquals(result, expected, "for column " + col);
+    }
+  
+    /*
+     * now make 0-5 the span of the colour range
+     * (annotation value == column number in this test)
+     */
+    testee.setThresholdIsMinMax(true);
+    for (int col = 0; col < WIDTH; col++)
+    {
+      /*
+       * colours for values <= threshold are graduated
+       * range is 0-5 so steps of 100/5 = 20
+       */
+      Color expected = col > 5 ? Color.white : new Color(50 + 20 * col,
+              200 - 20 * col, 150 + 20 * col);
+      Color result = testee.findColour('a', col, seq);
+      assertEquals(result, expected, "for column " + col);
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testFindColour_noThreshold()
+  {
+    AnnotationColourGradient testee = new AnnotationColourGradient(ann,
+            minColour, maxColour, AnnotationColourGradient.NO_THRESHOLD);
+    testee = (AnnotationColourGradient) testee.getInstance(al, null);
+
+    for (int col = 0; col < WIDTH; col++)
+    {
+      Color result = testee.findColour('a', col, seq);
+      /*
+       * column <n> is n/10 of the way from minCol to maxCol
+       */
+      Color expected = new Color(50 + 10 * col, 200 - 10 * col,
+              150 + 10 * col);
+      assertEquals(result, expected, "for column " + col);
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testFindColour_originalColours()
+  {
+    AnnotationColourGradient testee = new AnnotationColourGradient(ann,
+            minColour, maxColour, AnnotationColourGradient.NO_THRESHOLD);
+    testee = (AnnotationColourGradient) testee.getInstance(al, null);
+
+    /*
+     * flag corresponding to 'use original colours' checkbox
+     * - just use the individual annotation colours
+     */
+    testee.setPredefinedColours(true);
+
+    /*
+     * the annotation colour is returned, except for column 0 where it is
+     * black - in this case the colour scheme colour overrides it
+     */
+    for (int col = 0; col < WIDTH; col++)
+    {
+      int hue = col * 20;
+      Color c = col == 0 ? minColour : new Color(hue, hue, hue);
+      assertEquals(testee.findColour('a', col, seq), c, "for column " + col);
+    }
+  }
+}
index b3dd165..0b5b6bd 100644 (file)
@@ -29,11 +29,10 @@ public class Blosum62ColourSchemeTest
 
     /*
      * findColour does not use column, sequence or pid score
+     * we assume consensus residue is computed as upper case
      */
     assertEquals(blosum.findColour('A', 0, null, "A", 0f), darkBlue);
     assertEquals(blosum.findColour('a', 0, null, "A", 0f), darkBlue);
-    assertEquals(blosum.findColour('A', 0, null, "a", 0f), darkBlue);
-    assertEquals(blosum.findColour('a', 0, null, "a", 0f), darkBlue);
 
     /*
      * L has a Blosum score of 
index 4618ed7..0aaa38c 100644 (file)
@@ -228,9 +228,9 @@ public class ColourSchemesTest
      * set and check Taylor colours
      */
     af.changeColour_actionPerformed(JalviewColourScheme.Taylor.toString());
-    Color taylor1 = sr.getResidueBoxColour(seq, 88); // E 255,0,102
-    Color taylor2 = sr.getResidueBoxColour(seq, 89); // A 204,255,0
-    Color taylor3 = sr.getResidueBoxColour(seq, 90); // G 255,153,0
+    Color taylor1 = sr.getResidueColour(seq, 88, null); // E 255,0,102
+    Color taylor2 = sr.getResidueColour(seq, 89, null); // A 204,255,0
+    Color taylor3 = sr.getResidueColour(seq, 90, null); // G 255,153,0
     assertEquals(taylor1, new Color(255, 0, 102));
     assertEquals(taylor2, new Color(204, 255, 0));
     assertEquals(taylor3, new Color(255, 153, 0));
@@ -239,9 +239,9 @@ public class ColourSchemesTest
      * set and check Zappo colours
      */
     af.changeColour_actionPerformed(JalviewColourScheme.Zappo.toString());
-    Color zappo1 = sr.getResidueBoxColour(seq, 88); // E red
-    Color zappo2 = sr.getResidueBoxColour(seq, 89); // A pink
-    Color zappo3 = sr.getResidueBoxColour(seq, 90); // G magenta
+    Color zappo1 = sr.getResidueColour(seq, 88, null); // E red
+    Color zappo2 = sr.getResidueColour(seq, 89, null); // A pink
+    Color zappo3 = sr.getResidueColour(seq, 90, null); // G magenta
     assertEquals(zappo1, Color.red);
     assertEquals(zappo2, Color.pink);
     assertEquals(zappo3, Color.magenta);
@@ -250,9 +250,9 @@ public class ColourSchemesTest
      * set 'stripy' colours - odd columns are Taylor and even are Zappo 
      */
     af.changeColour_actionPerformed("stripy");
-    Color stripy1 = sr.getResidueBoxColour(seq, 88);
-    Color stripy2 = sr.getResidueBoxColour(seq, 89);
-    Color stripy3 = sr.getResidueBoxColour(seq, 90);
+    Color stripy1 = sr.getResidueColour(seq, 88, null);
+    Color stripy2 = sr.getResidueColour(seq, 89, null);
+    Color stripy3 = sr.getResidueColour(seq, 90, null);
     assertEquals(stripy1, zappo1);
     assertEquals(stripy2, taylor2);
     assertEquals(stripy3, zappo3);
@@ -261,9 +261,9 @@ public class ColourSchemesTest
      * set and check Clustal colours
      */
     af.changeColour_actionPerformed(JalviewColourScheme.Clustal.toString());
-    Color clustal1 = sr.getResidueBoxColour(seq, 88);
-    Color clustal2 = sr.getResidueBoxColour(seq, 89);
-    Color clustal3 = sr.getResidueBoxColour(seq, 90);
+    Color clustal1 = sr.getResidueColour(seq, 88, null);
+    Color clustal2 = sr.getResidueColour(seq, 89, null);
+    Color clustal3 = sr.getResidueColour(seq, 90, null);
     assertEquals(clustal1, ClustalColour.MAGENTA.colour);
     assertEquals(clustal2, ClustalColour.BLUE.colour);
     assertEquals(clustal3, ClustalColour.ORANGE.colour);
@@ -272,9 +272,9 @@ public class ColourSchemesTest
      * set 'MyClustal' colours - uses AWT colour equivalents
      */
     af.changeColour_actionPerformed("MyClustal");
-    Color myclustal1 = sr.getResidueBoxColour(seq, 88);
-    Color myclustal2 = sr.getResidueBoxColour(seq, 89);
-    Color myclustal3 = sr.getResidueBoxColour(seq, 90);
+    Color myclustal1 = sr.getResidueColour(seq, 88, null);
+    Color myclustal2 = sr.getResidueColour(seq, 89, null);
+    Color myclustal3 = sr.getResidueColour(seq, 90, null);
     assertEquals(myclustal1, Color.MAGENTA);
     assertEquals(myclustal2, Color.BLUE);
     assertEquals(myclustal3, Color.ORANGE);
index 73b270f..fa4b5d9 100644 (file)
@@ -42,6 +42,7 @@ public class PIDColourSchemeTest
 
     /*
      * doesn't use column or sequence
+     * we assume consensus residue is computed as upper case
      */
     assertEquals(scheme.findColour('A', 0, null, "A", 0f), white);
     assertEquals(scheme.findColour('A', 0, null, "A", 40f), white);
@@ -54,8 +55,6 @@ public class PIDColourSchemeTest
     assertEquals(scheme.findColour('A', 0, null, "KFV", 100f), white);
 
     assertEquals(scheme.findColour('a', 0, null, "A", 80f), over60);
-    assertEquals(scheme.findColour('A', 0, null, "a", 80f), over60);
-    assertEquals(scheme.findColour('a', 0, null, "a", 80f), over60);
     assertEquals(scheme.findColour('A', 0, null, "AC", 80f), over60);
     assertEquals(scheme.findColour('A', 0, null, "KCA", 80f), over60);
   }
@@ -78,10 +77,26 @@ public class PIDColourSchemeTest
      * first column PID is 50%, or 67% ignoring gaps
      */
     String seqs = ">seq1\nAAAAA\n>seq2\nAAAAA\n>seq3\n-CCCC\n>seq4\nFFFFF\n";
+
+    /*
+     * load data and wait for consensus to be computed
+     */
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqs,
             DataSourceType.PASTE);
     AlignViewport viewport = af.getViewport();
     viewport.setIgnoreGapsConsensus(false, af.alignPanel);
+    while (viewport.getConsensusSeq() == null)
+    {
+      synchronized (this)
+      {
+        try
+        {
+          wait(50);
+        } catch (InterruptedException e)
+        {
+        }
+      }
+    }
     af.changeColour_actionPerformed(JalviewColourScheme.PID.toString());
 
     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
index a743163..80241fb 100644 (file)
@@ -26,7 +26,6 @@ import jalview.gui.JvOptionPane;
 import java.util.Map;
 
 import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
 
 public class ScoreMatrixPrinter
 {
@@ -38,7 +37,6 @@ public class ScoreMatrixPrinter
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
-  @Test(groups = { "Functional" })
   public void printAllMatrices()
   {
     for (Map.Entry<String, ScoreModelI> sm : ResidueProperties.scoreMatrices
@@ -49,7 +47,6 @@ public class ScoreMatrixPrinter
     }
   }
 
-  @Test(groups = { "Functional" })
   public void printHTMLMatrices()
   {
     for (Map.Entry<String, ScoreModelI> _sm : ResidueProperties.scoreMatrices
diff --git a/test/jalview/schemes/ScoreMatrixTest.java b/test/jalview/schemes/ScoreMatrixTest.java
new file mode 100644 (file)
index 0000000..e15dd41
--- /dev/null
@@ -0,0 +1,168 @@
+package jalview.schemes;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.math.MatrixI;
+
+import org.testng.annotations.Test;
+
+public class ScoreMatrixTest
+{
+  @Test(groups = "Functional")
+  public void testSymmetric()
+  {
+    verifySymmetric(ResidueProperties.getScoreMatrix("BLOSUM62"));
+    verifySymmetric(ResidueProperties.getScoreMatrix("PAM250"));
+    verifySymmetric(ResidueProperties.getScoreMatrix("DNA"));
+  }
+
+  private void verifySymmetric(ScoreMatrix sm)
+  {
+    int[][] m = sm.getMatrix();
+    int rows = m.length;
+    for (int row = 0; row < rows; row++)
+    {
+      assertEquals(m[row].length, rows);
+      for (int col = 0; col < rows; col++)
+      {
+        assertEquals(m[row][col], m[col][row], String.format("%s [%s, %s]",
+                sm.getName(), ResidueProperties.aa[row],
+                ResidueProperties.aa[col]));
+      }
+    }
+
+    /*
+     * also check the score matrix is sized for 
+     * the number of symbols scored, plus gap
+     */
+    assertEquals(rows, (sm.isDNA() ? ResidueProperties.maxNucleotideIndex
+            : ResidueProperties.maxProteinIndex) + 1);
+  }
+
+  /**
+   * A test that just asserts the expected values in the Blosum62 score matrix
+   */
+  @Test(groups = "Functional")
+  public void testBlosum62_values()
+  {
+    ScoreMatrix sm = ResidueProperties.getScoreMatrix("BLOSUM62");
+
+    /*
+     * verify expected scores against ARNDCQEGHILKMFPSTWYVBZX
+     * scraped from https://www.ncbi.nlm.nih.gov/Class/FieldGuide/BLOSUM62.txt
+     */
+    verifyValues(sm, 'A', new int[] { 4, -1, -2, -2, 0, -1, -1, 0, -2, -1,
+        -1, -1, -1, -2, -1, 1, 0, -3, -2, 0, -2, -1, 0 });
+    verifyValues(sm, 'R', new int[] { -1, 5, 0, -2, -3, 1, 0, -2, 0, -3,
+        -2, 2, -1, -3, -2, -1, -1, -3, -2, -3, -1, 0, -1 });
+    verifyValues(sm, 'N', new int[] { -2, 0, 6, 1, -3, 0, 0, 0, 1, -3, -3,
+        0, -2, -3, -2, 1, 0, -4, -2, -3, 3, 0, -1 });
+    verifyValues(sm, 'D', new int[] { -2, -2, 1, 6, -3, 0, 2, -1, -1, -3,
+        -4, -1, -3, -3, -1, 0, -1, -4, -3, -3, 4, 1, -1 });
+    verifyValues(sm, 'C', new int[] { 0, -3, -3, -3, 9, -3, -4, -3, -3, -1,
+        -1, -3, -1, -2, -3, -1, -1, -2, -2, -1, -3, -3, -2 });
+    verifyValues(sm, 'Q', new int[] { -1, 1, 0, 0, -3, 5, 2, -2, 0, -3, -2,
+        1, 0, -3, -1, 0, -1, -2, -1, -2, 0, 3, -1 });
+    verifyValues(sm, 'E', new int[] { -1, 0, 0, 2, -4, 2, 5, -2, 0, -3, -3,
+        1, -2, -3, -1, 0, -1, -3, -2, -2, 1, 4, -1 });
+    verifyValues(sm, 'G', new int[] { 0, -2, 0, -1, -3, -2, -2, 6, -2, -4,
+        -4, -2, -3, -3, -2, 0, -2, -2, -3, -3, -1, -2, -1 });
+    verifyValues(sm, 'H', new int[] { -2, 0, 1, -1, -3, 0, 0, -2, 8, -3,
+        -3, -1, -2, -1, -2, -1, -2, -2, 2, -3, 0, 0, -1 });
+    verifyValues(sm, 'I', new int[] { -1, -3, -3, -3, -1, -3, -3, -4, -3,
+        4, 2, -3, 1, 0, -3, -2, -1, -3, -1, 3, -3, -3, -1 });
+    verifyValues(sm, 'L', new int[] { -1, -2, -3, -4, -1, -2, -3, -4, -3,
+        2, 4, -2, 2, 0, -3, -2, -1, -2, -1, 1, -4, -3, -1 });
+    verifyValues(sm, 'K', new int[] { -1, 2, 0, -1, -3, 1, 1, -2, -1, -3,
+        -2, 5, -1, -3, -1, 0, -1, -3, -2, -2, 0, 1, -1 });
+    verifyValues(sm, 'M', new int[] { -1, -1, -2, -3, -1, 0, -2, -3, -2, 1,
+        2, -1, 5, 0, -2, -1, -1, -1, -1, 1, -3, -1, -1 });
+    verifyValues(sm, 'F', new int[] { -2, -3, -3, -3, -2, -3, -3, -3, -1,
+        0, 0, -3, 0, 6, -4, -2, -2, 1, 3, -1, -3, -3, -1 });
+    verifyValues(sm, 'P', new int[] { -1, -2, -2, -1, -3, -1, -1, -2, -2,
+        -3, -3, -1, -2, -4, 7, -1, -1, -4, -3, -2, -2, -1, -2 });
+    verifyValues(sm, 'S', new int[] { 1, -1, 1, 0, -1, 0, 0, 0, -1, -2, -2,
+        0, -1, -2, -1, 4, 1, -3, -2, -2, 0, 0, 0 });
+    verifyValues(sm, 'T', new int[] { 0, -1, 0, -1, -1, -1, -1, -2, -2, -1,
+        -1, -1, -1, -2, -1, 1, 5, -2, -2, 0, -1, -1, 0 });
+    verifyValues(sm, 'W', new int[] { -3, -3, -4, -4, -2, -2, -3, -2, -2,
+        -3, -2, -3, -1, 1, -4, -3, -2, 11, 2, -3, -4, -3, -2 });
+    verifyValues(sm, 'Y', new int[] { -2, -2, -2, -3, -2, -1, -2, -3, 2,
+        -1, -1, -2, -1, 3, -3, -2, -2, 2, 7, -1, -3, -2, -1 });
+    verifyValues(sm, 'V', new int[] { 0, -3, -3, -3, -1, -2, -2, -3, -3, 3,
+        1, -2, 1, -1, -2, -2, 0, -3, -1, 4, -3, -2, -1 });
+    verifyValues(sm, 'B', new int[] { -2, -1, 3, 4, -3, 0, 1, -1, 0, -3,
+        -4, 0, -3, -3, -2, 0, -1, -4, -3, -3, 4, 1, -1 });
+    verifyValues(sm, 'Z', new int[] { -1, 0, 0, 1, -3, 3, 4, -2, 0, -3, -3,
+        1, -1, -3, -1, 0, -1, -3, -2, -2, 1, 4, -1 });
+    verifyValues(sm, 'X', new int[] { 0, -1, -1, -1, -2, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -2, 0, 0, -2, -1, -1, -1, -1, -1 });
+  }
+  /**
+   * Helper method to check pairwise scores for one residue
+   * 
+   * @param sm
+   * @param res
+   * @param expected
+   *          score values against 'res', in ResidueProperties.aaIndex order
+   */
+  private void verifyValues(ScoreMatrix sm, char res, int[] expected)
+  {
+    for (int j = 0; j < expected.length; j++)
+    {
+      char c2 = ResidueProperties.aa[j].charAt(0);
+      assertEquals(sm.getPairwiseScore(res, c2), expected[j],
+              String.format("%s->%s", res, c2));
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testComputePairwiseScores()
+  {
+    String[] seqs = new String[] { "FKL", "R-D", "QIA", "GWC" };
+    ScoreMatrix sm = ResidueProperties.getScoreMatrix("BLOSUM62");
+  
+    MatrixI pairwise = sm.computePairwiseScores(seqs);
+  
+    /*
+     * should be NxN where N = number of sequences
+     */
+    assertEquals(pairwise.height(), 4);
+    assertEquals(pairwise.width(), 4);
+  
+    /*
+     * should be symmetrical (because BLOSUM62 is)
+     */
+    for (int i = 0; i < pairwise.height(); i++)
+    {
+      for (int j = 0; j < pairwise.width(); j++)
+      {
+        assertEquals(pairwise.getValue(i, j), pairwise.getValue(j, i),
+                "Not symmetric");
+      }
+    }
+    /*
+     * verify expected BLOSUM dot product scores
+     */
+    // F.F + K.K + L.L = 6 + 5 + 4 = 15
+    assertEquals(pairwise.getValue(0, 0), 15d);
+    // R.R + -.- + D.D = 5 + 1 + 6 = 12
+    assertEquals(pairwise.getValue(1, 1), 12d);
+    // Q.Q + I.I + A.A = 5 + 4 + 4 = 13
+    assertEquals(pairwise.getValue(2, 2), 13d);
+    // G.G + W.W + C.C = 6 + 11 + 9 = 26
+    assertEquals(pairwise.getValue(3, 3), 26d);
+    // F.R + K.- + L.D = -3 + -4 + -4 = -11
+    assertEquals(pairwise.getValue(0, 1), -11d);
+    // F.Q + K.I + L.A = -3 + -3 + -1 = -7
+    assertEquals(pairwise.getValue(0, 2), -7d);
+    // F.G + K.W + L.C = -3 + -3 + -1 = -7
+    assertEquals(pairwise.getValue(0, 3), -7d);
+    // R.Q + -.I + D.A = 1 + -4 + -2 = -5
+    assertEquals(pairwise.getValue(1, 2), -5d);
+    // R.G + -.W + D.C = -2 + -4 + -3 = -9
+    assertEquals(pairwise.getValue(1, 3), -9d);
+    // Q.G + I.W + A.C = -2 + -3 + 0 = -5
+    assertEquals(pairwise.getValue(2, 3), -5d);
+  }
+}
diff --git a/test/jalview/structure/AtomSpecTest.java b/test/jalview/structure/AtomSpecTest.java
new file mode 100644 (file)
index 0000000..ea53131
--- /dev/null
@@ -0,0 +1,74 @@
+package jalview.structure;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.fail;
+
+import org.testng.annotations.Test;
+
+public class AtomSpecTest
+{
+  @Test
+  public void testFromChimeraAtomSpec()
+  {
+    AtomSpec as = AtomSpec.fromChimeraAtomspec("#1:12.B");
+    assertEquals(as.getModelNumber(), 1);
+    assertEquals(as.getPdbResNum(), 12);
+    assertEquals(as.getChain(), "B");
+    assertNull(as.getPdbFile());
+
+    // no model - default to zero
+    as = AtomSpec.fromChimeraAtomspec(":13.C");
+    assertEquals(as.getModelNumber(), 0);
+    assertEquals(as.getPdbResNum(), 13);
+    assertEquals(as.getChain(), "C");
+    assertNull(as.getPdbFile());
+
+    // model.submodel
+    as = AtomSpec.fromChimeraAtomspec("#3.2:15");
+    assertEquals(as.getModelNumber(), 3);
+    assertEquals(as.getPdbResNum(), 15);
+    assertEquals(as.getChain(), "");
+    assertNull(as.getPdbFile());
+
+    String spec = "3:12.B";
+    try
+    {
+      as = AtomSpec.fromChimeraAtomspec(spec);
+      fail("Expected exception for " + spec);
+    } catch (IllegalArgumentException e)
+    {
+      // ok
+    }
+
+    spec = "#3:12-14.B";
+    try
+    {
+      as = AtomSpec.fromChimeraAtomspec(spec);
+      fail("Expected exception for " + spec);
+    } catch (IllegalArgumentException e)
+    {
+      // ok
+    }
+
+    spec = "";
+    try
+    {
+      as = AtomSpec.fromChimeraAtomspec(spec);
+      fail("Expected exception for " + spec);
+    } catch (IllegalArgumentException e)
+    {
+      // ok
+    }
+
+    spec = null;
+    try
+    {
+      as = AtomSpec.fromChimeraAtomspec(spec);
+      fail("Expected exception for " + spec);
+    } catch (NullPointerException e)
+    {
+      // ok
+    }
+  }
+}
diff --git a/test/jalview/structure/StructureMappingTest.java b/test/jalview/structure/StructureMappingTest.java
new file mode 100644 (file)
index 0000000..f26c5f1
--- /dev/null
@@ -0,0 +1,46 @@
+package jalview.structure;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+public class StructureMappingTest
+{
+  @Test(groups = "Functional")
+  public void testgetPDBResNumRanges()
+  {
+    HashMap<Integer, int[]> map = new HashMap<Integer, int[]>();
+
+    StructureMapping mapping = new StructureMapping(null, null, null, null,
+            map, null);
+
+    List<int[]> ranges = mapping.getPDBResNumRanges(1, 2);
+    assertTrue(ranges.isEmpty());
+
+    map.put(1, new int[] { 12, 20 }); // 1 maps to 12
+    ranges = mapping.getPDBResNumRanges(2, 3);
+    assertTrue(ranges.isEmpty());
+    ranges = mapping.getPDBResNumRanges(1, 2);
+    assertEquals(ranges.size(), 1);
+    assertEquals(ranges.get(0)[0], 12);
+    assertEquals(ranges.get(0)[1], 12);
+
+    map.put(2, new int[] { 13, 20 }); // 2 maps to 13
+    ranges = mapping.getPDBResNumRanges(1, 2);
+    assertEquals(ranges.size(), 1);
+    assertEquals(ranges.get(0)[0], 12);
+    assertEquals(ranges.get(0)[1], 13);
+
+    map.put(3, new int[] { 15, 20 }); // 3 maps to 15 - break
+    ranges = mapping.getPDBResNumRanges(1, 5);
+    assertEquals(ranges.size(), 2);
+    assertEquals(ranges.get(0)[0], 12);
+    assertEquals(ranges.get(0)[1], 13);
+    assertEquals(ranges.get(1)[0], 15);
+    assertEquals(ranges.get(1)[1], 15);
+  }
+}
index 0422537..7ba22b4 100644 (file)
@@ -44,6 +44,7 @@ import jalview.structures.models.AAStructureBindingModel.SuperposeData;
 
 import java.awt.Color;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.List;
 
 import org.testng.annotations.BeforeClass;
@@ -169,9 +170,10 @@ public class AAStructureBindingModelTest
       }
 
       @Override
-      public void superposeStructures(AlignmentI[] als, int[] alm,
+      public String superposeStructures(AlignmentI[] als, int[] alm,
               ColumnSelection[] alc)
       {
+        return null;
       }
 
       @Override
@@ -181,14 +183,7 @@ public class AAStructureBindingModelTest
 
       @Override
       protected StructureMappingcommandSet[] getColourBySequenceCommands(
-              String[] files, SequenceRenderer sr, FeatureRenderer fr,
-              AlignmentI alignment)
-      {
-        return null;
-      }
-
-      @Override
-      public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
+              String[] files, SequenceRenderer sr, AlignmentViewPanel avp)
       {
         return null;
       }
@@ -215,6 +210,13 @@ public class AAStructureBindingModelTest
       public void colourByCharge()
       {
       }
+
+      @Override
+      public FeatureRenderer getFeatureRenderer(
+              AlignmentViewPanel alignment)
+      {
+        return null;
+      }
     };
   }
 
@@ -234,11 +236,14 @@ public class AAStructureBindingModelTest
       structs[i] = testee.new SuperposeData(al.getWidth());
     }
     /*
-     * initialise array of 'superposable columns' to true (would be false for
+     * initialise BitSet of 'superposable columns' to true (would be false for
      * hidden columns)
      */
-    boolean[] matched = new boolean[al.getWidth()];
-    Arrays.fill(matched, true);
+    BitSet matched = new BitSet();
+    for (int i = 0; i < al.getWidth(); i++)
+    {
+      matched.set(i);
+    }
 
     int refStructure = testee
             .findSuperposableResidues(al, matched, structs);
@@ -248,12 +253,12 @@ public class AAStructureBindingModelTest
     /*
      * only ungapped, structure-mapped columns are superposable
      */
-    assertFalse(matched[0]); // gap in first sequence
-    assertTrue(matched[1]);
-    assertFalse(matched[2]); // gap in third sequence
-    assertFalse(matched[3]); // gap in fourth sequence
-    assertTrue(matched[4]);
-    assertTrue(matched[5]); // gap in second sequence
+    assertFalse(matched.get(0)); // gap in first sequence
+    assertTrue(matched.get(1));
+    assertFalse(matched.get(2)); // gap in third sequence
+    assertFalse(matched.get(3)); // gap in fourth sequence
+    assertTrue(matched.get(4));
+    assertTrue(matched.get(5)); // gap in second sequence
 
     assertEquals("1YCS", structs[0].pdbId);
     assertEquals("3A6S", structs[1].pdbId);
@@ -278,13 +283,17 @@ public class AAStructureBindingModelTest
       structs[i] = testee.new SuperposeData(al.getWidth());
     }
     /*
-     * initialise array of 'superposable columns' to true (would be false for
+     * initialise BitSet of 'superposable columns' to true (would be false for
      * hidden columns)
      */
-    boolean[] matched = new boolean[al.getWidth()];
-    Arrays.fill(matched, true);
+    BitSet matched = new BitSet();
+    for (int i = 0; i < al.getWidth(); i++)
+    {
+      matched.set(i);
+    }
+
     // treat column 5 of the alignment as hidden
-    matched[4] = false;
+    matched.clear(4);
 
     int refStructure = testee
             .findSuperposableResidues(al, matched, structs);
@@ -292,21 +301,11 @@ public class AAStructureBindingModelTest
     assertEquals(0, refStructure);
 
     // only ungapped, structure-mapped columns are not superposable
-    assertFalse(matched[0]);
-    assertTrue(matched[1]);
-    assertFalse(matched[2]);
-    assertFalse(matched[3]);
-    assertFalse(matched[4]); // superposable, but hidden, column
-    assertTrue(matched[5]);
-  }
-
-  public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
-  {
-    return null;
-  }
-
-  public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment)
-  {
-    return null;
+    assertFalse(matched.get(0));
+    assertTrue(matched.get(1));
+    assertFalse(matched.get(2));
+    assertFalse(matched.get(3));
+    assertFalse(matched.get(4)); // superposable, but hidden, column
+    assertTrue(matched.get(5));
   }
 }
diff --git a/test/jalview/urls/AppletUrlProviderFactoryTest.java b/test/jalview/urls/AppletUrlProviderFactoryTest.java
new file mode 100644 (file)
index 0000000..2a967e4
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.urls;
+
+import jalview.urls.api.UrlProviderFactoryI;
+import jalview.urls.api.UrlProviderI;
+import jalview.urls.applet.AppletUrlProviderFactory;
+import jalview.util.UrlConstants;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class AppletUrlProviderFactoryTest {
+
+  @Test(groups = { "Functional" })
+  public void testCreateUrlProvider()
+  {
+    final String defaultUrl = UrlConstants.DEFAULT_STRING.substring(
+            UrlConstants.DEFAULT_STRING.indexOf(UrlConstants.SEP) + 1,
+            UrlConstants.DEFAULT_STRING.length());
+    Map<String, String> urlList = new HashMap<String, String>()
+    {
+      {
+        put("Test1", "http://identifiers.org/uniprot/$DB_ACCESSION$");
+        put("Test2", defaultUrl);
+      }
+    };
+
+    UrlProviderFactoryI factory = new AppletUrlProviderFactory("Test2",
+            urlList);
+    UrlProviderI prov = factory.createUrlProvider();
+
+    // default url correctly set
+    Assert.assertEquals(prov.getPrimaryUrlId(), "Test2");
+    Assert.assertEquals(prov.getPrimaryUrl("FER_CAPAN"),
+            defaultUrl.replace("$SEQUENCE_ID$",
+                    "FER_CAPAN"));
+
+    List<UrlLinkDisplay> allLinks = prov.getLinksForTable();
+
+    // 2 links in provider
+    Assert.assertEquals(allLinks.size(), 2);
+
+    // first link set correctly
+    Assert.assertEquals(allLinks.get(0).getId(), "Test1");
+    Assert.assertEquals(allLinks.get(0).getDescription(), "Test1");
+    Assert.assertEquals(allLinks.get(0).getUrl(),
+            "http://identifiers.org/uniprot/$DB_ACCESSION$");
+    Assert.assertFalse(allLinks.get(0).getIsPrimary());
+    Assert.assertTrue(allLinks.get(0).getIsSelected());
+
+    // second link set correctly
+    Assert.assertEquals(allLinks.get(1).getId(), "Test2");
+    Assert.assertEquals(allLinks.get(1).getDescription(), "Test2");
+    Assert.assertEquals(allLinks.get(1).getUrl(), defaultUrl);
+    Assert.assertTrue(allLinks.get(1).getIsPrimary());
+    Assert.assertTrue(allLinks.get(1).getIsSelected());
+  }
+}
diff --git a/test/jalview/urls/CustomUrlProviderTest.java b/test/jalview/urls/CustomUrlProviderTest.java
new file mode 100644 (file)
index 0000000..f9ed893
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.urls;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+import jalview.urls.api.UrlProviderI;
+import jalview.util.UrlConstants;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Vector;
+
+import org.testng.annotations.Test;
+
+public class CustomUrlProviderTest
+{
+
+  private static final String cachedList = "TEST|http://someurl.blah/$DB_ACCESSION$|"
+          + "ANOTHER|http://test/t$SEQUENCE_ID$|"
+          + "TEST2|http://address/$SEQUENCE_ID$|SRS|"
+          + "http://theSRSlink/$SEQUENCE_ID$";
+
+  private static final String unselectedList = "NON1|http://x/y/$DB_ACCESSION$|"
+          + "NON2|http://a/b/c/$DB_ACCESSION";
+
+  private static final HashMap<String, String> urlMap = new HashMap<String, String>()
+  {
+    {
+      put("TEST","http://someurl.blah/$DB_ACCESSION$");
+      put("ANOTHER","http://test/t$SEQUENCE_ID$");
+      put("TEST2", "http://address/$SEQUENCE_ID$");
+      put("SRS", "http://theSRSlink/$SEQUENCE_ID$");
+    }
+  };
+  
+  private static final HashMap<String, String> unselUrlMap = new HashMap<String, String>()
+  {
+    {
+      put("NON1", "http://x/y/$DB_ACCESSION$");
+      put("NON2", "http://a/b/c/$DB_ACCESSION");
+    }
+  };
+
+  private static final String[] dlinks = {
+      "TEST|http://someurl.blah/$DB_ACCESSION$",
+      "ANOTHER|http://test/t$SEQUENCE_ID$",
+      "TEST2|http://address/$SEQUENCE_ID$",
+ UrlConstants.DEFAULT_STRING };
+
+  private static final String[] unselDlinks = {
+      "NON1|http://x/y/$DB_ACCESSION$", "NON2|http://a/b/c/$DB_ACCESSION" };
+
+  private static final Vector<String> displayLinks = new Vector<String>(
+          Arrays.asList(dlinks));
+
+  private static final Vector<String> unselDisplayLinks = new Vector<String>(
+          Arrays.asList(unselDlinks));
+
+  private static final String[] dlinks2 = { "a|http://x.y.z/$SEQUENCE_ID$" };
+
+  private static final Vector<String> displayLinks2 = new Vector<String>(
+          Arrays.asList(dlinks2));
+
+  private static final String[] list1 = { "a" };
+
+  private static final String[] list2 = { "http://x.y.z/$SEQUENCE_ID$" };
+
+  private static final Vector<String> names = new Vector<String>(
+          Arrays.asList(list1));
+
+  private static final Vector<String> urls = new Vector<String>(
+          Arrays.asList(list2));
+
+  /*
+   * Test default url is set and returned correctly
+   */
+  @Test(groups = { "Functional" })
+  public void testDefaultUrl()
+  {
+    UrlProviderI customProv = new CustomUrlProvider(cachedList,
+            unselectedList);
+    
+    // default url can be set
+    assertTrue(customProv.setPrimaryUrl("ANOTHER"));
+
+    // supplied replacement id must be more than 4 chars
+    String result = customProv.getPrimaryUrl("123");
+    assertEquals(null, result);
+
+    // default url can be retrieved given a sequence id
+    result = customProv.getPrimaryUrl("seqid");
+    assertEquals("http://test/tseqid", result);
+
+    // if there is no default url it sets the default to null
+    assertFalse(customProv.setPrimaryUrl("No default"));
+    result = customProv.getPrimaryUrl("testid");
+    assertEquals(null, result);
+    
+    // choosing the default picks the DEFAULT_STRING option
+    customProv.choosePrimaryUrl();
+    result = customProv.getPrimaryUrl("seqid");
+    assertEquals(
+            UrlConstants.DEFAULT_STRING.split("\\|")[1].split("\\$")[0]
+            + "seqid",
+            result);
+  }
+
+  /*
+   * Test urls are set and returned correctly
+   */
+  @Test(groups = { "Functional" })
+  public void testUrlLinks()
+  {
+    // creation from cached url list works + old links upgraded
+    UrlProviderI customProv = new CustomUrlProvider(cachedList,
+            unselectedList);
+    assertTrue(displayLinks.containsAll(customProv.getLinksForMenu()));
+
+    // creation from map works + old links upgraded
+    UrlProviderI customProv2 = new CustomUrlProvider(urlMap, unselUrlMap);
+    assertTrue(displayLinks.containsAll(customProv2.getLinksForMenu()));
+
+    // writing url links as a string works
+    // because UrlProvider does not guarantee order of links, we can't just
+    // compare the output of writeUrlsAsString to a string, hence the hoops here
+    String result = customProv.writeUrlsAsString(true);
+    UrlProviderI up = new CustomUrlProvider(result, "");
+    assertTrue(displayLinks.containsAll(up.getLinksForMenu()));
+
+    result = customProv.writeUrlsAsString(false);
+    up = new CustomUrlProvider("", result);
+    assertTrue(unselDisplayLinks.containsAll(up.getLinksForMenu()));
+
+    result = customProv2.writeUrlsAsString(true);
+    UrlProviderI up2 = new CustomUrlProvider(result, "");
+    assertTrue(displayLinks.containsAll(up2.getLinksForMenu()));
+
+    result = customProv2.writeUrlsAsString(false);
+    up2 = new CustomUrlProvider("", result);
+    assertTrue(displayLinks.containsAll(up2.getLinksForMenu()));
+  }
+}
diff --git a/test/jalview/urls/DesktopUrlProviderFactoryTest.java b/test/jalview/urls/DesktopUrlProviderFactoryTest.java
new file mode 100644 (file)
index 0000000..cc55034
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.urls;
+
+import jalview.urls.api.UrlProviderI;
+import jalview.urls.desktop.DesktopUrlProviderFactory;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class DesktopUrlProviderFactoryTest
+{
+  private static final String testIdOrgString = "{\"Local\": [{\"id\":\"MIR:00000002\",\"name\":\"ChEBI\",\"pattern\":\"^CHEBI:\\d+$\","
+          + "\"definition\":\"Chemical Entities of Biological Interest (ChEBI)\",\"prefix\":\"chebi\","
+          + "\"url\":\"http://identifiers.org/chebi\"},{\"id\":\"MIR:00000005\",\"name\":\"UniProt Knowledgebase\","
+          + "\"pattern\":\"^([A-N,R-Z][0-9]([A-Z][A-Z, 0-9][A-Z, 0-9][0-9]){1,2})|([O,P,Q][0-9][A-Z, 0-9][A-Z, 0-9][A-Z, 0-9][0-9])(\\.\\d+)?$\","
+          + "\"definition\":\"The UniProt Knowledgebase (UniProtKB)\",\"prefix\":\"uniprot\",\"url\":\"http://identifiers.org/uniprot\"},"
+          + "{\"id\":\"MIR:00000011\",\"name\":\"InterPro\",\"pattern\":\"^IPR\\d{6}$\",\"definition\":\"InterPro\",\"prefix\":\"interpro\","
+          + "\"url\":\"http://identifiers.org/interpro\"},"
+          + "{\"id\":\"MIR:00000372\",\"name\":\"ENA\",\"pattern\":\"^[A-Z]+[0-9]+(\\.\\d+)?$\",\"definition\":\"The European Nucleotide Archive (ENA),\""
+          + "\"prefix\":\"ena.embl\",\"url\":\"http://identifiers.org/ena.embl\"}]}";
+
+  @BeforeMethod(alwaysRun = true)
+  public void setup()
+  {
+    // make a dummy identifiers.org download file
+    File temp = null;
+
+    try
+    {
+      temp = File.createTempFile("tempfile", ".tmp");
+      temp.deleteOnExit();
+      BufferedWriter bw = new BufferedWriter(new FileWriter(temp));
+      bw.write(testIdOrgString);
+      bw.close();
+    } catch (IOException e)
+    {
+      System.out
+              .println("Error initialising DesktopUrlProviderFactoryTest test: "
+                      + e.getMessage());
+    }
+
+    IdOrgSettings.setDownloadLocation(temp.getPath());
+  }
+
+  @Test(groups = { "Functional" })
+  public void testCreateUrlProvider()
+  {
+    String defaultUrlString = "Test1";
+    String defaultUrl = "http://blah.blah/$SEQUENCE_ID$";
+    String cachedUrlList = "MIR:00000005|MIR:00000011|Test1|http://blah.blah/$SEQUENCE_ID$|"
+            + "Test2|http://test2/$DB_ACCESSION$|Test3|http://test3/$SEQUENCE_ID$";
+    String userUrlList = "MIR:00000372|Test4|httpL//another.url/$SEQUENCE_ID$";
+
+    DesktopUrlProviderFactory factory = new DesktopUrlProviderFactory(
+            defaultUrlString, cachedUrlList, userUrlList);
+    UrlProviderI prov = factory.createUrlProvider();
+
+    // default url correctly set
+    Assert.assertEquals(prov.getPrimaryUrlId(), "Test1");
+    Assert.assertEquals(prov.getPrimaryUrl("FER_CAPAN"),
+            defaultUrl.replace("$SEQUENCE_ID$", "FER_CAPAN"));
+
+    List<String> menulinks = prov.getLinksForMenu();
+    List<UrlLinkDisplay> allLinks = prov.getLinksForTable();
+
+    // 8 links in provider - 4 from id file, 4 custom links
+    Assert.assertEquals(allLinks.size(), 8);
+
+    // 5 links in menu (cachedUrlList)
+    Assert.assertEquals(menulinks.size(), 5);
+
+    Assert.assertTrue(menulinks
+            .contains("Test1|http://blah.blah/$SEQUENCE_ID$"));
+    Assert.assertTrue(menulinks
+            .contains("Test2|http://test2/$DB_ACCESSION$"));
+    Assert.assertTrue(menulinks
+            .contains("Test3|http://test3/$SEQUENCE_ID$"));
+    Assert.assertTrue(menulinks
+            .contains("UniProt Knowledgebase|http://identifiers.org/uniprot/$DB_ACCESSION$|uniprot"));
+    Assert.assertTrue(menulinks
+            .contains("InterPro|http://identifiers.org/interpro/$DB_ACCESSION$|interpro"));
+  }
+}
diff --git a/test/jalview/urls/IdentifiersUrlProviderTest.java b/test/jalview/urls/IdentifiersUrlProviderTest.java
new file mode 100644 (file)
index 0000000..eed58b0
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.urls;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+import jalview.urls.api.UrlProviderI;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Vector;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class IdentifiersUrlProviderTest
+{
+  private static final String testIdOrgFile = "{\"Local\": [{\"id\":\"MIR:00000002\",\"name\":\"ChEBI\",\"pattern\":\"^CHEBI:\\d+$\","
+          + "\"definition\":\"Chemical Entities of Biological Interest (ChEBI)\",\"prefix\":\"chebi\","
+          + "\"url\":\"http://identifiers.org/chebi\"},{\"id\":\"MIR:00000005\",\"name\":\"UniProt Knowledgebase\","
+          + "\"pattern\":\"^([A-N,R-Z][0-9]([A-Z][A-Z, 0-9][A-Z, 0-9][0-9]){1,2})|([O,P,Q][0-9][A-Z, 0-9][A-Z, 0-9][A-Z, 0-9][0-9])(\\.\\d+)?$\","
+          + "\"definition\":\"The UniProt Knowledgebase (UniProtKB)\",\"prefix\":\"uniprot\",\"url\":\"http://identifiers.org/uniprot\"},"
+          + "{\"id\":\"MIR:00000011\",\"name\":\"InterPro\",\"pattern\":\"^IPR\\d{6}$\",\"definition\":\"InterPro\",\"prefix\":\"interpro\","
+          + "\"url\":\"http://identifiers.org/interpro\"},"
+          + "{\"id\":\"MIR:00000372\",\"name\":\"ENA\",\"pattern\":\"^[A-Z]+[0-9]+(\\.\\d+)?$\",\"definition\":\"The European Nucleotide Archive (ENA),\""
+          + "\"prefix\":\"ena.embl\",\"url\":\"http://identifiers.org/ena.embl\"}]}";
+  
+  private static final String[] dlinks = {
+      "UniProt Knowledgebase|http://identifiers.org/uniprot/$DB_ACCESSION$|uniprot",
+      "InterPro|http://identifiers.org/interpro/$DB_ACCESSION$|interpro",
+      "ENA|http://identifiers.org/ena.embl/$DB_ACCESSION$|ena.embl" };
+
+  private static final String[] dlinks1 = {
+      "MIR:00000011|http://identifiers.org/interpro/$DB_ACCESSION$",
+      "MIR:00000372|http://identifiers.org/ena.embl/$DB_ACCESSION$" };
+
+  private static final String[] dlinks2 = {
+      "MIR:00000005|http://identifiers.org/uniprot/$DB_ACCESSION$",
+      "MIR:00000011|http://identifiers.org/interpro/$DB_ACCESSION$" };
+
+  private static final String stringLinks = "MIR:00000005|http://identifiers.org/uniprot/$DB_ACCESSION$"
+          + "MIR:00000011|http://identifiers.org/interpro/$DB_ACCESSION$"
+          + "MIR:00000372|http://identifiers.org/ena.embl/$DB_ACCESSION$";
+
+  private static final String[] unselDlinks = { "ChEBI|http://identifiers.org/chebi/$DB_ACCESSION$" };
+
+  private static final Vector<String> displayLinks = new Vector<String>(
+        Arrays.asList(dlinks));
+  
+  private static final Vector<String> unselDisplayLinks = new Vector<String>(
+          Arrays.asList(unselDlinks));
+
+  private static final Vector<String> displayLinks1 = new Vector<String>(
+          Arrays.asList(dlinks1));
+
+  private static final Vector<String> displayLinks2 = new Vector<String>(
+          Arrays.asList(dlinks2));
+
+  private static final HashMap<String, String> urlMap = new HashMap<String, String>()
+  {
+    {
+      put("MIR:00000005", "http://identifiers.org/uniprot/$DB_ACCESSION$");
+      put("MIR:00000011", "http://identifiers.org/interpro/$DB_ACCESSION$");
+      put("MIR:00000372", "http://identifiers.org/ena.embl/$DB_ACCESSION$");
+    }
+  };
+
+  private String testfile = "";
+
+
+  @BeforeClass(alwaysRun = true)
+  public void setup()
+  {
+    // setup test ids in a file
+    File outFile = null;
+    try
+    {
+      outFile = File.createTempFile("testidsfile", "txt");
+      outFile.deleteOnExit();
+
+      FileWriter fw = new FileWriter(outFile);
+      fw.write(testIdOrgFile);
+      fw.close();
+
+      testfile = outFile.getAbsolutePath();
+
+    } catch (Exception ex)
+    {
+      System.err.println(ex);
+    }
+
+    IdOrgSettings.setDownloadLocation(testfile);
+  }
+
+  /*
+   * Test urls are set and returned correctly
+   */
+  @Test(groups = { "Functional" })
+  public void testUrlLinks()
+  {
+    // creation from cached id list
+    String idList = "MIR:00000005|MIR:00000011|MIR:00000372";
+    UrlProviderI idProv = new IdentifiersUrlProvider(idList);
+    
+    assertTrue(displayLinks.containsAll(idProv.getLinksForMenu()));
+
+    // because UrlProvider does not guarantee order of links, we can't just
+    // compare the output of writeUrlsAsString to a string, hence the hoops here
+    String result = idProv.writeUrlsAsString(true);
+    UrlProviderI up = new IdentifiersUrlProvider(result);
+    assertTrue(displayLinks.containsAll(up.getLinksForMenu()));
+
+    result = idProv.writeUrlsAsString(false);
+    up = new IdentifiersUrlProvider(result);
+    assertTrue(unselDisplayLinks.containsAll(up.getLinksForMenu()));
+
+  }
+
+  /*
+   * Test default is set and returned correctly
+   */
+  @Test(groups = { "Functional" })
+  public void testDefaultUrl()
+  {
+    // creation from cached id list
+    String idList = "MIR:00000005|MIR:00000011|MIR:00000372";
+    UrlProviderI idProv = new IdentifiersUrlProvider(idList);
+    
+    // initially no default
+    assertEquals(null, idProv.getPrimaryUrl("seqid"));
+    
+    // set and then retrieve default
+    assertTrue(idProv.setPrimaryUrl("MIR:00000005"));
+    assertEquals("http://identifiers.org/uniprot/seqid",
+            idProv.getPrimaryUrl("seqid"));
+
+    // ids less than length 4 return null
+    assertEquals(null,
+            idProv.getPrimaryUrl("123"));
+
+    // attempt to set bad default
+    assertFalse(idProv.setPrimaryUrl("MIR:00001234"));
+    // default set to null (as default should have been set elsewhere)
+    assertEquals(null, idProv.getPrimaryUrl("seqid"));
+
+    // chooseDefaultUrl not implemented for IdentifiersUrlProvider
+    assertEquals(null, idProv.choosePrimaryUrl());
+  }
+}
diff --git a/test/jalview/urls/UrlLinkDisplayTest.java b/test/jalview/urls/UrlLinkDisplayTest.java
new file mode 100644 (file)
index 0000000..8c50082
--- /dev/null
@@ -0,0 +1,145 @@
+package jalview.urls;
+
+import jalview.util.UrlLink;
+
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class UrlLinkDisplayTest {
+
+  @Test(groups = { "Functional" })
+  public void testDisplayColumnNames()
+  {
+    // 5 column names returned although 6 names internal to UrlLinkDisplay
+    List<String> names = UrlLinkDisplay.getDisplayColumnNames();
+    Assert.assertEquals(names.size(), 5);
+  }
+
+  @Test(groups = { "Functional" })
+  public void getValue()
+  {
+    UrlLink link = new UrlLink("Test Name",
+            "http://identifiers.org/$DB_ACCESSION$", "TestDB");
+    UrlLinkDisplay u = new UrlLinkDisplay("Test", link, false, false);
+
+    Assert.assertFalse((boolean) u.getValue(UrlLinkDisplay.PRIMARY));
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.ID), "Test");
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.DATABASE), "TestDB");
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.NAME), "Test Name");
+    Assert.assertFalse((boolean) u.getValue(UrlLinkDisplay.SELECTED));
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.URL),
+            "http://identifiers.org/$DB_ACCESSION$");
+  }
+
+  @Test(groups = { "Functional" })
+  public void testIsEditable()
+  {
+    // only default and selected columns are editable ever
+    // default only editable if link contains $SEQUENCE_ID$
+
+    UrlLink link = new UrlLink("Test Url",
+            "http://identifiers.org/$DB_ACCESSION$",
+ "TestName");
+    UrlLinkDisplay u = new UrlLinkDisplay("Test", link, false, false);
+
+    Assert.assertFalse(u.isEditable(UrlLinkDisplay.PRIMARY));
+    Assert.assertTrue(u.isEditable(UrlLinkDisplay.SELECTED));
+    Assert.assertFalse(u.isEditable(UrlLinkDisplay.ID));
+    Assert.assertFalse(u.isEditable(UrlLinkDisplay.URL));
+    Assert.assertFalse(u.isEditable(UrlLinkDisplay.NAME));
+    Assert.assertFalse(u.isEditable(UrlLinkDisplay.DATABASE));
+
+    UrlLink vlink = new UrlLink("Test Sequence ID Url",
+            "http://myurl/$SEQUENCE_ID$", "TestName");
+    UrlLinkDisplay v = new UrlLinkDisplay("Test", vlink, false, false);
+
+    Assert.assertTrue(v.isEditable(UrlLinkDisplay.PRIMARY));
+    Assert.assertTrue(v.isEditable(UrlLinkDisplay.SELECTED));
+    Assert.assertFalse(v.isEditable(UrlLinkDisplay.ID));
+    Assert.assertFalse(v.isEditable(UrlLinkDisplay.URL));
+    Assert.assertFalse(v.isEditable(UrlLinkDisplay.NAME));
+    Assert.assertFalse(v.isEditable(UrlLinkDisplay.DATABASE));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testName()
+  {
+    UrlLink link = new UrlLink("Test Url",
+            "http://identifiers.org/$DB_ACCESSION$", "TestName");
+    UrlLinkDisplay u = new UrlLinkDisplay("Test", link, false, false);
+
+    // Name initially as input in link
+    Assert.assertEquals(u.getDBName(), "TestName");
+
+    // Setting updates name
+    u.setDBName("NewName");
+    Assert.assertEquals(u.getDBName(), "NewName");
+  }
+
+  @Test(groups = { "Functional" })
+  public void testDescription()
+  {
+    UrlLink link = new UrlLink("Test Name",
+            "http://identifiers.org/$DB_ACCESSION$", "TestDB");
+    UrlLinkDisplay u = new UrlLinkDisplay("Test", link, false, false);
+
+    // Desc initially as input in link
+    Assert.assertEquals(u.getDescription(), "Test Name");
+
+    // Setting updates name
+    u.setDescription("New Desc");
+    Assert.assertEquals(u.getDescription(), "New Desc");
+  }
+
+  @Test(groups = { "Functional" })
+  public void testUrl()
+  {
+    UrlLink link = new UrlLink("Test Name",
+            "http://identifiers.org/$DB_ACCESSION$", "TestDB");
+    UrlLinkDisplay u = new UrlLinkDisplay("Test", link, false, false);
+
+    // Url initially as input in link
+    Assert.assertEquals(u.getUrl(), "http://identifiers.org/$DB_ACCESSION$");
+
+    // Setting updates url
+    u.setUrl("http://something.new/$SEQUENCE_ID$");
+    Assert.assertEquals(u.getUrl(), "http://something.new/$SEQUENCE_ID$");
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetSetValue()
+  {
+    UrlLink link = new UrlLink("Test Name",
+            "http://identifiers.org/$DB_ACCESSION$", "TestDB");
+    UrlLinkDisplay u = new UrlLinkDisplay("Test", link, false, false);
+
+    Assert.assertFalse((boolean) u.getValue(UrlLinkDisplay.PRIMARY));
+    Assert.assertFalse((boolean) u.getValue(UrlLinkDisplay.SELECTED));
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.DATABASE), "TestDB");
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.NAME), "Test Name");
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.ID), "Test");
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.URL),
+            "http://identifiers.org/$DB_ACCESSION$");
+
+    u.setValue(UrlLinkDisplay.PRIMARY, true);
+    Assert.assertTrue((boolean) u.getValue(UrlLinkDisplay.PRIMARY));
+
+    u.setValue(UrlLinkDisplay.SELECTED, true);
+    Assert.assertTrue((boolean) u.getValue(UrlLinkDisplay.SELECTED));
+
+    u.setValue(UrlLinkDisplay.NAME, "New Desc");
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.NAME), "New Desc");
+
+    u.setValue(UrlLinkDisplay.DATABASE, "NewName");
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.DATABASE), "NewName");
+
+    u.setValue(UrlLinkDisplay.ID, "New ID");
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.ID), "New ID");
+
+    u.setValue(UrlLinkDisplay.URL, "http://something.new/$SEQUENCE_ID$");
+    Assert.assertEquals(u.getValue(UrlLinkDisplay.URL),
+            "http://something.new/$SEQUENCE_ID$");
+  }
+}
diff --git a/test/jalview/urls/UrlLinkTableModelTest.java b/test/jalview/urls/UrlLinkTableModelTest.java
new file mode 100644 (file)
index 0000000..ab190ef
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.urls;
+
+import static jalview.util.UrlConstants.DELIM;
+import static jalview.util.UrlConstants.SEP;
+import static jalview.util.UrlConstants.SEQUENCE_ID;
+
+import jalview.urls.api.UrlProviderI;
+import jalview.util.MessageManager;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.event.TableModelListener;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class UrlLinkTableModelTest {
+
+  private static final String inmenu = "TEST|http://someurl.blah/$DB_ACCESSION$|"
+          + "ANOTHER|http://test/t$SEQUENCE_ID$|"
+          + "TEST2|http://address/$SEQUENCE_ID$|SRS|"
+          + "http://theSRSlink/$SEQUENCE_ID$|"
+          + "MIR:00000005|MIR:00000011|MIR:00000372";
+
+  private static final String notinmenu = "Not1|http://not.in.menu/$DB_ACCESSION$|"
+          + "Not2|http://not.in.menu.either/$DB_ACCESSION$";
+
+  private static final String testIdOrgString = "{\"Local\": [{\"id\":\"MIR:00000002\",\"name\":\"ChEBI\",\"pattern\":\"^CHEBI:\\d+$\","
+          + "\"definition\":\"Chemical Entities of Biological Interest (ChEBI)\",\"prefix\":\"chebi\","
+          + "\"url\":\"http://identifiers.org/chebi\"},{\"id\":\"MIR:00000005\",\"name\":\"UniProt Knowledgebase\","
+          + "\"pattern\":\"^([A-N,R-Z][0-9]([A-Z][A-Z, 0-9][A-Z, 0-9][0-9]){1,2})|([O,P,Q][0-9][A-Z, 0-9][A-Z, 0-9][A-Z, 0-9][0-9])(\\.\\d+)?$\","
+          + "\"definition\":\"The UniProt Knowledgebase (UniProtKB)\",\"prefix\":\"uniprot\",\"url\":\"http://identifiers.org/uniprot\"},"
+          + "{\"id\":\"MIR:00000011\",\"name\":\"InterPro\",\"pattern\":\"^IPR\\d{6}$\",\"definition\":\"InterPro\",\"prefix\":\"interpro\","
+          + "\"url\":\"http://identifiers.org/interpro\"},"
+          + "{\"id\":\"MIR:00000372\",\"name\":\"ENA\",\"pattern\":\"^[A-Z]+[0-9]+(\\.\\d+)?$\",\"definition\":\"The European Nucleotide Archive (ENA),\""
+          + "\"prefix\":\"ena.embl\",\"url\":\"http://identifiers.org/ena.embl\"}]}";
+
+  private UrlProviderI prov;
+
+  @BeforeMethod(alwaysRun = true)
+  public void setup()
+  {
+    // set up UrlProvider data as the source for the TableModel
+    // the data gets updated by the TableModel, so needs to be reinitialised for
+    // each test
+
+    // make a dummy identifiers.org download file
+    File temp = null;
+    try
+    {
+      temp = File.createTempFile("tempfile", ".tmp");
+      temp.deleteOnExit();
+      BufferedWriter bw = new BufferedWriter(new FileWriter(temp));
+      bw.write(testIdOrgString);
+      bw.close();
+    } catch (IOException e)
+    {
+      System.out.println("Error initialising UrlLinkTableModel test: "
+              + e.getMessage());
+    }
+
+    // set up custom and identifiers.org url providers
+    IdOrgSettings.setDownloadLocation(temp.getPath());
+    IdentifiersUrlProvider idprov = new IdentifiersUrlProvider(inmenu);
+    CustomUrlProvider cprov = new CustomUrlProvider(inmenu, notinmenu);
+    List<UrlProviderI> provlist = new ArrayList<UrlProviderI>();
+    provlist.add(idprov);
+    provlist.add(cprov);
+
+    prov = new UrlProvider("TEST2", provlist);
+  }
+
+  /*
+   * Test that the table model is correctly initialised
+   * Display columns and default row are set; data provider listening event set up
+   */
+  @Test(groups = { "Functional" })
+  public void testInitialisation()
+  {
+    int defaultCol = 4;
+    int dbCol = 0;
+    int descCol = 1;
+
+    UrlLinkTableModel m = new UrlLinkTableModel(prov);
+
+    // exactly one table model listener
+    TableModelListener[] listeners = m
+            .getListeners(TableModelListener.class);
+    Assert.assertEquals(listeners.length, 1);
+
+    // default row exists, there is exactly 1, and it matches the supplied
+    // default
+    int count = 0;
+    for (int row = 0; row < m.getRowCount(); row++)
+    {
+      boolean isDefault = (boolean) m.getValueAt(row, defaultCol);
+      if (isDefault)
+      {
+        count++;
+        String defaultDBName = (String) m.getValueAt(row, dbCol);
+        Assert.assertEquals(defaultDBName, "TEST2");
+
+        String defaultDesc = (String) m.getValueAt(row, descCol);
+        Assert.assertEquals(defaultDesc, "TEST2");
+      }
+    }
+    Assert.assertEquals(count, 1);
+  }
+
+  /*
+   * Test row and column counts
+   */
+  @Test(groups = { "Functional" })
+  public void testCounts()
+  {
+    UrlLinkTableModel m = new UrlLinkTableModel(prov);
+
+    // correct numbers of column and rows
+    Assert.assertEquals(m.getColumnCount(), 5);
+    Assert.assertEquals(m.getRowCount(), 10);
+  }
+
+  /*
+   * Test column access
+   */
+  @Test(groups = { "Functional" })
+  public void testColumns()
+  {
+    UrlLinkTableModel m = new UrlLinkTableModel(prov);
+
+    // check column names
+    Assert.assertEquals(m.getColumnName(0),
+            MessageManager.formatMessage("label.database"));
+    Assert.assertEquals(m.getColumnName(1),
+            MessageManager.formatMessage("label.name"));
+    Assert.assertEquals(m.getColumnName(2),
+            MessageManager.formatMessage("label.url"));
+    Assert.assertEquals(m.getColumnName(3),
+            MessageManager.formatMessage("label.inmenu"));
+    Assert.assertEquals(m.getColumnName(4),
+            MessageManager.formatMessage("label.primary"));
+
+    // check column classes
+    Assert.assertEquals(m.getColumnClass(0), String.class);
+    Assert.assertEquals(m.getColumnClass(1), String.class);
+    Assert.assertEquals(m.getColumnClass(2), String.class);
+    Assert.assertEquals(m.getColumnClass(3), Boolean.class);
+    Assert.assertEquals(m.getColumnClass(4), Boolean.class);
+  }
+
+  /*
+   * Test row insertion
+   */
+  @Test(groups = { "Functional" })
+  public void testRowInsert()
+  {
+    UrlLinkTableModel m = new UrlLinkTableModel(prov);
+
+    m.insertRow("newname", "newurl");
+
+    // check table has new row inserted
+    Assert.assertEquals(m.getValueAt(10, 0), "newname");
+    Assert.assertEquals(m.getValueAt(10, 1), "newname");
+    Assert.assertEquals(m.getValueAt(10, 2), "newurl");
+    Assert.assertEquals(m.getValueAt(10, 3), true);
+    Assert.assertEquals(m.getValueAt(10, 4), false);
+
+    // check data source has new row insrte
+    Assert.assertTrue(prov.getLinksForMenu().contains(
+            "newname" + SEP + "newurl"));
+  }
+
+  /*
+   * Test row deletion
+   */
+  @Test(groups = { "Functional" })
+  public void testRowDelete()
+  {
+    UrlLinkTableModel m = new UrlLinkTableModel(prov);
+
+    // get name and url at row 0
+    String name = (String) m.getValueAt(0, 0);
+    String url = (String) m.getValueAt(0, 1);
+
+    m.removeRow(0);
+
+    // check table no longer has row 0 elements in it
+    for (int row = 0; row < m.getRowCount(); row++)
+    {
+      Assert.assertNotEquals(m.getValueAt(row, 0), name);
+    }
+
+    // check data source likewise
+    Assert.assertFalse(prov.getLinksForMenu().contains(name + SEP + url));
+  }
+
+  /*
+   * Test value setting and getting
+   */
+  @Test(groups = { "Functional" })
+  public void testValues()
+  {
+    UrlLinkTableModel m = new UrlLinkTableModel(prov);
+
+    // get original default
+    int olddefault;
+    boolean isDefault = false;
+    for (olddefault = 0; olddefault < m.getRowCount() && !isDefault; olddefault++)
+    {
+      isDefault = (boolean) m.getValueAt(olddefault, 3);
+    }
+
+    // set new values, one in each row
+    m.setValueAt("dbnamechanged", 6, 0);
+    m.setValueAt("descchanged", 6, 1);
+    m.setValueAt("urlchanged", 7, 2);
+    m.setValueAt(false, 8, 3);
+    m.setValueAt(true, 6, 4);
+
+    m.setValueAt("dbnamechanged", 5, 0);
+
+    // check values updated in table
+    Assert.assertEquals(m.getValueAt(6, 0), "descchanged"); // custom url can't
+                                                            // change db name
+    Assert.assertEquals(m.getValueAt(6, 1), "descchanged");
+    Assert.assertEquals(m.getValueAt(7, 2), "urlchanged");
+    Assert.assertFalse((boolean) m.getValueAt(8, 3));
+    Assert.assertTrue((boolean) m.getValueAt(6, 4));
+    Assert.assertFalse((boolean) m.getValueAt(olddefault, 4));
+
+    Assert.assertEquals(m.getValueAt(5, 0), "dbnamechanged");
+
+    // check default row is exactly one row still
+    for (int row = 0; row < m.getRowCount(); row++)
+    {
+      isDefault = (boolean) m.getValueAt(row, 4);
+
+      // if isDefault is true, row is 9
+      // if isDefault is false, row is not 9
+      Assert.assertFalse(isDefault && !(row == 6));
+    }
+
+    // check table updated
+    Assert.assertTrue(prov.writeUrlsAsString(true).contains(
+            "descchanged" + SEP + m.getValueAt(6, 2)));
+    Assert.assertTrue(prov.writeUrlsAsString(true).contains(
+            m.getValueAt(7, 1) + SEP + "urlchanged"));
+    Assert.assertTrue(prov.writeUrlsAsString(false).contains(
+            (String) m.getValueAt(8, 1)));
+    Assert.assertEquals(prov.getPrimaryUrl("seqid"), m.getValueAt(6, 2)
+            .toString().replace(DELIM + SEQUENCE_ID + DELIM, "seqid"));
+  }
+
+  /*
+   * Test cell editability
+   */
+  @Test(groups = { "Functional" })
+  public void testEditable()
+  {
+    UrlLinkTableModel m = new UrlLinkTableModel(prov);
+
+    for (int row = 0; row < m.getRowCount(); row++)
+    {
+      Assert.assertFalse(m.isCellEditable(row, 0));
+      Assert.assertFalse(m.isCellEditable(row, 1));
+      Assert.assertFalse(m.isCellEditable(row, 2));
+      Assert.assertTrue(m.isCellEditable(row, 3));
+
+      if ((row == 4) || (row == 6) || (row == 7))
+      {
+        Assert.assertTrue(m.isCellEditable(row, 4));
+      }
+      else
+      {
+        Assert.assertFalse(m.isCellEditable(row, 4));
+      }
+    }
+  }
+
+  /*
+   * Test row 'deletability'
+   */
+  @Test(groups = { "Functional" })
+  public void testDeletable()
+  {
+    UrlLinkTableModel m = new UrlLinkTableModel(prov);
+
+    for (int row = 0; row < m.getRowCount(); row++)
+    {
+      if (row > 4)
+      {
+        Assert.assertTrue(m.isRowDeletable(row));
+      }
+      else
+      {
+        Assert.assertFalse(m.isRowDeletable(row));
+      }
+    }
+  }
+
+  /*
+   * Test indirect row editability
+   */
+  @Test(groups = { "Functional" })
+  public void testRowEditable()
+  {
+    UrlLinkTableModel m = new UrlLinkTableModel(prov);
+
+    for (int row = 0; row < m.getRowCount(); row++)
+    {
+      if (row > 3)
+      {
+        Assert.assertTrue(m.isRowEditable(row));
+      }
+      else
+      {
+        Assert.assertFalse(m.isRowEditable(row));
+      }
+    }
+  }
+}
diff --git a/test/jalview/urls/UrlProviderTest.java b/test/jalview/urls/UrlProviderTest.java
new file mode 100644 (file)
index 0000000..460ebe9
--- /dev/null
@@ -0,0 +1,120 @@
+package jalview.urls;
+
+import jalview.urls.api.UrlProviderI;
+import jalview.urls.desktop.DesktopUrlProviderFactory;
+import jalview.util.UrlConstants;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+
+public class UrlProviderTest {
+  
+  // Test identifiers.org download file
+  private static final String testIdOrgString = "{\"Local\": [{\"id\":\"MIR:00000002\",\"name\":\"ChEBI\",\"pattern\":\"^CHEBI:\\d+$\","
+         + "\"definition\":\"Chemical Entities of Biological Interest (ChEBI)\",\"prefix\":\"chebi\","
+         + "\"url\":\"http://identifiers.org/chebi\"},{\"id\":\"MIR:00000005\",\"name\":\"UniProt Knowledgebase\","
+         + "\"pattern\":\"^([A-N,R-Z][0-9]([A-Z][A-Z, 0-9][A-Z, 0-9][0-9]){1,2})|([O,P,Q][0-9][A-Z, 0-9][A-Z, 0-9][A-Z, 0-9][0-9])(\\.\\d+)?$\","
+         + "\"definition\":\"The UniProt Knowledgebase (UniProtKB)\",\"prefix\":\"uniprot\",\"url\":\"http://identifiers.org/uniprot\"},"
+         + "{\"id\":\"MIR:00000011\",\"name\":\"InterPro\",\"pattern\":\"^IPR\\d{6}$\",\"definition\":\"InterPro\",\"prefix\":\"interpro\","
+         + "\"url\":\"http://identifiers.org/interpro\"},"
+         + "{\"id\":\"MIR:00000372\",\"name\":\"ENA\",\"pattern\":\"^[A-Z]+[0-9]+(\\.\\d+)?$\",\"definition\":\"The European Nucleotide Archive (ENA),\""
+          + "\"prefix\":\"ena.embl\",\"url\":\"http://identifiers.org/ena.embl\"}]}";
+
+  private UrlProviderI prov;
+
+  @BeforeMethod(alwaysRun = true)
+  public void setup()
+  {
+   // make a dummy identifiers.org download file
+   File temp = null;
+
+   try
+   {
+     temp = File.createTempFile("tempfile", ".tmp");
+     temp.deleteOnExit();
+     BufferedWriter bw = new BufferedWriter(new FileWriter(temp));
+     bw.write(testIdOrgString);
+     bw.close();
+   } catch (IOException e)
+   {
+      System.out.println("Error initialising UrlProviderTest test: "
+              + e.getMessage());
+   }
+
+   IdOrgSettings.setDownloadLocation(temp.getPath());
+
+    String defaultUrlString = "No default";
+    String cachedUrlList = "MIR:00000005|MIR:00000011|Test1|http://blah.blah/$SEQUENCE_ID$|"
+            + "Test2|http://test2/$DB_ACCESSION$|Test3|http://test3/$SEQUENCE_ID$";
+    String userUrlList = "MIR:00000372|Test4|httpL//another.url/$SEQUENCE_ID$";
+
+    DesktopUrlProviderFactory factory = new DesktopUrlProviderFactory(
+            defaultUrlString, cachedUrlList, userUrlList);
+    prov = factory.createUrlProvider();
+ }
+
+  @Test(groups = { "Functional" })
+  public void testInitUrlProvider()
+  {
+    String emblUrl = UrlConstants.DEFAULT_STRING.substring(
+            UrlConstants.DEFAULT_STRING.indexOf(UrlConstants.SEP) + 1,
+            UrlConstants.DEFAULT_STRING.length());
+
+    // chooses EMBL url when default Url id does not exist in provided url lists
+    Assert.assertEquals(prov.getPrimaryUrlId(), UrlConstants.DEFAULT_LABEL);
+    Assert.assertEquals(prov.getPrimaryUrl("FER_CAPAN"),
+            emblUrl.replace("$SEQUENCE_ID$", "FER_CAPAN"));
+
+    List<String> menulinks = prov.getLinksForMenu();
+    List<UrlLinkDisplay> allLinks = prov.getLinksForTable();
+
+    // 9 links in provider - 4 from id file, 4 custom links, 1 additional
+    // default
+    Assert.assertEquals(allLinks.size(), 9);
+
+    // 6 links in menu (cachedUrlList) + new default
+    Assert.assertEquals(menulinks.size(), 6);
+
+    Assert.assertTrue(menulinks
+            .contains("Test1|http://blah.blah/$SEQUENCE_ID$"));
+    Assert.assertTrue(menulinks
+            .contains("Test2|http://test2/$DB_ACCESSION$"));
+    Assert.assertTrue(menulinks
+            .contains("Test3|http://test3/$SEQUENCE_ID$"));
+    Assert.assertTrue(menulinks
+            .contains("UniProt Knowledgebase|http://identifiers.org/uniprot/$DB_ACCESSION$|uniprot"));
+    Assert.assertTrue(menulinks
+            .contains("InterPro|http://identifiers.org/interpro/$DB_ACCESSION$|interpro"));
+    Assert.assertTrue(menulinks.contains(UrlConstants.DEFAULT_LABEL
+            + UrlConstants.SEP + emblUrl));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testSetDefaultUrl()
+  {
+    // set custom url as default
+    Assert.assertTrue(prov.setPrimaryUrl("Test1"));
+    Assert.assertEquals(prov.getPrimaryUrlId(), "Test1");
+
+    // set identifiers url as default
+    Assert.assertTrue(prov.setPrimaryUrl("MIR:00000011"));
+    Assert.assertEquals(prov.getPrimaryUrlId(), "MIR:00000011");
+  }
+
+  @Test(
+    groups = { "Functional" },
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testSetDefaultUrlWrongly()
+  {
+    // don't allow default to be a non-key
+    prov.setPrimaryUrl("not-a-key");
+  }
+}
index d07206f..4092cf2 100644 (file)
@@ -75,8 +75,8 @@ public class UrlLinkTest
             + DELIM + URL_SUFFIX);
     assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
-    assertEquals(URL_PREFIX, ul.getUrl_prefix());
-    assertEquals(URL_SUFFIX, ul.getUrl_suffix());
+    assertEquals(URL_PREFIX, ul.getUrlPrefix());
+    assertEquals(URL_SUFFIX, ul.getUrlSuffix());
     assertTrue(ul.isDynamic());
     assertFalse(ul.usesDBAccession());
     assertNull(ul.getRegexReplace());
@@ -88,8 +88,8 @@ public class UrlLinkTest
             + URL_SUFFIX);
     assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
-    assertEquals(URL_PREFIX, ul.getUrl_prefix());
-    assertEquals(URL_SUFFIX, ul.getUrl_suffix());
+    assertEquals(URL_PREFIX, ul.getUrlPrefix());
+    assertEquals(URL_SUFFIX, ul.getUrlSuffix());
     assertTrue(ul.isDynamic());
     assertTrue(ul.usesDBAccession());
     assertNull(ul.getRegexReplace());
@@ -100,7 +100,7 @@ public class UrlLinkTest
     ul = new UrlLink(DB + SEP + URL_PREFIX + URL_SUFFIX.substring(1));
     assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
-    assertEquals(URL_PREFIX + URL_SUFFIX.substring(1), ul.getUrl_prefix());
+    assertEquals(URL_PREFIX + URL_SUFFIX.substring(1), ul.getUrlPrefix());
     assertFalse(ul.isDynamic());
     assertFalse(ul.usesDBAccession());
     assertNull(ul.getRegexReplace());
@@ -119,8 +119,8 @@ public class UrlLinkTest
             + REGEX_NESTED + DELIM + URL_SUFFIX);
     assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
-    assertEquals(URL_PREFIX, ul.getUrl_prefix());
-    assertEquals(URL_SUFFIX, ul.getUrl_suffix());
+    assertEquals(URL_PREFIX, ul.getUrlPrefix());
+    assertEquals(URL_SUFFIX, ul.getUrlSuffix());
     assertTrue(ul.isDynamic());
     assertFalse(ul.usesDBAccession());
     assertEquals(REGEX_NESTED.substring(2, REGEX_NESTED.length() - 2),
@@ -133,8 +133,8 @@ public class UrlLinkTest
             + REGEX_NESTED + DELIM + URL_SUFFIX);
     assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
-    assertEquals(URL_PREFIX, ul.getUrl_prefix());
-    assertEquals(URL_SUFFIX, ul.getUrl_suffix());
+    assertEquals(URL_PREFIX, ul.getUrlPrefix());
+    assertEquals(URL_SUFFIX, ul.getUrlSuffix());
     assertTrue(ul.isDynamic());
     assertTrue(ul.usesDBAccession());
     assertEquals(REGEX_NESTED.substring(2, REGEX_NESTED.length() - 2),
@@ -147,8 +147,8 @@ public class UrlLinkTest
             + REGEX_RUBBISH + DELIM + URL_SUFFIX);
     assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
-    assertEquals(URL_PREFIX, ul.getUrl_prefix());
-    assertEquals(URL_SUFFIX, ul.getUrl_suffix());
+    assertEquals(URL_PREFIX, ul.getUrlPrefix());
+    assertEquals(URL_SUFFIX, ul.getUrlSuffix());
     assertTrue(ul.isDynamic());
     assertTrue(ul.usesDBAccession());
     assertEquals(REGEX_RUBBISH.substring(2, REGEX_RUBBISH.length() - 2),
@@ -277,10 +277,10 @@ public class UrlLinkTest
     String key = DB + SEP + URL_PREFIX;
     assertEquals(1, linkset.size());
     assertTrue(linkset.containsKey(key));
-    assertEquals(linkset.get(key).get(0), DB);
-    assertEquals(linkset.get(key).get(1), DB);
-    assertEquals(linkset.get(key).get(2), null);
-    assertEquals(linkset.get(key).get(3), URL_PREFIX);
+    assertEquals(DB, linkset.get(key).get(0));
+    assertEquals(DB, linkset.get(key).get(1));
+    assertEquals(null, linkset.get(key).get(2));
+    assertEquals(URL_PREFIX, linkset.get(key).get(3));
   }
 
   /**
@@ -297,10 +297,10 @@ public class UrlLinkTest
     String key = DB + SEP + URL_PREFIX + URL_SUFFIX;
     assertEquals(1, linkset.size());
     assertTrue(linkset.containsKey(key));
-    assertEquals(linkset.get(key).get(0), DB);
-    assertEquals(linkset.get(key).get(1), DB);
-    assertEquals(linkset.get(key).get(2), null);
-    assertEquals(linkset.get(key).get(3), URL_PREFIX + URL_SUFFIX);
+    assertEquals(DB, linkset.get(key).get(0));
+    assertEquals(DB, linkset.get(key).get(1));
+    assertEquals(null, linkset.get(key).get(2));
+    assertEquals(URL_PREFIX + URL_SUFFIX, linkset.get(key).get(3));
   }
 
   /**
@@ -348,11 +348,11 @@ public class UrlLinkTest
             + URL_SUFFIX;
     assertEquals(1, linkset.size());
     assertTrue(linkset.containsKey(key));
-    assertEquals(linkset.get(key).get(0), DB);
-    assertEquals(linkset.get(key).get(1), DB);
-    assertEquals(linkset.get(key).get(2), seq0.getName());
-    assertEquals(linkset.get(key).get(3), URL_PREFIX + seq0.getName()
-            + URL_SUFFIX);
+    assertEquals(DB, linkset.get(key).get(0));
+    assertEquals(DB, linkset.get(key).get(1));
+    assertEquals(seq0.getName(), linkset.get(key).get(2));
+    assertEquals(URL_PREFIX + seq0.getName() + URL_SUFFIX, linkset.get(key)
+            .get(3));
 
     // Test where link takes a db annotation id and only has one dbref
     ul = new UrlLink(links.get(1));
@@ -360,14 +360,14 @@ public class UrlLinkTest
     ul.createLinksFromSeq(seq0, linkset);
 
     key = "P83527|http://www.uniprot.org/uniprot/P83527";
-    assertEquals(1, linkset.size());
+    assertEquals(linkset.size(), 1);
     assertTrue(linkset.containsKey(key));
-    assertEquals(linkset.get(key).get(0), DBRefSource.UNIPROT);
-    assertEquals(linkset.get(key).get(1), DBRefSource.UNIPROT + SEP
-            + "P83527");
-    assertEquals(linkset.get(key).get(2), "P83527");
-    assertEquals(linkset.get(key).get(3),
-            "http://www.uniprot.org/uniprot/P83527");
+    assertEquals(DBRefSource.UNIPROT, linkset.get(key).get(0));
+    assertEquals(DBRefSource.UNIPROT + SEP + "P83527", linkset.get(key)
+            .get(1));
+    assertEquals("P83527", linkset.get(key).get(2));
+    assertEquals("http://www.uniprot.org/uniprot/P83527", linkset.get(key)
+            .get(3));
 
     // Test where link takes a db annotation id and has multiple dbrefs
     ul = new UrlLink(links.get(2));
@@ -378,27 +378,27 @@ public class UrlLinkTest
     // check each link made it in correctly
     key = "IPR001041|http://www.ebi.ac.uk/interpro/entry/IPR001041";
     assertTrue(linkset.containsKey(key));
-    assertEquals(linkset.get(key).get(0), "INTERPRO");
-    assertEquals(linkset.get(key).get(1), "INTERPRO" + SEP + "IPR001041");
-    assertEquals(linkset.get(key).get(2), "IPR001041");
-    assertEquals(linkset.get(key).get(3),
-            "http://www.ebi.ac.uk/interpro/entry/IPR001041");
+    assertEquals("INTERPRO", linkset.get(key).get(0));
+    assertEquals("INTERPRO" + SEP + "IPR001041", linkset.get(key).get(1));
+    assertEquals("IPR001041", linkset.get(key).get(2));
+    assertEquals("http://www.ebi.ac.uk/interpro/entry/IPR001041", linkset
+            .get(key).get(3));
 
     key = "IPR006058|http://www.ebi.ac.uk/interpro/entry/IPR006058";
     assertTrue(linkset.containsKey(key));
-    assertEquals(linkset.get(key).get(0), "INTERPRO");
-    assertEquals(linkset.get(key).get(1), "INTERPRO" + SEP + "IPR006058");
-    assertEquals(linkset.get(key).get(2), "IPR006058");
-    assertEquals(linkset.get(key).get(3),
-            "http://www.ebi.ac.uk/interpro/entry/IPR006058");
+    assertEquals("INTERPRO", linkset.get(key).get(0));
+    assertEquals("INTERPRO" + SEP + "IPR006058", linkset.get(key).get(1));
+    assertEquals("IPR006058", linkset.get(key).get(2));
+    assertEquals("http://www.ebi.ac.uk/interpro/entry/IPR006058", linkset
+            .get(key).get(3));
 
     key = "IPR012675|http://www.ebi.ac.uk/interpro/entry/IPR012675";
     assertTrue(linkset.containsKey(key));
-    assertEquals(linkset.get(key).get(0), "INTERPRO");
-    assertEquals(linkset.get(key).get(1), "INTERPRO" + SEP + "IPR012675");
-    assertEquals(linkset.get(key).get(2), "IPR012675");
-    assertEquals(linkset.get(key).get(3),
-            "http://www.ebi.ac.uk/interpro/entry/IPR012675");
+    assertEquals("INTERPRO", linkset.get(key).get(0));
+    assertEquals("INTERPRO" + SEP + "IPR012675", linkset.get(key).get(1));
+    assertEquals("IPR012675", linkset.get(key).get(2));
+    assertEquals("http://www.ebi.ac.uk/interpro/entry/IPR012675", linkset
+            .get(key).get(3));
 
     // Test where there are no matching dbrefs for the link
     ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + DB_ACCESSION + DELIM
@@ -408,4 +408,35 @@ public class UrlLinkTest
     assertTrue(linkset.isEmpty());
   }
 
+  /**
+   * Test links where label and target are both included
+   */
+  @Test(groups = { "Functional" })
+  public void testLinksWithTargets()
+  {
+    UrlLink ul = new UrlLink(
+            "Protein Data Bank | http://www.identifiers.org/pdb/$"
+                    + DB_ACCESSION + "$" + " | pdb");
+
+    assertEquals("Protein Data Bank", ul.getLabel());
+    assertEquals("pdb", ul.getTarget());
+    assertEquals("http://www.identifiers.org/pdb/$" + DB_ACCESSION + "$",
+            ul.getUrlWithToken());
+
+    assertEquals("Protein Data Bank|http://www.identifiers.org/pdb/$"
+            + DB_ACCESSION + "$" + "|pdb", ul.toStringWithTarget());
+
+    ul = new UrlLink("Protein Data Bank",
+            "http://www.identifiers.org/pdb/$" + DB_ACCESSION + "$", "pdb");
+
+    assertEquals("Protein Data Bank", ul.getLabel());
+    assertEquals("pdb", ul.getTarget());
+    assertEquals("http://www.identifiers.org/pdb/$" + DB_ACCESSION + "$",
+            ul.getUrlWithToken());
+
+    assertEquals("Protein Data Bank|http://www.identifiers.org/pdb/$"
+            + DB_ACCESSION + "$" + "|pdb", ul.toStringWithTarget());
+
+  }
+
 }
diff --git a/test/jalview/viewmodel/OverviewDimensionsTest.java b/test/jalview/viewmodel/OverviewDimensionsTest.java
new file mode 100644 (file)
index 0000000..398fec3
--- /dev/null
@@ -0,0 +1,1037 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.viewmodel;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.analysis.AlignmentGenerator;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceCollectionI;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+
+import java.util.Hashtable;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+@Test(singleThreaded = true)
+public class OverviewDimensionsTest
+{
+  AlignmentI al;
+  OverviewDimensions od;
+
+  // cached widths and heights
+  int boxWidth;
+  int boxHeight;
+  int viewHeight;
+  int viewWidth;
+  int alheight;
+  int alwidth;
+
+  ViewportRanges vpranges;
+
+  Hashtable<SequenceI, SequenceCollectionI> hiddenRepSequences = new Hashtable<SequenceI, SequenceCollectionI>();
+
+  ColumnSelection hiddenCols = new ColumnSelection();
+
+  @BeforeClass(alwaysRun = true)
+  public void setUpJvOptionPane()
+  {
+    // create random alignment
+    AlignmentGenerator gen = new AlignmentGenerator(false);
+    al = gen.generate(157, 525, 123, 5, 5);
+  }
+
+  @BeforeMethod(alwaysRun = true)
+  public void setUp()
+  {
+    if (!hiddenRepSequences.isEmpty())
+    {
+      al.getHiddenSequences().showAll(hiddenRepSequences);
+    }
+    hiddenCols.revealAllHiddenColumns();
+    
+    vpranges = new ViewportRanges(al);
+    vpranges.setStartRes(0);
+    vpranges.setEndRes(62);
+    vpranges.setStartSeq(0);
+    vpranges.setEndSeq(17);
+
+    viewHeight = vpranges.getEndSeq() - vpranges.getStartSeq() + 1;
+    viewWidth = vpranges.getEndRes() - vpranges.getStartRes() + 1;
+
+    ColumnSelection hiddenCols = new ColumnSelection();
+
+    od = new OverviewDimensions(vpranges, true);
+    // Initial box sizing - default path through code
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+
+    mouseClick(od, 0, 0);
+    moveViewport(0, 0);
+
+    // calculate before hidden columns so we get absolute values
+    alheight = vpranges.getAbsoluteAlignmentHeight();
+    alwidth = vpranges.getAbsoluteAlignmentWidth();
+
+    boxWidth = Math.round((float) (vpranges.getEndRes()
+            - vpranges.getStartRes() + 1)
+            * od.getWidth() / alwidth);
+    boxHeight = Math.round((float) (vpranges.getEndSeq()
+            - vpranges.getStartSeq() + 1)
+            * od.getSequencesHeight() / alheight);
+  }
+
+  @AfterClass(alwaysRun = true)
+  public void cleanUp()
+  {
+    al = null;
+  }
+
+  /**
+   * Test that the OverviewDimensions constructor sets width and height
+   * correctly
+   */
+  @Test(groups = { "Functional" })
+  public void testConstructor()
+  {
+    SequenceI seqa = new Sequence("Seq1", "ABC");
+    SequenceI seqb = new Sequence("Seq2", "ABC");
+    SequenceI seqc = new Sequence("Seq3", "ABC");
+    SequenceI seqd = new Sequence("Seq4", "ABC");
+    SequenceI seqe = new Sequence("Seq5",
+            "ABCABCABCABCABCABCABCABCBACBACBACBAC");
+
+    int defaultGraphHeight = 20;
+    int maxWidth = 400;
+    int minWidth = 120;
+    int maxSeqHeight = 300;
+    int minSeqHeight = 40;
+
+    // test for alignment with width > height
+    SequenceI[] seqs1 = new SequenceI[] { seqa, seqb };
+    Alignment al1 = new Alignment(seqs1);
+    ViewportRanges props = new ViewportRanges(al1);
+
+    OverviewDimensions od = new OverviewDimensions(props, true);
+    int scaledHeight = 267;
+    assertEquals(od.getGraphHeight(), defaultGraphHeight);
+    assertEquals(od.getSequencesHeight(), scaledHeight);
+    assertEquals(od.getWidth(), maxWidth);
+    assertEquals(od.getHeight(), scaledHeight + defaultGraphHeight);
+
+    // test for alignment with width < height
+    SequenceI[] seqs2 = new SequenceI[] { seqa, seqb, seqc, seqd };
+    Alignment al2 = new Alignment(seqs2);
+    props = new ViewportRanges(al2);
+
+    od = new OverviewDimensions(props, true);
+    int scaledWidth = 300;
+    assertEquals(od.getGraphHeight(), defaultGraphHeight);
+    assertEquals(od.getSequencesHeight(), maxSeqHeight);
+    assertEquals(od.getWidth(), scaledWidth);
+    assertEquals(od.getHeight(), scaledWidth + defaultGraphHeight);
+
+    // test for alignment with width > height and sequence height scaled below
+    // min value
+    SequenceI[] seqs3 = new SequenceI[] { seqe };
+    Alignment al3 = new Alignment(seqs3);
+    props = new ViewportRanges(al3);
+
+    od = new OverviewDimensions(props, true);
+    assertEquals(od.getGraphHeight(), defaultGraphHeight);
+    assertEquals(od.getSequencesHeight(), minSeqHeight);
+    assertEquals(od.getWidth(), maxWidth);
+    assertEquals(od.getHeight(), minSeqHeight + defaultGraphHeight);
+
+    // test for alignment with width < height and width scaled below min value
+    SequenceI[] seqs4 = new SequenceI[] { seqa, seqb, seqc, seqd, seqa,
+        seqb, seqc, seqd, seqa, seqb, seqc, seqd, seqa, seqb, seqc, seqd };
+    Alignment al4 = new Alignment(seqs4);
+    props = new ViewportRanges(al4);
+
+    od = new OverviewDimensions(props, true);
+    assertEquals(od.getGraphHeight(), defaultGraphHeight);
+    assertEquals(od.getSequencesHeight(), maxSeqHeight);
+    assertEquals(od.getWidth(), minWidth);
+    assertEquals(od.getHeight(), maxSeqHeight + defaultGraphHeight);
+
+    Alignment al5 = new Alignment(seqs4);
+    props = new ViewportRanges(al5);
+
+    od = new OverviewDimensions(props, false);
+    assertEquals(od.getGraphHeight(), 0);
+    assertEquals(od.getSequencesHeight(), maxSeqHeight);
+    assertEquals(od.getWidth(), minWidth);
+    assertEquals(od.getHeight(), maxSeqHeight);
+  }
+
+  /**
+   * Test that validation after mouse adjustments to boxX and boxY sets box
+   * dimensions and scroll values correctly, when there are no hidden rows or
+   * columns.
+   */
+  @Test(groups = { "Functional" })
+  public void testSetBoxFromMouseClick()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols,
+            vpranges);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getScrollCol(), 0);
+    assertEquals(od.getScrollRow(), 0);
+
+    // negative boxX value reset to 0
+    mouseClick(od, -5, 10);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollRow(),
+            Math.round((float) 10 * alheight / od.getSequencesHeight()));
+    assertEquals(od.getScrollCol(), 0);
+
+    // negative boxY value reset to 0
+    mouseClick(od, 6, -2);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) 6 * alwidth / od.getWidth()));
+    assertEquals(od.getScrollRow(), 0);
+
+    // overly large boxX value reset to width-boxWidth
+    mouseClick(od, 100, 6);
+    assertEquals(od.getBoxX(), od.getWidth() - od.getBoxWidth());
+    assertEquals(od.getBoxY(), 6);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
+    assertEquals(od.getScrollRow(),
+            Math.round((float) od.getBoxY() * alheight
+                    / od.getSequencesHeight()));
+
+    // overly large boxY value reset to sequenceHeight - boxHeight
+    mouseClick(od, 10, 520);
+    assertEquals(od.getBoxX(), 10);
+    assertEquals(od.getBoxY(), od.getSequencesHeight() - od.getBoxHeight());
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
+
+    // here (float) od.getBoxY() * alheight / od.getSequencesHeight() = 507.5
+    // and round rounds to 508; however we get 507 working with row values
+    // hence the subtraction of 1
+    assertEquals(od.getScrollRow(),
+            Math.round((float) od.getBoxY() * alheight
+                    / od.getSequencesHeight()) - 1);
+
+    // click past end of alignment, as above
+    mouseClick(od, 3000, 5);
+    assertEquals(od.getBoxX(), od.getWidth() - od.getBoxWidth());
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
+    assertEquals(od.getScrollRow(),
+            Math.round((float) od.getBoxY() * alheight
+                    / od.getSequencesHeight()));
+
+    // move viewport so startRes non-zero and then mouseclick
+    moveViewportH(50);
+
+    // click at viewport position
+    int oldboxx = od.getBoxX();
+    int oldboxy = od.getBoxY();
+    mouseClick(od, od.getBoxX() + 5, od.getBoxY() + 2);
+    assertEquals(od.getBoxX(), oldboxx + 5);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
+    assertEquals(od.getBoxY(), oldboxy + 2);
+    assertEquals(od.getScrollRow(),
+            Math.round((float) od.getBoxY() * alheight
+                    / od.getSequencesHeight()));
+
+    // click at top corner
+    mouseClick(od, 0, 0);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getScrollCol(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getScrollRow(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+  }
+
+  /**
+   * Test setting of the box position, when there are hidden cols at the start
+   * of the alignment
+   */
+  @Test(groups = { "Functional" })
+  public void testFromMouseWithHiddenColsAtStart()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols,
+            vpranges);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getScrollCol(), 0);
+    assertEquals(od.getScrollRow(), 0);
+
+    // hide cols at start and check updated box position is correct
+    // changes boxX but not boxwidth
+    int lastHiddenCol = 30;
+    hiddenCols.hideColumns(0, lastHiddenCol);
+
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+    assertEquals(od.getBoxX(),
+            Math.round((float) (lastHiddenCol + 1) * od.getWidth()
+                    / alwidth));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // try to click in hidden cols, check box does not move
+    int xpos = 10;
+    mouseClick(od, xpos, 0);
+    assertEquals(
+            od.getBoxX(),
+            Math.round((float) (lastHiddenCol + 1) * od.getWidth()
+                    / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollRow(), 0);
+    assertEquals(od.getScrollCol(), 0);
+
+    // click to right of hidden columns, box moves to click point
+    testBoxIsAtClickPoint(40, 0);
+    assertEquals(od.getScrollRow(), 0);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) 40 * alwidth / od.getWidth())
+                    - (lastHiddenCol + 1));
+
+    // click to right of hidden columns such that box runs over right hand side
+    // of alignment
+    // box position is adjusted away from the edge
+    // overly large boxX value reset to width-boxWidth
+    xpos = 100;
+    mouseClick(od, xpos, 5);
+    assertEquals(od.getBoxX(), od.getWidth() - od.getBoxWidth());
+    assertEquals(od.getBoxY(), 5);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) od.getBoxX() * alwidth / od.getWidth())
+                    - (lastHiddenCol + 1));
+    assertEquals(od.getScrollRow(),
+            Math.round((float) od.getBoxY() * alheight
+                    / od.getSequencesHeight()));
+  }
+
+  /**
+   * Test setting of the box position, when there are hidden cols in the middle
+   * of the alignment
+   */
+  @Test(groups = { "Functional" })
+  public void testFromMouseWithHiddenColsInMiddle()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols,
+            vpranges);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getScrollCol(), 0);
+    assertEquals(od.getScrollRow(), 0);
+    
+    // hide columns 63-73, no change to box position or dimensions
+    int firstHidden = 63;
+    int lastHidden = 73;
+    hiddenCols.hideColumns(firstHidden, lastHidden);
+
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getScrollCol(), 0);
+    assertEquals(od.getScrollRow(), 0);
+
+    // move box so that it overlaps with hidden cols on one side
+    // box width changes, boxX and scrollCol as for unhidden case
+    int xpos = 55 - boxWidth; // 55 is position in overview approx halfway
+                              // between cols 60 and 70
+    mouseClick(od, xpos, 0);
+    assertEquals(od.getBoxX(), xpos);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(
+            od.getBoxWidth(),
+            Math.round(boxWidth + (float) (lastHidden - firstHidden + 1)
+                    * od.getWidth() / alwidth));
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round(xpos * alwidth / od.getWidth()));
+    assertEquals(od.getScrollRow(), 0);
+
+    // move box so that it completely covers hidden cols
+    // box width changes, boxX and scrollCol as for hidden case
+    xpos = 33;
+    mouseClick(od, xpos, 0);
+    assertEquals(od.getBoxX(), xpos);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(
+            od.getBoxWidth(),
+            Math.round(boxWidth + (float) (lastHidden - firstHidden + 1)
+                    * od.getWidth() / alwidth));
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) xpos * alwidth / od.getWidth()));
+    assertEquals(od.getScrollRow(), 0);
+
+    // move box so boxX is in hidden cols, box overhangs at right
+    // boxX and scrollCol at left of hidden area, box width extends across
+    // hidden region
+    xpos = 50;
+    mouseClick(od, xpos, 0);
+    assertEquals(od.getBoxX(),
+            Math.round((float) (firstHidden - 1) * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(
+            od.getBoxWidth(),
+            boxWidth
+                    + Math.round((float) (lastHidden - firstHidden + 1)
+                            * od.getWidth() / alwidth));
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(), firstHidden - 1);
+    assertEquals(od.getScrollRow(), 0);
+
+    // move box so boxX is to right of hidden cols, but does not go beyond full
+    // width of alignment
+    // box width, boxX and scrollCol all as for non-hidden case
+    xpos = 75;
+    testBoxIsAtClickPoint(xpos, 0);
+    assertEquals(od.getScrollRow(), 0);
+    assertEquals(od.getScrollCol(),
+            Math.round(xpos * alwidth / od.getWidth())
+                    - (lastHidden - firstHidden + 1));
+    
+    // move box so it goes beyond full width of alignment
+    // boxX, scrollCol adjusted back, box width normal
+    xpos = 3000;
+    mouseClick(od, xpos, 5);
+    assertEquals(od.getBoxX(), od.getWidth() - od.getBoxWidth());
+    assertEquals(od.getBoxY(), 5);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round(((float) od.getBoxX() * alwidth / od.getWidth())
+                    - (lastHidden - firstHidden + 1)));
+    assertEquals(od.getScrollRow(),
+            Math.round((float) od.getBoxY() * alheight
+                    / od.getSequencesHeight()));
+
+  }
+
+  /**
+   * Test setting of the box position, when there are hidden cols at the end of
+   * the alignment
+   */
+  @Test(groups = { "Functional" })
+  public void testFromMouseWithHiddenColsAtEnd()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols,
+            vpranges);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getScrollCol(), 0);
+    assertEquals(od.getScrollRow(), 0);
+
+    // hide columns 140-164, no change to box position or dimensions
+    int firstHidden = 140;
+    int lastHidden = 164;
+    hiddenCols.hideColumns(firstHidden, lastHidden);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getScrollCol(), 0);
+    assertEquals(od.getScrollRow(), 0);
+
+    // click to left of hidden cols, without overlapping
+    // boxX, scrollCol and width as normal
+    int xpos = 5;
+    testBoxIsAtClickPoint(xpos, 0);
+    assertEquals(od.getScrollRow(), 0);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) xpos * alwidth / od.getWidth()));
+
+    // click to left of hidden cols, with overlap
+    // boxX and scrollCol adjusted for hidden cols, width normal
+    xpos = Math.round((float) 145 * od.getWidth() / alwidth) - boxWidth;
+    mouseClick(od, xpos, 0);
+    assertEquals(od.getBoxX(),
+            Math.round((float) (firstHidden - 1) * od.getWidth() / alwidth)
+                    - boxWidth + 1);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
+    assertEquals(od.getScrollRow(), 0);
+
+    // click in hidden cols
+    // boxX and scrollCol adjusted for hidden cols, width normal
+    xpos = 115;
+    assertEquals(od.getBoxX(),
+            Math.round((float) (firstHidden - 1) * od.getWidth() / alwidth)
+                    - boxWidth + 1);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
+    assertEquals(od.getScrollRow(), 0);
+
+    // click off end of alignment
+    // boxX and scrollCol adjusted for hidden cols, width normal
+    xpos = 3000;
+    assertEquals(od.getBoxX(),
+            Math.round((float) (firstHidden - 1) * od.getWidth() / alwidth)
+                    - boxWidth + 1);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(),
+            Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
+    assertEquals(od.getScrollRow(), 0);
+  }
+
+  /**
+   * Test that the box position is set correctly when set from the viewport,
+   * with no hidden rows or columns
+   */
+  @Test(groups = { "Functional" })
+  public void testSetBoxFromViewport()
+  {
+    // move viewport to start of alignment
+    moveViewport(0, 0);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport to right
+    moveViewportH(70);
+    assertEquals(od.getBoxX(),
+            Math.round((float) 70 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport down
+    moveViewportV(100);
+    assertEquals(od.getBoxX(),
+            Math.round((float) 70 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(),
+            Math.round(100 * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport to bottom right
+    moveViewport(98, 508);
+    assertEquals(od.getBoxX(),
+            Math.round((float) 98 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(),
+            Math.round((float) 508 * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+  }
+
+  /**
+   * Test that the box position is set correctly when there are hidden columns
+   * at the start
+   */
+  @Test(groups = { "Functional" })
+  public void testSetBoxFromViewportHiddenColsAtStart()
+  {
+    int firstHidden = 0;
+    int lastHidden = 20;
+    hiddenCols.hideColumns(firstHidden, lastHidden);
+
+    // move viewport to start of alignment
+    moveViewport(0, 0);
+    assertEquals(od.getBoxX(),
+            Math.round((float) (lastHidden + 1) * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport to end of alignment - need to make startRes by removing
+    // hidden cols because of how viewport/overview are implemented
+    moveViewport(98 - lastHidden - 1, 0);
+    assertEquals(od.getBoxX(),
+            Math.round((float) 98 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+  }
+
+  /**
+   * Test that the box position is set correctly when there are hidden columns
+   * in the middle
+   */
+  @Test(groups = { "Functional" })
+  public void testSetBoxFromViewportHiddenColsInMiddle()
+  {
+    int firstHidden = 68;
+    int lastHidden = 78;
+    hiddenCols.hideColumns(firstHidden, lastHidden);
+
+    // move viewport before hidden columns
+    moveViewport(3, 0);
+
+    assertEquals(od.getBoxX(),
+            Math.round((float) 3 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    System.out.println(od.getBoxWidth());
+    assertEquals(od.getBoxWidth(), boxWidth);
+    System.out.println(od.getBoxWidth());
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport to left of hidden columns with overlap
+    moveViewport(10, 0);
+    assertEquals(od.getBoxX(),
+            Math.round((float) 10 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(
+            od.getBoxWidth(),
+            boxWidth
+                    + Math.round((float) (lastHidden - firstHidden + 1)
+                            * od.getWidth() / alwidth));
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport to straddle hidden columns
+    moveViewport(63, 0);
+    assertEquals(od.getBoxX(),
+            Math.round((float) 63 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(
+            od.getBoxWidth(),
+            boxWidth
+                    + Math.round((lastHidden - firstHidden + 1)
+                            * od.getWidth() / alwidth));
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport to right of hidden columns, no overlap
+    moveViewport(80 - (lastHidden - firstHidden + 1), 0);
+    assertEquals(od.getBoxX(),
+            Math.round((float) 80 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+  }
+
+  /**
+   * Test that the box position is set correctly when there are hidden columns
+   * at the end
+   */
+  @Test(groups = { "Functional" })
+  public void testSetBoxFromViewportHiddenColsAtEnd()
+  {
+    int firstHidden = 152;
+    int lastHidden = 164;
+    hiddenCols.hideColumns(firstHidden, lastHidden);
+
+    // move viewport before hidden columns
+    moveViewport(3, 0);
+    assertEquals(od.getBoxX(),
+            Math.round((float) 3 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport to hidden columns
+    // viewport can't actually extend into hidden cols,
+    // so move to the far right edge of the viewport
+    moveViewport(firstHidden - viewWidth, 0);
+    assertEquals(od.getBoxX(),
+            Math.round((float) (firstHidden - viewWidth)
+                    * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+  }
+
+  /**
+   * Test that the box position is set correctly when there are hidden rows at
+   * the start
+   */
+  @Test(groups = { "Functional" })
+  public void testSetBoxFromViewportHiddenRowsAtStart()
+  {
+    int firstHidden = 0;
+    int lastHidden = 20;
+    hideSequences(firstHidden, lastHidden);
+
+    // move viewport to start of alignment:
+    // box moves to below hidden rows, height remains same
+    moveViewport(0, 0);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(),
+            Math.round((float) (lastHidden + 1) * od.getSequencesHeight()
+                    / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport to end of alignment
+    moveViewport(0, 525 - viewHeight - lastHidden - 1);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(
+            od.getBoxY(),
+            Math.round((float) (525 - viewHeight) * od.getSequencesHeight()
+                    / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+  }
+
+  /**
+   * Test that the box position is set correctly when there are hidden rows in
+   * the middle
+   */
+  @Test(groups = { "Functional" })
+  public void testSetBoxFromViewportHiddenRowsInMiddle()
+  {
+    int firstHidden = 200;
+    int lastHidden = 210;
+    hideSequences(firstHidden, lastHidden);
+
+    // move viewport to start of alignment:
+    // box, height etc as in non-hidden case
+    moveViewport(0, 0);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport to straddle hidden rows
+    moveViewport(0, 198);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), Math.round ((float)198 * od.getSequencesHeight()
+            / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(
+            od.getBoxHeight(),
+            Math.round((float) (viewHeight + lastHidden - firstHidden + 1)
+                    * od.getSequencesHeight() / alheight));
+  }
+
+  /**
+   * Test that the box position is set correctly when there are hidden rows at
+   * the bottom
+   */
+  @Test(groups = { "Functional" })
+  public void testSetBoxFromViewportHiddenRowsAtEnd()
+  {
+    int firstHidden = 500;
+    int lastHidden = 524;
+    hideSequences(firstHidden, lastHidden);
+
+    // move viewport to start of alignment:
+    // box, height etc as in non-hidden case
+    moveViewport(0, 0);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // move viewport to end of alignment
+    // viewport sits above hidden rows and does not include them
+    moveViewport(0, firstHidden - viewHeight - 1);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(
+            od.getBoxY(),
+            Math.round((float) (firstHidden - viewHeight - 1)
+                    * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+  }
+
+  /**
+   * Test setting of the box position, when there are hidden rows at the start
+   * of the alignment
+   */
+  @Test(groups = { "Functional" })
+  public void testFromMouseWithHiddenRowsAtStart()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols,
+            vpranges);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getScrollCol(), 0);
+    assertEquals(od.getScrollRow(), 0);
+
+    // hide rows at start and check updated box position is correct
+    // changes boxY but not boxheight
+    int lastHiddenRow = 30;
+    hideSequences(0, lastHiddenRow);
+
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(),
+            Math.round((float) (lastHiddenRow + 1)
+                    * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // click in hidden rows - same result
+    mouseClick(od, 0, 0);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(
+            od.getBoxY(),
+            Math.round((float) (lastHiddenRow + 1)
+                    * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // click below hidden rows
+    mouseClick(od, 0, 150);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 150);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+  }
+
+  /**
+   * Test setting of the box position, when there are hidden rows at the middle
+   * of the alignment
+   */
+  @Test(groups = { "Functional" })
+  public void testFromMouseWithHiddenRowsInMiddle()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols,
+            vpranges);
+
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(), 0);
+    assertEquals(od.getScrollRow(), 0);
+
+    // hide rows in middle and check updated box position is correct
+    // no changes
+    int firstHiddenRow = 50;
+    int lastHiddenRow = 54;
+    hideSequences(firstHiddenRow, lastHiddenRow);
+
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // click above hidden rows, so that box overlaps
+    int ypos = 35; // column value in residues
+    mouseClick(od, 0,
+            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(),
+            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(
+            od.getBoxHeight(),
+            boxHeight
+                    + Math.round((float) (lastHiddenRow - firstHiddenRow + 1)
+                            * od.getSequencesHeight() / alheight));
+
+    // click so that box straddles hidden rows
+    ypos = 44; // column value in residues
+    mouseClick(od, 0,
+            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(),
+            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(
+            od.getBoxHeight(),
+            boxHeight
+                    + Math.round((float) (lastHiddenRow - firstHiddenRow + 1)
+                            * od.getSequencesHeight() / alheight));
+  }
+
+  /**
+   * Test setting of the box position, when there are hidden rows at the end of
+   * the alignment
+   */
+  @Test(groups = { "Functional" })
+  public void testFromMouseWithHiddenRowsAtEnd()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols,
+            vpranges);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(od.getScrollCol(), 0);
+    assertEquals(od.getScrollRow(), 0);
+
+    // hide rows at end and check updated box position is correct
+    // no changes
+    int firstHidden = 500;
+    int lastHidden = 524;
+    hideSequences(firstHidden, lastHidden);
+
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // click above hidden rows
+    int ypos = 40; // row 40
+    mouseClick(od, 0,
+            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(od.getBoxY(),
+            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // click above hidden rows so box overlaps
+    // boxY moved upwards, boxHeight remains same
+    ypos = 497; // row 497
+    mouseClick(od, 0,
+            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(
+            od.getBoxY(),
+            Math.round((float) (firstHidden - viewHeight)
+                    * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+    // click within hidden rows
+    ypos = 505;
+    mouseClick(od, 0,
+            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+    assertEquals(od.getBoxX(), 0);
+    assertEquals(
+            od.getBoxY(),
+            Math.round((firstHidden - viewHeight) * od.getSequencesHeight()
+                    / alheight));
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+  }
+
+  /*
+   * Move viewport horizontally: startRes + previous width gives new horizontal extent. Vertical extent stays the same.
+   */
+  private void moveViewportH(int startRes)
+  {
+    vpranges.setStartRes(startRes);
+    vpranges.setEndRes(startRes + viewWidth - 1);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+  }
+
+  /*
+   * Move viewport vertically: startSeq and endSeq give new vertical extent. Horizontal extent stays the same.
+   */
+  private void moveViewportV(int startSeq)
+  {
+    vpranges.setStartSeq(startSeq);
+    vpranges.setEndSeq(startSeq + viewHeight - 1);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+  }
+
+  /*
+   * Move viewport horizontally and vertically.
+   */
+  private void moveViewport(int startRes, int startSeq)
+  {
+    vpranges.setStartRes(startRes);
+    vpranges.setEndRes(startRes + viewWidth - 1);
+    vpranges.setStartSeq(startSeq);
+    vpranges.setEndSeq(startSeq + viewHeight - 1);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+  }
+
+  /*
+   * Mouse click as position x,y in overview window
+   */
+  private void mouseClick(OverviewDimensions od, int x, int y)
+  {
+    od.updateViewportFromMouse(x, y, al.getHiddenSequences(), hiddenCols,
+            vpranges);
+
+    // updates require an OverviewPanel to exist which it doesn't here
+    // so call setBoxPosition() as it would be called by the AlignmentPanel
+    // normally
+
+    vpranges.setStartRes(od.getScrollCol());
+    vpranges.setEndRes(od.getScrollCol() + viewWidth - 1);
+    vpranges.setStartSeq(od.getScrollRow());
+    vpranges.setEndSeq(od.getScrollRow() + viewHeight - 1);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges);
+  }
+  
+  /*
+   * Test that the box is positioned with the top left corner at xpos, ypos
+   * and with the original width and height
+   */
+  private void testBoxIsAtClickPoint(int xpos, int ypos)
+  {
+    mouseClick(od, xpos, ypos);
+    assertEquals(od.getBoxX(), xpos);
+    assertEquals(od.getBoxY(), ypos);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+
+  }
+
+  /*
+   * Hide sequences between start and end
+   */
+  private void hideSequences(int start, int end)
+  {
+    SequenceI[] allseqs = al.getSequencesArray();
+    SequenceGroup theseSeqs = new SequenceGroup();
+    
+    for (int i = start; i <= end; i++)
+    {
+      theseSeqs.addSequence(allseqs[i], false);
+      al.getHiddenSequences().hideSequence(allseqs[i]);
+    }
+
+    hiddenRepSequences.put(allseqs[start], theseSeqs);
+  }
+}
diff --git a/test/jalview/viewmodel/ViewportRangesTest.java b/test/jalview/viewmodel/ViewportRangesTest.java
new file mode 100644 (file)
index 0000000..cfd03cd
--- /dev/null
@@ -0,0 +1,100 @@
+package jalview.viewmodel;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.analysis.AlignmentGenerator;
+import jalview.datamodel.AlignmentI;
+
+import org.testng.annotations.Test;
+
+public class ViewportRangesTest {
+
+  AlignmentGenerator gen = new AlignmentGenerator(false);
+
+  AlignmentI al = gen.generate(20, 30, 1, 5, 5);
+
+  @Test
+  public void testViewportRanges() 
+  {
+    ViewportRanges vr = new ViewportRanges(al);
+    
+    assertEquals(vr.getStartRes(),0);
+    assertEquals(vr.getEndRes(), al.getWidth()-1);
+    assertEquals(vr.getStartSeq(), 0);
+    assertEquals(vr.getEndSeq(), al.getHeight() - 1);
+  }
+
+  @Test
+  public void testGetAbsoluteAlignmentHeight()
+  {
+    ViewportRanges vr = new ViewportRanges(al);
+
+    assertEquals(vr.getAbsoluteAlignmentHeight(), al.getHeight());
+
+    al.getHiddenSequences().hideSequence(al.getSequenceAt(3));
+    assertEquals(vr.getAbsoluteAlignmentHeight(), al.getHeight() + 1);
+  }
+
+  @Test
+  public void testGetAbsoluteAlignmentWidth()
+  {
+    ViewportRanges vr = new ViewportRanges(al);
+    assertEquals(vr.getAbsoluteAlignmentWidth(), al.getWidth());
+  }
+
+  @Test
+  public void testSetEndRes()
+  {
+    ViewportRanges vr = new ViewportRanges(al);
+    vr.setEndRes(-1);
+    assertEquals(vr.getEndRes(), 0);
+
+    vr.setEndRes(al.getWidth());
+    assertEquals(vr.getEndRes(), al.getWidth() - 1);
+
+    vr.setEndRes(al.getWidth() - 1);
+    assertEquals(vr.getEndRes(), al.getWidth() - 1);
+  }
+
+  @Test
+  public void testSetEndSeq()
+  {
+    ViewportRanges vr = new ViewportRanges(al);
+    vr.setEndSeq(-1);
+    assertEquals(vr.getEndSeq(), 0);
+
+    vr.setEndSeq(al.getHeight());
+    assertEquals(vr.getEndSeq(), al.getHeight() - 1);
+
+    vr.setEndRes(al.getHeight() - 1);
+    assertEquals(vr.getEndSeq(), al.getHeight() - 1);
+  }
+
+  @Test
+  public void testSetStartRes()
+  {
+    ViewportRanges vr = new ViewportRanges(al);
+    vr.setStartRes(-1);
+    assertEquals(vr.getStartRes(), 0);
+
+    vr.setStartRes(al.getWidth());
+    assertEquals(vr.getStartRes(), al.getWidth() - 1);
+
+    vr.setStartRes(al.getWidth() - 1);
+    assertEquals(vr.getStartRes(), al.getWidth() - 1);
+  }
+
+  @Test
+  public void testSetStartSeq()
+  {
+    ViewportRanges vr = new ViewportRanges(al);
+    vr.setStartSeq(-1);
+    assertEquals(vr.getStartSeq(), 0);
+
+    vr.setStartSeq(al.getHeight());
+    assertEquals(vr.getStartSeq(), al.getHeight() - 1);
+
+    vr.setStartSeq(al.getHeight() - 1);
+    assertEquals(vr.getStartSeq(), al.getHeight() - 1);
+  }
+}
index 98ca303..f1dafcb 100644 (file)
  */
 package jalview.ws.seqfetcher;
 
+import static org.testng.Assert.assertTrue;
+
+import jalview.bin.Cache;
 import jalview.gui.JvOptionPane;
 
-import org.testng.AssertJUnit;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -36,15 +38,12 @@ public class DasSequenceFetcher
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
-  @Test(groups = { "Functional" })
+  @Test(groups = { "Network" })
   public void testDasRegistryContact()
   {
-    jalview.bin.Cache.getDasSourceRegistry().refreshSources();
-    AssertJUnit
-            .assertTrue(
-                    "Expected to find at least one DAS source at the registry. Check config.",
-                    jalview.bin.Cache.getDasSourceRegistry().getSources()
-                            .size() > 0);
+    Cache.getDasSourceRegistry().refreshSources();
+    assertTrue(Cache.getDasSourceRegistry().getSources().isEmpty(),
+            "Expected to find no DAS sources at the registry. Check config.");
   }
 
 }
index d805e47..7f8adc9 100644 (file)
@@ -38,6 +38,8 @@ import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 
 import org.testng.Assert;
 import org.testng.FileAssert;
@@ -280,7 +282,19 @@ public class SiftsClientTest
               "A", testSeq, null);
       Assert.assertEquals(testSeq.getStart(), 1);
       Assert.assertEquals(testSeq.getEnd(), 147);
-      Assert.assertEquals(actualMapping, expectedMapping);
+      // Can't do Assert.assertEquals(actualMapping, expectedMapping);
+      // because this fails in our version of TestNG
+      Assert.assertEquals(actualMapping.size(), expectedMapping.size());
+      Iterator<Map.Entry<Integer, int[]>> it = expectedMapping.entrySet()
+              .iterator();
+      while (it.hasNext())
+      {
+        Map.Entry<Integer, int[]> pair = it.next();
+        Assert.assertTrue(actualMapping.containsKey(pair.getKey()));
+        Assert.assertEquals(actualMapping.get(pair.getKey()),
+                pair.getValue());
+      }
+
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -399,7 +413,21 @@ groups = { "Network" },
 
     Assert.assertEquals(strucMapping.getMappingDetailsOutput(),
             expectedMappingOutput);
-    Assert.assertEquals(strucMapping.getMapping(), expectedMapping);
+
+    // Can't do Assert.assertEquals(strucMapping.getMapping(), expectedMapping);
+    // because this fails in our version of TestNG
+    Assert.assertEquals(strucMapping.getMapping().size(),
+            expectedMapping.size());
+    Iterator<Map.Entry<Integer, int[]>> it = expectedMapping.entrySet()
+            .iterator();
+    while (it.hasNext())
+    {
+      Map.Entry<Integer, int[]> pair = it.next();
+      Assert.assertTrue(strucMapping.getMapping()
+              .containsKey(pair.getKey()));
+      Assert.assertEquals(strucMapping.getMapping().get(pair.getKey()),
+              pair.getValue());
+    }
   }
 
   @Test(groups = { "Network" })
diff --git a/test/jalview/ws/utils/UrlDownloadClientTest.java b/test/jalview/ws/utils/UrlDownloadClientTest.java
new file mode 100644 (file)
index 0000000..2bd8dd0
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.utils;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class UrlDownloadClientTest {
+
+  /**
+   * Test that url is successfully loaded into download file
+   */
+  @Test(groups = { "Network" }, enabled = true)
+  public void UrlDownloadTest()
+  {
+    UrlDownloadClient client = new UrlDownloadClient();
+    String urlstring = "http://identifiers.org/rest/collections/";
+    String outfile = "testfile.tmp";
+
+    try
+    {
+      client.download(urlstring, outfile);
+    } catch (IOException e)
+    {
+      Assert.fail("Exception was thrown from UrlDownloadClient download: "
+              + e.getMessage());
+      File f = new File(outfile);
+      if (f.exists())
+      {
+        f.delete();
+      }
+    }
+
+    // download file exists
+    File f = new File(outfile);
+    Assert.assertTrue(f.exists());
+
+    // download file has a believable size
+    // identifiers.org file typically at least 250K
+    Assert.assertTrue(f.length() > 250000);
+
+    if (f.exists())
+    {
+      f.delete();
+    }
+
+  }
+
+  /**
+   * Test that garbage in results in IOException
+   */
+  @Test(
+    groups = { "Network" },
+    enabled = true,
+    expectedExceptions = { IOException.class })
+  public void DownloadGarbageUrlTest() throws IOException
+  {
+    UrlDownloadClient client = new UrlDownloadClient();
+    String urlstring = "identifiers.org/rest/collections/";
+    String outfile = "testfile.tmp";
+
+    client.download(urlstring, outfile);
+  }
+}