Merge develop to Release_2_8_3_Branch
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 26 Mar 2015 11:14:54 +0000 (11:14 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 26 Mar 2015 11:14:54 +0000 (11:14 +0000)
250 files changed:
.classpath
examples-jbake/templates/jvl_examples.ftl
examples/applets.html
examples/estrogenReceptorCdna.fa [new file with mode: 0644]
examples/estrogenReceptorProtein.fa [new file with mode: 0644]
help/html/features/multipleViews.html
lib/jersey-client-1.19.jar [new file with mode: 0644]
lib/jersey-core-1.19.jar [new file with mode: 0644]
lib/jersey-json-1.19.jar [new file with mode: 0644]
lib/jsr311-api-1.1.1.jar [new file with mode: 0644]
resources/images/dna.png [new file with mode: 0644]
resources/images/error.png [new file with mode: 0644]
resources/images/good.png [new file with mode: 0644]
resources/images/loading.gif [new file with mode: 0644]
resources/images/protein.png [new file with mode: 0644]
resources/images/sugar.png [new file with mode: 0644]
resources/lang/Messages.properties
schemas/jalview.xsd
src/MCview/AppletPDBCanvas.java
src/MCview/Atom.java
src/MCview/PDBCanvas.java
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
src/ext/vamsas/ServiceHandle.java
src/ext/vamsas/ServiceHandles.java
src/jalview/analysis/AAFrequency.java
src/jalview/analysis/AlignSeq.java
src/jalview/analysis/AlignmentSorter.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/CodingUtils.java [new file with mode: 0644]
src/jalview/analysis/CodonComparator.java [new file with mode: 0644]
src/jalview/analysis/CrossRef.java
src/jalview/analysis/Dna.java
src/jalview/analysis/SequenceIdMatcher.java
src/jalview/analysis/StructureFrequency.java
src/jalview/api/AlignViewportI.java
src/jalview/api/AlignmentViewPanel.java
src/jalview/api/SplitContainerI.java [new file with mode: 0644]
src/jalview/api/ViewStyleI.java [new file with mode: 0644]
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/appletgui/AnnotationLabels.java
src/jalview/appletgui/AnnotationPanel.java
src/jalview/appletgui/CutAndPasteTransfer.java
src/jalview/appletgui/EmbmenuFrame.java
src/jalview/appletgui/FontChooser.java
src/jalview/appletgui/IdCanvas.java
src/jalview/appletgui/IdPanel.java
src/jalview/appletgui/RedundancyPanel.java
src/jalview/appletgui/ScalePanel.java
src/jalview/appletgui/SeqCanvas.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/SequenceRenderer.java
src/jalview/appletgui/SliderPanel.java
src/jalview/appletgui/SplitFrame.java [new file with mode: 0644]
src/jalview/appletgui/TreeCanvas.java
src/jalview/appletgui/UserDefinedColours.java
src/jalview/bin/JalviewLite.java
src/jalview/commands/ChangeCaseCommand.java
src/jalview/commands/EditCommand.java
src/jalview/commands/OrderCommand.java
src/jalview/controller/AlignViewController.java
src/jalview/datamodel/AlignedCodon.java [new file with mode: 0644]
src/jalview/datamodel/AlignedCodonFrame.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentAnnotation.java
src/jalview/datamodel/AlignmentI.java
src/jalview/datamodel/AlignmentOrder.java
src/jalview/datamodel/AlignmentView.java
src/jalview/datamodel/ColumnSelection.java
src/jalview/datamodel/DBRefEntry.java
src/jalview/datamodel/DBRefSource.java
src/jalview/datamodel/IncompleteCodonException.java [new file with mode: 0644]
src/jalview/datamodel/Mapping.java
src/jalview/datamodel/SearchResults.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceFeature.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/datamodel/SequenceI.java
src/jalview/datamodel/xdb/embl/EmblEntry.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/paradise/Annotate3D.java
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/AnnotationExporter.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/AppVarnaBinding.java
src/jalview/gui/CutAndPasteHtmlTransfer.java
src/jalview/gui/CutAndPasteTransfer.java
src/jalview/gui/DasSourceBrowser.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/Finder.java
src/jalview/gui/FontChooser.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/IdPanel.java
src/jalview/gui/IdwidthAdjuster.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/Jalview2XML_V1.java
src/jalview/gui/JvSwingUtils.java
src/jalview/gui/OverviewPanel.java
src/jalview/gui/PCAPanel.java
src/jalview/gui/PDBSearchPanel.java [new file with mode: 0644]
src/jalview/gui/PaintRefresher.java
src/jalview/gui/PairwiseAlignPanel.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/gui/RedundancyPanel.java
src/jalview/gui/RotatableCanvas.java
src/jalview/gui/ScalePanel.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SequenceFetcher.java
src/jalview/gui/SequenceRenderer.java
src/jalview/gui/SliderPanel.java
src/jalview/gui/SplitFrame.java [new file with mode: 0644]
src/jalview/gui/StructureChooser.java [new file with mode: 0644]
src/jalview/gui/TextColourChooser.java
src/jalview/gui/TreeCanvas.java
src/jalview/gui/TreePanel.java
src/jalview/gui/UserDefinedColours.java
src/jalview/gui/VamsasApplication.java
src/jalview/gui/WsJobParameters.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/BioJsHTMLOutput.java
src/jalview/io/FileLoader.java
src/jalview/io/MSFfile.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/io/StockholmFile.java
src/jalview/io/VamsasAppDatastore.java
src/jalview/io/vamsas/Rangetype.java
src/jalview/io/vamsas/Sequencemapping.java
src/jalview/io/vamsas/Tree.java
src/jalview/javascript/MouseOverListener.java
src/jalview/javascript/MouseOverStructureListener.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GPDBSearchPanel.java [new file with mode: 0644]
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GSplitFrame.java [new file with mode: 0644]
src/jalview/jbgui/GStructureChooser.java [new file with mode: 0644]
src/jalview/jbgui/GTreePanel.java
src/jalview/jbgui/PDBDocFieldPreferences.java [new file with mode: 0644]
src/jalview/renderer/AnnotationRenderer.java
src/jalview/renderer/AwtRenderPanelI.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/schemabinding/version2/Viewport.java
src/jalview/schemabinding/version2/descriptors/ViewportDescriptor.java
src/jalview/schemes/ClustalxColourScheme.java
src/jalview/schemes/ResidueProperties.java
src/jalview/structure/AtomSpec.java [new file with mode: 0644]
src/jalview/structure/CommandListener.java [new file with mode: 0644]
src/jalview/structure/SequenceListener.java
src/jalview/structure/StructureListener.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/structure/VamsasListener.java
src/jalview/structures/models/AAStructureBindingModel.java
src/jalview/util/Comparison.java
src/jalview/util/DBRefUtils.java
src/jalview/util/Format.java
src/jalview/util/MapList.java
src/jalview/util/MappingUtils.java [new file with mode: 0644]
src/jalview/util/MessageManager.java
src/jalview/util/ParseHtmlBodyAndLinks.java
src/jalview/util/QuickSort.java
src/jalview/util/ReverseListIterator.java [new file with mode: 0644]
src/jalview/util/ShiftList.java
src/jalview/util/StringUtils.java [new file with mode: 0644]
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/viewmodel/styles/ViewStyle.java [new file with mode: 0644]
src/jalview/workers/AlignCalcManager.java
src/jalview/workers/ComplementConsensusThread.java [new file with mode: 0644]
src/jalview/workers/ConsensusThread.java
src/jalview/workers/StrucConsensusThread.java
src/jalview/ws/AWSThread.java
src/jalview/ws/HttpClientUtils.java
src/jalview/ws/SequenceFetcher.java
src/jalview/ws/dbsources/PDBRestClient.java [new file with mode: 0644]
src/jalview/ws/jws2/JPred301Client.java
src/jalview/ws/jws2/MsaWSThread.java
src/jalview/ws/rest/InputType.java
src/jalview/ws/uimodel/PDBRestRequest.java [new file with mode: 0644]
src/jalview/ws/uimodel/PDBRestResponse.java [new file with mode: 0644]
src/uk/ac/ebi/picr/model/CrossReference.java
src/uk/ac/ebi/picr/model/UPEntry.java
src/uk/ac/ebi/www/Data.java
src/uk/ac/ebi/www/InputParams.java
src/uk/ac/ebi/www/WSFile.java
src/vamsas/objects/simple/Alignment.java
src/vamsas/objects/simple/JpredResult.java
src/vamsas/objects/simple/MsaResult.java
src/vamsas/objects/simple/Msfalignment.java
src/vamsas/objects/simple/Object.java
src/vamsas/objects/simple/Result.java
src/vamsas/objects/simple/Secstructpred.java
src/vamsas/objects/simple/SeqSearchResult.java
src/vamsas/objects/simple/Sequence.java
src/vamsas/objects/simple/SequenceSet.java
src/vamsas/objects/simple/WsJobId.java
test/jalview/analysis/AAFrequencyTest.java [new file with mode: 0644]
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/analysis/CodingUtilsTest.java [new file with mode: 0644]
test/jalview/analysis/CrossRefTest.java [new file with mode: 0644]
test/jalview/analysis/DnaAlignmentGenerator.java [new file with mode: 0644]
test/jalview/analysis/DnaTest.java [new file with mode: 0644]
test/jalview/analysis/DnaTranslation.java [deleted file]
test/jalview/analysis/TestAlignSeq.java
test/jalview/commands/EditCommandTest.java
test/jalview/datamodel/AlignedCodonFrameTest.java [new file with mode: 0644]
test/jalview/datamodel/AlignedCodonIteratorTest.java [new file with mode: 0644]
test/jalview/datamodel/AlignedCodonTest.java [new file with mode: 0644]
test/jalview/datamodel/AlignmentTest.java
test/jalview/datamodel/ColumnSelectionTest.java [new file with mode: 0644]
test/jalview/datamodel/DBRefEntryTest.java [new file with mode: 0644]
test/jalview/datamodel/SearchResultsTest.java [new file with mode: 0644]
test/jalview/datamodel/SequenceTest.java
test/jalview/ext/paradise/TestAnnotate3D.java
test/jalview/ext/rbvi/chimera/ChimeraConnect.java
test/jalview/ext/rbvi/chimera/JalviewChimeraView.java
test/jalview/gui/AnnotationChooserTest.java
test/jalview/gui/JvSwingUtilsTest.java [new file with mode: 0644]
test/jalview/gui/PDBSearchPanelTest.java [new file with mode: 0644]
test/jalview/gui/PaintRefresherTest.java [new file with mode: 0644]
test/jalview/gui/PopupMenuTest.java
test/jalview/gui/StructureChooserTest.java [new file with mode: 0644]
test/jalview/io/AnnotatedPDBFileInputTest.java
test/jalview/io/HtmlFileTest.java
test/jalview/io/Jalview2xmlTests.java
test/jalview/io/pdb_request_json_error.txt [new file with mode: 0644]
test/jalview/io/pdb_response_json.txt [new file with mode: 0644]
test/jalview/schemes/DnaCodonTests.java
test/jalview/schemes/ResiduePropertiesTest.java [new file with mode: 0644]
test/jalview/structure/Mapping.java
test/jalview/structure/StructureSelectionManagerTest.java [new file with mode: 0644]
test/jalview/util/ComparisonTest.java [new file with mode: 0644]
test/jalview/util/MapListTest.java [new file with mode: 0644]
test/jalview/util/MappingUtilsTest.java [new file with mode: 0644]
test/jalview/util/QuickSortTest.java [new file with mode: 0644]
test/jalview/util/ShiftListTest.java [new file with mode: 0644]
test/jalview/util/StringUtilsTest.java [new file with mode: 0644]
test/jalview/viewmodel/styles/TestviewStyle.java [new file with mode: 0644]
test/jalview/ws/dbsources/PDBRestClientTest.java [new file with mode: 0644]
test/jalview/ws/jabaws/JalviewJabawsTestUtils.java
test/jalview/ws/jabaws/RNAStructExportImport.java
test/jalview/ws/rest/ShmmrRSBSService.java
test/jalview/ws/seqfetcher/DbRefFetcherTest.java

index c110217..c19e381 100644 (file)
        <classpathentry kind="lib" path="lib/VARNAv3-91.jar"/>
        <classpathentry kind="lib" path="lib/jfreesvg-2.1.jar"/>
        <classpathentry kind="lib" path="lib/quaqua-filechooser-only-8.0.jar"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/plugin.jar"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/plugin"/>
        <classpathentry kind="lib" path="lib/xml-apis.jar"/>
        <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
        <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/Plugin.jar"/>
+       <classpathentry kind="lib" path="lib/jersey-client-1.19.jar"/>
+       <classpathentry kind="lib" path="lib/jersey-core-1.19.jar"/>
+       <classpathentry kind="lib" path="lib/jersey-json-1.19.jar"/>
+       <classpathentry kind="lib" path="lib/jsr311-api-1.1.1.jar"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/plugin.jar"/>
        <classpathentry kind="output" path="classes"/>
 </classpath>
index ec5c684..d0e40bf 100644 (file)
@@ -85,4 +85,20 @@ Try out JalviewLite by pressing one of the buttons below.<br/>
        secondary structure annotation</td>
     </tr>
   </table>
+  <p>
+    <h2>Linked amino acid and cDNA alignments for homologous proteins</h2>
+  </p>
+  <table width="90%">
+    <tr>
+      <td width="10%" valign="center"><@jvlitebutton appjar="${content.jvl},${content.jmol}" params={ "permissions":"all-permissions"
+                                                    , "file":"estrogenReceptorCdna.fa"
+                                                    , "file2":"estrogenReceptorProtein.fa"
+                                                    , "defaultColour":"Purine/Pyrimidine"
+                                                    , "showAnnotation":"false"
+                                                    , "windowHeight":"300"
+                                                    , "windowWidth":"800"
+                                                    , "showFullId":"false"} prots=true /></td>
+      <td valign="center">Displays a split window view of protein and its related cDNA</td>
+    </tr>
+  </table>
 </div>
index 9d94c69..b557efc 100755 (executable)
@@ -313,6 +313,29 @@ Try out JalviewLite by pressing one of the buttons below.<br/>
        secondary structure annotation</td>
     </tr>
   </table>
+  <p>
+    <h2>Linked amino acid and cDNA alignments for homologous proteins</h2>
+  </p>
+  <table width="90%">
+    <tr>
+      <td width="10%" valign="center"><applet
+   code="jalview.bin.JalviewLite" width="140" height="35"
+   archive="jalviewApplet.jar,JmolApplet-12.2.4.jar">
+<param name="permissions" value="sandbox"/>
+<param name="file" value="estrogenReceptorCdna.fa"/>
+<param name="file2" value="estrogenReceptorProtein.fa"/>
+<param name="showFullId" value="false"/>
+<param name="windowHeight" value="300"/>
+<param name="windowWidth" value="800"/>
+<param name="showAnnotation" value="false"/>
+<param name="defaultColour" value="Purine/Pyrimidine"/>
+   <param name="APPLICATION_URL"
+     value="http://www.jalview.org/services/launchApp"/>
+</applet>
+</td>
+      <td valign="center">Displays a split window view of protein and its related cDNA</td>
+    </tr>
+  </table>
 </div>
 <!-- content template end -->
 
diff --git a/examples/estrogenReceptorCdna.fa b/examples/estrogenReceptorCdna.fa
new file mode 100644 (file)
index 0000000..9d857db
--- /dev/null
@@ -0,0 +1,221 @@
+>EMBLCDS|ADZ17331/1-1593 Homo sapiens (human) estrogen nuclear receptor beta variant a
+atggatataaaaaactcaccatctagccttaattctccttcctcctacaactgcagtcaatccatcttaccc
+ctggagcacggctccatatacataccttcctcctatgtagacagccaccatgaatatccagccatgacattc
+tatagccctgctgtgatgaattacagcattcccagcaatgtcactaacttggaaggtgggcctggtcggcag
+accacaagcccaaatgtgttgtggccaacacctgggcacctttctcctttagtggtccatcgccagttatca
+catctgtatgcggaacctcaaaagagtccctggtgtgaagcaagatcgctagaacacaccttacctgtaaac
+agagagacactgaaaaggaaggttagtgggaaccgttgcgccagccctgttactggtccaggttcaaagagg
+gatgctcacttctgcgctgtctgcagcgattacgcatcgggatatcactatggagtctggtcgtgtgaagga
+tgtaaggccttttttaaaagaagcattcaaggacataatgattatatttgtccagctacaaatcagtgtaca
+atcgataaaaaccggcgcaagagctgccaggcctgccgacttcggaagtgttacgaagtgggaatggtgaag
+tgtggctcccggagagagagatgtgggtaccgccttgtgcggagacagagaagtgccgacgagcagctgcac
+tgtgccggcaaggccaagagaagtggcggccacgcgccccgagtgcgggagctgctgctggacgccctgagc
+cccgagcagctagtgctcaccctcctggaggctgagccgccccatgtgctgatcagccgccccagtgcgccc
+ttcaccgaggcctccatgatgatgtccctgaccaagttggccgacaaggagttggtacacatgatcagctgg
+gccaagaagattcccggctttgtggagctcagcctgttcgaccaagtgcggctcttggagagctgttggatg
+gaggtgttaatgatggggctgatgtggcgctcaattgaccaccccggcaagctcatctttgctccagatctt
+gttctggacagggatgaggggaaatgcgtagaaggaattctggaaatctttgacatgctcctggcaactact
+tcaaggtttcgagagttaaaactccaacacaaagaatatctctgtgtcaaggccatgatcctgctcaattcc
+agtatgtaccctctggtcacagcgacccaggatgctgacagcagccggaagctggctcacttgctgaacgcc
+gtgaccgatgctttggtttgggtgattgccaagagcggcatctcctcccagcagcaatccatgcgcctggct
+aacctcctgatgctcctgtcccacgtcaggcatgcgagtaacaagggcatggaacatctgctcaacatgaag
+tgcaaaaatgtggtcccagtgtatgacctgctgctggagatgctgaatgcccacgtgcttcgcgggtgcaag
+tcctccatcacggggtccgagtgcagcccggcagaggacagtaaaagcaaagagggctcccagaacccacag
+tctcagtga
+>EMBLCDS|AAK93056/1-1455 Drosophila melanogaster (fruit fly) GH28308p
+atgtccgacggcgtcagcatcttgcacatcaaacaggaggtggacactccatcggcgtcctgctttagtccc
+agctccaagtcaacggccacgcagagtggcacaaacggcctgaaatcctcgccctcggtttcgccggaaagg
+cagctctgcagctcgacgacctctctatcctgcgatttgcacaatgtatccttaagcaatgatggcgatagt
+ctgaaaggaagtggtacaagtggcggcaatggcggaggaggaggtggtggtacgagtggtggaaatgcgacc
+aatgcgagtgccggagctggatcgggatccgtcagggacgagctccgccgattgtgtttggtttgtggcgat
+gtggccagtggattccactatggtgtggcgagttgtgaggcttgcaaagcgttctttaaacgcaccatccaa
+ggcaacatcgagtacacgtgtccggcgaacaacgagtgtgagattaacaagcggagacgcaaggcctgccaa
+gcgtgtcgcttccagaaatgtctactaatgggcatgctcaaggagggtgtgcgcttggatcgagttcgtgga
+ggacggcagaagtaccgaaggaatcctgtatcaaactcttaccagactatgcagctgctataccaatccaac
+accacctcgctgtgcgatgtcaagatactggaggtgctcaattcatatgagccggatgccttgagcgtccaa
+acgccgccgccgcaagtccacacgactagcataactaatgatgaggcctcatcctcctcgggcagcataaaa
+ctggagtccagcgttgttacgcccaatgggacttgcattttccaaaacaacaacaacaatgatcccaatgag
+atactaagcgtccttagtgatatttacgacaaggaattggtcagcgtcattggctgggccaagcagatacct
+ggctttatagatctgccacttaacgaccagatgaagcttctccaggtgtcgtgggcagagatcctgacgctc
+cagctgaccttccggtccctaccgttcaatggcaagttatgcttcgccacggatgtctggatggatgaacat
+ttggccaaggagtgcggttacacggagttctactaccactgcgtccagatcgcacagcgcatggaaagaata
+tcgccacgaagggaggagtactacttgctaaaggcgctcctgctggccaactgcgacattctgctggatgat
+cagagttccctgcgcgcatttcgtgatacgattcttaattctctaaacgatgtggtctacttgctgcgtcat
+tcgtcggccgtgtcgcatcagcaacaattgctgcttttgctgccttcgctgcggcaggcggatgatatcctg
+cgaagattttggcgtggaattgcacgcgatgaagtcattaccatgaagaaactgttcctcgagatgctcgag
+ccgctggccaggtga
+>EMBLCDS|BAA89539/1-2133 Anguilla japonica (Japanese eel) progesterone receptor
+atggacaacaatcaccaagacaagatggaaagtctatacacgccagccagagcatcaccaactcctgatgca
+gaatcgattaaaagagccaggaatttgattaaaacatactcggagtcttttgggagttatgtggaggagata
+gttcgagacgactcgaataacatacaatctttgagcagcgtccctctcttgatgcgtaattttggaaacatg
+gacaccctaacctgcgcacctggctcgggtagtgacagtgagatttggaaagactttgttgttcccgggaac
+tctgtggtaagcaaagacacctgtggtcatgttgaaatatccactaaagccgaaaatttgtcttgggctgcc
+gcgcccttaagtagagaagaaaccctcgcgaaaggaactgttacggtcccagcgactgtgcctaaagaaagt
+tttaccgcaacatcaaacacttcttcagccagtggaatctctattaaagatgaacaacaatctttgctcaaa
+atggaaccacagtcttctgacttttgtccttatacagcaaatataccgaaattgaatccatcttatctgacc
+aatactgcgagtacgaaacaacttggatatggcgaacagccggacacttcagcgcactcctctccacctgct
+cagaagattgttttagatactgctcgatactcggccgatttatgttcggataaccctttaccgcaagcgaca
+aatatcaaaacagatccttgtagtagtttctcttctttcgttggagaagggatccttactagggcatctatg
+ggctactcacagcaagcgattcagacattgccggtgcacaagagtgaaccgttcaggttgtctgcttcgagc
+gcgcccgcggattctccgttttggtgccaatccacgggtccttctgaggatcatcatctgcagattgactat
+ctatctcccgctggactccacagcacatgcaaatacagttccacgaacgcgtacagctcctatttaggtgtg
+ctgccccagagggtgtgcgtcatctgtggggatgaagcatcaggctgtcactatggtgtcctcacctgtggc
+agctgtaaggtgttctttaagagggcagtggaaggccatcataactacttgtgtgctggacggaatgactgc
+atcgtggacaagatccgtaggaaaaactgtcctgcttgtcgcctcagaaagtgctaccaggcgggaatgata
+ctgggaggtcggaagctgaagaagttgggggctctgaaggcagcagggctgacccaggccctggtggcccac
+tcactgactcctcggaggctctctggtgacagccaggccctgatgcccctgggctgccttccaggggtccgg
+gagctgcacctttccccacagatcatcagcgtgctggagagcattgagcctgaggtggtgtactctggttat
+gacaactcccagcctgacatgcccaatatgctgctcaacagcctcaaccgcttatgtgagaggcagctgctg
+aggattgtcaagtggtccaagtctttaccaggttttcgcagtttacacatcaatgaccaaatggctctgatc
+cagtactcctggatgagcttaatggtattttctttgggttggcggtctttccaaaatgtcaccagtgattac
+ctgtactttgcacctgacctcattctcaacgaagagtatatgaggaggtctccaatatttgacctgtgcatg
+gccatgcagttcatccctcaagagtttgccaatctccaggtgaccaaggaggagtttctgtgcatgaaggtc
+ttgctgttgctcaacacagtgcctctggaggggttgaagagccagccacagtttgatgagatgaggcagaat
+tacatccatgaactcaccaaggccattcacctgcgagagaatggtgtggtcgcctgctcccagcgtttctac
+cacctgaccaagctgatggaccacatgcatgacattgtgaagaagctccacctgtactgcctgagcactttc
+attcaggcagatgccatgcgggtagagttccccgagatgatgtcagaagtcatcgcctcccagctgcctcgg
+gtgctcgctggaatggtgaaaccccttctttttcacaccaaatga
+>EMBLCDS|AHW56473/1-1590 Homo sapiens (human) partial estrogen receptor 2 isoform A
+atggatataaaaaactcaccatctagccttaattctccttcctcctacaactgcagtcaatccatcttaccc
+ctggagcacggctccatatacataccttcctcctatgtagacagccaccatgaatatccagccatgacattc
+tatagccctgctgtgatgaattacagcattcccagcaatgtcactaacttggaaggtgggcctggtcggcag
+accacaagcccaaatgtgttgtggccaacacctgggcacctttctcctttagtggtccatcgccagttatca
+catctgtatgcggaacctcaaaagagtccctggtgtgaagcaagatcgctagaacacaccttacctgtaaac
+agagagacactgaaaaggaaggttagtgggaaccgttgcgccagccctgttactggtccaggttcaaagagg
+gatgctcacttctgcgctgtctgcagcgattacgcatcgggatatcactatggagtctggtcgtgtgaagga
+tgtaaggccttttttaaaagaagcattcaaggacataatgattatatttgtccagctacaaatcagtgtaca
+atcgataaaaaccggcgcaagagctgccaggcctgccgacttcggaagtgttacgaagtgggaatggtgaag
+tgtggctcccggagagagagatgtgggtaccgccttgtgcggagacagagaagtgccgacgagcagctgcac
+tgtgccggcaaggccaagagaagtggcggccacgcgccccgagtgcgggagctgctgctggacgccctgagc
+cccgagcagctagtgctcaccctcctggaggctgagccgccccatgtgctgatcagccgccccagtgcgccc
+ttcaccgaggcctccatgatgatgtccctgaccaagttggccgacaaggagttggtacacatgatcagctgg
+gccaagaagattcccggctttgtggagctcagcctgttcgaccaagtgcggctcttggagagctgttggatg
+gaggtgttaatgatggggctgatgtggcgctcaattgaccaccccggcaagctcatctttgctccagatctt
+gttctggacagggatgaggggaaatgcgtagaaggaattctggaaatctttgacatgctcctggcaactact
+tcaaggtttcgagagttaaaactccaacacaaagaatatctctgtgtcaaggccatgatcctgctcaattcc
+agtatgtaccctctggtcacagcgacccaggatgctgacagcagccggaagctggctcacttgctgaacgcc
+gtgaccgatgctttggtttgggtgattgccaagagcggcatctcctcccagcagcaatccatgcgcctggct
+aacctcctgatgctcctgtcccacgtcaggcatgcgagtaacaagggcatggaacatctgctcaacatgaag
+tgcaaaaatgtggtcccagtgtatgacctgctgctggagatgctgaatgcccacgtgcttcgcgggtgcaag
+tcctccatcacggggtccgagtgcagcccggcagaggacagtaaaagcaaagagggctcccagaacccacag
+tctcag
+>EMBLCDS|BAA75464/1-2547 Anguilla japonica (Japanese eel) androgen receptor alpha
+atggagattccagttgggttaggaggagtttcagatgccacaaacgccgtttttcgcggaccttaccaaaac
+gttttccacagccttcaagtggcatttcagagtcacggtgccgtctccaggagcttagattttccaaataca
+aagtacggttttttacaaaacagacatttctgtgaaatgcgtcaggagaacaagcagccgccatgcaaagga
+ctcggcctattttacgggaaccatcgtaattcggacactgggacaaacgaagacgacatcgcttgtttttcc
+agacagtccgacgctgaagccagacctggtattttttctgaaagctcattggatactggagacgagattact
+tgcaaactccagtcagacaaccaaggggtaagagcgagcggtcctctcctaccgggctctagcggctgcaat
+tcgggacaaaagtcctcccttgcttgtacgtcccaacaaagggagacaacatctcaaagtgacacctgcgca
+ggagagagctgctcggaacatcaagcaactaccatttcggaaactgcgcgcgaattgtgcaacgccgtttcc
+gtgtcgctgggcttgaatttagatcttaatgatatgaatgacctaagttcaaaccaaatatcgtctaccgaa
+agtgacacaagtcaagccatctacttatttgaatcttcacctgggtatactggggtcggactgaacgccttg
+gtaagagactgtaaatgtcagagtgcacgcgaagggacatcgacacaacagtacgaccgcggggcaatgttt
+aagataaaccgtgtaaatgacttgccgcttcagccagcacccccgcgacacaccagcattagcgatgctaaa
+tgggacatggaagcaggtttgtgtgcgcagatggagcacaaagactctgaaaagtgcgcgaatatggatggt
+gcacactccacttctgtcttctcccagttcgaccaactgttgccagtaaacgcgtcgcactacagtcagaac
+gtttcggtcagagtggaaccacaaagtgatttctctccgattttgtacaaatcacctggtattcagaaaaat
+gccgaaaagtacaatgtccaatatgatgccacaattaaatcagaagatgggaaaacgacatctgaacgggaa
+tggggttttcagtacaggtacaatgaaagctgcagcacaccgtcagcacctcctagacattgtgcacatcag
+aacagggccggaccgtacaaccagttcttttttaatccatttgaatatgcgaaaagaggtgttgtctcaagg
+gaaggatattctctcgaacatgggttcccaaacaatctcgctcggacaccctactctggttccttgaaaaac
+gaactaggagatcgtctgagtgggccataccctgacgtcagttacaggtacgagggcgagcgggagaacgtg
+ttccccgtggagttcttctttccgccgcagaggacctgcctgatctgcggggacgaggcctcgggctgtcac
+tatggagccctcacctgcggcagctgcaaggtgttcttcaagagggccgcggaagggaaacagaagtacctg
+tgcgccagcatcaatgattgcaccattgataaacttcgaaggaagaactgcccctcttgccgtctcaaaagg
+tgctttgctgccggaatgacccttggagcgcggaagctgaagaagatcgggcaaatgagggcccccgaggat
+ggccaggggcagggcccggcggaagcggagctgagcgtctcccccaagtacgacctgggcttccacacccag
+tccatgttcctcaacatcctggaggccatcgagccggaggtggtgaacgccgggcacgactatggccagccg
+gactctgcggccagcctgctgaccagcctcaacgagctcggagaacggcaactcgtcaaggtcgtcaagtgg
+gccaagggcatgccaggttttcggagtctgtacgtggatgaccagatgacagtcatccagcactcctggatg
+gcagtgatggtgttcgctctgggctggaggtcatttaagaatgtgaagtccaggatgctttactttgctcct
+gaccttgttttcaacgagcaccgaatgcaggtgtccaccatgtatgaacactgcatccggatgaagaacttc
+tcccaggagtttgctatgctgcaggtctcccaggaagagttcctgtgcatgaaagctctgcttctcttcagc
+accatccccgttgaagggctgaaggggcagaatttctttgacgagctgcggaggagctacattaacgagctg
+gaccggctggttagcttcaggagcaagtccagctgttccgagaggttccagcagctcacccgcctcctggac
+tccctccaacctgttctgaagaagctccaccagtttacgttcgaccttttcgtccagtcccagaacctctcc
+aaccaagtttgctttcccgagatgatctcagagatcatatccgtgcacgtgccaaagattctcgctggcacg
+gtgaagccaatcctcttccacaagtag
+>EMBLCDS|AAK20930/1-1317 Petromyzon marinus (sea lamprey) partial corticoid receptor
+ggggtggagtttcagttgccctactcggcatctgccacatcctttcgtccgtccgttgccacctcgtccgcc
+tcgggcatctccaacttttcaaatgggaataattttggattcctttctcccaatggagtacaacaggatgga
+tttccttaccctggtttcacgagtcccgcacagtcctcagtccctccgcagaaggcgtgtctcatctgtagt
+gatgaggcttcgggctgccactacggagtgctcacctgtggaagctgcaaggtgttcttcaagcgtgccgtg
+gaaggacagcacaattatctgtgcgccggacgaaacgactgcatcattgacaagatccgccgcaagaactgc
+ccagcttgccgtctgcgcaagtgcatccaggctggaatgacgctaggagcacgcaagcttaagaagcaaggc
+cgggtaaagggagagaaccagcgcagcccagcgtcctccacagccaccacctcgtctgccaccccgcaaccc
+tccagcaactcgacggccgtgaccacgttctcgccaccgccgaccggagagcccattttctcacccacactc
+atcgccatcctgcaggcgatcgagcccgaggtggtcatgtccggctatgacaacacgcggtcccagaccacc
+gcctacatgctgtcgagcctcaaccgcctctgcgacaagcagctcgtgtccattgtcaagtgggccaagtct
+ctgccaggtttccgaaacctgcacatcgacgaccagatggtgttaatccagtactcatggatgggcctgatg
+tcatttgccatgagctggaggtccttccagcacaccaacagcaagctgctctactttgctcctgatctggtt
+tttgatgagacgcgcatgcagcartcggcgatgtatcaattgtgcgtggaaatgaggcaagtctcggaggac
+ttcatgaagttgcaagtcacttcagaggagtttctgtgcatgaaagccatcttgctcctgagtactgtccca
+caagagggtctgaagagccagggctgcttcgaggagatgcggatcagctacatccgggaattgaaccggacc
+atcgcgcggacggagaagaatgccgtgcagtgttggcagcgcttctaccagctcaccaagctgctggrctgc
+atgcaggatctcgtgagcaagctcctggagttctgcttcgcaaccttcacgcagacgcaggtgtggagtgtg
+gagtttcctgacatgatggccgagatcatcagtgcgcagcttgcctcgcatcatggccgagaagcccgggca
+ctccacttccacaagaaatga
+>EMBLCDS|AAA17402/1-1032 Serinus canaria (common canary) partial androgen receptor
+gaagcctccgggtgccactacggagccctgacgtgtgggagctgcaaagtgttcttcaaacgggcagctgaa
+ggtaaacagaagtacctctgtgccagccgcaacgactgcaccatcgacaagttccggcggaaaaactgcccc
+tcctgccgcctgcgcaagtgctacgaggccgggatgacgcttggagcccgcaagctgaagaaactgggtaac
+ctgaaggcacaggacgacatagagggagccagctcgtccagcccaacggaggagcaagctcccaagctggtg
+atgacacgcattgatggctacgagtgccagcccatcttcctcaacgtcctggaggccatcgagcctggggtg
+gtgtgtgctggccatgacaacagccagcctgactccttctccaacctgctgaccagcctgaatgagcttggg
+gagaggcagctggtctacgtggtcaaatgggcaaaggctctgccaggatttcgcaacctgcatgtggatgac
+cagatgtcaataatccagtactcttggatgggcctgatggtgtttgctatggggtggagatccttcaccaac
+gtcaattccaggatgctttactttgctccagacctggtcttcaatgagtaccgcatgcacaaatccaggatg
+tacagccagtgcatcaggatgcggcacctctcccaggaattcgggtggcttcagatcacaccccaggggttc
+ctctgtatgaaggctctcctcttcttcagtattattccagtggatggcctgaagaaccagaagctcttcgat
+gagctccgcatgaattacatcaaggaacttgaccgtatcattgcctgcaagaggaagaaccccacctcatgc
+tcccggaggttttaccagctcaccaaggtcctggactccgtgactccgattgccaaggacctgcatcagttt
+acatttgatctcctaatcaaggcacacatggtgagcgtggactacccagaaatgatggctgagatcatctct
+gtgcaggttcccaagatcctgtct
+>EMBLCDS|AAL37553/1-1455 Drosophila melanogaster (fruit fly) estrogen-related receptor
+atgtccgacggcgtcagcatcttgcacatcaaacaggaggtggacactccatcggcgtcctgctttagtccc
+agctccaagtcaacggccacgcagagtggcacaaacggcctgaaatcctcgccctcggtttcgccggaaagg
+cagctctgcagctcgacgacctctctatcctgcgatttgcacaatgtatccttaagcaatgatggcgatagt
+ctgaaaggaagtggtacaagtggcggcaatggcggaggaggaggtggtggtacgagtggtggaaatgcgacc
+aatgcgagtgccggagctggatcgggatccgtcagggacgagctccgccgattgtgtttggtttgtggcgat
+gtggccagtggattccactatggtgtggcgagttgtgaggcttgcaaagcgttctttaaacgcaccatccaa
+ggcaacatcgagtacacgtgtccggcgaacaacgagtgtgagattaacaagcggagacgcaaggcctgccaa
+gcgtgtcgcttccagaaatgtctactaatgggcatgctcaaggagggtgtgcgcttggatcgagttcgtgga
+ggacggcagaagtaccgaaggaatcctgtatcaaactcttaccagactatgcagctgctataccaatccaac
+accacctcgctgtgcgatgtcaagatactggaggtgctcaattcatatgagccggatgccttgagcgtccaa
+acgccgccgccgcaagtccacacgactagcataactaatgatgaggcctcatcctcctcgggcagcataaaa
+ctggagtccagcgttgttacgcccaatgggacttgcattttccaaaacaacaacaacaatgatcccaatgag
+atactaagcgtccttagtgatatttacgacaaggaattggtcagcgtcattggctgggccaagcagatacct
+ggctttatagatctgccacttaacgaccagatgaagcttctccaggtgtcgtgggcagagatcctgacgctc
+cagctgaccttccggtccctaccgttcaatggcaagttatgcttcgccacggatgtctggatggatgaacat
+ttggccaaggagtgcggttacacggagttctactaccactgcgtccagatcgcacagcgcatggaaagaata
+tcgccacgaagggaggagtactacttgctaaaggcgctcctgctggccaactgcgacattctgctggatgat
+cagagttccctgcgcgcatttcgtgatacgattcttaattctctaaacgatgtggtctacttgctgcgtcat
+tcgtcggccgtgtcgcatcagcaacaattgctgcttttgctgccttcgctgcggcaggcggatgatatcctg
+cgaagattttggcgtggaattgcacgcgatgaagtcattaccatgaagaaactgttcctcgagatgctcgag
+ccgctggccaggtga
+>EMBLCDS|AAK20929/1-1662 Petromyzon marinus (sea lamprey) partial estrogen receptor
+gcacgaggcttcagcgaggcacatggctacgagtactccggggcctcgctctaccagccactgcctccctcg
+tgcacagagttctcaattggagctcatcaacaacaacaacatcagcaccagcatcaccagcaccagcatcag
+cagcaccaccaccagcagcagcagcagcagccacagccgcagcagaatggagttttgggcgaggggcagagt
+tcccatctctcttatcttccgccctcgaccgagctgccccagtacgtgccctccagccccagcgcgccctac
+agcatggagctcggggcagggcgtcctcacggctacgacccagggccacagagcctctacaggggcggtgtg
+gagagcagcgcccccccgtacagcgagcagcagcaggtggtgggcggcggcggagccatgtcggccatgggc
+ttgacagagccacgccacgtcagctccggatcgctacccagcagcacgaggcccgagcgcagcacccagttc
+tgtgccgtgtgcagcgactatgcctcggggtaccactacggcgtgtggtcctgcgagggctgcaaagccttc
+ttcaagcgcagcacgcaaggccacaatgactacatgtgcccggccaccaaccagtgcaccatcgacaggaac
+cgtcgcaagagctgtcaggcttgccgcctgcgtaagtgctacgaagtgggcatggtcaaaggcgttcgcaag
+gaccgcaaaggctttcgaggggtcaagcacaaacgtaagcgccccatcccccaaaagaatgggggagaagga
+ggtgccggcggcggccaagacgtgagcgagacgaggcctcagggtgagaggccctccgggccgagggaccgg
+gagagcgccgtcagctcactcgaggctgaccaggtgatctcggctcttctggaggctgagccacccaccgta
+ctgtcctcgtatgaccccgacaagcctgtgacggaggcctcgctcatggctgctctcaccagcctggctgac
+cgagagctcgtgcacatgatcacctgggctaagaagattccaggattcacggccatcgggctgagtgaccag
+gtgcagctgctggagtgctgctggctggagatcctaatcgtggggctcatctggaggtctatcgatcgccct
+ggtcagctccactttgctccaaacctcatcctaggaagggaggacgcgcgcaatgtggagggcatgctggac
+atgttcgacatgctgctcgtcaccgtgagtcgcttccgtgagctgcatctccgccgggaggaatacgtctgc
+ctcaaggccatgatcctcctcaactcgggggtgtttttctgcctctccaattccgccggggagcagacgaat
+gtgcagctcatccagcagatcctcgagaaggtgatggacgccctgggcagcaccatcggccacattgaggcg
+tccccgccgcaacactcgcgtcgcctctcccagctgctcctgctgctttcacagatccggcacattagcaac
+aagggcatcgagcatctcaacagcatgaagcgtaagaatgtgatcccgctatacgacctgctccttgagctg
+ctggacgctcacagcctgcagaatactggcttacggacgtcgcccccaccgcaggatttcagggcaaccctc
+gtgccg
diff --git a/examples/estrogenReceptorProtein.fa b/examples/estrogenReceptorProtein.fa
new file mode 100644 (file)
index 0000000..0541d39
--- /dev/null
@@ -0,0 +1,81 @@
+>UNIPROT|Q7LCB3/1-530 estrogen nuclear receptor beta variant a
+MDIKNSPSSLNSPSSYNCSQSILPLEHGSIYIPSSYVDSHHEYPAMTFYSPAVMNYSIPSNVTNLEGGPGRQ
+TTSPNVLWPTPGHLSPLVVHRQLSHLYAEPQKSPWCEARSLEHTLPVNRETLKRKVSGNRCASPVTGPGSKR
+DAHFCAVCSDYASGYHYGVWSCEGCKAFFKRSIQGHNDYICPATNQCTIDKNRRKSCQACRLRKCYEVGMVK
+CGSRRERCGYRLVRRQRSADEQLHCAGKAKRSGGHAPRVRELLLDALSPEQLVLTLLEAEPPHVLISRPSAP
+FTEASMMMSLTKLADKELVHMISWAKKIPGFVELSLFDQVRLLESCWMEVLMMGLMWRSIDHPGKLIFAPDL
+VLDRDEGKCVEGILEIFDMLLATTSRFRELKLQHKEYLCVKAMILLNSSMYPLVTATQDADSSRKLAHLLNA
+VTDALVWVIAKSGISSQQQSMRLANLLMLLSHVRHASNKGMEHLLNMKCKNVVPVYDLLLEMLNAHVLRGCK
+SSITGSECSPAEDSKSKEGSQNPQSQ
+>UNIPROT|Q9VSE9/1-484 GH28308p
+MSDGVSILHIKQEVDTPSASCFSPSSKSTATQSGTNGLKSSPSVSPERQLCSSTTSLSCDLHNVSLSNDGDS
+LKGSGTSGGNGGGGGGGTSGGNATNASAGAGSGSVRDELRRLCLVCGDVASGFHYGVASCEACKAFFKRTIQ
+GNIEYTCPANNECEINKRRRKACQACRFQKCLLMGMLKEGVRLDRVRGGRQKYRRNPVSNSYQTMQLLYQSN
+TTSLCDVKILEVLNSYEPDALSVQTPPPQVHTTSITNDEASSSSGSIKLESSVVTPNGTCIFQNNNNNDPNE
+ILSVLSDIYDKELVSVIGWAKQIPGFIDLPLNDQMKLLQVSWAEILTLQLTFRSLPFNGKLCFATDVWMDEH
+LAKECGYTEFYYHCVQIAQRMERISPRREEYYLLKALLLANCDILLDDQSSLRAFRDTILNSLNDVVYLLRH
+SSAVSHQQQLLLLLPSLRQADDILRRFWRGIARDEVITMKKLFLEMLEPLAR
+>UNIPROT|Q9IBD5/1-710 progesterone receptor
+MDNNHQDKMESLYTPARASPTPDAESIKRARNLIKTYSESFGSYVEEIVRDDSNNIQSLSSVPLLMRNFGNM
+DTLTCAPGSGSDSEIWKDFVVPGNSVVSKDTCGHVEISTKAENLSWAAAPLSREETLAKGTVTVPATVPKES
+FTATSNTSSASGISIKDEQQSLLKMEPQSSDFCPYTANIPKLNPSYLTNTASTKQLGYGEQPDTSAHSSPPA
+QKIVLDTARYSADLCSDNPLPQATNIKTDPCSSFSSFVGEGILTRASMGYSQQAIQTLPVHKSEPFRLSASS
+APADSPFWCQSTGPSEDHHLQIDYLSPAGLHSTCKYSSTNAYSSYLGVLPQRVCVICGDEASGCHYGVLTCG
+SCKVFFKRAVEGHHNYLCAGRNDCIVDKIRRKNCPACRLRKCYQAGMILGGRKLKKLGALKAAGLTQALVAH
+SLTPRRLSGDSQALMPLGCLPGVRELHLSPQIISVLESIEPEVVYSGYDNSQPDMPNMLLNSLNRLCERQLL
+RIVKWSKSLPGFRSLHINDQMALIQYSWMSLMVFSLGWRSFQNVTSDYLYFAPDLILNEEYMRRSPIFDLCM
+AMQFIPQEFANLQVTKEEFLCMKVLLLLNTVPLEGLKSQPQFDEMRQNYIHELTKAIHLRENGVVACSQRFY
+HLTKLMDHMHDIVKKLHLYCLSTFIQADAMRVEFPEMMSEVIASQLPRVLAGMVKPLLFHTK
+>UNIPROT|Q7LCB3/1-530 estrogen receptor 2 isoform A
+MDIKNSPSSLNSPSSYNCSQSILPLEHGSIYIPSSYVDSHHEYPAMTFYSPAVMNYSIPSNVTNLEGGPGRQ
+TTSPNVLWPTPGHLSPLVVHRQLSHLYAEPQKSPWCEARSLEHTLPVNRETLKRKVSGNRCASPVTGPGSKR
+DAHFCAVCSDYASGYHYGVWSCEGCKAFFKRSIQGHNDYICPATNQCTIDKNRRKSCQACRLRKCYEVGMVK
+CGSRRERCGYRLVRRQRSADEQLHCAGKAKRSGGHAPRVRELLLDALSPEQLVLTLLEAEPPHVLISRPSAP
+FTEASMMMSLTKLADKELVHMISWAKKIPGFVELSLFDQVRLLESCWMEVLMMGLMWRSIDHPGKLIFAPDL
+VLDRDEGKCVEGILEIFDMLLATTSRFRELKLQHKEYLCVKAMILLNSSMYPLVTATQDADSSRKLAHLLNA
+VTDALVWVIAKSGISSQQQSMRLANLLMLLSHVRHASNKGMEHLLNMKCKNVVPVYDLLLEMLNAHVLRGCK
+SSITGSECSPAEDSKSKEGSQNPQSQ
+>UNIPROT|Q9YGV9/1-848 androgen receptor alpha
+MEIPVGLGGVSDATNAVFRGPYQNVFHSLQVAFQSHGAVSRSLDFPNTKYGFLQNRHFCEMRQENKQPPCKG
+LGLFYGNHRNSDTGTNEDDIACFSRQSDAEARPGIFSESSLDTGDEITCKLQSDNQGVRASGPLLPGSSGCN
+SGQKSSLACTSQQRETTSQSDTCAGESCSEHQATTISETARELCNAVSVSLGLNLDLNDMNDLSSNQISSTE
+SDTSQAIYLFESSPGYTGVGLNALVRDCKCQSAREGTSTQQYDRGAMFKINRVNDLPLQPAPPRHTSISDAK
+WDMEAGLCAQMEHKDSEKCANMDGAHSTSVFSQFDQLLPVNASHYSQNVSVRVEPQSDFSPILYKSPGIQKN
+AEKYNVQYDATIKSEDGKTTSEREWGFQYRYNESCSTPSAPPRHCAHQNRAGPYNQFFFNPFEYAKRGVVSR
+EGYSLEHGFPNNLARTPYSGSLKNELGDRLSGPYPDVSYRYEGERENVFPVEFFFPPQRTCLICGDEASGCH
+YGALTCGSCKVFFKRAAEGKQKYLCASINDCTIDKLRRKNCPSCRLKRCFAAGMTLGARKLKKIGQMRAPED
+GQGQGPAEAELSVSPKYDLGFHTQSMFLNILEAIEPEVVNAGHDYGQPDSAASLLTSLNELGERQLVKVVKW
+AKGMPGFRSLYVDDQMTVIQHSWMAVMVFALGWRSFKNVKSRMLYFAPDLVFNEHRMQVSTMYEHCIRMKNF
+SQEFAMLQVSQEEFLCMKALLLFSTIPVEGLKGQNFFDELRRSYINELDRLVSFRSKSSCSERFQQLTRLLD
+SLQPVLKKLHQFTFDLFVQSQNLSNQVCFPEMISEIISVHVPKILAGTVKPILFHK
+>UNIPROT|Q90ZM7/1-438 corticoid receptor
+GVEFQLPYSASATSFRPSVATSSASGISNFSNGNNFGFLSPNGVQQDGFPYPGFTSPAQSSVPPQKACLICS
+DEASGCHYGVLTCGSCKVFFKRAVEGQHNYLCAGRNDCIIDKIRRKNCPACRLRKCIQAGMTLGARKLKKQG
+RVKGENQRSPASSTATTSSATPQPSSNSTAVTTFSPPPTGEPIFSPTLIAILQAIEPEVVMSGYDNTRSQTT
+AYMLSSLNRLCDKQLVSIVKWAKSLPGFRNLHIDDQMVLIQYSWMGLMSFAMSWRSFQHTNSKLLYFAPDLV
+FDETRMQQSAMYQLCVEMRQVSEDFMKLQVTSEEFLCMKAILLLSTVPQEGLKSQGCFEEMRISYIRELNRT
+IARTEKNAVQCWQRFYQLTKLLXCMQDLVSKLLEFCFATFTQTQVWSVEFPDMMAEIISAQLASHHGREARA
+LHFHKK
+>UNIPROT|Q91445/1-344 androgen receptor
+EASGCHYGALTCGSCKVFFKRAAEGKQKYLCASRNDCTIDKFRRKNCPSCRLRKCYEAGMTLGARKLKKLGN
+LKAQDDIEGASSSSPTEEQAPKLVMTRIDGYECQPIFLNVLEAIEPGVVCAGHDNSQPDSFSNLLTSLNELG
+ERQLVYVVKWAKALPGFRNLHVDDQMSIIQYSWMGLMVFAMGWRSFTNVNSRMLYFAPDLVFNEYRMHKSRM
+YSQCIRMRHLSQEFGWLQITPQGFLCMKALLFFSIIPVDGLKNQKLFDELRMNYIKELDRIIACKRKNPTSC
+SRRFYQLTKVLDSVTPIAKDLHQFTFDLLIKAHMVSVDYPEMMAEIISVQVPKILS
+>UNIPROT|Q9VSE9/1-484 estrogen-related receptor
+MSDGVSILHIKQEVDTPSASCFSPSSKSTATQSGTNGLKSSPSVSPERQLCSSTTSLSCDLHNVSLSNDGDS
+LKGSGTSGGNGGGGGGGTSGGNATNASAGAGSGSVRDELRRLCLVCGDVASGFHYGVASCEACKAFFKRTIQ
+GNIEYTCPANNECEINKRRRKACQACRFQKCLLMGMLKEGVRLDRVRGGRQKYRRNPVSNSYQTMQLLYQSN
+TTSLCDVKILEVLNSYEPDALSVQTPPPQVHTTSITNDEASSSSGSIKLESSVVTPNGTCIFQNNNNNDPNE
+ILSVLSDIYDKELVSVIGWAKQIPGFIDLPLNDQMKLLQVSWAEILTLQLTFRSLPFNGKLCFATDVWMDEH
+LAKECGYTEFYYHCVQIAQRMERISPRREEYYLLKALLLANCDILLDDQSSLRAFRDTILNSLNDVVYLLRH
+SSAVSHQQQLLLLLPSLRQADDILRRFWRGIARDEVITMKKLFLEMLEPLAR
+>UNIPROT|Q90ZM8/1-554 estrogen receptor
+ARGFSEAHGYEYSGASLYQPLPPSCTEFSIGAHQQQQHQHQHHQHQHQQHHHQQQQQQPQPQQNGVLGEGQS
+SHLSYLPPSTELPQYVPSSPSAPYSMELGAGRPHGYDPGPQSLYRGGVESSAPPYSEQQQVVGGGGAMSAMG
+LTEPRHVSSGSLPSSTRPERSTQFCAVCSDYASGYHYGVWSCEGCKAFFKRSTQGHNDYMCPATNQCTIDRN
+RRKSCQACRLRKCYEVGMVKGVRKDRKGFRGVKHKRKRPIPQKNGGEGGAGGGQDVSETRPQGERPSGPRDR
+ESAVSSLEADQVISALLEAEPPTVLSSYDPDKPVTEASLMAALTSLADRELVHMITWAKKIPGFTAIGLSDQ
+VQLLECCWLEILIVGLIWRSIDRPGQLHFAPNLILGREDARNVEGMLDMFDMLLVTVSRFRELHLRREEYVC
+LKAMILLNSGVFFCLSNSAGEQTNVQLIQQILEKVMDALGSTIGHIEASPPQHSRRLSQLLLLLSQIRHISN
+KGIEHLNSMKRKNVIPLYDLLLELLDAHSLQNTGLRTSPPPQDFRATLVP
index dc9160f..2506456 100644 (file)
@@ -47,8 +47,8 @@ something more meaningful.</p>
 as tabs within a single alignment window. They can be viewed
 simultanously by pressing <strong>X</strong> (or via <strong>&quot;View&#8594;Expand&quot;</strong>)
 to expand each view into its own linked alignment window. Expanded views
-are gathered back into into a single tabbed alignment window by pressing
-<strong>G</strong>, or by selecting <strong>&quot;View&#8594;Gather&quot;</strong>).
+are gathered back into a single tabbed alignment window by pressing
+<strong>G</strong>, or by selecting <strong>&quot;View&#8594;Gather&quot;</strong>.
 </p>
 <p><strong>Hidden Sequence Representatives and Multiple
 Views</strong></p>
diff --git a/lib/jersey-client-1.19.jar b/lib/jersey-client-1.19.jar
new file mode 100644 (file)
index 0000000..c9e0f56
Binary files /dev/null and b/lib/jersey-client-1.19.jar differ
diff --git a/lib/jersey-core-1.19.jar b/lib/jersey-core-1.19.jar
new file mode 100644 (file)
index 0000000..92feb63
Binary files /dev/null and b/lib/jersey-core-1.19.jar differ
diff --git a/lib/jersey-json-1.19.jar b/lib/jersey-json-1.19.jar
new file mode 100644 (file)
index 0000000..b609411
Binary files /dev/null and b/lib/jersey-json-1.19.jar differ
diff --git a/lib/jsr311-api-1.1.1.jar b/lib/jsr311-api-1.1.1.jar
new file mode 100644 (file)
index 0000000..ec8bc81
Binary files /dev/null and b/lib/jsr311-api-1.1.1.jar differ
diff --git a/resources/images/dna.png b/resources/images/dna.png
new file mode 100644 (file)
index 0000000..f9854fe
Binary files /dev/null and b/resources/images/dna.png differ
diff --git a/resources/images/error.png b/resources/images/error.png
new file mode 100644 (file)
index 0000000..6d68a8c
Binary files /dev/null and b/resources/images/error.png differ
diff --git a/resources/images/good.png b/resources/images/good.png
new file mode 100644 (file)
index 0000000..ebbacc9
Binary files /dev/null and b/resources/images/good.png differ
diff --git a/resources/images/loading.gif b/resources/images/loading.gif
new file mode 100644 (file)
index 0000000..23ed238
Binary files /dev/null and b/resources/images/loading.gif differ
diff --git a/resources/images/protein.png b/resources/images/protein.png
new file mode 100644 (file)
index 0000000..3789793
Binary files /dev/null and b/resources/images/protein.png differ
diff --git a/resources/images/sugar.png b/resources/images/sugar.png
new file mode 100644 (file)
index 0000000..5d62ce5
Binary files /dev/null and b/resources/images/sugar.png differ
index 2e37e06..0d3d491 100644 (file)
@@ -216,6 +216,7 @@ label.none = None
 label.above_identity_threshold = Above Identity Threshold
 label.show_sequence_features = Show Sequence Features
 label.nucleotide = Nucleotide
+label.protein = Protein
 label.to_new_alignment = To New Alignment
 label.to_this_alignment = Add To This Alignment
 label.apply_colour_to_all_groups = Apply Colour To All Groups
@@ -228,14 +229,17 @@ label.documentation = Documentation
 label.about = About...
 label.show_sequence_limits = Show Sequence Limits
 label.feature_settings = Feature Settings...
-label.sequence_features = Sequence Features
 label.all_columns = All Columns
 label.all_sequences = All Sequences
 label.selected_columns = Selected Columns 
 label.selected_sequences = Selected Sequences
+label.except_selected_sequences = All except selected sequences
 label.all_but_selected_region = All but Selected Region (Shift+Ctrl+H)
 label.selected_region = Selected Region
 label.all_sequences_columns = All Sequences and Columns
+label.hide_insertions = Hide columns gapped for selection
+label.hide_selected_annotations = Hide selected annotations
+label.show_selected_annotations = Show selected annotations
 label.group_consensus = Group Consensus
 label.group_conservation = Group Conservation
 label.show_consensus_histogram = Show Consensus Histogram
@@ -382,6 +386,7 @@ label.automatically_associate_pdb_files_with_sequences_same_name = Do you want t
 label.automatically_associate_pdb_files_by_name = Automatically Associate PDB files by name
 label.ignore_unmatched_dropped_files_info = <html>Do you want to <em>ignore</em> the {0} files whose names did not match any sequence IDs ?</html>
 label.ignore_unmatched_dropped_files = Ignore unmatched dropped files?
+label.view_name_original = Original
 label.enter_view_name = Enter View Name
 label.enter_label = Enter label
 label.enter_label_for_the_structure = Enter a label for the structure?
@@ -480,8 +485,6 @@ label.load_associated_tree = Load Associated Tree ...
 label.load_features_annotations = Load Features/Annotations ...
 label.export_features = Export Features ...
 label.export_annotations = Export Annotations ...
-label.jalview_copy = Copy (Jalview Only)
-label.jalview_cut = Cut (Jalview Only)
 label.to_upper_case = To Upper Case
 label.to_lower_case = To Lower Case
 label.toggle_case = Toggle Case
@@ -576,7 +579,8 @@ label.database_references = Database References
 label.share_selection_across_views = Share selection across views
 label.scroll_highlighted_regions = Scroll to highlighted regions
 label.gap_symbol = Gap Symbol
-label.alignment_colour = Alignment Colour
+label.prot_alignment_colour = Protein Alignment Colour
+label.nuc_alignment_colour = Nucleotide Alignment Colour
 label.address = Address
 label.port = Port
 label.default_browser_unix = Default Browser (Unix)
@@ -671,7 +675,7 @@ label.view_structure = View Structure
 label.clustalx_colours = Clustalx colours
 label.above_identity_percentage = Above % Identity
 label.create_sequence_details_report_annotation_for = Annotation for {0}
-label.sequece_details_for = Sequece Details for {0}
+label.sequence_details_for = Sequence Details for {0}
 label.sequence_name = Sequence Name
 label.sequence_description = Sequence Description
 label.edit_sequence_name_description = Edit Sequence Name/Description
@@ -689,12 +693,15 @@ label.save_png_image = Save As PNG Image
 label.load_tree_for_sequence_set = Load a tree for this sequence set
 label.export_image = Export Image
 label.vamsas_store = VAMSAS store
-label.translate_cDNA = Translate cDNA
+label.translate_cDNA = Translate as cDNA
+label.linked_view_title = Linked cDNA and protein view
+label.align = Align
 label.extract_scores = Extract Scores
 label.get_cross_refs = Get Cross References
 label.sort_alignment_new_tree = Sort Alignment With New Tree
 label.add_sequences = Add Sequences
 label.new_window = New Window
+label.split_window = Split Window
 label.refresh_available_sources = Refresh Available Sources
 label.use_registry = Use Registry
 label.add_local_source = Add Local Source
@@ -734,6 +741,7 @@ label.paste_new_window = Paste To New Window
 label.settings_for_param = Settings for {0}
 label.view_params = View {0}
 label.select_all_views = Select all views
+label.all_views = All Views
 label.align_sequences_to_existing_alignment = Align sequences to an existing alignment
 label.realign_with_params = Realign with {0}
 label.calcname_with_default_settings = {0} with Defaults
@@ -775,7 +783,6 @@ label.use_sequence_id_1 = Use $SEQUENCE_ID$ or $SEQUENCE_ID=/<regex>/=$
 label.use_sequence_id_2 = \nto embed sequence id in URL
 label.ws_parameters_for = Parameters for {0}
 label.switch_server = Switch server
-label.open_jabaws_web_page = Opens the JABAWS server's homepage in web browser
 label.choose_jabaws_server = Choose a server for running this service
 label.services_at = Services at {0}
 label.rest_client_submit = {0} using {1}
@@ -1178,6 +1185,13 @@ label.show_logo = Show Logo
 label.normalise_logo = Normalise Logo
 label.no_colour_selection_in_scheme = Please, make a colour selection before to apply colour scheme
 label.no_colour_selection_warn = Error saving colour scheme
+label.open_split_window? = Would you like to open as a split window, with cDNA and protein linked?
+label.open_split_window = Open split window
+label.no_mappings = No mappings found
+label.mapping_failed = No sequence mapping could be made between the alignments.<br>A mapping requires sequence names to match, and equivalent sequence lengths.
+action.no = No
+action.yes = Yes
+label.for = for
 label.select_by_annotation = Select By Annotation
 action.select_by_annotation = Select by Annotation...
 label.threshold_filter =  Threshold Filter
@@ -1192,3 +1206,22 @@ label.search_filter = Search Filter
 label.display_name = Display Label
 label.description = Description
 label.include_description= Include Description
+action.back = Back
+label.hide_insertions = Hide Insertions
+label.mark_as_representative = Mark as representative
+label.open_jabaws_web_page = Open JABAWS web page
+label.opens_the_jabaws_server_homepage = Opens the JABAWS server's homepage in web browser
+label.pdb_sequence_getcher = PDB Sequence Fetcher
+label.result = result
+label.results = results
+label.structure_chooser = Structure Chooser
+label.select = Select : 
+label.invert = Invert 
+label.select_pdb_file = Select PDB File
+info.select_filter_option = Select Filter Option/Manual Entry
+info.associate_wit_sequence = Associate with Sequence
+label.search_result = Search Result
+label.found_structures_summary = Found Structures Summary
+label.configure_displayed_columns = Configure Displayed Columns
+label.start_jalview = Start Jalview
+label.biojs_html_export = BioJS
index f2ba0a5..d643648 100755 (executable)
                                                        type="xs:boolean" use="optional" default="true" />
                                                <xs:attribute name="showSequenceLogo"
                                                        type="xs:boolean" use="optional" default="false" />
-            <xs:attribute name="normaliseSequenceLogo"
-              type="xs:boolean" use="optional" default="false" />
+                                   <xs:attribute name="normaliseSequenceLogo"
+                                     type="xs:boolean" use="optional" default="false" />
                                                <xs:attribute name="ignoreGapsinConsensus"
                                                        type="xs:boolean" use="optional" default="true" />
-
-                                               <xs:attribute name="startRes" type="xs:int" />
+                                                       <xs:attribute name="startRes" type="xs:int" />
                                                <xs:attribute name="startSeq" type="xs:int" />
                                                <xs:attribute name="fontName" type="xs:string" />
                                                <xs:attribute name="fontSize" type="xs:int" />
                                                                </xs:documentation>
                                                        </xs:annotation>
                                                </xs:attribute>
+                                               <xs:attribute name="complementId" type="xs:string"
+                                                       use="optional">
+                                                       <xs:annotation>
+                                                               <xs:documentation>
+                                                                       The viewport id of this viewport's (cdna/protein) coding complement, if any
+                                                               </xs:documentation>
+                                                       </xs:annotation>
+                                               </xs:attribute>
                                        </xs:complexType>
                                </xs:element>
                                <xs:element name="UserColours" minOccurs="0"
index 69a958e..211cff0 100644 (file)
  */
 package MCview;
 
-import java.io.*;
-import java.util.*;
+import jalview.analysis.AlignSeq;
+import jalview.appletgui.AlignmentPanel;
+import jalview.appletgui.FeatureRenderer;
+import jalview.appletgui.SequenceRenderer;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.structure.AtomSpec;
+import jalview.structure.StructureListener;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
 
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Event;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Image;
 // JBPNote TODO: This class is quite noisy - needs proper log.info/log.debug
-import java.awt.*;
-import java.awt.event.*;
-
-import jalview.analysis.*;
-import jalview.datamodel.*;
-
-import jalview.appletgui.*;
-import jalview.structure.*;
-import jalview.util.MessageManager;
+import java.awt.Panel;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Vector;
 
 public class AppletPDBCanvas extends Panel implements MouseListener,
         MouseMotionListener, StructureListener
@@ -144,7 +159,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
       pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol);
 
       if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
+      {
         pdbentry.setFile("INLINE" + pdb.id);
+      }
 
     } catch (Exception ex)
     {
@@ -172,10 +189,10 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     {
 
       mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
-              + ((PDBChain) pdb.chains.elementAt(i)).sequence
+              + pdb.chains.elementAt(i).sequence
                       .getSequenceAsString());
       mappingDetails.append("\nNo of residues = "
-              + ((PDBChain) pdb.chains.elementAt(i)).residues.size()
+              + pdb.chains.elementAt(i).residues.size()
               + "\n\n");
 
       // Now lets compare the sequences to get
@@ -183,8 +200,8 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
       // Align the sequence to the pdb
       // TODO: DNa/Pep switch
       AlignSeq as = new AlignSeq(sequence,
-              ((PDBChain) pdb.chains.elementAt(i)).sequence,
-              ((PDBChain) pdb.chains.elementAt(i)).isNa ? AlignSeq.DNA
+              pdb.chains.elementAt(i).sequence,
+              pdb.chains.elementAt(i).isNa ? AlignSeq.DNA
                       : AlignSeq.PEP);
       as.calcScoreMatrix();
       as.traceAlignment();
@@ -218,7 +235,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
       mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend);
     }
 
-    mainchain = (PDBChain) pdb.chains.elementAt(maxchain);
+    mainchain = pdb.chains.elementAt(maxchain);
 
     mainchain.pdbstart = pdbstart;
     mainchain.pdbend = pdbend;
@@ -277,9 +294,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+      if (pdb.chains.elementAt(ii).isVisible)
       {
-        Vector tmp = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+        Vector tmp = pdb.chains.elementAt(ii).bonds;
 
         for (int i = 0; i < tmp.size(); i++)
         {
@@ -308,9 +325,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+      if (pdb.chains.elementAt(ii).isVisible)
       {
-        Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+        Vector bonds = pdb.chains.elementAt(ii).bonds;
 
         for (int i = 0; i < bonds.size(); i++)
         {
@@ -379,9 +396,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
       }
     }
 
-    width[0] = (float) Math.abs(max[0] - min[0]);
-    width[1] = (float) Math.abs(max[1] - min[1]);
-    width[2] = (float) Math.abs(max[2] - min[2]);
+    width[0] = Math.abs(max[0] - min[0]);
+    width[1] = Math.abs(max[1] - min[1]);
+    width[2] = Math.abs(max[2] - min[2]);
 
     maxwidth = width[0];
 
@@ -438,9 +455,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     // Find centre coordinate
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+      if (pdb.chains.elementAt(ii).isVisible)
       {
-        Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+        Vector bonds = pdb.chains.elementAt(ii).bonds;
 
         bsize += bonds.size();
 
@@ -567,7 +584,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     {
       for (int ii = 0; ii < pdb.chains.size(); ii++)
       {
-        chain = (PDBChain) pdb.chains.elementAt(ii);
+        chain = pdb.chains.elementAt(ii);
 
         for (int i = 0; i < chain.bonds.size(); i++)
         {
@@ -779,7 +796,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
       repaint();
       if (foundchain != -1)
       {
-        PDBChain chain = (PDBChain) pdb.chains.elementAt(foundchain);
+        PDBChain chain = pdb.chains.elementAt(foundchain);
         if (chain == mainchain)
         {
           if (fatom.alignmentMapping != -1)
@@ -825,7 +842,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     PDBChain chain = null;
     if (foundchain != -1)
     {
-      chain = (PDBChain) pdb.chains.elementAt(foundchain);
+      chain = pdb.chains.elementAt(foundchain);
       if (chain == mainchain)
       {
         mouseOverStructure(fatom.resNumber, chain.id);
@@ -875,18 +892,18 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
     if ((evt.getModifiers() & Event.META_MASK) != 0)
     {
-      objmat.rotatez((float) ((mx - omx)));
+      objmat.rotatez(((mx - omx)));
     }
     else
     {
-      objmat.rotatex((float) ((omy - my)));
-      objmat.rotatey((float) ((omx - mx)));
+      objmat.rotatex(((omy - my)));
+      objmat.rotatey(((omx - mx)));
     }
 
     // Alter the bonds
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+      Vector bonds = pdb.chains.elementAt(ii).bonds;
 
       for (int i = 0; i < bonds.size(); i++)
       {
@@ -927,11 +944,11 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+      PDBChain chain = pdb.chains.elementAt(ii);
 
       if (chain.isVisible)
       {
-        Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+        Vector bonds = pdb.chains.elementAt(ii).bonds;
 
         for (int i = 0; i < bonds.size(); i++)
         {
@@ -985,13 +1002,13 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+      PDBChain chain = pdb.chains.elementAt(ii);
       int truex;
       Bond tmpBond = null;
 
       if (chain.isVisible)
       {
-        Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+        Vector bonds = pdb.chains.elementAt(ii).bonds;
 
         for (int i = 0; i < bonds.size(); i++)
         {
@@ -1032,7 +1049,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
       if (fatom != null) // )&& chain.ds != null)
       {
-        chain = (PDBChain) pdb.chains.elementAt(foundchain);
+        chain = pdb.chains.elementAt(foundchain);
       }
     }
 
@@ -1100,7 +1117,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
   {
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+      PDBChain chain = pdb.chains.elementAt(ii);
       chain.isVisible = b;
     }
     mainchain.isVisible = true;
@@ -1121,7 +1138,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
   public void mouseOverStructure(int pdbResNum, String chain)
   {
     if (lastMessage == null || !lastMessage.equals(pdbResNum + chain))
+    {
       ssm.mouseOverStructure(pdbResNum, chain, pdbentry.getFile());
+    }
 
     lastMessage = pdbResNum + chain;
   }
@@ -1130,19 +1149,40 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
   StringBuffer eval = new StringBuffer();
 
-  public void highlightAtom(int atomIndex, int pdbResNum, String chain,
-          String pdbfile)
+  /**
+   * Highlight the specified atoms in the structure.
+   * 
+   * @param atoms
+   */
+  @Override
+  public void highlightAtoms(List<AtomSpec> atoms)
   {
     if (!seqColoursReady)
     {
       return;
     }
-
-    if (highlightRes != null && highlightRes.contains((atomIndex - 1) + ""))
+    for (AtomSpec atom : atoms)
     {
-      return;
+      int atomIndex = atom.getAtomIndex();
+
+      if (highlightRes != null
+              && highlightRes.contains((atomIndex - 1) + ""))
+      {
+        continue;
+      }
+
+      highlightAtom(atomIndex);
     }
 
+    redrawneeded = true;
+    repaint();
+  }
+
+  /**
+   * @param atomIndex
+   */
+  protected void highlightAtom(int atomIndex)
+  {
     int index = -1;
     Bond tmpBond;
     for (index = 0; index < mainchain.bonds.size(); index++)
@@ -1178,9 +1218,6 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
         break;
       }
     }
-
-    redrawneeded = true;
-    repaint();
   }
 
   public Color getColour(int atomIndex, int pdbResNum, String chain,
index b600bc9..9cbcebb 100755 (executable)
@@ -20,7 +20,7 @@
  */
 package MCview;
 
-import java.awt.*;
+import java.awt.Color;
 
 public class Atom
 {
@@ -77,16 +77,16 @@ public class Atom
     chain = str.substring(21, 22);
 
     resNumber = Integer.parseInt(str.substring(22, 26).trim());
-    resNumIns = str.substring(22, 27);
+    resNumIns = str.substring(22, 27).trim();
     insCode = str.substring(26, 27).charAt(0);
-    this.x = (float) (new Float(str.substring(30, 38).trim()).floatValue());
-    this.y = (float) (new Float(str.substring(38, 46).trim()).floatValue());
-    this.z = (float) (new Float(str.substring(47, 55).trim()).floatValue());
+    this.x = (new Float(str.substring(30, 38).trim()).floatValue());
+    this.y = (new Float(str.substring(38, 46).trim()).floatValue());
+    this.z = (new Float(str.substring(47, 55).trim()).floatValue());
     // optional entries - see JAL-730
     String tm = str.substring(54, 60).trim();
     if (tm.length() > 0)
     {
-      occupancy = (float) (new Float(tm)).floatValue();
+      occupancy = (new Float(tm)).floatValue();
     }
     else
     {
@@ -96,7 +96,7 @@ public class Atom
     tm = str.substring(60, 66).trim();
     if (tm.length() > 0)
     {
-      tfactor = (float) (new Float(tm).floatValue());
+      tfactor = (new Float(tm).floatValue());
     }
     else
     {
index f6e6427..6f76a25 100644 (file)
  */
 package MCview;
 
-import java.io.*;
-import java.util.*;
-
+import jalview.analysis.AlignSeq;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.FeatureRenderer;
+import jalview.gui.SequenceRenderer;
+import jalview.structure.AtomSpec;
+import jalview.structure.StructureListener;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Event;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
 // JBPNote TODO: This class is quite noisy - needs proper log.info/log.debug
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-
-import jalview.analysis.*;
-import jalview.datamodel.*;
-import jalview.gui.*;
-import jalview.structure.*;
+import java.awt.Image;
+import java.awt.RenderingHints;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JPanel;
+import javax.swing.ToolTipManager;
 
 public class PDBCanvas extends JPanel implements MouseListener,
         MouseMotionListener, StructureListener
@@ -134,7 +153,9 @@ public class PDBCanvas extends JPanel implements MouseListener,
       pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol);
 
       if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
+      {
         pdbentry.setFile("INLINE" + pdb.id);
+      }
 
     } catch (Exception ex)
     {
@@ -167,17 +188,17 @@ public class PDBCanvas extends JPanel implements MouseListener,
     {
 
       mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
-              + ((PDBChain) pdb.chains.elementAt(i)).sequence
+              + pdb.chains.elementAt(i).sequence
                       .getSequenceAsString());
       mappingDetails.append("\nNo of residues = "
-              + ((PDBChain) pdb.chains.elementAt(i)).residues.size()
+              + pdb.chains.elementAt(i).residues.size()
               + "\n\n");
 
       // Now lets compare the sequences to get
       // the start and end points.
       // Align the sequence to the pdb
       AlignSeq as = new AlignSeq(sequence,
-              ((PDBChain) pdb.chains.elementAt(i)).sequence, "pep");
+              pdb.chains.elementAt(i).sequence, "pep");
       as.calcScoreMatrix();
       as.traceAlignment();
       PrintStream ps = new PrintStream(System.out)
@@ -210,7 +231,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
       mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend);
     }
 
-    mainchain = (PDBChain) pdb.chains.elementAt(maxchain);
+    mainchain = pdb.chains.elementAt(maxchain);
 
     mainchain.pdbstart = pdbstart;
     mainchain.pdbend = pdbend;
@@ -254,9 +275,9 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+      if (pdb.chains.elementAt(ii).isVisible)
       {
-        Vector tmp = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+        Vector tmp = pdb.chains.elementAt(ii).bonds;
 
         for (int i = 0; i < tmp.size(); i++)
         {
@@ -286,9 +307,9 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+      if (pdb.chains.elementAt(ii).isVisible)
       {
-        Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+        Vector bonds = pdb.chains.elementAt(ii).bonds;
 
         for (int i = 0; i < bonds.size(); i++)
         {
@@ -362,9 +383,9 @@ public class PDBCanvas extends JPanel implements MouseListener,
      * System.out.println("zmax " + max[2] + " min " + min[2]);
      */
 
-    width[0] = (float) Math.abs(max[0] - min[0]);
-    width[1] = (float) Math.abs(max[1] - min[1]);
-    width[2] = (float) Math.abs(max[2] - min[2]);
+    width[0] = Math.abs(max[0] - min[0]);
+    width[1] = Math.abs(max[1] - min[1]);
+    width[2] = Math.abs(max[2] - min[2]);
 
     maxwidth = width[0];
 
@@ -421,9 +442,9 @@ public class PDBCanvas extends JPanel implements MouseListener,
     // Find centre coordinate
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+      if (pdb.chains.elementAt(ii).isVisible)
       {
-        Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+        Vector bonds = pdb.chains.elementAt(ii).bonds;
 
         bsize += bonds.size();
 
@@ -537,7 +558,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     {
       for (int ii = 0; ii < pdb.chains.size(); ii++)
       {
-        chain = (PDBChain) pdb.chains.elementAt(ii);
+        chain = pdb.chains.elementAt(ii);
 
         for (int i = 0; i < chain.bonds.size(); i++)
         {
@@ -751,7 +772,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
       repaint();
       if (foundchain != -1)
       {
-        PDBChain chain = (PDBChain) pdb.chains.elementAt(foundchain);
+        PDBChain chain = pdb.chains.elementAt(foundchain);
         if (chain == mainchain)
         {
           if (fatom.alignmentMapping != -1)
@@ -797,7 +818,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     PDBChain chain = null;
     if (foundchain != -1)
     {
-      chain = (PDBChain) pdb.chains.elementAt(foundchain);
+      chain = pdb.chains.elementAt(foundchain);
       if (chain == mainchain)
       {
         mouseOverStructure(fatom.resNumber, chain.id);
@@ -840,18 +861,18 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
     if ((evt.getModifiers() & Event.META_MASK) != 0)
     {
-      objmat.rotatez((float) ((mx - omx)));
+      objmat.rotatez(((mx - omx)));
     }
     else
     {
-      objmat.rotatex((float) ((my - omy)));
-      objmat.rotatey((float) ((omx - mx)));
+      objmat.rotatex(((my - omy)));
+      objmat.rotatey(((omx - mx)));
     }
 
     // Alter the bonds
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+      Vector bonds = pdb.chains.elementAt(ii).bonds;
 
       for (int i = 0; i < bonds.size(); i++)
       {
@@ -892,11 +913,11 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+      PDBChain chain = pdb.chains.elementAt(ii);
 
       if (chain.isVisible)
       {
-        Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+        Vector bonds = pdb.chains.elementAt(ii).bonds;
 
         for (int i = 0; i < bonds.size(); i++)
         {
@@ -948,13 +969,13 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+      PDBChain chain = pdb.chains.elementAt(ii);
       int truex;
       Bond tmpBond = null;
 
       if (chain.isVisible)
       {
-        Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+        Vector bonds = pdb.chains.elementAt(ii).bonds;
 
         for (int i = 0; i < bonds.size(); i++)
         {
@@ -995,7 +1016,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
       if (fatom != null) // )&& chain.ds != null)
       {
-        chain = (PDBChain) pdb.chains.elementAt(foundchain);
+        chain = pdb.chains.elementAt(foundchain);
       }
     }
 
@@ -1060,7 +1081,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
   {
     for (int ii = 0; ii < pdb.chains.size(); ii++)
     {
-      PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+      PDBChain chain = pdb.chains.elementAt(ii);
       chain.isVisible = b;
     }
     mainchain.isVisible = true;
@@ -1081,7 +1102,9 @@ public class PDBCanvas extends JPanel implements MouseListener,
   public void mouseOverStructure(int pdbResNum, String chain)
   {
     if (lastMessage == null || !lastMessage.equals(pdbResNum + chain))
+    {
       ssm.mouseOverStructure(pdbResNum, chain, pdbentry.getFile());
+    }
 
     lastMessage = pdbResNum + chain;
   }
@@ -1090,19 +1113,42 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
   StringBuffer eval = new StringBuffer();
 
-  public void highlightAtom(int atomIndex, int pdbResNum, String chain,
-          String pdbfile)
+  /**
+   * Highlight the specified atoms in the structure.
+   * 
+   * @param atoms
+   */
+  @Override
+  public void highlightAtoms(List<AtomSpec> atoms)
   {
     if (!seqColoursReady)
     {
       return;
     }
 
-    if (highlightRes != null && highlightRes.contains((atomIndex - 1) + ""))
+    for (AtomSpec atom : atoms)
     {
-      return;
+      int atomIndex = atom.getAtomIndex();
+      if (highlightRes != null
+              && highlightRes.contains((atomIndex - 1) + ""))
+      {
+        continue;
+      }
+
+      highlightAtom(atomIndex);
     }
 
+    redrawneeded = true;
+    repaint();
+  }
+
+  /**
+   * Highlight the atom at the specified index.
+   * 
+   * @param atomIndex
+   */
+  protected void highlightAtom(int atomIndex)
+  {
     int index = -1;
     Bond tmpBond;
     for (index = 0; index < mainchain.bonds.size(); index++)
@@ -1138,9 +1184,6 @@ public class PDBCanvas extends JPanel implements MouseListener,
         break;
       }
     }
-
-    redrawneeded = true;
-    repaint();
   }
 
   public Color getColour(int atomIndex, int pdbResNum, String chain,
index 2c40e1c..b74e65a 100644 (file)
@@ -783,8 +783,8 @@ public class ChimeraManager
     List<String> reply = new ArrayList<String>();
     BufferedReader response = null;
     try {
-      response = HttpClientUtils.doHttpUrlPost(restUrl,
-              commands);
+      response = HttpClientUtils
+              .doHttpUrlPost(restUrl, commands, 100, 2000);
       String line = "";
       while ((line = response.readLine()) != null) {
         reply.add(line);
index 442e5f7..83412ea 100755 (executable)
@@ -128,15 +128,15 @@ public class ServiceHandle implements java.io.Serializable
 
   public synchronized boolean equals(java.lang.Object obj)
   {
-    if (!(obj instanceof ServiceHandle))
+    if (obj == null)
     {
       return false;
     }
-    ServiceHandle other = (ServiceHandle) obj;
-    if (obj == null)
+    if (!(obj instanceof ServiceHandle))
     {
       return false;
     }
+    ServiceHandle other = (ServiceHandle) obj;
     if (this == obj)
     {
       return true;
index 45c49d8..5ed466f 100755 (executable)
@@ -57,15 +57,15 @@ public class ServiceHandles implements java.io.Serializable
 
   public synchronized boolean equals(java.lang.Object obj)
   {
-    if (!(obj instanceof ServiceHandles))
+    if (obj == null)
     {
       return false;
     }
-    ServiceHandles other = (ServiceHandles) obj;
-    if (obj == null)
+    if (!(obj instanceof ServiceHandles))
     {
       return false;
     }
+    ServiceHandles other = (ServiceHandles) obj;
     if (this == obj)
     {
       return true;
index b96bf8e..d6088e4 100755 (executable)
  */
 package jalview.analysis;
 
-import java.util.*;
-
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceI;
 import jalview.util.Format;
-import jalview.datamodel.*;
+import jalview.util.MappingUtils;
+import jalview.util.QuickSort;
+
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Takes in a vector or array of sequences and column start and column end and
@@ -36,8 +45,8 @@ import jalview.datamodel.*;
  */
 public class AAFrequency
 {
-  // No need to store 1000s of strings which are not
-  // visible to the user.
+  private static final int TO_UPPER_CASE = 'A' - 'a'; // -32
+
   public static final String MAXCOUNT = "C";
 
   public static final String MAXRESIDUE = "R";
@@ -48,6 +57,21 @@ public class AAFrequency
 
   public static final String PROFILE = "P";
 
+  public static final String ENCODED_CHARS = "E";
+
+  /*
+   * Quick look-up of String value of char 'A' to 'Z'
+   */
+  private static final String[] CHARS = new String['Z' - 'A' + 1];
+
+  static
+  {
+    for (char c = 'A'; c <= 'Z'; c++)
+    {
+      CHARS[c - 'A'] = String.valueOf(c);
+    }
+  }
+
   public static final Hashtable[] calculate(List<SequenceI> list,
           int start, int end)
   {
@@ -83,16 +107,11 @@ public class AAFrequency
   }
 
   public static final void calculate(SequenceI[] sequences, int start,
-          int end, Hashtable[] result)
-  {
-    calculate(sequences, start, end, result, false);
-  }
-
-  public static final void calculate(SequenceI[] sequences, int start,
           int end, Hashtable[] result, boolean profile)
   {
     Hashtable residueHash;
-    int maxCount, nongap, i, j, v, jSize = sequences.length;
+    int maxCount, nongap, i, j, v;
+    int jSize = sequences.length;
     String maxResidue;
     char c = '-';
     float percentage;
@@ -134,7 +153,7 @@ public class AAFrequency
           }
           else if ('a' <= c && c <= 'z')
           {
-            c -= 32; // ('a' - 'A');
+            c += TO_UPPER_CASE;
           }
 
           nongap++;
@@ -153,20 +172,21 @@ public class AAFrequency
       }
       else
       {
-        for (v = 'A'; v < 'Z'; v++)
+        for (v = 'A'; v <= 'Z'; v++)
         {
-          if (values[v] < 2 || values[v] < maxCount)
+          // TODO why ignore values[v] == 1?
+          if (values[v] < 1 /* 2 */|| values[v] < maxCount)
           {
             continue;
           }
 
           if (values[v] > maxCount)
           {
-            maxResidue = String.valueOf((char) v);
+            maxResidue = CHARS[v - 'A'];
           }
           else if (values[v] == maxCount)
           {
-            maxResidue += String.valueOf((char) v);
+            maxResidue += CHARS[v - 'A'];
           }
           maxCount = values[v];
         }
@@ -177,6 +197,7 @@ public class AAFrequency
       }
       if (profile)
       {
+        // TODO use a 1-dimensional array with jSize, nongap in [0] and [1]
         residueHash.put(PROFILE, new int[][]
         { values, new int[]
         { jSize, nongap } });
@@ -218,17 +239,37 @@ public class AAFrequency
   {
     completeConsensus(consensus, hconsensus, iStart, width,
             ignoreGapsInConsensusCalculation, includeAllConsSymbols, null,
-            nseq); // new
-    // char[]
-    // { 'A', 'C', 'G', 'T', 'U' });
+            nseq);
   }
 
+  /**
+   * Derive the consensus annotations to be added to the alignment for display.
+   * This does not recompute the raw data, but may be called on a change in
+   * display options, such as 'show logo', which may in turn result in a change
+   * in the derived values.
+   * 
+   * @param consensus
+   *          the annotation row to add annotations to
+   * @param hconsensus
+   *          the source consensus data
+   * @param iStart
+   *          start column
+   * @param width
+   *          end column
+   * @param ignoreGapsInConsensusCalculation
+   *          if true, use the consensus calculated ignoring gaps
+   * @param includeAllConsSymbols
+   *          if true include all consensus symbols, else just show modal
+   *          residue
+   * @param alphabet
+   * @param nseq
+   *          number of sequences
+   */
   public static void completeConsensus(AlignmentAnnotation consensus,
           Hashtable[] hconsensus, int iStart, int width,
           boolean ignoreGapsInConsensusCalculation,
           boolean includeAllConsSymbols, char[] alphabet, long nseq)
   {
-    float tval, value;
     if (consensus == null || consensus.annotations == null
             || consensus.annotations.length < width)
     {
@@ -236,26 +277,9 @@ public class AAFrequency
       // initialised properly
       return;
     }
-    String fmtstr = "%3.1f";
-    int precision = 0;
-    while (nseq >= 10)
-    {
-      precision++;
-      nseq /= 10;
-    }
-    final Format fmt;
-    if (precision > 1)
-    {
-      // if (precision>2)
-      {
-        fmtstr = "%" + (2 + precision) + "." + (precision) + "f";
-      }
-      fmt = new Format(fmtstr);
-    }
-    else
-    {
-      fmt = null;
-    }
+
+    final Format fmt = getPercentageFormat(nseq);
+
     for (int i = iStart; i < width; i++)
     {
       Hashtable hci;
@@ -266,119 +290,377 @@ public class AAFrequency
         consensus.annotations[i] = null;
         continue;
       }
-      value = 0;
-      Float fv;
-      if (ignoreGapsInConsensusCalculation)
-      {
-        fv = (Float) hci.get(AAFrequency.PID_NOGAPS);
-      }
-      else
-      {
-        fv = (Float) hci.get(AAFrequency.PID_GAPS);
-      }
+      Float fv = (Float) hci
+              .get(ignoreGapsInConsensusCalculation ? PID_NOGAPS : PID_GAPS);
       if (fv == null)
       {
         consensus.annotations[i] = null;
         // data has changed below us .. give up and
         continue;
       }
-      value = fv.floatValue();
+      float value = fv.floatValue();
       String maxRes = hci.get(AAFrequency.MAXRESIDUE).toString();
-      String mouseOver = hci.get(AAFrequency.MAXRESIDUE) + " ";
+      StringBuilder mouseOver = new StringBuilder(64);
       if (maxRes.length() > 1)
       {
-        mouseOver = "[" + maxRes + "] ";
+        mouseOver.append("[").append(maxRes).append("] ");
         maxRes = "+";
       }
+      else
+      {
+        mouseOver.append(hci.get(AAFrequency.MAXRESIDUE) + " ");
+      }
       int[][] profile = (int[][]) hci.get(AAFrequency.PROFILE);
+      int sequenceCount = profile[1][0];
+      int nonGappedCount = profile[1][1];
+      int normalisedBy = ignoreGapsInConsensusCalculation ? nonGappedCount
+              : sequenceCount;
       if (profile != null && includeAllConsSymbols)
       {
-        mouseOver = "";
+        mouseOver.setLength(0);
         if (alphabet != null)
         {
           for (int c = 0; c < alphabet.length; c++)
           {
-            tval = profile[0][alphabet[c]] * 100f
-                    / profile[1][ignoreGapsInConsensusCalculation ? 1 : 0];
-            mouseOver += ((c == 0) ? "" : "; ") + alphabet[c] + " "
-                    + ((fmt != null) ? fmt.form(tval) : ((int) tval)) + "%";
+            float tval = profile[0][alphabet[c]] * 100f / normalisedBy;
+            mouseOver
+                    .append(((c == 0) ? "" : "; "))
+                    .append(alphabet[c])
+                    .append(" ")
+                    .append(((fmt != null) ? fmt.form(tval) : ((int) tval)))
+                    .append("%");
           }
         }
         else
         {
-          Object[] ca = new Object[profile[0].length];
+          // TODO do this sort once only in calculate()?
+          // char[][] ca = new char[profile[0].length][];
+          char[] ca = new char[profile[0].length];
           float[] vl = new float[profile[0].length];
           for (int c = 0; c < ca.length; c++)
           {
-            ca[c] = new char[]
-            { (char) c };
+            ca[c] = (char) c;
+            // ca[c] = new char[]
+            // { (char) c };
             vl[c] = profile[0][c];
           }
-          ;
-          jalview.util.QuickSort.sort(vl, ca);
-          for (int p = 0, c = ca.length - 1; profile[0][((char[]) ca[c])[0]] > 0; c--)
+          QuickSort.sort(vl, ca);
+          for (int p = 0, c = ca.length - 1; profile[0][ca[c]] > 0; c--)
           {
-            if (((char[]) ca[c])[0] != '-')
+            final char residue = ca[c];
+            if (residue != '-')
             {
-              tval = profile[0][((char[]) ca[c])[0]]
-                      * 100f
-                      / profile[1][ignoreGapsInConsensusCalculation ? 1 : 0];
-              mouseOver += ((p == 0) ? "" : "; ") + ((char[]) ca[c])[0]
-                      + " "
-                      + ((fmt != null) ? fmt.form(tval) : ((int) tval))
-                      + "%";
+              float tval = profile[0][residue] * 100f / normalisedBy;
+              mouseOver
+                      .append((((p == 0) ? "" : "; ")))
+                      .append(residue)
+                      .append(" ")
+                      .append(((fmt != null) ? fmt.form(tval)
+                              : ((int) tval))).append("%");
               p++;
-
             }
           }
-
         }
       }
       else
       {
-        mouseOver += ((fmt != null) ? fmt.form(value) : ((int) value))
-                + "%";
+        mouseOver.append(
+                (((fmt != null) ? fmt.form(value) : ((int) value))))
+                .append("%");
       }
-      consensus.annotations[i] = new Annotation(maxRes, mouseOver, ' ',
+      consensus.annotations[i] = new Annotation(maxRes,
+              mouseOver.toString(), ' ',
               value);
     }
   }
 
   /**
-   * get the sorted profile for the given position of the consensus
+   * Returns a Format designed to show all significant figures for profile
+   * percentages. For less than 100 sequences, returns null (the integer
+   * percentage value will be displayed). For 100-999 sequences, returns "%3.1f"
+   * 
+   * @param nseq
+   * @return
+   */
+  protected static Format getPercentageFormat(long nseq)
+  {
+    int scale = 0;
+    while (nseq >= 10)
+    {
+      scale++;
+      nseq /= 10;
+    }
+    return scale <= 1 ? null : new Format("%3." + (scale - 1) + "f");
+  }
+
+  /**
+   * Returns the sorted profile for the given consensus data. The returned array
+   * contains
+   * 
+   * <pre>
+   *    [profileType, numberOfValues, nonGapCount, charValue1, percentage1, charValue2, percentage2, ...]
+   * in descending order of percentage value
+   * </pre>
    * 
    * @param hconsensus
+   *          the data table from which to extract and sort values
+   * @param ignoreGaps
+   *          if true, only non-gapped values are included in percentage
+   *          calculations
    * @return
    */
   public static int[] extractProfile(Hashtable hconsensus,
-          boolean ignoreGapsInConsensusCalculation)
+          boolean ignoreGaps)
   {
     int[] rtnval = new int[64];
     int[][] profile = (int[][]) hconsensus.get(AAFrequency.PROFILE);
     if (profile == null)
+    {
       return null;
-    Object[] ca = new Object[profile[0].length];
+    }
+    char[] ca = new char[profile[0].length];
     float[] vl = new float[profile[0].length];
     for (int c = 0; c < ca.length; c++)
     {
-      ca[c] = new char[]
-      { (char) c };
+      ca[c] = (char) c;
       vl[c] = profile[0][c];
     }
-    ;
-    jalview.util.QuickSort.sort(vl, ca);
-    rtnval[0] = 2;
-    rtnval[1] = 0;
-    for (int c = ca.length - 1; profile[0][((char[]) ca[c])[0]] > 0; c--)
+    QuickSort.sort(vl, ca);
+    int nextArrayPos = 2;
+    int totalPercentage = 0;
+    int distinctValuesCount = 0;
+    final int divisor = profile[1][ignoreGaps ? 1 : 0];
+    for (int c = ca.length - 1; profile[0][ca[c]] > 0; c--)
+    {
+      if (ca[c] != '-')
+      {
+        rtnval[nextArrayPos++] = ca[c];
+        final int percentage = (int) (profile[0][ca[c]] * 100f / divisor);
+        rtnval[nextArrayPos++] = percentage;
+        totalPercentage += percentage;
+        distinctValuesCount++;
+      }
+    }
+    rtnval[0] = distinctValuesCount;
+    rtnval[1] = totalPercentage;
+    int[] result = new int[rtnval.length + 1];
+    result[0] = AlignmentAnnotation.SEQUENCE_PROFILE;
+    System.arraycopy(rtnval, 0, result, 1, rtnval.length);
+
+    return result;
+  }
+
+  /**
+   * Extract a sorted extract of cDNA codon profile data. The returned array
+   * contains
+   * 
+   * <pre>
+   *    [profileType, numberOfValues, totalCount, charValue1, percentage1, charValue2, percentage2, ...]
+   * in descending order of percentage value, where the character values encode codon triplets
+   * </pre>
+   * 
+   * @param hashtable
+   * @return
+   */
+  public static int[] extractCdnaProfile(Hashtable hashtable, boolean ignoreGaps)
+  {
+    // this holds #seqs, #ungapped, and then codon count, indexed by encoded
+    // codon triplet
+    int[] codonCounts = (int[]) hashtable.get(PROFILE);
+    int[] sortedCounts = new int[codonCounts.length - 2];
+    System.arraycopy(codonCounts, 2, sortedCounts, 0,
+            codonCounts.length - 2);
+
+    int[] result = new int[3 + 2 * sortedCounts.length];
+    // first value is just the type of profile data
+    result[0] = AlignmentAnnotation.CDNA_PROFILE;
+
+    char[] codons = new char[sortedCounts.length];
+    for (int i = 0; i < codons.length; i++)
+    {
+      codons[i] = (char) i;
+    }
+    QuickSort.sort(sortedCounts, codons);
+    int totalPercentage = 0;
+    int distinctValuesCount = 0;
+    int j = 3;
+    int divisor = ignoreGaps ? codonCounts[1] : codonCounts[0];
+    for (int i = codons.length - 1; i >= 0; i--)
+    {
+      final int codonCount = sortedCounts[i];
+      if (codonCount == 0)
+      {
+        break; // nothing else of interest here
+      }
+      distinctValuesCount++;
+      result[j++] = codons[i];
+      final int percentage = codonCount * 100 / divisor;
+      result[j++] = percentage;
+      totalPercentage += percentage;
+    }
+    result[2] = totalPercentage;
+
+    /*
+     * Just return the non-zero values
+     */
+    // todo next value is redundant if we limit the array to non-zero counts
+    result[1] = distinctValuesCount;
+    return Arrays.copyOfRange(result, 0, j);
+  }
+    
+  /**
+   * Compute a consensus for the cDNA coding for a protein alignment.
+   * 
+   * @param alignment
+   *          the protein alignment (which should hold mappings to cDNA
+   *          sequences)
+   * @param hconsensus
+   *          the consensus data stores to be populated (one per column)
+   */
+  public static void calculateCdna(AlignmentI alignment,
+          Hashtable[] hconsensus)
+  {
+    final char gapCharacter = alignment.getGapCharacter();
+    Set<AlignedCodonFrame> mappings = alignment.getCodonFrames();
+    if (mappings == null || mappings.isEmpty())
+    {
+      return;
+    }
+
+    int cols = alignment.getWidth();
+    for (int col = 0; col < cols; col++)
+    {
+      // todo would prefer a Java bean for consensus data
+      Hashtable<String, int[]> columnHash = new Hashtable<String, int[]>();
+      // #seqs, #ungapped seqs, counts indexed by (codon encoded + 1)
+      int[] codonCounts = new int[66];
+      codonCounts[0] = alignment.getSequences().size();
+      int ungappedCount = 0;
+      for (SequenceI seq : alignment.getSequences())
+      {
+        if (seq.getCharAt(col) == gapCharacter)
+        {
+          continue;
+        }
+        char[] codon = MappingUtils.findCodonFor(seq, col, mappings);
+        int codonEncoded = CodingUtils.encodeCodon(codon);
+        if (codonEncoded >= 0)
+        {
+          codonCounts[codonEncoded + 2]++;
+          ungappedCount++;
+        }
+      }
+      codonCounts[1] = ungappedCount;
+      // todo: sort values here, save counts and codons?
+      columnHash.put(PROFILE, codonCounts);
+      hconsensus[col] = columnHash;
+    }
+  }
+
+  /**
+   * Derive displayable cDNA consensus annotation from computed consensus data.
+   * 
+   * @param consensusAnnotation
+   *          the annotation row to be populated for display
+   * @param consensusData
+   *          the computed consensus data
+   * @param showProfileLogo
+   *          if true show all symbols present at each position, else only the
+   *          modal value
+   * @param nseqs
+   *          the number of sequences in the alignment
+   */
+  public static void completeCdnaConsensus(
+          AlignmentAnnotation consensusAnnotation,
+          Hashtable[] consensusData, boolean showProfileLogo, int nseqs)
+  {
+    if (consensusAnnotation == null
+            || consensusAnnotation.annotations == null
+            || consensusAnnotation.annotations.length < consensusData.length)
+    {
+      // called with a bad alignment annotation row - wait for it to be
+      // initialised properly
+      return;
+    }
+
+    // ensure codon triplet scales with font size
+    consensusAnnotation.scaleColLabel = true;
+    for (int col = 0; col < consensusData.length; col++)
     {
-      if (((char[]) ca[c])[0] != '-')
+      Hashtable hci = consensusData[col];
+      if (hci == null)
       {
-        rtnval[rtnval[0]++] = ((char[]) ca[c])[0];
-        rtnval[rtnval[0]] = (int) (profile[0][((char[]) ca[c])[0]] * 100f / profile[1][ignoreGapsInConsensusCalculation ? 1
-                : 0]);
-        rtnval[1] += rtnval[rtnval[0]++];
+        // gapped protein column?
+        continue;
       }
+      // array holds #seqs, #ungapped, then codon counts indexed by codon
+      final int[] codonCounts = (int[]) hci.get(PROFILE);
+      int totalCount = 0;
+      StringBuilder mouseOver = new StringBuilder(32);
+
+      /*
+       * First pass - get total count and find the highest
+       */
+      final char[] codons = new char[codonCounts.length - 2];
+      for (int j = 2; j < codonCounts.length; j++)
+      {
+        final int codonCount = codonCounts[j];
+        codons[j - 2] = (char) (j - 2);
+        totalCount += codonCount;
+      }
+
+      /*
+       * Sort array of encoded codons by count ascending - so the modal value
+       * goes to the end; start by copying the count (dropping the first value)
+       */
+      int[] sortedCodonCounts = new int[codonCounts.length - 2];
+      System.arraycopy(codonCounts, 2, sortedCodonCounts, 0,
+              codonCounts.length - 2);
+      QuickSort.sort(sortedCodonCounts, codons);
+
+      int modalCodonEncoded = codons[codons.length - 1];
+      int modalCodonCount = sortedCodonCounts[codons.length - 1];
+      String modalCodon = String.valueOf(CodingUtils
+              .decodeCodon(modalCodonEncoded));
+      if (sortedCodonCounts.length > 1
+              && sortedCodonCounts[codons.length - 2] == modalCodonEncoded)
+      {
+        modalCodon = "+";
+      }
+      float pid = sortedCodonCounts[sortedCodonCounts.length - 1] * 100
+              / (float) totalCount;
+
+      /*
+       * todo ? Replace consensus hashtable with sorted arrays of codons and
+       * counts (non-zero only). Include total count in count array [0].
+       */
+
+      /*
+       * Scan sorted array backwards for most frequent values first.
+       */
+      for (int j = codons.length - 1; j >= 0; j--)
+      {
+        int codonCount = sortedCodonCounts[j];
+        if (codonCount == 0)
+        {
+          break;
+        }
+        int codonEncoded = codons[j];
+        final int pct = codonCount * 100 / totalCount;
+        String codon = String
+                .valueOf(CodingUtils.decodeCodon(codonEncoded));
+        Format fmt = getPercentageFormat(nseqs);
+        String formatted = fmt == null ? Integer.toString(pct) : fmt
+                .form(pct);
+        if (showProfileLogo || codonCount == modalCodonCount)
+        {
+          mouseOver.append(codon).append(": ").append(formatted)
+                  .append("% ");
+        }
+      }
+
+      consensusAnnotation.annotations[col] = new Annotation(modalCodon,
+              mouseOver.toString(), ' ', pid);
     }
-    return rtnval;
   }
 }
index 96c9b72..bd4cc22 100755 (executable)
@@ -804,19 +804,23 @@ public class AlignSeq
   }
 
   /**
-   * DOCUMENT ME!
+   * Returns the given sequence with all of the given gap characters removed.
    * 
-   * @param gapChar
-   *          DOCUMENT ME!
+   * @param gapChars
+   *          a string of characters to be treated as gaps
    * @param seq
-   *          DOCUMENT ME!
+   *          the input sequence
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
-  public static String extractGaps(String gapChar, String seq)
+  public static String extractGaps(String gapChars, String seq)
   {
-    StringTokenizer str = new StringTokenizer(seq, gapChar);
-    StringBuffer newString = new StringBuffer();
+    if (gapChars == null || seq == null)
+    {
+      return null;
+    }
+    StringTokenizer str = new StringTokenizer(seq, gapChars);
+    StringBuilder newString = new StringBuilder(seq.length());
 
     while (str.hasMoreTokens())
     {
index d1962c5..2fad332 100755 (executable)
  */
 package jalview.analysis;
 
-import java.util.*;
-
-import jalview.datamodel.*;
-import jalview.util.*;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequenceNode;
+import jalview.util.Comparison;
+import jalview.util.MessageManager;
+import jalview.util.QuickSort;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Routines for manipulating the order of a multiple sequence alignment TODO:
@@ -120,7 +129,7 @@ public class AlignmentSorter
       seqs[i] = align.getSequenceAt(i);
     }
 
-    QuickSort.sort(scores, 0, scores.length - 1, seqs);
+    QuickSort.sort(scores, seqs);
 
     setReverseOrder(align, seqs);
   }
@@ -169,7 +178,7 @@ public class AlignmentSorter
    * @param tmp
    *          sequences as a vector
    */
-  private static void setOrder(AlignmentI align, Vector tmp)
+  private static void setOrder(AlignmentI align, List<SequenceI> tmp)
   {
     setOrder(align, vectorSubsetToArray(tmp, align.getSequences()));
   }
@@ -285,7 +294,7 @@ public class AlignmentSorter
   {
     // MAINTAINS ORIGNAL SEQUENCE ORDER,
     // ORDERS BY GROUP SIZE
-    Vector groups = new Vector();
+    List<SequenceGroup> groups = new ArrayList<SequenceGroup>();
 
     if (groups.hashCode() != lastGroupHash)
     {
@@ -303,11 +312,11 @@ public class AlignmentSorter
     {
       for (int j = 0; j < groups.size(); j++)
       {
-        SequenceGroup sg2 = (SequenceGroup) groups.elementAt(j);
+        SequenceGroup sg2 = groups.get(j);
 
         if (sg.getSize() > sg2.getSize())
         {
-          groups.insertElementAt(sg, j);
+          groups.add(j, sg);
 
           break;
         }
@@ -315,22 +324,22 @@ public class AlignmentSorter
 
       if (!groups.contains(sg))
       {
-        groups.addElement(sg);
+        groups.add(sg);
       }
     }
 
     // NOW ADD SEQUENCES MAINTAINING ALIGNMENT ORDER
     // /////////////////////////////////////////////
-    Vector seqs = new Vector();
+    List<SequenceI> seqs = new ArrayList<SequenceI>();
 
     for (int i = 0; i < groups.size(); i++)
     {
-      SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
+      SequenceGroup sg = groups.get(i);
       SequenceI[] orderedseqs = sg.getSequencesInOrder(align);
 
       for (int j = 0; j < orderedseqs.length; j++)
       {
-        seqs.addElement(orderedseqs[j]);
+        seqs.add(orderedseqs[j]);
       }
     }
 
@@ -346,28 +355,8 @@ public class AlignmentSorter
   }
 
   /**
-   * Converts Vector to array. java 1.18 does not have Vector.toArray()
-   * 
-   * @param tmp
-   *          Vector of SequenceI objects
-   * 
-   * @return array of Sequence[]
-   */
-  private static SequenceI[] vectorToArray(Vector tmp)
-  {
-    SequenceI[] seqs = new SequenceI[tmp.size()];
-
-    for (int i = 0; i < tmp.size(); i++)
-    {
-      seqs[i] = (SequenceI) tmp.elementAt(i);
-    }
-
-    return seqs;
-  }
-
-  /**
    * Select sequences in order from tmp that is present in mask, and any
-   * remaining seqeunces in mask not in tmp
+   * remaining sequences in mask not in tmp
    * 
    * @param tmp
    *          thread safe collection of sequences
@@ -379,6 +368,10 @@ public class AlignmentSorter
   private static SequenceI[] vectorSubsetToArray(List<SequenceI> tmp,
           List<SequenceI> mask)
   {
+    // or?
+    // tmp2 = tmp.retainAll(mask);
+    // return tmp2.addAll(mask.removeAll(tmp2))
+
     ArrayList<SequenceI> seqs = new ArrayList<SequenceI>();
     int i, idx;
     boolean[] tmask = new boolean[mask.size()];
@@ -421,7 +414,7 @@ public class AlignmentSorter
   public static void sortBy(AlignmentI align, AlignmentOrder order)
   {
     // Get an ordered vector of sequences which may also be present in align
-    Vector tmp = order.getOrder();
+    List<SequenceI> tmp = order.getOrder();
 
     if (lastOrder == order)
     {
@@ -452,11 +445,12 @@ public class AlignmentSorter
    * 
    * @return DOCUMENT ME!
    */
-  private static Vector getOrderByTree(AlignmentI align, NJTree tree)
+  private static List<SequenceI> getOrderByTree(AlignmentI align,
+          NJTree tree)
   {
     int nSeq = align.getHeight();
 
-    Vector tmp = new Vector();
+    List<SequenceI> tmp = new ArrayList<SequenceI>();
 
     tmp = _sortByTree(tree.getTopNode(), tmp, align.getSequences());
 
@@ -494,7 +488,7 @@ public class AlignmentSorter
    */
   public static void sortByTree(AlignmentI align, NJTree tree)
   {
-    Vector tmp = getOrderByTree(align, tree);
+    List<SequenceI> tmp = getOrderByTree(align, tree);
 
     // tmp should properly permute align with tree.
     if (lastTree != tree)
@@ -522,22 +516,22 @@ public class AlignmentSorter
    * 
    * @param align
    *          DOCUMENT ME!
-   * @param seqs
+   * @param tmp
    *          DOCUMENT ME!
    */
-  private static void addStrays(AlignmentI align, Vector seqs)
+  private static void addStrays(AlignmentI align, List<SequenceI> tmp)
   {
     int nSeq = align.getHeight();
 
     for (int i = 0; i < nSeq; i++)
     {
-      if (!seqs.contains(align.getSequenceAt(i)))
+      if (!tmp.contains(align.getSequenceAt(i)))
       {
-        seqs.addElement(align.getSequenceAt(i));
+        tmp.add(align.getSequenceAt(i));
       }
     }
 
-    if (nSeq != seqs.size())
+    if (nSeq != tmp.size())
     {
       System.err
               .println("ERROR: Size still not right even after addStrays");
@@ -556,7 +550,8 @@ public class AlignmentSorter
    * 
    * @return DOCUMENT ME!
    */
-  private static Vector _sortByTree(SequenceNode node, Vector tmp,
+  private static List<SequenceI> _sortByTree(SequenceNode node,
+          List<SequenceI> tmp,
           List<SequenceI> seqset)
   {
     if (node == null)
@@ -577,7 +572,7 @@ public class AlignmentSorter
                                              // seqset.size()==0 ||
                                              // seqset.contains(tmp)))
           {
-            tmp.addElement(node.element());
+            tmp.add((SequenceI) node.element());
           }
         }
       }
@@ -785,10 +780,6 @@ public class AlignmentSorter
     for (int i = 0; i < seqs.length; i++)
     {
       SequenceFeature[] sf = seqs[i].getSequenceFeatures();
-      if (sf == null && seqs[i].getDatasetSequence() != null)
-      {
-        sf = seqs[i].getDatasetSequence().getSequenceFeatures();
-      }
       if (sf == null)
       {
         sf = new SequenceFeature[0];
index b55d58d..6f0125d 100644 (file)
  */
 package jalview.analysis;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+import jalview.datamodel.AlignedCodon;
+import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
-
-import java.util.ArrayList;
-import java.util.List;
+import jalview.schemes.ResidueProperties;
+import jalview.util.MapList;
 
 /**
  * grab bag of useful alignment manipulation operations Expect these to be
@@ -159,4 +177,1039 @@ public class AlignmentUtils
     }
     return result;
   }
+
+  /**
+   * Returns a map of lists of sequences in the alignment, keyed by sequence
+   * name. For use in mapping between different alignment views of the same
+   * sequences.
+   * 
+   * @see jalview.datamodel.AlignmentI#getSequencesByName()
+   */
+  public static Map<String, List<SequenceI>> getSequencesByName(
+          AlignmentI al)
+  {
+    Map<String, List<SequenceI>> theMap = new LinkedHashMap<String, List<SequenceI>>();
+    for (SequenceI seq : al.getSequences())
+    {
+      String name = seq.getName();
+      if (name != null)
+      {
+        List<SequenceI> seqs = theMap.get(name);
+        if (seqs == null)
+        {
+          seqs = new ArrayList<SequenceI>();
+          theMap.put(name, seqs);
+        }
+        seqs.add(seq);
+      }
+    }
+    return theMap;
+  }
+
+  /**
+   * Build mapping of protein to cDNA alignment. Mappings are made between
+   * sequences where the cDNA translates to the protein sequence. Any new
+   * mappings are added to the protein alignment. Returns true if any mappings
+   * either already exist or were added, else false.
+   * 
+   * @param proteinAlignment
+   * @param cdnaAlignment
+   * @return
+   */
+  public static boolean mapProteinToCdna(
+          final AlignmentI proteinAlignment,
+          final AlignmentI cdnaAlignment)
+  {
+    if (proteinAlignment == null || cdnaAlignment == null)
+    {
+      return false;
+    }
+
+    Set<SequenceI> mappedDna = new HashSet<SequenceI>();
+    Set<SequenceI> mappedProtein = new HashSet<SequenceI>();
+
+    /*
+     * First pass - map sequences where cross-references exist. This include
+     * 1-to-many mappings to support, for example, variant cDNA.
+     */
+    boolean mappingPerformed = mapProteinToCdna(proteinAlignment,
+            cdnaAlignment, mappedDna, mappedProtein, true);
+
+    /*
+     * Second pass - map sequences where no cross-references exist. This only
+     * does 1-to-1 mappings and assumes corresponding sequences are in the same
+     * order in the alignments.
+     */
+    mappingPerformed |= mapProteinToCdna(proteinAlignment, cdnaAlignment,
+            mappedDna, mappedProtein, false);
+    return mappingPerformed;
+  }
+
+  /**
+   * Make mappings between compatible sequences (where the cDNA translation
+   * matches the protein).
+   * 
+   * @param proteinAlignment
+   * @param cdnaAlignment
+   * @param mappedDna
+   *          a set of mapped DNA sequences (to add to)
+   * @param mappedProtein
+   *          a set of mapped Protein sequences (to add to)
+   * @param xrefsOnly
+   *          if true, only map sequences where xrefs exist
+   * @return
+   */
+  protected static boolean mapProteinToCdna(
+          final AlignmentI proteinAlignment,
+          final AlignmentI cdnaAlignment, Set<SequenceI> mappedDna,
+          Set<SequenceI> mappedProtein, boolean xrefsOnly)
+  {
+    boolean mappingPerformed = false;
+    List<SequenceI> thisSeqs = proteinAlignment.getSequences();
+    for (SequenceI aaSeq : thisSeqs)
+    {
+      boolean proteinMapped = false;
+      AlignedCodonFrame acf = new AlignedCodonFrame();
+
+      for (SequenceI cdnaSeq : cdnaAlignment.getSequences())
+      {
+        /*
+         * Always try to map if sequences have xref to each other; this supports
+         * variant cDNA or alternative splicing for a protein sequence.
+         * 
+         * If no xrefs, try to map progressively, assuming that alignments have
+         * mappable sequences in corresponding order. These are not
+         * many-to-many, as that would risk mixing species with similar cDNA
+         * sequences.
+         */
+        if (xrefsOnly && !CrossRef.haveCrossRef(aaSeq, cdnaSeq))
+        {
+          continue;
+        }
+
+        /*
+         * Don't map non-xrefd sequences more than once each. This heuristic
+         * allows us to pair up similar sequences in ordered alignments.
+         */
+        if (!xrefsOnly
+                && (mappedProtein.contains(aaSeq) || mappedDna
+                        .contains(cdnaSeq)))
+        {
+          continue;
+        }
+        if (!mappingExists(proteinAlignment.getCodonFrames(),
+                aaSeq.getDatasetSequence(), cdnaSeq.getDatasetSequence()))
+        {
+          MapList map = mapProteinToCdna(aaSeq, cdnaSeq);
+          if (map != null)
+          {
+            acf.addMap(cdnaSeq, aaSeq, map);
+            mappingPerformed = true;
+            proteinMapped = true;
+            mappedDna.add(cdnaSeq);
+            mappedProtein.add(aaSeq);
+          }
+        }
+      }
+      if (proteinMapped)
+      {
+        proteinAlignment.addCodonFrame(acf);
+      }
+    }
+    return mappingPerformed;
+  }
+
+  /**
+   * Answers true if the mappings include one between the given (dataset)
+   * sequences.
+   */
+  public static boolean mappingExists(Set<AlignedCodonFrame> set,
+          SequenceI aaSeq, SequenceI cdnaSeq)
+  {
+    if (set != null)
+    {
+      for (AlignedCodonFrame acf : set)
+      {
+        if (cdnaSeq == acf.getDnaForAaSeq(aaSeq))
+        {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Build a mapping (if possible) of a protein to a cDNA sequence. The cDNA
+   * must be three times the length of the protein, possibly after ignoring
+   * start and/or stop codons, and must translate to the protein. Returns null
+   * if no mapping is determined.
+   * 
+   * @param proteinSeqs
+   * @param cdnaSeq
+   * @return
+   */
+  public static MapList mapProteinToCdna(SequenceI proteinSeq,
+          SequenceI cdnaSeq)
+  {
+    /*
+     * Here we handle either dataset sequence set (desktop) or absent (applet).
+     * Use only the char[] form of the sequence to avoid creating possibly large
+     * String objects.
+     */
+    final SequenceI proteinDataset = proteinSeq.getDatasetSequence();
+    char[] aaSeqChars = proteinDataset != null ? proteinDataset
+            .getSequence() : proteinSeq.getSequence();
+    final SequenceI cdnaDataset = cdnaSeq.getDatasetSequence();
+    char[] cdnaSeqChars = cdnaDataset != null ? cdnaDataset.getSequence()
+            : cdnaSeq.getSequence();
+    if (aaSeqChars == null || cdnaSeqChars == null)
+    {
+      return null;
+    }
+
+    /*
+     * cdnaStart/End, proteinStartEnd are base 1 (for dataset sequence mapping)
+     */
+    final int mappedLength = 3 * aaSeqChars.length;
+    int cdnaLength = cdnaSeqChars.length;
+    int cdnaStart = 1;
+    int cdnaEnd = cdnaLength;
+    final int proteinStart = 1;
+    final int proteinEnd = aaSeqChars.length;
+
+    /*
+     * If lengths don't match, try ignoring stop codon.
+     */
+    if (cdnaLength != mappedLength && cdnaLength > 2)
+    {
+      String lastCodon = String.valueOf(cdnaSeqChars, cdnaLength - 3, 3)
+              .toUpperCase();
+      for (String stop : ResidueProperties.STOP)
+      {
+        if (lastCodon.equals(stop))
+        {
+          cdnaEnd -= 3;
+          cdnaLength -= 3;
+          break;
+        }
+      }
+    }
+
+    /*
+     * If lengths still don't match, try ignoring start codon.
+     */
+    if (cdnaLength != mappedLength
+            && cdnaLength > 2
+            && String.valueOf(cdnaSeqChars, 0, 3).toUpperCase()
+                    .equals(
+                    ResidueProperties.START))
+    {
+      cdnaStart += 3;
+      cdnaLength -= 3;
+    }
+
+    if (cdnaLength != mappedLength)
+    {
+      return null;
+    }
+    if (!translatesAs(cdnaSeqChars, cdnaStart - 1, aaSeqChars))
+    {
+      return null;
+    }
+    MapList map = new MapList(new int[]
+    { cdnaStart, cdnaEnd }, new int[]
+    { proteinStart, proteinEnd }, 3, 1);
+    return map;
+  }
+
+  /**
+   * Test whether the given cdna sequence, starting at the given offset,
+   * translates to the given amino acid sequence, using the standard translation
+   * table. Designed to fail fast i.e. as soon as a mismatch position is found.
+   * 
+   * @param cdnaSeqChars
+   * @param cdnaStart
+   * @param aaSeqChars
+   * @return
+   */
+  protected static boolean translatesAs(char[] cdnaSeqChars, int cdnaStart,
+          char[] aaSeqChars)
+  {
+    int aaResidue = 0;
+    for (int i = cdnaStart; i < cdnaSeqChars.length - 2
+            && aaResidue < aaSeqChars.length; i += 3, aaResidue++)
+    {
+      String codon = String.valueOf(cdnaSeqChars, i, 3);
+      final String translated = ResidueProperties.codonTranslate(
+              codon);
+      /*
+       * ? allow X in protein to match untranslatable in dna ?
+       */
+      final char aaRes = aaSeqChars[aaResidue];
+      if ((translated == null || "STOP".equals(translated)) && aaRes == 'X')
+      {
+        continue;
+      }
+      if (translated == null
+              || !(aaRes == translated.charAt(0)))
+      {
+        // debug
+        // System.out.println(("Mismatch at " + i + "/" + aaResidue + ": "
+        // + codon + "(" + translated + ") != " + aaRes));
+        return false;
+      }
+    }
+    // fail if we didn't match all of the aa sequence
+    return (aaResidue == aaSeqChars.length);
+  }
+
+  /**
+   * Align sequence 'seq' to match the alignment of a mapped sequence. Note this
+   * currently assumes that we are aligning cDNA to match protein.
+   * 
+   * @param seq
+   *          the sequence to be realigned
+   * @param al
+   *          the alignment whose sequence alignment is to be 'copied'
+   * @param gap
+   *          character string represent a gap in the realigned sequence
+   * @param preserveUnmappedGaps
+   * @param preserveMappedGaps
+   * @return true if the sequence was realigned, false if it could not be
+   */
+  public static boolean alignSequenceAs(SequenceI seq, AlignmentI al,
+          String gap, boolean preserveMappedGaps,
+          boolean preserveUnmappedGaps)
+  {
+    /*
+     * Get any mappings from the source alignment to the target (dataset) sequence.
+     */
+    // TODO there may be one AlignedCodonFrame per dataset sequence, or one with
+    // all mappings. Would it help to constrain this?
+    List<AlignedCodonFrame> mappings = al.getCodonFrame(seq);
+    if (mappings == null || mappings.isEmpty())
+    {
+      return false;
+    }
+  
+    /*
+     * Locate the aligned source sequence whose dataset sequence is mapped. We
+     * just take the first match here (as we can't align cDNA like more than one
+     * protein sequence).
+     */
+    SequenceI alignFrom = null;
+    AlignedCodonFrame mapping = null;
+    for (AlignedCodonFrame mp : mappings)
+    {
+      alignFrom = mp.findAlignedSequence(seq.getDatasetSequence(), al);
+      if (alignFrom != null)
+      {
+        mapping = mp;
+        break;
+      }
+    }
+  
+    if (alignFrom == null)
+    {
+      return false;
+    }
+    alignSequenceAs(seq, alignFrom, mapping, gap, al.getGapCharacter(),
+            preserveMappedGaps, preserveUnmappedGaps);
+    return true;
+  }
+
+  /**
+   * Align sequence 'alignTo' the same way as 'alignFrom', using the mapping to
+   * match residues and codons. Flags control whether existing gaps in unmapped
+   * (intron) and mapped (exon) regions are preserved or not. Gaps linking intro
+   * and exon are only retained if both flags are set.
+   * 
+   * @param alignTo
+   * @param alignFrom
+   * @param mapping
+   * @param myGap
+   * @param sourceGap
+   * @param preserveUnmappedGaps
+   * @param preserveMappedGaps
+   */
+  public static void alignSequenceAs(SequenceI alignTo,
+          SequenceI alignFrom,
+          AlignedCodonFrame mapping, String myGap, char sourceGap,
+          boolean preserveMappedGaps, boolean preserveUnmappedGaps)
+  {
+    // TODO generalise to work for Protein-Protein, dna-dna, dna-protein
+    final char[] thisSeq = alignTo.getSequence();
+    final char[] thatAligned = alignFrom.getSequence();
+    StringBuilder thisAligned = new StringBuilder(2 * thisSeq.length);
+  
+    // aligned and dataset sequence positions, all base zero
+    int thisSeqPos = 0;
+    int sourceDsPos = 0;
+
+    int basesWritten = 0;
+    char myGapChar = myGap.charAt(0);
+    int ratio = myGap.length();
+
+    /*
+     * Traverse the aligned protein sequence.
+     */
+    int sourceGapMappedLength = 0;
+    boolean inExon = false;
+    for (char sourceChar : thatAligned)
+    {
+      if (sourceChar == sourceGap)
+      {
+        sourceGapMappedLength += ratio;
+        continue;
+      }
+
+      /*
+       * Found a residue. Locate its mapped codon (start) position.
+       */
+      sourceDsPos++;
+      // Note mapping positions are base 1, our sequence positions base 0
+      int[] mappedPos = mapping.getMappedRegion(alignTo, alignFrom,
+              sourceDsPos);
+      if (mappedPos == null)
+      {
+        /*
+         * Abort realignment if unmapped protein. Or could ignore it??
+         */
+        System.err.println("Can't align: no codon mapping to residue "
+                + sourceDsPos + "(" + sourceChar + ")");
+        return;
+      }
+
+      int mappedCodonStart = mappedPos[0]; // position (1...) of codon start
+      int mappedCodonEnd = mappedPos[mappedPos.length - 1]; // codon end pos
+      StringBuilder trailingCopiedGap = new StringBuilder();
+
+      /*
+       * Copy dna sequence up to and including this codon. Optionally, include
+       * gaps before the codon starts (in introns) and/or after the codon starts
+       * (in exons).
+       * 
+       * Note this only works for 'linear' splicing, not reverse or interleaved.
+       * But then 'align dna as protein' doesn't make much sense otherwise.
+       */
+      int intronLength = 0;
+      while (basesWritten < mappedCodonEnd && thisSeqPos < thisSeq.length)
+      {
+        final char c = thisSeq[thisSeqPos++];
+        if (c != myGapChar)
+        {
+          basesWritten++;
+
+          if (basesWritten < mappedCodonStart)
+          {
+            /*
+             * Found an unmapped (intron) base. First add in any preceding gaps
+             * (if wanted).
+             */
+            if (preserveUnmappedGaps && trailingCopiedGap.length() > 0)
+            {
+              thisAligned.append(trailingCopiedGap.toString());
+              intronLength += trailingCopiedGap.length();
+              trailingCopiedGap = new StringBuilder();
+            }
+            intronLength++;
+            inExon = false;
+          }
+          else
+          {
+            final boolean startOfCodon = basesWritten == mappedCodonStart;
+            int gapsToAdd = calculateGapsToInsert(preserveMappedGaps,
+                    preserveUnmappedGaps, sourceGapMappedLength, inExon,
+                    trailingCopiedGap.length(), intronLength, startOfCodon);
+            for (int i = 0; i < gapsToAdd; i++)
+            {
+              thisAligned.append(myGapChar);
+            }
+            sourceGapMappedLength = 0;
+            inExon = true;
+          }
+          thisAligned.append(c);
+          trailingCopiedGap = new StringBuilder();
+        }
+        else
+        {
+          if (inExon && preserveMappedGaps)
+          {
+            trailingCopiedGap.append(myGapChar);
+          }
+          else if (!inExon && preserveUnmappedGaps)
+          {
+            trailingCopiedGap.append(myGapChar);
+          }
+        }
+      }
+    }
+
+    /*
+     * At end of protein sequence. Copy any remaining dna sequence, optionally
+     * including (intron) gaps. We do not copy trailing gaps in protein.
+     */
+    while (thisSeqPos < thisSeq.length)
+    {
+      final char c = thisSeq[thisSeqPos++];
+      if (c != myGapChar || preserveUnmappedGaps)
+      {
+        thisAligned.append(c);
+      }
+    }
+
+    /*
+     * All done aligning, set the aligned sequence.
+     */
+    alignTo.setSequence(new String(thisAligned));
+  }
+
+  /**
+   * Helper method to work out how many gaps to insert when realigning.
+   * 
+   * @param preserveMappedGaps
+   * @param preserveUnmappedGaps
+   * @param sourceGapMappedLength
+   * @param inExon
+   * @param trailingCopiedGap
+   * @param intronLength
+   * @param startOfCodon
+   * @return
+   */
+  protected static int calculateGapsToInsert(boolean preserveMappedGaps,
+          boolean preserveUnmappedGaps, int sourceGapMappedLength,
+          boolean inExon, int trailingGapLength,
+          int intronLength, final boolean startOfCodon)
+  {
+    int gapsToAdd = 0;
+    if (startOfCodon)
+    {
+      /*
+       * Reached start of codon. Ignore trailing gaps in intron unless we are
+       * preserving gaps in both exon and intron. Ignore them anyway if the
+       * protein alignment introduces a gap at least as large as the intronic
+       * region.
+       */
+      if (inExon && !preserveMappedGaps)
+      {
+        trailingGapLength = 0;
+      }
+      if (!inExon && !(preserveMappedGaps && preserveUnmappedGaps))
+      {
+        trailingGapLength = 0;
+      }
+      if (inExon)
+      {
+        gapsToAdd = Math.max(sourceGapMappedLength, trailingGapLength);
+      }
+      else
+      {
+        if (intronLength + trailingGapLength <= sourceGapMappedLength)
+        {
+          gapsToAdd = sourceGapMappedLength - intronLength;
+        }
+        else
+        {
+          gapsToAdd = Math.min(intronLength + trailingGapLength
+                  - sourceGapMappedLength, trailingGapLength);
+        }
+      }
+    }
+    else
+    {
+      /*
+       * second or third base of codon; check for any gaps in dna
+       */
+      if (!preserveMappedGaps)
+      {
+        trailingGapLength = 0;
+      }
+      gapsToAdd = Math.max(sourceGapMappedLength, trailingGapLength);
+    }
+    return gapsToAdd;
+  }
+
+  /**
+   * Returns a list of sequences mapped from the given sequences and aligned
+   * (gapped) in the same way. For example, the cDNA for aligned protein, where
+   * a single gap in protein generates three gaps in cDNA.
+   * 
+   * @param sequences
+   * @param gapCharacter
+   * @param mappings
+   * @return
+   */
+  public static List<SequenceI> getAlignedTranslation(
+          List<SequenceI> sequences, char gapCharacter,
+          Set<AlignedCodonFrame> mappings)
+  {
+    List<SequenceI> alignedSeqs = new ArrayList<SequenceI>();
+
+    for (SequenceI seq : sequences)
+    {
+      List<SequenceI> mapped = getAlignedTranslation(seq, gapCharacter,
+              mappings);
+      alignedSeqs.addAll(mapped);
+    }
+    return alignedSeqs;
+  }
+
+  /**
+   * Returns sequences aligned 'like' the source sequence, as mapped by the
+   * given mappings. Normally we expect zero or one 'mapped' sequences, but this
+   * will support 1-to-many as well.
+   * 
+   * @param seq
+   * @param gapCharacter
+   * @param mappings
+   * @return
+   */
+  protected static List<SequenceI> getAlignedTranslation(SequenceI seq,
+          char gapCharacter, Set<AlignedCodonFrame> mappings)
+  {
+    List<SequenceI> result = new ArrayList<SequenceI>();
+    for (AlignedCodonFrame mapping : mappings)
+    {
+      if (mapping.involvesSequence(seq))
+      {
+        SequenceI mapped = getAlignedTranslation(seq, gapCharacter, mapping);
+        if (mapped != null)
+        {
+          result.add(mapped);
+        }
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Returns the translation of 'seq' (as held in the mapping) with
+   * corresponding alignment (gaps).
+   * 
+   * @param seq
+   * @param gapCharacter
+   * @param mapping
+   * @return
+   */
+  protected static SequenceI getAlignedTranslation(SequenceI seq,
+          char gapCharacter, AlignedCodonFrame mapping)
+  {
+    String gap = String.valueOf(gapCharacter);
+    boolean toDna = false;
+    int fromRatio = 1;
+    SequenceI mapTo = mapping.getDnaForAaSeq(seq);
+    if (mapTo != null)
+    {
+      // mapping is from protein to nucleotide
+      toDna = true;
+      // should ideally get gap count ratio from mapping
+      gap = String.valueOf(new char[]
+      { gapCharacter, gapCharacter, gapCharacter });
+    }
+    else
+    {
+      // mapping is from nucleotide to protein
+      mapTo = mapping.getAaForDnaSeq(seq);
+      fromRatio = 3;
+    }
+    StringBuilder newseq = new StringBuilder(seq.getLength()
+            * (toDna ? 3 : 1));
+
+    int residueNo = 0; // in seq, base 1
+    int[] phrase = new int[fromRatio];
+    int phraseOffset = 0;
+    int gapWidth = 0;
+    boolean first = true;
+    final Sequence alignedSeq = new Sequence("", "");
+
+    for (char c : seq.getSequence())
+    {
+      if (c == gapCharacter)
+      {
+        gapWidth++;
+        if (gapWidth >= fromRatio)
+        {
+          newseq.append(gap);
+          gapWidth = 0;
+        }
+      }
+      else
+      {
+        phrase[phraseOffset++] = residueNo + 1;
+        if (phraseOffset == fromRatio)
+        {
+          /*
+           * Have read a whole codon (or protein residue), now translate: map
+           * source phrase to positions in target sequence add characters at
+           * these positions to newseq Note mapping positions are base 1, our
+           * sequence positions base 0.
+           */
+          SearchResults sr = new SearchResults();
+          for (int pos : phrase)
+          {
+            mapping.markMappedRegion(seq, pos, sr);
+          }
+          newseq.append(sr.toString());
+          if (first)
+          {
+            first = false;
+            // Hack: Copy sequence dataset, name and description from
+            // SearchResults.match[0].sequence
+            // TODO? carry over sequence names from original 'complement'
+            // alignment
+            SequenceI mappedTo = sr.getResultSequence(0);
+            alignedSeq.setName(mappedTo.getName());
+            alignedSeq.setDescription(mappedTo.getDescription());
+            alignedSeq.setDatasetSequence(mappedTo);
+          }
+          phraseOffset = 0;
+        }
+        residueNo++;
+      }
+    }
+    alignedSeq.setSequence(newseq.toString());
+    return alignedSeq;
+  }
+
+  /**
+   * Realigns the given protein to match the alignment of the dna, using codon
+   * mappings to translate aligned codon positions to protein residues.
+   * 
+   * @param protein
+   *          the alignment whose sequences are realigned by this method
+   * @param dna
+   *          the dna alignment whose alignment we are 'copying'
+   * @return the number of sequences that were realigned
+   */
+  public static int alignProteinAsDna(AlignmentI protein, AlignmentI dna)
+  {
+    Set<AlignedCodonFrame> mappings = protein.getCodonFrames();
+
+    /*
+     * Map will hold, for each aligned codon position e.g. [3, 5, 6], a map of
+     * {dnaSequence, {proteinSequence, codonProduct}} at that position. The
+     * comparator keeps the codon positions ordered.
+     */
+    Map<AlignedCodon, Map<SequenceI, String>> alignedCodons = new TreeMap<AlignedCodon, Map<SequenceI, String>>(
+            new CodonComparator());
+    for (SequenceI dnaSeq : dna.getSequences())
+    {
+      for (AlignedCodonFrame mapping : mappings)
+      {
+        Mapping seqMap = mapping.getMappingForSequence(dnaSeq);
+        SequenceI prot = mapping.findAlignedSequence(
+                dnaSeq.getDatasetSequence(), protein);
+        if (prot != null)
+        {
+          addCodonPositions(dnaSeq, prot, protein.getGapCharacter(),
+                  seqMap, alignedCodons);
+        }
+      }
+    }
+    return alignProteinAs(protein, alignedCodons);
+  }
+
+  /**
+   * Update the aligned protein sequences to match the codon alignments given in
+   * the map.
+   * 
+   * @param protein
+   * @param alignedCodons
+   *          an ordered map of codon positions (columns), with sequence/peptide
+   *          values present in each column
+   * @return
+   */
+  protected static int alignProteinAs(AlignmentI protein,
+          Map<AlignedCodon, Map<SequenceI, String>> alignedCodons)
+  {
+    /*
+     * Prefill aligned sequences with gaps before inserting aligned protein
+     * residues.
+     */
+    int alignedWidth = alignedCodons.size();
+    char[] gaps = new char[alignedWidth];
+    Arrays.fill(gaps, protein.getGapCharacter());
+    String allGaps = String.valueOf(gaps);
+    for (SequenceI seq : protein.getSequences())
+    {
+      seq.setSequence(allGaps);
+    }
+
+    int column = 0;
+    for (AlignedCodon codon : alignedCodons.keySet())
+    {
+      final Map<SequenceI, String> columnResidues = alignedCodons.get(codon);
+      for (Entry<SequenceI, String> entry : columnResidues
+              .entrySet())
+      {
+        // place translated codon at its column position in sequence
+        entry.getKey().getSequence()[column] = entry.getValue().charAt(0);
+      }
+      column++;
+    }
+    return 0;
+  }
+
+  /**
+   * Populate the map of aligned codons by traversing the given sequence
+   * mapping, locating the aligned positions of mapped codons, and adding those
+   * positions and their translation products to the map.
+   * 
+   * @param dna
+   *          the aligned sequence we are mapping from
+   * @param protein
+   *          the sequence to be aligned to the codons
+   * @param gapChar
+   *          the gap character in the dna sequence
+   * @param seqMap
+   *          a mapping to a sequence translation
+   * @param alignedCodons
+   *          the map we are building up
+   */
+  static void addCodonPositions(SequenceI dna, SequenceI protein,
+          char gapChar,
+          Mapping seqMap,
+          Map<AlignedCodon, Map<SequenceI, String>> alignedCodons)
+  {
+    Iterator<AlignedCodon> codons = seqMap.getCodonIterator(dna, gapChar);
+    while (codons.hasNext())
+    {
+      AlignedCodon codon = codons.next();
+      Map<SequenceI, String> seqProduct = alignedCodons.get(codon);
+      if (seqProduct == null)
+      {
+        seqProduct = new HashMap<SequenceI, String>();
+        alignedCodons.put(codon, seqProduct);
+      }
+      seqProduct.put(protein, codon.product);
+    }
+  }
+
+  /**
+   * Returns true if a cDNA/Protein mapping either exists, or could be made,
+   * between at least one pair of sequences in the two alignments. Currently,
+   * the logic is:
+   * <ul>
+   * <li>One alignment must be nucleotide, and the other protein</li>
+   * <li>At least one pair of sequences must be already mapped, or mappable</li>
+   * <li>Mappable means the nucleotide translation matches the protein sequence</li>
+   * <li>The translation may ignore start and stop codons if present in the
+   * nucleotide</li>
+   * </ul>
+   * 
+   * @param al1
+   * @param al2
+   * @return
+   */
+  public static boolean isMappable(AlignmentI al1, AlignmentI al2)
+  {
+    /*
+     * Require one nucleotide and one protein
+     */
+    if (al1.isNucleotide() == al2.isNucleotide())
+    {
+      return false;
+    }
+    AlignmentI dna = al1.isNucleotide() ? al1 : al2;
+    AlignmentI protein = dna == al1 ? al2 : al1;
+    Set<AlignedCodonFrame> mappings = protein.getCodonFrames();
+    for (SequenceI dnaSeq : dna.getSequences())
+    {
+      for (SequenceI proteinSeq : protein.getSequences())
+      {
+        if (isMappable(dnaSeq, proteinSeq, mappings))
+        {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns true if the dna sequence is mapped, or could be mapped, to the
+   * protein sequence.
+   * 
+   * @param dnaSeq
+   * @param proteinSeq
+   * @param mappings
+   * @return
+   */
+  public static boolean isMappable(SequenceI dnaSeq, SequenceI proteinSeq,
+          Set<AlignedCodonFrame> mappings)
+  {
+    SequenceI dnaDs = dnaSeq.getDatasetSequence() == null ? dnaSeq : dnaSeq.getDatasetSequence();
+    SequenceI proteinDs = proteinSeq.getDatasetSequence() == null ? proteinSeq
+            : proteinSeq.getDatasetSequence();
+    
+    /*
+     * Already mapped?
+     */
+    for (AlignedCodonFrame mapping : mappings) {
+      if ( proteinDs == mapping.getAaForDnaSeq(dnaDs)) {
+        return true;
+      }
+    }
+
+    /*
+     * Just try to make a mapping (it is not yet stored), test whether
+     * successful.
+     */
+    return mapProteinToCdna(proteinDs, dnaDs) != null;
+  }
+
+  /**
+   * Finds any reference annotations associated with the sequences in
+   * sequenceScope, that are not already added to the alignment, and adds them
+   * to the 'candidates' map. Also populates a lookup table of annotation
+   * labels, keyed by calcId, for use in constructing tooltips or the like.
+   * 
+   * @param sequenceScope
+   *          the sequences to scan for reference annotations
+   * @param labelForCalcId
+   *          (optional) map to populate with label for calcId
+   * @param candidates
+   *          map to populate with annotations for sequence
+   * @param al
+   *          the alignment to check for presence of annotations
+   */
+  public static void findAddableReferenceAnnotations(
+          List<SequenceI> sequenceScope, Map<String, String> labelForCalcId,
+          final Map<SequenceI, List<AlignmentAnnotation>> candidates,
+          AlignmentI al)
+  {
+    if (sequenceScope == null)
+    {
+      return;
+    }
+  
+    /*
+     * For each sequence in scope, make a list of any annotations on the
+     * underlying dataset sequence which are not already on the alignment.
+     * 
+     * Add to a map of { alignmentSequence, <List of annotations to add> }
+     */
+    for (SequenceI seq : sequenceScope)
+    {
+      SequenceI dataset = seq.getDatasetSequence();
+      if (dataset == null)
+      {
+        continue;
+      }
+      AlignmentAnnotation[] datasetAnnotations = dataset.getAnnotation();
+      if (datasetAnnotations == null)
+      {
+        continue;
+      }
+      final List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
+      for (AlignmentAnnotation dsann : datasetAnnotations)
+      {
+        /*
+         * Find matching annotations on the alignment. If none is found, then
+         * add this annotation to the list of 'addable' annotations for this
+         * sequence.
+         */
+        final Iterable<AlignmentAnnotation> matchedAlignmentAnnotations = al
+                .findAnnotations(seq, dsann.getCalcId(),
+                        dsann.label);
+        if (!matchedAlignmentAnnotations.iterator().hasNext())
+        {
+          result.add(dsann);
+          if (labelForCalcId != null)
+          {
+            labelForCalcId.put(dsann.getCalcId(), dsann.label);
+          }
+        }
+      }
+      /*
+       * Save any addable annotations for this sequence
+       */
+      if (!result.isEmpty())
+      {
+        candidates.put(seq, result);
+      }
+    }
+  }
+
+  /**
+   * Adds annotations to the top of the alignment annotations, in the same order
+   * as their related sequences.
+   * 
+   * @param annotations
+   *          the annotations to add
+   * @param alignment
+   *          the alignment to add them to
+   * @param selectionGroup
+   *          current selection group (or null if none)
+   */
+  public static void addReferenceAnnotations(
+          Map<SequenceI, List<AlignmentAnnotation>> annotations,
+          final AlignmentI alignment, final SequenceGroup selectionGroup)
+  {
+    for (SequenceI seq : annotations.keySet())
+    {
+      for (AlignmentAnnotation ann : annotations.get(seq))
+      {
+        AlignmentAnnotation copyAnn = new AlignmentAnnotation(ann);
+        int startRes = 0;
+        int endRes = ann.annotations.length;
+        if (selectionGroup != null)
+        {
+          startRes = selectionGroup.getStartRes();
+          endRes = selectionGroup.getEndRes();
+        }
+        copyAnn.restrict(startRes, endRes);
+  
+        /*
+         * Add to the sequence (sets copyAnn.datasetSequence), unless the
+         * original annotation is already on the sequence.
+         */
+        if (!seq.hasAnnotation(ann))
+        {
+          seq.addAlignmentAnnotation(copyAnn);
+        }
+        // adjust for gaps
+        copyAnn.adjustForAlignment();
+        // add to the alignment and set visible
+        alignment.addAnnotation(copyAnn);
+        copyAnn.visible = true;
+      }
+    }
+  }
+
+  /**
+   * Set visibility of alignment annotations of specified types (labels), for
+   * specified sequences. This supports controls like
+   * "Show all secondary structure", "Hide all Temp factor", etc.
+   * 
+   * @al the alignment to scan for annotations
+   * @param types
+   *          the types (labels) of annotations to be updated
+   * @param forSequences
+   *          if not null, only annotations linked to one of these sequences are
+   *          in scope for update; if null, acts on all sequence annotations
+   * @param anyType
+   *          if this flag is true, 'types' is ignored (label not checked)
+   * @param doShow
+   *          if true, set visibility on, else set off
+   */
+  public static void showOrHideSequenceAnnotations(AlignmentI al,
+          Collection<String> types, List<SequenceI> forSequences,
+          boolean anyType, boolean doShow)
+  {
+    for (AlignmentAnnotation aa : al
+            .getAlignmentAnnotation())
+    {
+      if (anyType || types.contains(aa.label))
+      {
+        if ((aa.sequenceRef != null)
+                && (forSequences == null || forSequences
+                        .contains(aa.sequenceRef)))
+        {
+          aa.visible = doShow;
+        }
+      }
+    }
+  }
 }
diff --git a/src/jalview/analysis/CodingUtils.java b/src/jalview/analysis/CodingUtils.java
new file mode 100644 (file)
index 0000000..a434465
--- /dev/null
@@ -0,0 +1,119 @@
+package jalview.analysis;
+
+/**
+ * A utility class to provide encoding/decoding schemes for data.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class CodingUtils
+{
+
+  /*
+   * Number of bits used when encoding codon characters. 2 is enough for ACGT.
+   * To accommodate more (e.g. ambiguity codes), simply increase this number
+   * (and adjust unit tests to match).
+   */
+  private static final int CODON_ENCODING_BITSHIFT = 2;
+
+  /**
+   * Encode a codon from e.g. ['A', 'G', 'C'] to a number in the range 0 - 63.
+   * Converts lower to upper case, U to T, then assembles a binary value by
+   * encoding A/C/G/T as 00/01/10/11 respectively and shifting.
+   * 
+   * @param codon
+   * @return the encoded codon, or a negative number if unexpected characters
+   *         found
+   */
+  public static int encodeCodon(char[] codon)
+  {
+    if (codon == null)
+    {
+      return -1;
+    }
+    return encodeCodon(codon[2])
+            + (encodeCodon(codon[1]) << CODON_ENCODING_BITSHIFT)
+            + (encodeCodon(codon[0]) << (2 * CODON_ENCODING_BITSHIFT));
+  }
+
+  /**
+   * Encodes aA/cC/gG/tTuU as 0/1/2/3 respectively. Returns Integer.MIN_VALUE (a
+   * large negative value) for any other character.
+   * 
+   * @param c
+   * @return
+   */
+  public static int encodeCodon(char c)
+  {
+    int result = Integer.MIN_VALUE;
+    switch (c)
+    {
+    case 'A':
+    case 'a':
+      result = 0;
+      break;
+    case 'C':
+    case 'c':
+      result = 1;
+      break;
+    case 'G':
+    case 'g':
+      result = 2;
+      break;
+    case 'T':
+    case 't':
+    case 'U':
+    case 'u':
+      result = 3;
+      break;
+    }
+    return result;
+  }
+
+  /**
+   * Converts a binary encoded codon into an ['A', 'C', 'G'] (or 'T') triplet.
+   * 
+   * The two low-order bits encode for A/C/G/T as 0/1/2/3, etc.
+   * 
+   * @param encoded
+   * @return
+   */
+  public static char[] decodeCodon(int encoded)
+  {
+    char[] result = new char[3];
+    result[2] = decodeNucleotide(encoded & 3);
+    encoded = encoded >>> CODON_ENCODING_BITSHIFT;
+    result[1] = decodeNucleotide(encoded & 3);
+    encoded = encoded >>> CODON_ENCODING_BITSHIFT;
+    result[0] = decodeNucleotide(encoded & 3);
+    return result;
+  }
+
+  /**
+   * Convert value 0/1/2/3 to 'A'/'C'/'G'/'T'
+   * 
+   * @param i
+   * @return
+   */
+  public static char decodeNucleotide(int i)
+  {
+    char result = '0';
+    switch (i)
+    {
+    case 0:
+      result = 'A';
+      break;
+    case 1:
+      result = 'C';
+      break;
+    case 2:
+      result = 'G';
+      break;
+    case 3:
+      result = 'T';
+      break;
+    }
+    return result;
+  }
+
+}
diff --git a/src/jalview/analysis/CodonComparator.java b/src/jalview/analysis/CodonComparator.java
new file mode 100644 (file)
index 0000000..fc196de
--- /dev/null
@@ -0,0 +1,91 @@
+package jalview.analysis;
+
+import jalview.datamodel.AlignedCodon;
+
+import java.util.Comparator;
+
+/**
+ * Implements rules for comparing two aligned codons, i.e. determining whether
+ * they should occupy the same position in a translated protein alignment, or
+ * one or the other should 'follow' (by preceded by a gap).
+ * 
+ * @author gmcarstairs
+ *
+ */
+public final class CodonComparator implements Comparator<AlignedCodon>
+{
+
+  @Override
+  public int compare(AlignedCodon ac1, AlignedCodon ac2)
+  {
+    if (ac1 == null || ac2 == null || ac1.equals(ac2))
+    {
+      return 0;
+    }
+
+    /**
+     * <pre>
+     * Case 1: if one starts before the other, and doesn't end after it, then it
+     * precedes. We ignore the middle base position here.
+     * A--GT
+     * -CT-G
+     * </pre>
+     */
+    if (ac1.pos1 < ac2.pos1 && ac1.pos3 <= ac2.pos3)
+    {
+      return -1;
+    }
+    if (ac2.pos1 < ac1.pos1 && ac2.pos3 <= ac1.pos3)
+    {
+      return 1;
+    }
+
+    /**
+     * <pre>
+     * Case 2: if one ends after the other, and doesn't start before it, then it
+     * follows. We ignore the middle base position here.
+     * -TG-A
+     * G-TC
+     * </pre>
+     */
+    if (ac1.pos3 > ac2.pos3 && ac1.pos1 >= ac2.pos1)
+    {
+      return 1;
+    }
+    if (ac2.pos3 > ac1.pos3 && ac2.pos1 >= ac1.pos1)
+    {
+      return -1;
+    }
+
+    /*
+     * Case 3: if start and end match, compare middle base positions.
+     */
+    if (ac1.pos1 == ac2.pos1 && ac1.pos3 == ac2.pos3)
+    {
+      return Integer.compare(ac1.pos2, ac2.pos2);
+    }
+
+    /*
+     * That just leaves the 'enclosing' case - one codon starts after but ends
+     * before the other. If the middle bases don't match, use their comparison
+     * (majority vote).
+     */
+    int compareMiddles = Integer.compare(ac1.pos2, ac2.pos2);
+    if (compareMiddles != 0)
+    {
+      return compareMiddles;
+    }
+
+    /**
+     * <pre>
+     * Finally just leaves overlap with matching middle base, e.g. 
+     * -A-A-A
+     * G--GG 
+     * In this case the choice is arbitrary whether to compare based on
+     * first or last base position. We pick the first. Note this preserves
+     * symmetricality of the comparison.
+     * </pre>
+     */
+    return Integer.compare(ac1.pos1, ac2.pos1);
+  }
+}
index f34b822..eed2d7e 100644 (file)
  */
 package jalview.analysis;
 
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Vector;
-import java.util.Hashtable;
-
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefSource;
 import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.ws.SequenceFetcher;
 import jalview.ws.seqfetcher.ASequenceFetcher;
 
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+
 /**
  * Functions for cross-referencing sequence databases. user must first specify
  * if cross-referencing from protein or dna (set dna==true)
@@ -230,7 +230,7 @@ public class CrossRef
   {
     Vector rseqs = new Vector();
     Alignment ral = null;
-    AlignedCodonFrame cf = new AlignedCodonFrame(0); // nominal width
+    AlignedCodonFrame cf = new AlignedCodonFrame(); // nominal width
     for (int s = 0; s < seqs.length; s++)
     {
       SequenceI dss = seqs[s];
@@ -258,7 +258,9 @@ public class CrossRef
       for (int r = 0; xrfs != null && r < xrfs.length; r++)
       {
         if (source != null && !source.equals(xrfs[r].getSource()))
+        {
           continue;
+        }
         if (xrfs[r].hasMap())
         {
           if (xrfs[r].getMap().getTo() != null)
@@ -291,7 +293,9 @@ public class CrossRef
           {
             found |= searchDataset(dss, xrfs[r], dataset, rseqs, cf); // ,false,!dna);
             if (found)
+             {
               xrfs[r] = null; // we've recovered seqs for this one.
+            }
           }
         }
       }
@@ -328,7 +332,9 @@ public class CrossRef
             for (int r = 0; r < xrfs.length; r++)
             {
               if (xrfs[r] != null)
+              {
                 t[l++] = xrfs[r];
+              }
             }
             xrfs = t;
             try
@@ -432,7 +438,9 @@ public class CrossRef
   {
     boolean found = false;
     if (lrfs == null)
+    {
       return false;
+    }
     for (int i = 0; i < lrfs.length; i++)
     {
       DBRefEntry xref = new DBRefEntry(lrfs[i]);
@@ -484,7 +492,9 @@ public class CrossRef
     boolean found = false;
     SequenceI[] typer = new SequenceI[1];
     if (dataset == null)
+    {
       return false;
+    }
     if (dataset.getSequences() == null)
     {
       System.err.println("Empty dataset sequence set - NO VECTOR");
@@ -494,6 +504,7 @@ public class CrossRef
     synchronized (ds = dataset.getSequences())
     {
       for (SequenceI nxt : ds)
+      {
         if (nxt != null)
         {
           if (nxt.getDatasetSequence() != null)
@@ -566,11 +577,55 @@ public class CrossRef
 
           }
         }
+      }
     }
     return found;
   }
 
   /**
+   * Returns true if either sequence has a cross-reference to the other
+   * 
+   * @param seq1
+   * @param seq2
+   * @return
+   */
+  public static boolean haveCrossRef(SequenceI seq1, SequenceI seq2)
+  {
+    return hasCrossRef(seq1, seq2) || hasCrossRef(seq2, seq1);
+  }
+
+  /**
+   * Returns true if seq1 has a cross-reference to seq2. Currently this assumes
+   * that sequence name is structured as Source|AccessId.
+   * 
+   * @param seq1
+   * @param seq2
+   * @return
+   */
+  public static boolean hasCrossRef(SequenceI seq1, SequenceI seq2)
+  {
+    if (seq1 == null || seq2 == null)
+    {
+      return false;
+    }
+    String name = seq2.getName();
+    final DBRefEntry[] xrefs = seq1.getDBRef();
+    if (xrefs != null)
+    {
+      for (DBRefEntry xref : xrefs)
+      {
+        String xrefName = xref.getSource() + "|" + xref.getAccessionId();
+        // case-insensitive test, consistent with DBRefEntry.equalRef()
+        if (xrefName.equalsIgnoreCase(name))
+        {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
    * precalculate different products that can be found for seqs in dataset and
    * return them.
    * 
index 00793be..ab606f7 100644 (file)
  */
 package jalview.analysis;
 
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.Vector;
-
+import jalview.api.AlignViewportI;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignedCodon;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
 import jalview.datamodel.FeatureProperties;
+import jalview.datamodel.GraphLine;
 import jalview.datamodel.Mapping;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
+import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 import jalview.util.ShiftList;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
 public class Dna
 {
-  /**
+  private static final String STOP_X = "X";
+
+  private static final Comparator<AlignedCodon> comparator = new CodonComparator();
+
+  /*
+   * 'final' variables describe the inputs to the translation, which should not
+   * be modified.
+   */
+  final private List<SequenceI> selection;
+
+  final private String[] seqstring;
+
+  final private int[] contigs;
+
+  final private char gapChar;
+
+  final private AlignmentAnnotation[] annotations;
+
+  final private int dnaWidth;
+
+  final private Alignment dataset;
+
+  /*
+   * Working variables for the translation.
    * 
-   * @param cdp1
-   * @param cdp2
-   * @return -1 if cdp1 aligns before cdp2, 0 if in the same column or cdp2 is
-   *         null, +1 if after cdp2
+   * The width of the translation-in-progress protein alignment.
    */
-  private static int compare_codonpos(int[] cdp1, int[] cdp2)
-  {
-    if (cdp2 == null
-            || (cdp1[0] == cdp2[0] && cdp1[1] == cdp2[1] && cdp1[2] == cdp2[2]))
-      return 0;
-    if (cdp1[0] < cdp2[0] || cdp1[1] < cdp2[1] || cdp1[2] < cdp2[2])
-      return -1; // one base in cdp1 precedes the corresponding base in the
-    // other codon
-    return 1; // one base in cdp1 appears after the corresponding base in the
-    // other codon.
-  }
+  private int aaWidth = 0;
 
-  /**
-   * DNA->mapped protein sequence alignment translation given set of sequences
-   * 1. id distinct coding regions within selected region for each sequence 2.
-   * generate peptides based on inframe (or given) translation or (optionally
-   * and where specified) out of frame translations (annotated appropriately) 3.
-   * align peptides based on codon alignment
+  /*
+   * This array will be built up so that position i holds the codon positions
+   * e.g. [7, 9, 10] that match column i (base 0) in the aligned translation.
+   * Note this implies a contract that if two codons do not align exactly, their
+   * translated products must occupy different column positions.
    */
+  private AlignedCodon[] alignedCodons;
+
   /**
-   * id potential products from dna 1. search for distinct products within
-   * selected region for each selected sequence 2. group by associated DB type.
-   * 3. return as form for input into above function
+   * Constructor given a viewport and the visible contigs.
+   * 
+   * @param viewport
+   * @param visibleContigs
    */
+  public Dna(AlignViewportI viewport, int[] visibleContigs)
+  {
+    this.selection = Arrays.asList(viewport.getSequenceSelection());
+    this.seqstring = viewport.getViewAsString(true);
+    this.contigs = visibleContigs;
+    this.gapChar = viewport.getGapCharacter();
+    this.annotations = viewport.getAlignment().getAlignmentAnnotation();
+    this.dnaWidth = viewport.getAlignment().getWidth();
+    this.dataset = viewport.getAlignment().getDataset();
+  }
+
   /**
+   * Test whether codon positions cdp1 should align before, with, or after cdp2.
+   * Returns zero if all positions match (or either argument is null). Returns
+   * -1 if any position in the first codon precedes the corresponding position
+   * in the second codon. Else returns +1 (some position in the second codon
+   * precedes the corresponding position in the first).
+   *
+   * Note this is not necessarily symmetric, for example:
+   * <ul>
+   * <li>compareCodonPos([2,5,6], [3,4,5]) returns -1</li>
+   * <li>compareCodonPos([3,4,5], [2,5,6]) also returns -1</li>
+   * </ul>
    * 
+   * @param ac1
+   * @param ac2
+   * @return
    */
+  public static final int compareCodonPos(AlignedCodon ac1, AlignedCodon ac2)
+  {
+    return comparator.compare(ac1, ac2);
+    // return jalview_2_8_2compare(ac1, ac2);
+  }
+
   /**
-   * create a new alignment of protein sequences by an inframe translation of
-   * the provided NA sequences
+   * Codon comparison up to Jalview 2.8.2. This rule is sequence order dependent
+   * - see http://issues.jalview.org/browse/JAL-1635
    * 
-   * @param selection
-   * @param seqstring
-   * @param viscontigs
-   * @param gapCharacter
-   * @param annotations
-   * @param aWidth
-   * @param dataset
-   *          destination dataset for translated sequences and mappings
+   * @param ac1
+   * @param ac2
    * @return
    */
-  public static AlignmentI CdnaTranslate(SequenceI[] selection,
-          String[] seqstring, int viscontigs[], char gapCharacter,
-          AlignmentAnnotation[] annotations, int aWidth, Alignment dataset)
+  private static int jalview_2_8_2compare(AlignedCodon ac1, AlignedCodon ac2)
   {
-    return CdnaTranslate(selection, seqstring, null, viscontigs,
-            gapCharacter, annotations, aWidth, dataset);
+    if (ac1 == null || ac2 == null || (ac1.equals(ac2)))
+    {
+      return 0;
+    }
+    if (ac1.pos1 < ac2.pos1 || ac1.pos2 < ac2.pos2 || ac1.pos3 < ac2.pos3)
+    {
+      // one base in cdp1 precedes the corresponding base in the other codon
+      return -1;
+    }
+    // one base in cdp1 appears after the corresponding base in the other codon.
+    return 1;
   }
 
   /**
    * 
-   * @param selection
-   * @param seqstring
-   * @param product
-   *          - array of DbRefEntry objects from which exon map in seqstring is
-   *          derived
-   * @param viscontigs
-   * @param gapCharacter
-   * @param annotations
-   * @param aWidth
-   * @param dataset
    * @return
    */
-  public static AlignmentI CdnaTranslate(SequenceI[] selection,
-          String[] seqstring, DBRefEntry[] product, int viscontigs[],
-          char gapCharacter, AlignmentAnnotation[] annotations, int aWidth,
-          Alignment dataset)
+  public AlignmentI translateCdna()
   {
-    AlignedCodonFrame codons = new AlignedCodonFrame(aWidth); // stores hash of
-    // subsequent
-    // positions for
-    // each codon
-    // start position
-    // in alignment
-    int s, sSize = selection.length;
-    Vector pepseqs = new Vector();
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+
+    alignedCodons = new AlignedCodon[dnaWidth];
+
+    int s;
+    int sSize = selection.size();
+    List<SequenceI> pepseqs = new ArrayList<SequenceI>();
     for (s = 0; s < sSize; s++)
     {
-      SequenceI newseq = translateCodingRegion(selection[s], seqstring[s],
-              viscontigs, codons, gapCharacter,
-              (product != null) ? product[s] : null, false); // possibly
-                                                             // anonymous
-      // product
+      SequenceI newseq = translateCodingRegion(selection.get(s),
+              seqstring[s], acf, pepseqs);
+
       if (newseq != null)
       {
-        pepseqs.addElement(newseq);
+        pepseqs.add(newseq);
         SequenceI ds = newseq;
         if (dataset != null)
         {
@@ -145,15 +182,15 @@ public class Dna
         }
       }
     }
-    if (codons.aaWidth == 0)
-      return null;
-    SequenceI[] newseqs = new SequenceI[pepseqs.size()];
-    pepseqs.copyInto(newseqs);
+
+    SequenceI[] newseqs = pepseqs.toArray(new SequenceI[pepseqs.size()]);
     AlignmentI al = new Alignment(newseqs);
-    al.padGaps(); // ensure we look aligned.
+    // ensure we look aligned.
+    al.padGaps();
+    // link the protein translation to the DNA dataset
     al.setDataset(dataset);
-    translateAlignedAnnotations(annotations, al, codons);
-    al.addCodonFrame(codons);
+    translateAlignedAnnotations(al, acf);
+    al.addCodonFrame(acf);
     return al;
   }
 
@@ -172,14 +209,13 @@ public class Dna
     for (int gd = 0; gd < selection.length; gd++)
     {
       SequenceI dna = selection[gd];
-      jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils
+      DBRefEntry[] dnarefs = DBRefUtils
               .selectRefs(dna.getDBRef(),
                       jalview.datamodel.DBRefSource.DNACODINGDBS);
       if (dnarefs != null)
       {
         // intersect with pep
-        // intersect with pep
-        Vector mappedrefs = new Vector();
+        List<DBRefEntry> mappedrefs = new ArrayList<DBRefEntry>();
         DBRefEntry[] refs = dna.getDBRef();
         for (int d = 0; d < refs.length; d++)
         {
@@ -187,11 +223,10 @@ public class Dna
                   && refs[d].getMap().getMap().getFromRatio() == 3
                   && refs[d].getMap().getMap().getToRatio() == 1)
           {
-            mappedrefs.addElement(refs[d]); // add translated protein maps
+            mappedrefs.add(refs[d]); // add translated protein maps
           }
         }
-        dnarefs = new DBRefEntry[mappedrefs.size()];
-        mappedrefs.copyInto(dnarefs);
+        dnarefs = mappedrefs.toArray(new DBRefEntry[mappedrefs.size()]);
         for (int d = 0; d < dnarefs.length; d++)
         {
           Mapping mp = dnarefs[d].getMap();
@@ -214,176 +249,107 @@ public class Dna
   }
 
   /**
-   * generate a set of translated protein products from annotated sequenceI
+   * Translate nucleotide alignment annotations onto translated amino acid
+   * alignment using codon mapping codons
    * 
-   * @param selection
-   * @param viscontigs
-   * @param gapCharacter
-   * @param dataset
-   *          destination dataset for translated sequences
-   * @param annotations
-   * @param aWidth
-   * @return
-   */
-  public static AlignmentI CdnaTranslate(SequenceI[] selection,
-          int viscontigs[], char gapCharacter, Alignment dataset)
-  {
-    int alwidth = 0;
-    Vector cdnasqs = new Vector();
-    Vector cdnasqi = new Vector();
-    Vector cdnaprod = new Vector();
-    for (int gd = 0; gd < selection.length; gd++)
-    {
-      SequenceI dna = selection[gd];
-      jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils
-              .selectRefs(dna.getDBRef(),
-                      jalview.datamodel.DBRefSource.DNACODINGDBS);
-      if (dnarefs != null)
-      {
-        // intersect with pep
-        Vector mappedrefs = new Vector();
-        DBRefEntry[] refs = dna.getDBRef();
-        for (int d = 0; d < refs.length; d++)
-        {
-          if (refs[d].getMap() != null && refs[d].getMap().getMap() != null
-                  && refs[d].getMap().getMap().getFromRatio() == 3
-                  && refs[d].getMap().getMap().getToRatio() == 1)
-          {
-            mappedrefs.addElement(refs[d]); // add translated protein maps
-          }
-        }
-        dnarefs = new DBRefEntry[mappedrefs.size()];
-        mappedrefs.copyInto(dnarefs);
-        for (int d = 0; d < dnarefs.length; d++)
-        {
-          Mapping mp = dnarefs[d].getMap();
-          StringBuffer sqstr = new StringBuffer();
-          if (mp != null)
-          {
-            Mapping intersect = mp.intersectVisContigs(viscontigs);
-            // generate seqstring for this sequence based on mapping
-
-            if (sqstr.length() > alwidth)
-              alwidth = sqstr.length();
-            cdnasqs.addElement(sqstr.toString());
-            cdnasqi.addElement(dna);
-            cdnaprod.addElement(intersect);
-          }
-        }
-      }
-      SequenceI[] cdna = new SequenceI[cdnasqs.size()];
-      DBRefEntry[] prods = new DBRefEntry[cdnaprod.size()];
-      String[] xons = new String[cdnasqs.size()];
-      cdnasqs.copyInto(xons);
-      cdnaprod.copyInto(prods);
-      cdnasqi.copyInto(cdna);
-      return CdnaTranslate(cdna, xons, prods, viscontigs, gapCharacter,
-              null, alwidth, dataset);
-    }
-    return null;
-  }
-
-  /**
-   * translate na alignment annotations onto translated amino acid alignment al
-   * using codon mapping codons
-   * 
-   * @param annotations
    * @param al
-   * @param codons
+   *          the translated protein alignment
    */
-  public static void translateAlignedAnnotations(
-          AlignmentAnnotation[] annotations, AlignmentI al,
-          AlignedCodonFrame codons)
+  protected void translateAlignedAnnotations(AlignmentI al,
+          AlignedCodonFrame acf)
   {
-    // //////////////////////////////
-    // Copy annotations across
-    //
     // Can only do this for columns with consecutive codons, or where
     // annotation is sequence associated.
 
-    int pos, a, aSize;
     if (annotations != null)
     {
-      for (int i = 0; i < annotations.length; i++)
+      for (AlignmentAnnotation annotation : annotations)
       {
-        // Skip any autogenerated annotation
-        if (annotations[i].autoCalculated)
+        /*
+         * Skip hidden or autogenerated annotation. Also (for now), RNA
+         * secondary structure annotation. If we want to show this against
+         * protein we need a smarter way to 'translate' without generating
+         * invalid (unbalanced) structure annotation.
+         */
+        if (annotation.autoCalculated || !annotation.visible
+                || annotation.isRNA())
         {
           continue;
         }
 
-        aSize = codons.getaaWidth(); // aa alignment width.
-        jalview.datamodel.Annotation[] anots = (annotations[i].annotations == null) ? null
-                : new jalview.datamodel.Annotation[aSize];
+        int aSize = aaWidth;
+        Annotation[] anots = (annotation.annotations == null) ? null
+                : new Annotation[aSize];
         if (anots != null)
         {
-          for (a = 0; a < aSize; a++)
+          for (int a = 0; a < aSize; a++)
           {
             // process through codon map.
-            if (codons.codons[a] != null
-                    && codons.codons[a][0] == (codons.codons[a][2] - 2))
+            if (a < alignedCodons.length && alignedCodons[a] != null
+                    && alignedCodons[a].pos1 == (alignedCodons[a].pos3 - 2))
             {
-              anots[a] = getCodonAnnotation(codons.codons[a],
-                      annotations[i].annotations);
+              anots[a] = getCodonAnnotation(alignedCodons[a],
+                      annotation.annotations);
             }
           }
         }
 
-        jalview.datamodel.AlignmentAnnotation aa = new jalview.datamodel.AlignmentAnnotation(
-                annotations[i].label, annotations[i].description, anots);
-        aa.graph = annotations[i].graph;
-        aa.graphGroup = annotations[i].graphGroup;
-        aa.graphHeight = annotations[i].graphHeight;
-        if (annotations[i].getThreshold() != null)
+        AlignmentAnnotation aa = new AlignmentAnnotation(annotation.label,
+                annotation.description, anots);
+        aa.graph = annotation.graph;
+        aa.graphGroup = annotation.graphGroup;
+        aa.graphHeight = annotation.graphHeight;
+        if (annotation.getThreshold() != null)
         {
-          aa.setThreshold(new jalview.datamodel.GraphLine(annotations[i]
+          aa.setThreshold(new GraphLine(annotation
                   .getThreshold()));
         }
-        if (annotations[i].hasScore)
+        if (annotation.hasScore)
         {
-          aa.setScore(annotations[i].getScore());
+          aa.setScore(annotation.getScore());
         }
-        if (annotations[i].sequenceRef != null)
+
+        final SequenceI seqRef = annotation.sequenceRef;
+        if (seqRef != null)
         {
-          SequenceI aaSeq = codons
-                  .getAaForDnaSeq(annotations[i].sequenceRef);
+          SequenceI aaSeq = acf.getAaForDnaSeq(seqRef);
           if (aaSeq != null)
           {
             // aa.compactAnnotationArray(); // throw away alignment annotation
             // positioning
             aa.setSequenceRef(aaSeq);
-            aa.createSequenceMapping(aaSeq, aaSeq.getStart(), true); // rebuild
-            // mapping
+            // rebuild mapping
+            aa.createSequenceMapping(aaSeq, aaSeq.getStart(), true);
             aa.adjustForAlignment();
             aaSeq.addAlignmentAnnotation(aa);
           }
-
         }
         al.addAnnotation(aa);
       }
     }
   }
 
-  private static Annotation getCodonAnnotation(int[] is,
+  private static Annotation getCodonAnnotation(AlignedCodon is,
           Annotation[] annotations)
   {
     // Have a look at all the codon positions for annotation and put the first
     // one found into the translated annotation pos.
     int contrib = 0;
     Annotation annot = null;
-    for (int p = 0; p < 3; p++)
+    for (int p = 1; p <= 3; p++)
     {
-      if (annotations[is[p]] != null)
+      int dnaCol = is.getBaseColumn(p);
+      if (annotations[dnaCol] != null)
       {
         if (annot == null)
         {
-          annot = new Annotation(annotations[is[p]]);
+          annot = new Annotation(annotations[dnaCol]);
           contrib = 1;
         }
         else
         {
           // merge with last
-          Annotation cpy = new Annotation(annotations[is[p]]);
+          Annotation cpy = new Annotation(annotations[dnaCol]);
           if (annot.colour == null)
           {
             annot.colour = cpy.colour;
@@ -407,7 +373,7 @@ public class Dna
     }
     if (contrib > 1)
     {
-      annot.value /= (float) contrib;
+      annot.value /= contrib;
     }
     return annot;
   }
@@ -419,92 +385,72 @@ public class Dna
    *          sequence displayed under viscontigs visible columns
    * @param seqstring
    *          ORF read in some global alignment reference frame
-   * @param viscontigs
-   *          mapping from global reference frame to visible seqstring ORF read
-   * @param codons
-   *          Definition of global ORF alignment reference frame
-   * @param gapCharacter
-   * @return sequence ready to be added to alignment.
-   * @deprecated Use
-   *             {@link #translateCodingRegion(SequenceI,String,int[],AlignedCodonFrame,char,DBRefEntry,boolean)}
-   *             instead
-   */
-  public static SequenceI translateCodingRegion(SequenceI selection,
-          String seqstring, int[] viscontigs, AlignedCodonFrame codons,
-          char gapCharacter, DBRefEntry product)
-  {
-    return translateCodingRegion(selection, seqstring, viscontigs, codons,
-            gapCharacter, product, false);
-  }
-
-  /**
-   * Translate a na sequence
-   * 
-   * @param selection
-   *          sequence displayed under viscontigs visible columns
-   * @param seqstring
-   *          ORF read in some global alignment reference frame
-   * @param viscontigs
-   *          mapping from global reference frame to visible seqstring ORF read
-   * @param codons
+   * @param acf
    *          Definition of global ORF alignment reference frame
-   * @param gapCharacter
-   * @param starForStop
-   *          when true stop codons will translate as '*', otherwise as 'X'
+   * @param proteinSeqs
    * @return sequence ready to be added to alignment.
    */
-  public static SequenceI translateCodingRegion(SequenceI selection,
-          String seqstring, int[] viscontigs, AlignedCodonFrame codons,
-          char gapCharacter, DBRefEntry product, final boolean starForStop)
+  protected SequenceI translateCodingRegion(SequenceI selection,
+          String seqstring, AlignedCodonFrame acf,
+          List<SequenceI> proteinSeqs)
   {
-    java.util.List skip = new ArrayList();
+    List<int[]> skip = new ArrayList<int[]>();
     int skipint[] = null;
     ShiftList vismapping = new ShiftList(); // map from viscontigs to seqstring
     // intervals
-    int vc, scontigs[] = new int[viscontigs.length];
+    int vc;
+    int[] scontigs = new int[contigs.length];
     int npos = 0;
-    for (vc = 0; vc < viscontigs.length; vc += 2)
+    for (vc = 0; vc < contigs.length; vc += 2)
     {
       if (vc == 0)
       {
-        vismapping.addShift(npos, viscontigs[vc]);
+        vismapping.addShift(npos, contigs[vc]);
       }
       else
       {
         // hidden region
-        vismapping.addShift(npos, viscontigs[vc] - viscontigs[vc - 1] + 1);
+        vismapping.addShift(npos, contigs[vc] - contigs[vc - 1] + 1);
       }
-      scontigs[vc] = viscontigs[vc];
-      scontigs[vc + 1] = viscontigs[vc + 1];
+      scontigs[vc] = contigs[vc];
+      scontigs[vc + 1] = contigs[vc + 1];
     }
 
-    StringBuffer protein = new StringBuffer();
-    String seq = seqstring.replace('U', 'T');
+    // allocate a roughly sized buffer for the protein sequence
+    StringBuilder protein = new StringBuilder(seqstring.length() / 2);
+    String seq = seqstring.replace('U', 'T').replace('u', 'T');
     char codon[] = new char[3];
-    int cdp[] = new int[3], rf = 0, lastnpos = 0, nend;
+    int cdp[] = new int[3];
+    int rf = 0;
+    int lastnpos = 0;
+    int nend;
     int aspos = 0;
     int resSize = 0;
     for (npos = 0, nend = seq.length(); npos < nend; npos++)
     {
-      if (!jalview.util.Comparison.isGap(seq.charAt(npos)))
+      if (!Comparison.isGap(seq.charAt(npos)))
       {
         cdp[rf] = npos; // store position
         codon[rf++] = seq.charAt(npos); // store base
       }
-      // filled an RF yet ?
       if (rf == 3)
       {
+        /*
+         * Filled up a reading frame...
+         */
+        AlignedCodon alignedCodon = new AlignedCodon(cdp[0], cdp[1], cdp[2]);
         String aa = ResidueProperties.codonTranslate(new String(codon));
         rf = 0;
+        final String gapString = String.valueOf(gapChar);
         if (aa == null)
         {
-          aa = String.valueOf(gapCharacter);
+          aa = gapString;
           if (skipint == null)
           {
             skipint = new int[]
-            { cdp[0], cdp[2] };
+            { alignedCodon.pos1, alignedCodon.pos3 /* cdp[0], cdp[2] */};
           }
-          skipint[1] = cdp[2];
+          skipint[1] = alignedCodon.pos3; // cdp[2];
         }
         else
         {
@@ -599,52 +545,66 @@ public class Dna
           }
           if (aa.equals("STOP"))
           {
-            aa = starForStop ? "*" : "X";
+            aa = STOP_X;
           }
           resSize++;
         }
-        // insert/delete gaps prior to this codon - if necessary
         boolean findpos = true;
         while (findpos)
         {
-          // first ensure that the codons array is long enough.
-          codons.checkCodonFrameWidth(aspos);
-          // now check to see if we place the aa at the current aspos in the
-          // protein alignment
-          switch (Dna.compare_codonpos(cdp, codons.codons[aspos]))
+          /*
+           * Compare this codon's base positions with those currently aligned to
+           * this column in the translation.
+           */
+          final int compareCodonPos = compareCodonPos(alignedCodon,
+                  alignedCodons[aspos]);
+          switch (compareCodonPos)
           {
           case -1:
-            codons.insertAAGap(aspos, gapCharacter);
+
+            /*
+             * This codon should precede the mapped positions - need to insert a
+             * gap in all prior sequences.
+             */
+            insertAAGap(aspos, proteinSeqs);
             findpos = false;
             break;
+
           case +1:
-            // this aa appears after the aligned codons at aspos, so prefix it
-            // with a gap
-            aa = "" + gapCharacter + aa;
+
+            /*
+             * This codon belongs after the aligned codons at aspos. Prefix it
+             * with a gap and try the next position.
+             */
+            aa = gapString + aa;
             aspos++;
-            // if (aspos >= codons.aaWidth)
-            // codons.aaWidth = aspos + 1;
-            break; // check the next position for alignment
+            break;
+
           case 0:
-            // codon aligns at aspos position.
+
+            /*
+             * Exact match - codon 'belongs' at this translated position.
+             */
             findpos = false;
           }
         }
-        // codon aligns with all other sequence residues found at aspos
         protein.append(aa);
         lastnpos = npos;
-        if (codons.codons[aspos] == null)
+        if (alignedCodons[aspos] == null)
         {
           // mark this column as aligning to this aligned reading frame
-          codons.codons[aspos] = new int[]
-          { cdp[0], cdp[1], cdp[2] };
+          alignedCodons[aspos] = alignedCodon;
+        }
+        else if (!alignedCodons[aspos].equals(alignedCodon))
+        {
+          throw new IllegalStateException("Tried to coalign "
+                  + alignedCodons[aspos].toString() + " with "
+                  + alignedCodon.toString());
         }
-        if (aspos >= codons.aaWidth)
+        if (aspos >= aaWidth)
         {
           // update maximum alignment width
-          // (we can do this without calling checkCodonFrameWidth because it was
-          // already done above)
-          codons.setAaWidth(aspos);
+          aaWidth = aspos;
         }
         // ready for next translated reading frame alignment position (if any)
         aspos++;
@@ -656,15 +616,14 @@ public class Dna
               protein.toString());
       if (rf != 0)
       {
-        if (jalview.bin.Cache.log != null)
+        final String errMsg = "trimming contigs for incomplete terminal codon.";
+        if (Cache.log != null)
         {
-          jalview.bin.Cache.log
-                  .debug("trimming contigs for incomplete terminal codon.");
+          Cache.log.debug(errMsg);
         }
         else
         {
-          System.err
-                  .println("trimming contigs for incomplete terminal codon.");
+          System.err.println(errMsg);
         }
         // map and trim contigs to ORF region
         vc = scontigs.length - 1;
@@ -694,7 +653,9 @@ public class Dna
           scontigs = t;
         }
         if (vc <= 0)
+        {
           scontigs = null;
+        }
       }
       if (scontigs != null)
       {
@@ -705,7 +666,9 @@ public class Dna
           scontigs[vc] = selection.findPosition(scontigs[vc]); // not from 1!
           scontigs[vc + 1] = selection.findPosition(scontigs[vc + 1]); // exclusive
           if (scontigs[vc + 1] == selection.getEnd())
+          {
             break;
+          }
         }
         // trim trailing empty intervals.
         if ((vc + 2) < scontigs.length)
@@ -731,27 +694,19 @@ public class Dna
         MapList map = new MapList(scontigs, new int[]
         { 1, resSize }, 3, 1);
 
-        // update newseq as if it was generated as mapping from product
-
-        if (product != null)
-        {
-          newseq.setName(product.getSource() + "|"
-                  + product.getAccessionId());
-          if (product.getMap() != null)
-          {
-            // Mapping mp = product.getMap();
-            // newseq.setStart(mp.getPosition(scontigs[0]));
-            // newseq.setEnd(mp
-            // .getPosition(scontigs[scontigs.length - 1]));
-          }
-        }
         transferCodedFeatures(selection, newseq, map, null, null);
-        SequenceI rseq = newseq.deriveSequence(); // construct a dataset
-        // sequence for our new
-        // peptide, regardless.
-        // store a mapping (this actually stores a mapping between the dataset
-        // sequences for the two sequences
-        codons.addMap(selection, rseq, map);
+
+        /*
+         * Construct a dataset sequence for our new peptide.
+         */
+        SequenceI rseq = newseq.deriveSequence();
+
+        /*
+         * Store a mapping (between the dataset sequences for the two
+         * sequences).
+         */
+        // SIDE-EFFECT: acf stores the aligned sequence reseq; to remove!
+        acf.addMap(selection, rseq, map);
         return rseq;
       }
     }
@@ -761,6 +716,53 @@ public class Dna
   }
 
   /**
+   * Insert a gap into the aligned proteins and the codon mapping array.
+   * 
+   * @param pos
+   * @param proteinSeqs
+   * @return
+   */
+  protected void insertAAGap(int pos,
+          List<SequenceI> proteinSeqs)
+  {
+    aaWidth++;
+    for (SequenceI seq : proteinSeqs)
+    {
+      seq.insertCharAt(pos, gapChar);
+    }
+
+    checkCodonFrameWidth();
+    if (pos < aaWidth)
+    {
+      aaWidth++;
+
+      /*
+       * Shift from [pos] to the end one to the right, and null out [pos]
+       */
+      System.arraycopy(alignedCodons, pos, alignedCodons, pos + 1,
+              alignedCodons.length - pos - 1);
+      alignedCodons[pos] = null;
+    }
+  }
+
+  /**
+   * Check the codons array can accommodate a single insertion, if not resize
+   * it.
+   */
+  protected void checkCodonFrameWidth()
+  {
+    if (alignedCodons[alignedCodons.length - 1] != null)
+    {
+      /*
+       * arraycopy insertion would bump a filled slot off the end, so expand.
+       */
+      AlignedCodon[] c = new AlignedCodon[alignedCodons.length + 10];
+      System.arraycopy(alignedCodons, 0, c, 0, alignedCodons.length);
+      alignedCodons = c;
+    }
+  }
+
+  /**
    * Given a peptide newly translated from a dna sequence, copy over and set any
    * features on the peptide from the DNA. If featureTypes is null, all features
    * on the dna sequence are searched (rather than just the displayed ones), and
@@ -770,20 +772,19 @@ public class Dna
    * @param pep
    * @param map
    * @param featureTypes
-   *          hash who's keys are the displayed feature type strings
+   *          hash whose keys are the displayed feature type strings
    * @param featureGroups
    *          hash where keys are feature groups and values are Boolean objects
    *          indicating if they are displayed.
    */
   private static void transferCodedFeatures(SequenceI dna, SequenceI pep,
-          MapList map, Hashtable featureTypes, Hashtable featureGroups)
+          MapList map, Map<String, Object> featureTypes,
+          Map<String, Boolean> featureGroups)
   {
-    SequenceFeature[] sf = (dna.getDatasetSequence() != null ? dna
-            .getDatasetSequence() : dna).getSequenceFeatures();
+    SequenceFeature[] sfs = dna.getSequenceFeatures();
     Boolean fgstate;
-    jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils
-            .selectRefs(dna.getDBRef(),
-                    jalview.datamodel.DBRefSource.DNACODINGDBS);
+    DBRefEntry[] dnarefs = DBRefUtils.selectRefs(dna.getDBRef(),
+            DBRefSource.DNACODINGDBS);
     if (dnarefs != null)
     {
       // intersect with pep
@@ -795,16 +796,16 @@ public class Dna
         }
       }
     }
-    if (sf != null)
+    if (sfs != null)
     {
-      for (int f = 0; f < sf.length; f++)
+      for (SequenceFeature sf : sfs)
       {
-        fgstate = (featureGroups == null) ? null : ((Boolean) featureGroups
-                .get(sf[f].featureGroup));
-        if ((featureTypes == null || featureTypes.containsKey(sf[f]
-                .getType())) && (fgstate == null || fgstate.booleanValue()))
+        fgstate = (featureGroups == null) ? null : featureGroups
+                .get(sf.featureGroup);
+        if ((featureTypes == null || featureTypes.containsKey(sf.getType()))
+                && (fgstate == null || fgstate.booleanValue()))
         {
-          if (FeatureProperties.isCodingFeature(null, sf[f].getType()))
+          if (FeatureProperties.isCodingFeature(null, sf.getType()))
           {
             // if (map.intersectsFrom(sf[f].begin, sf[f].end))
             {
index c3d7ac1..e6a4853 100755 (executable)
  */
 package jalview.analysis;
 
-import java.util.*;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.SequenceI;
 
-import jalview.datamodel.*;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Vector;
 
 /**
  * Routines for approximate Sequence Id resolution by name using string
@@ -258,14 +261,20 @@ public class SequenceIdMatcher
       }
     }
 
+    @Override
     public int hashCode()
     {
       return ((id.length() >= 4) ? id.substring(0, 4).hashCode() : id
               .hashCode());
     }
 
+    @Override
     public boolean equals(Object s)
     {
+      if (s == null)
+      {
+        return false;
+      }
       if (s instanceof SeqIdName)
       {
         return this.equals((SeqIdName) s);
index 65dd84a..3c8274e 100644 (file)
  */
 package jalview.analysis;
 
-import java.util.*;
-
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
 import jalview.util.Format;
-import jalview.datamodel.*;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
 
 /**
  * Takes in a vector or array of sequences and column start and column end and
@@ -36,6 +40,8 @@ import jalview.datamodel.*;
  */
 public class StructureFrequency
 {
+  public static final int STRUCTURE_PROFILE_LENGTH = 74;
+
   // No need to store 1000s of strings which are not
   // visible to the user.
   public static final String MAXCOUNT = "C";
@@ -187,6 +193,7 @@ public class StructureFrequency
       // UPDATE this for new values
       if (profile)
       {
+        // TODO 1-dim array with jsize in [0], nongapped in [1]; or Pojo
         residueHash.put(PROFILE, new int[][]
         { values, new int[]
         { jSize, (jSize - values['-']) } });
@@ -413,7 +420,7 @@ public class StructureFrequency
          * ((c == 0) ? "" : "; ") + alphabet[c] + " " + ((int) tval) + "%"; } }
          * else {
          */
-        Object[] ca = new Object[625];
+        int[][] ca = new int[625][];
         float[] vl = new float[625];
         int x = 0;
         for (int c = 65; c < 90; c++)
@@ -429,14 +436,18 @@ public class StructureFrequency
         jalview.util.QuickSort.sort(vl, ca);
         int p = 0;
 
+        /*
+         * profile[1] is {total, ungappedTotal}
+         */
+        final int divisor = profile[1][ignoreGapsInConsensusCalculation ? 1
+                : 0];
         for (int c = 624; c > 0; c--)
         {
           if (vl[c] > 0)
           {
-            tval = (vl[c] * 100f / profile[1][ignoreGapsInConsensusCalculation ? 1
-                    : 0]);
-            mouseOver += ((p == 0) ? "" : "; ") + (char) ((int[]) ca[c])[0]
-                    + (char) ((int[]) ca[c])[1] + " " + fmt.form(tval)
+            tval = (vl[c] * 100f / divisor);
+            mouseOver += ((p == 0) ? "" : "; ") + (char) ca[c][0]
+                    + (char) ca[c][1] + " " + fmt.form(tval)
                     + "%";
             p++;
 
@@ -463,16 +474,19 @@ public class StructureFrequency
   public static int[] extractProfile(Hashtable hconsensus,
           boolean ignoreGapsInConsensusCalculation)
   {
-    int[] rtnval = new int[74]; // 2*(5*5)+2
+    int[] rtnval = new int[STRUCTURE_PROFILE_LENGTH]; // 2*(5*5)+2
     int[][] profile = (int[][]) hconsensus.get(StructureFrequency.PROFILE);
     int[][] pairs = (int[][]) hconsensus
             .get(StructureFrequency.PAIRPROFILE);
 
     if (profile == null)
+    {
       return null;
+    }
 
     // TODO fix the object length, also do it in completeConsensus
-    Object[] ca = new Object[625];
+    // Object[] ca = new Object[625];
+    int[][] ca = new int[625][];
     float[] vl = new float[625];
     int x = 0;
     for (int c = 65; c < 90; c++)
@@ -487,21 +501,28 @@ public class StructureFrequency
     }
     jalview.util.QuickSort.sort(vl, ca);
 
-    rtnval[0] = 2;
+    int valuesCount = 0;
     rtnval[1] = 0;
+    int offset = 2;
+    final int divisor = profile[1][ignoreGapsInConsensusCalculation ? 1 : 0];
     for (int c = 624; c > 0; c--)
     {
       if (vl[c] > 0)
       {
-        rtnval[rtnval[0]++] = ((int[]) ca[c])[0];
-        rtnval[rtnval[0]++] = ((int[]) ca[c])[1];
-        rtnval[rtnval[0]] = (int) (vl[c] * 100f / profile[1][ignoreGapsInConsensusCalculation ? 1
-                : 0]);
-        rtnval[1] += rtnval[rtnval[0]++];
+        rtnval[offset++] = ca[c][0];
+        rtnval[offset++] = ca[c][1];
+        rtnval[offset] = (int) (vl[c] * 100f / divisor);
+        rtnval[1] += rtnval[offset++];
+        valuesCount++;
       }
     }
+    rtnval[0] = valuesCount;
 
-    return rtnval;
+    // insert profile type code in position 0
+    int[] result = new int[rtnval.length + 1];
+    result[0] = AlignmentAnnotation.STRUCTURE_PROFILE;
+    System.arraycopy(rtnval, 0, result, 1, rtnval.length);
+    return result;
   }
 
   public static void main(String args[])
index 6faa133..c49ee39 100644 (file)
@@ -40,7 +40,7 @@ import java.util.Map;
  * @author jimp
  * 
  */
-public interface AlignViewportI
+public interface AlignViewportI extends ViewStyleI
 {
 
   int getCharWidth();
@@ -75,11 +75,16 @@ public interface AlignViewportI
 
   Hashtable[] getSequenceConsensusHash();
 
-  Hashtable[] getRnaStructureConsensusHash();
+  /**
+   * Get consensus data table for the cDNA complement of this alignment (if any)
+   * 
+   * @return
+   */
+  Hashtable[] getComplementConsensusHash();
 
-  boolean getIgnoreGapsConsensus();
+  Hashtable[] getRnaStructureConsensusHash();
 
-  boolean getCentreColumnLabels();
+  boolean isIgnoreGapsConsensus();
 
   boolean isCalculationInProgress(AlignmentAnnotation alignmentAnnotation);
 
@@ -95,6 +100,13 @@ public interface AlignViewportI
   AlignmentAnnotation getAlignmentConsensusAnnotation();
 
   /**
+   * get the container for cDNA complement consensus annotation
+   * 
+   * @return
+   */
+  AlignmentAnnotation getComplementConsensusAnnotation();
+
+  /**
    * Test to see if viewport is still open and active
    * 
    * @return true indicates that all references to viewport should be dropped
@@ -122,6 +134,13 @@ public interface AlignViewportI
   void setSequenceConsensusHash(Hashtable[] hconsensus);
 
   /**
+   * Set the cDNA complement consensus for the viewport
+   * 
+   * @param hconsensus
+   */
+  void setComplementConsensusHash(Hashtable[] hconsensus);
+
+  /**
    * 
    * @return the alignment annotatino row for the structure consensus
    *         calculation
@@ -320,10 +339,9 @@ public interface AlignViewportI
    *          first column (inclusive, from 0)
    * @param max
    *          last column (exclusive)
-   * @return int[][] range of {start,end} visible positions TODO: change to list
-   *         of int ranges
+   * @return int[][] range of {start,end} visible positions
    */
-  int[][] getVisibleRegionBoundaries(int min, int max);
+  List<int[]> getVisibleRegionBoundaries(int min, int max);
 
   /**
    * This method returns an array of new SequenceI objects derived from the
@@ -354,4 +372,45 @@ public interface AlignViewportI
 
   boolean hasHiddenRows();
 
+  /**
+   * 
+   * @return a copy of this view's current display settings
+   */
+  public ViewStyleI getViewStyle();
+
+  /**
+   * update the view's display settings with the given style set
+   * 
+   * @param settingsForView
+   */
+  public void setViewStyle(ViewStyleI settingsForView);
+
+  /**
+   * Returns a viewport which holds the cDna for this (protein), or vice versa,
+   * or null if none is set.
+   * 
+   * @return
+   */
+  AlignViewportI getCodingComplement();
+
+  /**
+   * Sets the viewport which holds the cDna for this (protein), or vice versa.
+   * Implementation should guarantee that the reciprocal relationship is always
+   * set, i.e. each viewport is the complement of the other.
+   */
+  void setCodingComplement(AlignViewportI sl);
+
+  /**
+   * Answers true if viewport hosts DNA/RNA, else false.
+   * 
+   * @return
+   */
+  boolean isNucleotide();
+
+  /**
+   * Returns an id guaranteed to be unique for this viewport.
+   * 
+   * @return
+   */
+  String getViewId();
 }
index 09ccbb4..f0fd2e1 100644 (file)
@@ -32,6 +32,8 @@ import jalview.structure.StructureSelectionManager;
 public interface AlignmentViewPanel extends OOMHandlerI
 {
 
+  AlignViewportI getAlignViewport();
+
   AlignmentI getAlignment();
 
   StructureSelectionManager getStructureSelectionManager();
diff --git a/src/jalview/api/SplitContainerI.java b/src/jalview/api/SplitContainerI.java
new file mode 100644 (file)
index 0000000..b9c3121
--- /dev/null
@@ -0,0 +1,38 @@
+package jalview.api;
+
+import jalview.datamodel.AlignmentI;
+
+/**
+ * Describes a visual container that can show two alignments.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public interface SplitContainerI
+{
+
+  /**
+   * Set visibility of the specified split view component.
+   * 
+   * @param alignFrame
+   * @param show
+   */
+  // TODO need an interface for AlignFrame?
+  void setComplementVisible(Object alignFrame, boolean show);
+
+  /**
+   * Returns the alignment that is complementary to the one in the given
+   * AlignFrame, or null.
+   */
+  AlignmentI getComplement(Object af);
+
+  /**
+   * Returns the frame title for the alignment that is complementary to the one
+   * in the given AlignFrame, or null.
+   * 
+   * @param af
+   * @return
+   */
+  String getComplementTitle(Object af);
+
+}
diff --git a/src/jalview/api/ViewStyleI.java b/src/jalview/api/ViewStyleI.java
new file mode 100644 (file)
index 0000000..8fa7f8f
--- /dev/null
@@ -0,0 +1,202 @@
+package jalview.api;
+
+import java.awt.Color;
+
+public interface ViewStyleI
+{
+
+  void setColourAppliesToAllGroups(boolean b);
+
+  boolean getColourAppliesToAllGroups();
+
+  boolean getAbovePIDThreshold();
+
+  void setIncrement(int inc);
+
+  int getIncrement();
+
+  boolean getConservationSelected();
+
+  void setConservationSelected(boolean b);
+
+  void setShowHiddenMarkers(boolean show);
+
+  boolean getShowHiddenMarkers();
+
+  void setScaleRightWrapped(boolean b);
+
+  void setScaleLeftWrapped(boolean b);
+
+  void setScaleAboveWrapped(boolean b);
+
+  boolean getScaleLeftWrapped();
+
+  boolean getScaleAboveWrapped();
+
+  boolean getScaleRightWrapped();
+
+  void setAbovePIDThreshold(boolean b);
+
+  void setThreshold(int thresh);
+
+  int getThreshold();
+
+  boolean getShowJVSuffix();
+
+  void setShowJVSuffix(boolean b);
+
+  void setWrapAlignment(boolean state);
+
+  void setShowText(boolean state);
+
+  void setRenderGaps(boolean state);
+
+  boolean getColourText();
+
+  void setColourText(boolean state);
+
+  void setShowBoxes(boolean state);
+
+  boolean getWrapAlignment();
+
+  boolean getShowText();
+
+  int getWrappedWidth();
+
+  void setWrappedWidth(int w);
+
+  int getCharHeight();
+
+  void setCharHeight(int h);
+
+  int getCharWidth();
+
+  void setCharWidth(int w);
+
+  boolean getShowBoxes();
+
+  boolean getShowUnconserved();
+
+  void setShowUnconserved(boolean showunconserved);
+
+  boolean isDisplayReferenceSeq();
+
+  void setDisplayReferenceSeq(boolean displayReferenceSeq);
+
+  boolean isColourByReferenceSeq();
+
+  void setSeqNameItalics(boolean default1);
+
+  void setShowSequenceFeatures(boolean b);
+
+  boolean isShowSequenceFeatures();
+
+  boolean isRightAlignIds();
+
+  void setRightAlignIds(boolean rightAlignIds);
+
+  boolean isShowAnnotation();
+
+  void setShowAnnotation(boolean b);
+
+  void setShowSeqFeaturesHeight(boolean selected);
+
+  boolean isShowSequenceFeaturesHeight();
+
+  void setColourByReferenceSeq(boolean colourByReferenceSeq);
+
+  Color getTextColour();
+
+  Color getTextColour2();
+
+  int getThresholdTextColour();
+
+  boolean isConservationColourSelected();
+
+  boolean isRenderGaps();
+
+  boolean isShowColourText();
+
+  boolean isShowSeqFeaturesHeight();
+
+  void setConservationColourSelected(boolean conservationColourSelected);
+
+  void setShowColourText(boolean showColourText);
+
+  void setTextColour(Color textColour);
+
+  void setThresholdTextColour(int thresholdTextColour);
+
+  void setTextColour2(Color textColour2);
+
+  boolean isSeqNameItalics();
+
+  void setUpperCasebold(boolean upperCasebold);
+
+  boolean isUpperCasebold();
+  
+  boolean sameStyle(ViewStyleI them);
+
+  void setFontName(String name);
+
+  void setFontStyle(int style);
+
+  void setFontSize(int size);
+
+  int getFontStyle();
+
+  String getFontName();
+
+  int getFontSize();
+
+  /**
+   * @return width of Sequence and Annotation ID margin. If less than zero, then
+   *         width will be autocalculated
+   */
+  int getIdWidth();
+
+  /**
+   * Set width if
+   * 
+   * @param i
+   */
+
+  void setIdWidth(int i);
+
+  /**
+   * centre columnar annotation labels in displayed alignment annotation
+   */
+  boolean isCentreColumnLabels();
+
+  /**
+   * centre columnar annotation labels in displayed alignment annotation
+   */
+  void setCentreColumnLabels(boolean centreColumnLabels);
+
+  /**
+   * enable or disable the display of Database Cross References in the sequence
+   * ID tooltip
+   */
+  void setShowDBRefs(boolean showdbrefs);
+
+  /**
+   * 
+   * @return true if Database References are to be displayed on tooltips.
+   */
+  boolean isShowDBRefs();
+
+  /**
+   * 
+   * @return true if Non-positional features are to be displayed on tooltips.
+   */
+  boolean isShowNPFeats();
+
+  /**
+   * enable or disable the display of Non-Positional sequence features in the
+   * sequence ID tooltip
+   * 
+   * @param show
+   */
+  void setShowNPFeats(boolean shownpfeats);
+
+}
index 1059863..d71fdd4 100644 (file)
  */
 package jalview.appletgui;
 
+import java.awt.CheckboxMenuItem;
+import java.awt.Frame;
+import java.awt.Menu;
+import java.awt.MenuItem;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Vector;
+
 import jalview.analysis.AAFrequency;
+import jalview.analysis.AlignmentAnnotationUtils;
+import jalview.analysis.AlignmentUtils;
 import jalview.analysis.Conservation;
 import jalview.commands.ChangeCaseCommand;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.PDBEntry;
-import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -48,19 +67,11 @@ import jalview.schemes.ZappoColourScheme;
 import jalview.util.MessageManager;
 import jalview.util.UrlLink;
 
-import java.awt.CheckboxMenuItem;
-import java.awt.Frame;
-import java.awt.Menu;
-import java.awt.MenuItem;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.util.Vector;
-
 public class APopupMenu extends java.awt.PopupMenu implements
         ActionListener, ItemListener
 {
+  private static final String ALL_ANNOTATIONS = "All";
+
   Menu groupMenu = new Menu();
 
   MenuItem editGroupName = new MenuItem();
@@ -111,12 +122,29 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
   CheckboxMenuItem displayNonconserved = new CheckboxMenuItem();
 
+  Menu seqShowAnnotationsMenu = new Menu(
+          MessageManager.getString("label.show_annotations"));
+
+  Menu seqHideAnnotationsMenu = new Menu(
+          MessageManager.getString("label.hide_annotations"));
+
+  MenuItem seqAddReferenceAnnotations = new MenuItem(
+          MessageManager.getString("label.add_reference_annotations"));
+
+  Menu groupShowAnnotationsMenu = new Menu(
+          MessageManager.getString("label.show_annotations"));
+
+  Menu groupHideAnnotationsMenu = new Menu(
+          MessageManager.getString("label.hide_annotations"));
+
+  MenuItem groupAddReferenceAnnotations = new MenuItem(
+          MessageManager.getString("label.add_reference_annotations"));
+
   Menu editMenu = new Menu(MessageManager.getString("action.edit"));
 
-  MenuItem copy = new MenuItem(
-          MessageManager.getString("label.jalview_copy"));
+  MenuItem copy = new MenuItem(MessageManager.getString("action.copy"));
 
-  MenuItem cut = new MenuItem(MessageManager.getString("label.jalview_cut"));
+  MenuItem cut = new MenuItem(MessageManager.getString("action.cut"));
 
   MenuItem toUpper = new MenuItem(
           MessageManager.getString("label.to_upper_case"));
@@ -154,7 +182,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
   MenuItem makeReferenceSeq = new MenuItem();
   
-  Sequence seq;
+  SequenceI seq;
 
   MenuItem revealAll = new MenuItem();
 
@@ -167,7 +195,8 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
   Menu menu1 = new Menu();
 
-  public APopupMenu(AlignmentPanel apanel, final Sequence seq, Vector links)
+  public APopupMenu(AlignmentPanel apanel, final SequenceI seq,
+          Vector<String> links)
   {
     // /////////////////////////////////////////////////////////
     // If this is activated from the sequence panel, the user may want to
@@ -196,12 +225,13 @@ public class APopupMenu extends java.awt.PopupMenu implements
       outputmenu.add(item);
     }
 
-    SequenceGroup sg = ap.av.getSelectionGroup();
+    buildAnnotationSubmenus();
 
+    SequenceGroup sg = ap.av.getSelectionGroup();
     if (sg != null && sg.getSize() > 0)
     {
       editGroupName.setLabel(MessageManager.formatMessage(
-              "label.name_param", new String[]
+              "label.name_param", new Object[]
               { sg.getName() }));
       showText.setState(sg.getDisplayText());
       showColourText.setState(sg.getColourText());
@@ -228,10 +258,9 @@ public class APopupMenu extends java.awt.PopupMenu implements
     if (links != null && links.size() > 0)
     {
       Menu linkMenu = new Menu(MessageManager.getString("action.link"));
-      String link;
       for (int i = 0; i < links.size(); i++)
       {
-        link = links.elementAt(i).toString();
+        String link = links.elementAt(i);
         UrlLink urlLink = new UrlLink(link);
         if (!urlLink.isValid())
         {
@@ -361,7 +390,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
                 .getString("action.set_as_reference")); // );
       }
       repGroup.setLabel(MessageManager.formatMessage(
-              "label.represent_group_with", new String[]
+              "label.represent_group_with", new Object[]
               { seq.getName() }));
     }
     else
@@ -391,6 +420,95 @@ public class APopupMenu extends java.awt.PopupMenu implements
   }
 
   /**
+   * Build menus for annotation types that may be shown or hidden, and for
+   * 'reference annotations' that may be added to the alignment.
+   */
+  private void buildAnnotationSubmenus()
+  {
+    /*
+     * First for the currently selected sequence (if there is one):
+     */
+    final List<SequenceI> selectedSequence = (seq == null ? Collections
+            .<SequenceI> emptyList() : Arrays.asList(seq));
+    buildAnnotationTypesMenus(seqShowAnnotationsMenu,
+            seqHideAnnotationsMenu, selectedSequence);
+    configureReferenceAnnotationsMenu(seqAddReferenceAnnotations,
+            selectedSequence);
+
+    /*
+     * and repeat for the current selection group (if there is one):
+     */
+    final List<SequenceI> selectedGroup = (ap.av.getSelectionGroup() == null ? Collections
+            .<SequenceI> emptyList() : ap.av.getSelectionGroup()
+            .getSequences());
+    buildAnnotationTypesMenus(groupShowAnnotationsMenu,
+            groupHideAnnotationsMenu, selectedGroup);
+    configureReferenceAnnotationsMenu(groupAddReferenceAnnotations,
+            selectedGroup);
+  }
+
+  /**
+   * Determine whether or not to enable 'add reference annotations' menu item.
+   * It is enable if there are any annotations, on any of the selected
+   * sequences, which are not yet on the alignment (visible or not).
+   * 
+   * @param menu
+   * @param forSequences
+   */
+  private void configureReferenceAnnotationsMenu(MenuItem menuItem,
+          List<SequenceI> forSequences)
+  {
+    menuItem.setEnabled(false);
+
+    /*
+     * Temporary store to hold distinct calcId / type pairs for the tooltip.
+     * Using TreeMap means calcIds are shown in alphabetical order.
+     */
+    Map<String, String> tipEntries = new TreeMap<String, String>();
+    final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<SequenceI, List<AlignmentAnnotation>>();
+    AlignmentI al = this.ap.av.getAlignment();
+    AlignmentUtils.findAddableReferenceAnnotations(forSequences,
+            tipEntries, candidates, al);
+    if (!candidates.isEmpty())
+    {
+      StringBuilder tooltip = new StringBuilder(64);
+      tooltip.append(MessageManager.getString("label.add_annotations_for"));
+
+      /*
+       * Found annotations that could be added. Enable the menu item, and
+       * configure its action.
+       */
+      menuItem.setEnabled(true);
+
+      menuItem.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          addReferenceAnnotations_actionPerformed(candidates);
+        }
+      });
+    }
+  }
+
+  /**
+   * Add annotations to the sequences and to the alignment.
+   * 
+   * @param candidates
+   *          a map whose keys are sequences on the alignment, and values a list
+   *          of annotations to add to each sequence
+   */
+  protected void addReferenceAnnotations_actionPerformed(
+          Map<SequenceI, List<AlignmentAnnotation>> candidates)
+  {
+    final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
+    final AlignmentI alignment = this.ap.getAlignment();
+    AlignmentUtils.addReferenceAnnotations(candidates, alignment,
+            selectionGroup);
+    refresh();
+  }
+
+  /**
    * add a show URL menu item to the given linkMenu
    * 
    * @param linkMenu
@@ -591,7 +709,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
       {
         if (seq == null)
         {
-          seq = (Sequence) sg.getSequenceAt(0);
+          seq = sg.getSequenceAt(0);
         }
 
         EditNameDialog dialog = new EditNameDialog(seq.getSequenceAsString(
@@ -619,10 +737,9 @@ public class APopupMenu extends java.awt.PopupMenu implements
     else if (source == toUpper || source == toLower || source == toggleCase)
     {
       SequenceGroup sg = ap.av.getSelectionGroup();
-      Vector regions = new Vector();
       if (sg != null)
       {
-        int[][] startEnd = ap.av.getVisibleRegionBoundaries(
+        List<int[]> startEnd = ap.av.getVisibleRegionBoundaries(
                 sg.getStartRes(), sg.getEndRes() + 1);
 
         String description;
@@ -708,15 +825,14 @@ public class APopupMenu extends java.awt.PopupMenu implements
     Frame frame = new Frame();
     frame.add(cap);
     jalview.bin.JalviewLite.addFrame(frame, MessageManager.formatMessage(
-            "label.selection_output_command", new String[]
+            "label.selection_output_command", new Object[]
             { e.getActionCommand() }), 600, 500);
     // JBPNote: getSelectionAsNewSequence behaviour has changed - this method
     // now returns a full copy of sequence data
     // TODO consider using getSequenceSelection instead here
 
     cap.setText(new jalview.io.AppletFormatAdapter().formatSequences(
-            e.getActionCommand(), 
-            ap.av.showJVSuffix, ap.av, true));
+            e.getActionCommand(), ap.av.getShowJVSuffix(), ap.av, true));
 
   }
 
@@ -740,7 +856,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
     for (SequenceI seq : sequences)
     {
       contents.append(MessageManager.formatMessage(
-              "label.annotation_for_displayid", new String[]
+              "label.annotation_for_displayid", new Object[]
               { seq.getDisplayId(true) }));
       new SequenceAnnotationReport(null)
               .createSequenceAnnotationReport(
@@ -759,7 +875,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
             + (sequences.length == 1 ? sequences[0].getDisplayId(true)
                     : "Selection"), 600, 500);
     cap.setText(MessageManager.formatMessage("label.html_content",
-            new String[]
+            new Object[]
             { contents.toString() }));
   }
 
@@ -786,12 +902,12 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
       if (ap.av.applet.jmolAvailable)
       {
-        new jalview.appletgui.AppletJmol(entry, new Sequence[]
+        new jalview.appletgui.AppletJmol(entry, new SequenceI[]
         { seq }, null, ap, AppletFormatAdapter.URL);
       }
       else
       {
-        new MCview.AppletPDBViewer(entry, new Sequence[]
+        new MCview.AppletPDBViewer(entry, new SequenceI[]
         { seq }, null, ap, AppletFormatAdapter.URL);
       }
 
@@ -804,7 +920,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
       Frame frame = new Frame();
       frame.add(cap);
       jalview.bin.JalviewLite.addFrame(frame, MessageManager.formatMessage(
-              "label.paste_pdb_file_for_sequence", new String[]
+              "label.paste_pdb_file_for_sequence", new Object[]
               { seq.getName() }), 400, 300);
     }
   }
@@ -848,7 +964,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
     pdb.setLabel(MessageManager.getString("label.view_pdb_structure"));
     hideSeqs.setLabel(MessageManager.getString("action.hide_sequences"));
     repGroup.setLabel(MessageManager.formatMessage(
-            "label.represent_group_with", new String[]
+            "label.represent_group_with", new Object[]
             { "" }));
     revealAll.setLabel(MessageManager.getString("action.reveal_all"));
     revealSeq.setLabel(MessageManager.getString("action.reveal_sequences"));
@@ -859,6 +975,9 @@ public class APopupMenu extends java.awt.PopupMenu implements
     this.add(revealSeq);
     this.add(revealAll);
     // groupMenu.add(selSeqDetails);
+    groupMenu.add(groupShowAnnotationsMenu);
+    groupMenu.add(groupHideAnnotationsMenu);
+    groupMenu.add(groupAddReferenceAnnotations);
     groupMenu.add(editMenu);
     groupMenu.add(outputmenu);
     groupMenu.add(sequenceFeature);
@@ -925,6 +1044,9 @@ public class APopupMenu extends java.awt.PopupMenu implements
     editMenu.add(toLower);
     toLower.addActionListener(this);
     editMenu.add(toggleCase);
+    seqMenu.add(seqShowAnnotationsMenu);
+    seqMenu.add(seqHideAnnotationsMenu);
+    seqMenu.add(seqAddReferenceAnnotations);
     seqMenu.add(sequenceName);
     seqMenu.add(makeReferenceSeq);
     // seqMenu.add(sequenceDetails);
@@ -1024,7 +1146,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
       int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup()
               .getName());
 
-      sg.cs.setThreshold(threshold, ap.av.getIgnoreGapsConsensus());
+      sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus());
 
       SliderPanel.showPIDSlider();
 
@@ -1032,7 +1154,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
     else
     // remove PIDColouring
     {
-      sg.cs.setThreshold(0, ap.av.getIgnoreGapsConsensus());
+      sg.cs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
     }
 
     refresh();
@@ -1205,4 +1327,109 @@ public class APopupMenu extends java.awt.PopupMenu implements
     ap.av.sendSelection();
   }
 
+  /**
+   * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
+   * "All" is added first, followed by a separator. Then add any annotation
+   * types associated with the current selection. Separate menus are built for
+   * the selected sequence group (if any), and the selected sequence.
+   * <p>
+   * Some annotation rows are always rendered together - these can be identified
+   * by a common graphGroup property > -1. Only one of each group will be marked
+   * as visible (to avoid duplication of the display). For such groups we add a
+   * composite type name, e.g.
+   * <p>
+   * IUPredWS (Long), IUPredWS (Short)
+   * 
+   * @param seq
+   */
+  protected void buildAnnotationTypesMenus(Menu showMenu, Menu hideMenu,
+          List<SequenceI> forSequences)
+  {
+    showMenu.removeAll();
+    hideMenu.removeAll();
+  
+    final List<String> all = Arrays.asList(ALL_ANNOTATIONS);
+    addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, true);
+    addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
+            false);
+    showMenu.addSeparator();
+    hideMenu.addSeparator();
+  
+    final AlignmentAnnotation[] annotations = ap.getAlignment()
+            .getAlignmentAnnotation();
+  
+    /*
+     * Find shown/hidden annotations types, distinguished by source (calcId),
+     * and grouped by graphGroup. Using LinkedHashMap means we will retrieve in
+     * the insertion order, which is the order of the annotations on the
+     * alignment.
+     */
+    Map<String, List<List<String>>> shownTypes = new LinkedHashMap<String, List<List<String>>>();
+    Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<String, List<List<String>>>();
+    AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes,
+            hiddenTypes,
+            AlignmentAnnotationUtils.asList(annotations),
+            forSequences);
+  
+    for (String calcId : hiddenTypes.keySet())
+    {
+      for (List<String> type : hiddenTypes.get(calcId))
+      {
+        addAnnotationTypeToShowHide(showMenu, forSequences,
+                calcId, type, false, true);
+      }
+    }
+    // grey out 'show annotations' if none are hidden
+    showMenu.setEnabled(!hiddenTypes.isEmpty());
+  
+    for (String calcId : shownTypes.keySet())
+    {
+      for (List<String> type : shownTypes.get(calcId))
+      {
+        addAnnotationTypeToShowHide(hideMenu, forSequences,
+                calcId, type, false, false);
+      }
+    }
+    // grey out 'hide annotations' if none are shown
+    hideMenu.setEnabled(!shownTypes.isEmpty());
+  }
+
+  /**
+   * Add one annotation type to the 'Show Annotations' or 'Hide Annotations'
+   * menus.
+   * 
+   * @param showOrHideMenu
+   *          the menu to add to
+   * @param forSequences
+   *          the sequences whose annotations may be shown or hidden
+   * @param calcId
+   * @param types
+   *          the label to add
+   * @param allTypes
+   *          if true this is a special label meaning 'All'
+   * @param actionIsShow
+   *          if true, the select menu item action is to show the annotation
+   *          type, else hide
+   */
+  protected void addAnnotationTypeToShowHide(Menu showOrHideMenu,
+          final List<SequenceI> forSequences, String calcId,
+          final List<String> types, final boolean allTypes,
+          final boolean actionIsShow)
+  {
+    String label = types.toString(); // [a, b, c]
+    label = label.substring(1, label.length() - 1);
+    final MenuItem item = new MenuItem(label);
+    item.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        AlignmentUtils.showOrHideSequenceAnnotations(ap.getAlignment(), types,
+                forSequences, allTypes, actionIsShow);
+        refresh();
+      }
+    });
+    showOrHideMenu.add(item);
+  }
+
 }
index 623388b..3e919f6 100644 (file)
  */
 package jalview.appletgui;
 
+import java.awt.BorderLayout;
+import java.awt.Canvas;
+import java.awt.CheckboxMenuItem;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Label;
+import java.awt.Menu;
+import java.awt.MenuBar;
+import java.awt.MenuItem;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
 import jalview.analysis.AlignmentSorter;
+import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
+import jalview.api.AlignViewportI;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceStructureBinding;
 import jalview.bin.JalviewLite;
@@ -35,6 +71,7 @@ import jalview.commands.RemoveGapsCommand;
 import jalview.commands.SlideSequencesCommand;
 import jalview.commands.TrimRegionCommand;
 import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.ColumnSelection;
@@ -64,40 +101,9 @@ import jalview.schemes.TurnColourScheme;
 import jalview.schemes.ZappoColourScheme;
 import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 
-import java.awt.BorderLayout;
-import java.awt.Canvas;
-import java.awt.CheckboxMenuItem;
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Frame;
-import java.awt.Graphics;
-import java.awt.Label;
-import java.awt.Menu;
-import java.awt.MenuBar;
-import java.awt.MenuItem;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.FocusEvent;
-import java.awt.event.FocusListener;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.awt.event.KeyEvent;
-import java.awt.event.KeyListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.util.Arrays;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.Vector;
-
 public class AlignFrame extends EmbmenuFrame implements ActionListener,
         ItemListener, KeyListener, AlignViewControllerGuiI
 {
@@ -113,9 +119,41 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   String jalviewServletURL;
 
-  public AlignFrame(AlignmentI al, jalview.bin.JalviewLite applet,
+  /*
+   * Flag for showing autocalculated consensus above or below other consensus
+   * rows
+   */
+  private boolean showAutoCalculatedAbove;
+
+  private SequenceAnnotationOrder annotationSortOrder;
+
+  /**
+   * Constructor that creates the frame and adds it to the display.
+   * 
+   * @param al
+   * @param applet
+   * @param title
+   * @param embedded
+   */
+  public AlignFrame(AlignmentI al, JalviewLite applet,
           String title, boolean embedded)
   {
+    this(al, applet, title, embedded, true);
+  }
+
+  /**
+   * Constructor that optionally allows the frame to be displayed or only
+   * created.
+   * 
+   * @param al
+   * @param applet
+   * @param title
+   * @param embedded
+   * @param addToDisplay
+   */
+  public AlignFrame(AlignmentI al, JalviewLite applet,
+          String title, boolean embedded, boolean addToDisplay)
+  {
     if (applet != null)
     {
       jalviewServletURL = applet.getParameter("APPLICATION_URL");
@@ -157,7 +195,6 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.updateConservation(alignPanel);
     viewport.updateConsensus(alignPanel);
 
-    annotationPanelMenuItem.setState(viewport.showAnnotation);
     displayNonconservedMenuItem.setState(viewport.getShowUnconserved());
     followMouseOverFlag.setState(viewport.getFollowHighlight());
     showGroupConsensus.setState(viewport.isShowGroupConsensus());
@@ -166,8 +203,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     showSequenceLogo.setState(viewport.isShowSequenceLogo());
     normSequenceLogo.setState(viewport.isNormaliseSequenceLogo());
     applyToAllGroups.setState(viewport.getColourAppliesToAllGroups());
+    showAlignmentAnnotations.setState(viewport.isShowAnnotation());
+    showSequenceAnnotations.setState(viewport.isShowAnnotation());
 
-    seqLimits.setState(viewport.showJVSuffix);
+    seqLimits.setState(viewport.getShowJVSuffix());
 
     if (applet != null)
     {
@@ -233,8 +272,19 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     alignPanel.annotationPanelHolder.addKeyListener(this);
     alignPanel.annotationSpaceFillerHolder.addKeyListener(this);
     alignPanel.alabels.addKeyListener(this);
-    createAlignFrameWindow(embedded, title);
 
+    if (addToDisplay)
+    {
+      addToDisplay(embedded);
+    }
+  }
+
+  /**
+   * @param embedded
+   */
+  public void addToDisplay(boolean embedded)
+  {
+    createAlignFrameWindow(embedded);
     validate();
     alignPanel.adjustAnnotationHeight();
     alignPanel.paintAlignment(true);
@@ -498,7 +548,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
 
     case KeyEvent.VK_PAGE_UP:
-      if (viewport.wrapAlignment)
+      if (viewport.getWrapAlignment())
       {
         alignPanel.scrollUp(true);
       }
@@ -510,7 +560,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       break;
 
     case KeyEvent.VK_PAGE_DOWN:
-      if (viewport.wrapAlignment)
+      if (viewport.getWrapAlignment())
       {
         alignPanel.scrollUp(false);
       }
@@ -690,111 +740,188 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   @Override
   public void itemStateChanged(ItemEvent evt)
   {
-    if (evt.getSource() == displayNonconservedMenuItem)
+    final Object source = evt.getSource();
+    if (source == displayNonconservedMenuItem)
     {
       displayNonconservedMenuItem_actionPerformed();
     }
-    else if (evt.getSource() == colourTextMenuItem)
+    else if (source == colourTextMenuItem)
     {
       colourTextMenuItem_actionPerformed();
     }
-    else if (evt.getSource() == wrapMenuItem)
+    else if (source == wrapMenuItem)
     {
       wrapMenuItem_actionPerformed();
     }
-    else if (evt.getSource() == scaleAbove)
+    else if (source == scaleAbove)
     {
       viewport.setScaleAboveWrapped(scaleAbove.getState());
     }
-    else if (evt.getSource() == scaleLeft)
+    else if (source == scaleLeft)
     {
       viewport.setScaleLeftWrapped(scaleLeft.getState());
     }
-    else if (evt.getSource() == scaleRight)
+    else if (source == scaleRight)
     {
       viewport.setScaleRightWrapped(scaleRight.getState());
     }
-    else if (evt.getSource() == seqLimits)
+    else if (source == seqLimits)
     {
       seqLimits_itemStateChanged();
     }
-    else if (evt.getSource() == viewBoxesMenuItem)
+    else if (source == viewBoxesMenuItem)
     {
       viewport.setShowBoxes(viewBoxesMenuItem.getState());
     }
-    else if (evt.getSource() == viewTextMenuItem)
+    else if (source == viewTextMenuItem)
     {
       viewport.setShowText(viewTextMenuItem.getState());
     }
-    else if (evt.getSource() == renderGapsMenuItem)
+    else if (source == renderGapsMenuItem)
     {
       viewport.setRenderGaps(renderGapsMenuItem.getState());
     }
-    else if (evt.getSource() == annotationPanelMenuItem)
+    else if (source == annotationPanelMenuItem)
     {
       viewport.setShowAnnotation(annotationPanelMenuItem.getState());
       alignPanel.setAnnotationVisible(annotationPanelMenuItem.getState());
     }
-    else if (evt.getSource() == sequenceFeatures)
+    else if (source == sequenceFeatures)
     {
       viewport.setShowSequenceFeatures(sequenceFeatures.getState());
       alignPanel.seqPanel.seqCanvas.repaint();
     }
-    else if (evt.getSource() == conservationMenuItem)
+    else if (source == showAlignmentAnnotations)
+    {
+      setAnnotationsVisibility();
+    }
+    else if (source == showSequenceAnnotations)
+    {
+      setAnnotationsVisibility();
+    }
+    else if (source == sortAnnBySequence)
+    {
+      boolean newState = sortAnnBySequence.getState();
+      sortAnnByLabel.setState(false);
+      setAnnotationSortOrder(newState ? SequenceAnnotationOrder.SEQUENCE_AND_LABEL
+              : SequenceAnnotationOrder.NONE);
+      setViewportAnnotationOrder();
+    }
+    else if (source == sortAnnByLabel)
+    {
+      boolean newState = sortAnnByLabel.getState();
+      sortAnnBySequence.setState(false);
+      setAnnotationSortOrder(newState ? SequenceAnnotationOrder.LABEL_AND_SEQUENCE
+              : SequenceAnnotationOrder.NONE);
+      setViewportAnnotationOrder();
+    }
+    else if (source == showAutoFirst)
+    {
+      showAutoLast.setState(!showAutoFirst.getState());
+      setShowAutoCalculatedAbove(showAutoFirst.getState());
+      setViewportAnnotationOrder();
+    }
+    else if (source == showAutoLast)
+    {
+      showAutoFirst.setState(!showAutoLast.getState());
+      setShowAutoCalculatedAbove(showAutoFirst.getState());
+      setViewportAnnotationOrder();
+    }
+    else if (source == conservationMenuItem)
     {
       conservationMenuItem_actionPerformed();
     }
-    else if (evt.getSource() == abovePIDThreshold)
+    else if (source == abovePIDThreshold)
     {
       abovePIDThreshold_actionPerformed();
     }
-    else if (evt.getSource() == applyToAllGroups)
+    else if (source == applyToAllGroups)
     {
       viewport.setColourAppliesToAllGroups(applyToAllGroups.getState());
     }
-    else if (evt.getSource() == autoCalculate)
+    else if (source == autoCalculate)
     {
       viewport.autoCalculateConsensus = autoCalculate.getState();
     }
-    else if (evt.getSource() == sortByTree)
+    else if (source == sortByTree)
     {
       viewport.sortByTree = sortByTree.getState();
     }
-    else if (evt.getSource() == this.centreColumnLabelFlag)
+    else if (source == this.centreColumnLabelFlag)
     {
       centreColumnLabelFlag_stateChanged();
     }
-    else if (evt.getSource() == this.followMouseOverFlag)
+    else if (source == this.followMouseOverFlag)
     {
       mouseOverFlag_stateChanged();
     }
-    else if (evt.getSource() == showGroupConsensus)
+    else if (source == showGroupConsensus)
     {
       showGroupConsensus_actionPerformed();
     }
-    else if (evt.getSource() == showGroupConservation)
+    else if (source == showGroupConservation)
     {
       showGroupConservation_actionPerformed();
     }
-    else if (evt.getSource() == showSequenceLogo)
+    else if (source == showSequenceLogo)
     {
       showSequenceLogo_actionPerformed();
     }
-    else if (evt.getSource() == normSequenceLogo)
+    else if (source == normSequenceLogo)
     {
       normSequenceLogo_actionPerformed();
     }
-    else if (evt.getSource() == showConsensusHistogram)
+    else if (source == showConsensusHistogram)
     {
       showConsensusHistogram_actionPerformed();
     }
-    else if (evt.getSource() == applyAutoAnnotationSettings)
+    else if (source == applyAutoAnnotationSettings)
     {
       applyAutoAnnotationSettings_actionPerformed();
     }
     alignPanel.paintAlignment(true);
   }
 
+  /**
+   * Set the visibility state of sequence-related and/or alignment-related
+   * annotations depending on checkbox selections. Repaint after calling.
+   * 
+   * @param visible
+   */
+  private void setAnnotationsVisibility()
+  {
+    boolean showForAlignment = showAlignmentAnnotations.getState();
+    boolean showForSequences = showSequenceAnnotations.getState();
+    for (AlignmentAnnotation aa : alignPanel.getAlignment()
+            .getAlignmentAnnotation())
+    {
+      boolean visible = (aa.sequenceRef == null ? showForAlignment
+              : showForSequences);
+        aa.visible = visible;
+    }
+    alignPanel.validateAnnotationDimensions(false);
+  }
+
+  private void setAnnotationSortOrder(SequenceAnnotationOrder order)
+  {
+    this.annotationSortOrder = order;
+  }
+
+  /**
+   * Set flags on the viewport that control annotation ordering
+   */
+  private void setViewportAnnotationOrder()
+  {
+    this.alignPanel.av.setSortAnnotationsBy(this.annotationSortOrder);
+    this.alignPanel.av
+            .setShowAutocalculatedAbove(this.showAutoCalculatedAbove);
+  }
+
+  private void setShowAutoCalculatedAbove(boolean showAbove)
+  {
+    this.showAutoCalculatedAbove = showAbove;
+  }
+
   private void mouseOverFlag_stateChanged()
   {
     viewport.followHighlight = followMouseOverFlag.getState();
@@ -1181,11 +1308,11 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     Frame frame = new Frame();
     frame.add(cap);
     jalview.bin.JalviewLite.addFrame(frame, MessageManager.formatMessage(
-            "label.alignment_output_command", new String[]
+            "label.alignment_output_command", new Object[]
             { e.getActionCommand() }), 600, 500);
     cap.setText(new AppletFormatAdapter().formatSequences(
             e.getActionCommand(), viewport.getAlignment(),
-            viewport.showJVSuffix));
+            viewport.getShowJVSuffix()));
   }
 
   public void loadAnnotations()
@@ -1403,12 +1530,12 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   void updateEditMenuBar()
   {
 
-    if (viewport.historyList.size() > 0)
+    if (viewport.getHistoryList().size() > 0)
     {
       undoMenuItem.setEnabled(true);
-      CommandI command = (CommandI) viewport.historyList.peek();
+      CommandI command = viewport.getHistoryList().peek();
       undoMenuItem.setLabel(MessageManager.formatMessage(
-              "label.undo_command", new String[]
+              "label.undo_command", new Object[]
               { command.getDescription() }));
     }
     else
@@ -1417,13 +1544,13 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       undoMenuItem.setLabel(MessageManager.getString("action.undo"));
     }
 
-    if (viewport.redoList.size() > 0)
+    if (viewport.getRedoList().size() > 0)
     {
       redoMenuItem.setEnabled(true);
 
-      CommandI command = (CommandI) viewport.redoList.peek();
+      CommandI command = viewport.getRedoList().peek();
       redoMenuItem.setLabel(MessageManager.formatMessage(
-              "label.redo_command", new String[]
+              "label.redo_command", new Object[]
               { command.getDescription() }));
     }
     else
@@ -1441,8 +1568,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   {
     if (command.getSize() > 0)
     {
-      viewport.historyList.push(command);
-      viewport.redoList.removeAllElements();
+      viewport.addToHistoryList(command);
+      viewport.clearRedoList();
       updateEditMenuBar();
       viewport.updateHiddenColumns();
     }
@@ -1456,13 +1583,13 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
    */
   protected void undoMenuItem_actionPerformed()
   {
-    if (viewport.historyList.size() < 1)
+    if (viewport.getHistoryList().isEmpty())
     {
       return;
     }
 
-    CommandI command = (CommandI) viewport.historyList.pop();
-    viewport.redoList.push(command);
+    CommandI command = viewport.getHistoryList().pop();
+    viewport.addToRedoList(command);
     command.undoCommand(null);
 
     AlignViewport originalSource = getOriginatingSource(command);
@@ -1488,13 +1615,13 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
    */
   protected void redoMenuItem_actionPerformed()
   {
-    if (viewport.redoList.size() < 1)
+    if (viewport.getRedoList().isEmpty())
     {
       return;
     }
 
-    CommandI command = (CommandI) viewport.redoList.pop();
-    viewport.historyList.push(command);
+    CommandI command = viewport.getRedoList().pop();
+    viewport.addToHistoryList(command);
     command.doCommand(null);
 
     AlignViewport originalSource = getOriginatingSource(command);
@@ -1554,6 +1681,12 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     return originalSource;
   }
 
+  /**
+   * Move the currently selected sequences up or down one position in the
+   * alignment
+   * 
+   * @param up
+   */
   public void moveSelectedSequences(boolean up)
   {
     SequenceGroup sg = viewport.getSelectionGroup();
@@ -1564,6 +1697,21 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.getAlignment().moveSelectedSequencesByOne(sg,
             up ? null : viewport.getHiddenRepSequences(), up);
     alignPanel.paintAlignment(true);
+
+    /*
+     * Also move cDNA/protein complement sequences
+     */
+    AlignViewportI complement = viewport.getCodingComplement();
+    if (complement != null)
+    {
+      SequenceGroup mappedSelection = MappingUtils.mapSequenceGroup(sg,
+              viewport, complement);
+      complement.getAlignment().moveSelectedSequencesByOne(mappedSelection,
+              up ? null : complement.getHiddenRepSequences(), up);
+      // TODO need to trigger a repaint of the complementary panel - how?
+      // would prefer to handle in SplitFrame but it is not overriding key
+      // listener chiz
+    }
   }
 
   synchronized void slideSequences(boolean right, int size)
@@ -1651,11 +1799,12 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
 
     boolean appendHistoryItem = false;
-    if (viewport.historyList != null && viewport.historyList.size() > 0
-            && viewport.historyList.peek() instanceof SlideSequencesCommand)
+    Deque<CommandI> historyList = viewport.getHistoryList();
+    if (historyList != null && historyList.size() > 0
+            && historyList.peek() instanceof SlideSequencesCommand)
     {
       appendHistoryItem = ssc
-              .appendSlideCommand((SlideSequencesCommand) viewport.historyList
+              .appendSlideCommand((SlideSequencesCommand) historyList
                       .peek());
     }
 
@@ -1680,12 +1829,12 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
     SequenceGroup sg = viewport.getSelectionGroup();
     copiedSequences = new StringBuffer();
-    Hashtable orderedSeqs = new Hashtable();
+    Map<Integer, SequenceI> orderedSeqs = new HashMap<Integer, SequenceI>();
     for (int i = 0; i < sg.getSize(); i++)
     {
       SequenceI seq = sg.getSequenceAt(i);
       int index = viewport.getAlignment().findIndex(seq);
-      orderedSeqs.put(index + "", seq);
+      orderedSeqs.put(index, seq);
     }
 
     int index = 0, startRes, endRes;
@@ -1697,7 +1846,6 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       int hiddenOffset = viewport.getSelectionGroup().getStartRes();
       for (int[] region : viewport.getColumnSelection().getHiddenColumns())
       {
-
         copiedHiddenColumns.addElement(new int[]
         { region[0] - hiddenOffset, region[1] - hiddenOffset });
       }
@@ -1713,11 +1861,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
       while (seq == null)
       {
-        if (orderedSeqs.containsKey(index + ""))
+        if (orderedSeqs.containsKey(index))
         {
-          seq = (SequenceI) orderedSeqs.get(index + "");
+          seq = orderedSeqs.get(index);
           index++;
-
           break;
         }
         else
@@ -2261,8 +2408,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
     newaf.setTitle(title.toString());
 
-    newaf.viewport.historyList = viewport.historyList;
-    newaf.viewport.redoList = viewport.redoList;
+    newaf.viewport.setHistoryList(viewport.getHistoryList());
+    newaf.viewport.setRedoList(viewport.getRedoList());
     return newaf;
   }
 
@@ -2276,13 +2423,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     if (alignPanel != null
             && (fr = alignPanel.getFeatureRenderer()) != null)
     {
-      List gps = fr.getFeatureGroups();
-      int p=0;
-      String[] _gps = new String[gps.size()];
-      for (Object gp:gps)
-      {
-        _gps[p++] = gp.toString();
-      }
+      List<String> gps = fr.getFeatureGroups();
+      String[] _gps = gps.toArray(new String[gps.size()]);
       return _gps;
     }
     return null;
@@ -2301,13 +2443,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     if (alignPanel != null
             && (fr = alignPanel.getFeatureRenderer()) != null)
     {
-      List gps = fr.getGroups(visible);
-      int p=0;
-      String[] _gps = new String[gps.size()];
-      for (Object gp:gps)
-      {
-        _gps[p++] = gp.toString();
-      }
+      List<String> gps = fr.getGroups(visible);
+      String[] _gps = gps.toArray(new String[gps.size()]);
       return _gps;
     }
     return null;
@@ -2354,7 +2491,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   protected void displayNonconservedMenuItem_actionPerformed()
   {
-    viewport.setShowunconserved(displayNonconservedMenuItem.getState());
+    viewport.setShowUnconserved(displayNonconservedMenuItem.getState());
     alignPanel.paintAlignment(true);
   }
 
@@ -2839,15 +2976,6 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   MenuItem closeMenuItem = new MenuItem(
           MessageManager.getString("action.close"));
 
-  Menu editMenu = new Menu(MessageManager.getString("action.edit"));
-
-  Menu viewMenu = new Menu(MessageManager.getString("action.view"));
-
-  Menu colourMenu = new Menu(MessageManager.getString("action.colour"));
-
-  Menu calculateMenu = new Menu(
-          MessageManager.getString("action.calculate"));
-
   MenuItem selectAllSequenceMenuItem = new MenuItem(
           MessageManager.getString("action.select_all"));
 
@@ -2891,8 +3019,6 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   public Label statusBar = new Label();
 
-  Menu outputTextboxMenu = new Menu();
-
   MenuItem clustalColour = new MenuItem();
 
   MenuItem zappoColour = new MenuItem();
@@ -2990,22 +3116,15 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   MenuItem modifyConservation = new MenuItem();
 
-  CheckboxMenuItem autoCalculate = new CheckboxMenuItem(
-          "Autocalculate Consensus", true);
+  CheckboxMenuItem autoCalculate = null;
 
   CheckboxMenuItem sortByTree = new CheckboxMenuItem(
           "Sort Alignment With New Tree", true);
 
   Menu sortByTreeMenu = new Menu();
 
-  Menu sort = new Menu();
-
-  Menu calculate = new Menu();
-
   MenuItem inputText = new MenuItem();
 
-  Menu helpMenu = new Menu();
-
   MenuItem documentation = new MenuItem();
 
   MenuItem about = new MenuItem();
@@ -3016,8 +3135,6 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   CheckboxMenuItem followMouseOverFlag = new CheckboxMenuItem();
 
-  Menu autoAnnMenu = new Menu();
-
   CheckboxMenuItem showSequenceLogo = new CheckboxMenuItem();
 
   CheckboxMenuItem applyAutoAnnotationSettings = new CheckboxMenuItem();
@@ -3030,18 +3147,27 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   CheckboxMenuItem normSequenceLogo = new CheckboxMenuItem();
 
+  /**
+   * Initialise menus and other items
+   * 
+   * @throws Exception
+   */
   private void jbInit() throws Exception
   {
-
     setMenuBar(alignFrameMenuBar);
 
-    MenuItem item;
-
-    // dynamically fill save as menu with available formats
+    /*
+     * Configure File menu items and actions
+     */
+    inputText
+            .setLabel(MessageManager.getString("label.input_from_textbox"));
+    inputText.addActionListener(this);
+    Menu outputTextboxMenu = new Menu(
+            MessageManager.getString("label.out_to_textbox"));
     for (int i = 0; i < jalview.io.AppletFormatAdapter.WRITEABLE_FORMATS.length; i++)
     {
 
-      item = new MenuItem(
+      MenuItem item = new MenuItem(
               jalview.io.AppletFormatAdapter.WRITEABLE_FORMATS[i]);
 
       item.addActionListener(new java.awt.event.ActionListener()
@@ -3057,14 +3183,31 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
     closeMenuItem.addActionListener(this);
     loadApplication.addActionListener(this);
-
     loadTree.addActionListener(this);
     loadAnnotations.addActionListener(this);
     outputFeatures.addActionListener(this);
     outputAnnotations.addActionListener(this);
-    selectAllSequenceMenuItem.addActionListener(this);
-    deselectAllSequenceMenuItem.addActionListener(this);
-    invertSequenceMenuItem.addActionListener(this);
+
+    /*
+     * Configure Edit menu items and actions
+     */
+    undoMenuItem.setEnabled(false);
+    undoMenuItem.setLabel(MessageManager.getString("action.undo"));
+    undoMenuItem.addActionListener(this);
+    redoMenuItem.setEnabled(false);
+    redoMenuItem.setLabel(MessageManager.getString("action.redo"));
+    redoMenuItem.addActionListener(this);
+    copy.setLabel(MessageManager.getString("action.copy"));
+    copy.addActionListener(this);
+    cut.setLabel(MessageManager.getString("action.cut"));
+    cut.addActionListener(this);
+    delete.setLabel(MessageManager.getString("action.delete"));
+    delete.addActionListener(this);
+    pasteMenu.setLabel(MessageManager.getString("action.paste"));
+    pasteNew.setLabel(MessageManager.getString("label.to_new_alignment"));
+    pasteNew.addActionListener(this);
+    pasteThis.setLabel(MessageManager.getString("label.to_this_alignment"));
+    pasteThis.addActionListener(this);
     remove2LeftMenuItem.setLabel(MessageManager
             .getString("action.remove_left"));
     remove2LeftMenuItem.addActionListener(this);
@@ -3077,128 +3220,23 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     removeAllGapsMenuItem.setLabel(MessageManager
             .getString("action.remove_all_gaps"));
     removeAllGapsMenuItem.addActionListener(this);
-    viewBoxesMenuItem.setLabel(MessageManager.getString("action.boxes"));
-    viewBoxesMenuItem.setState(true);
-    viewBoxesMenuItem.addItemListener(this);
-    viewTextMenuItem.setLabel(MessageManager.getString("action.text"));
-    viewTextMenuItem.setState(true);
-    viewTextMenuItem.addItemListener(this);
-    sortPairwiseMenuItem.setLabel(MessageManager
-            .getString("action.by_pairwise_id"));
-    sortPairwiseMenuItem.addActionListener(this);
-    sortIDMenuItem.setLabel(MessageManager.getString("action.by_id"));
-    sortIDMenuItem.addActionListener(this);
-    sortLengthMenuItem.setLabel(MessageManager
-            .getString("action.by_length"));
-    sortLengthMenuItem.addActionListener(this);
-    sortGroupMenuItem.setLabel(MessageManager.getString("action.by_group"));
-    sortGroupMenuItem.addActionListener(this);
-    removeRedundancyMenuItem.setLabel(MessageManager
-            .getString("action.remove_redundancy").concat("..."));
+    removeRedundancyMenuItem.setLabel(MessageManager.getString(
+            "action.remove_redundancy").concat("..."));
     removeRedundancyMenuItem.addActionListener(this);
-    pairwiseAlignmentMenuItem.setLabel(MessageManager
-            .getString("action.pairwise_alignment"));
-    pairwiseAlignmentMenuItem.addActionListener(this);
-    PCAMenuItem.setLabel(MessageManager
-            .getString("label.principal_component_analysis"));
-    PCAMenuItem.addActionListener(this);
-    averageDistanceTreeMenuItem.setLabel(MessageManager
-            .getString("label.average_distance_identity"));
-    averageDistanceTreeMenuItem.addActionListener(this);
-    neighbourTreeMenuItem.setLabel(MessageManager
-            .getString("label.neighbour_joining_identity"));
-    neighbourTreeMenuItem.addActionListener(this);
-    statusBar.setBackground(Color.white);
-    statusBar.setFont(new java.awt.Font("Verdana", 0, 11));
-    statusBar.setText(MessageManager.getString("label.status_bar"));
-    outputTextboxMenu.setLabel(MessageManager
-            .getString("label.out_to_textbox"));
-    clustalColour.setLabel(MessageManager.getString("label.clustalx"));
 
-    clustalColour.addActionListener(this);
-    zappoColour.setLabel(MessageManager.getString("label.zappo"));
-    zappoColour.addActionListener(this);
-    taylorColour.setLabel(MessageManager.getString("label.taylor"));
-    taylorColour.addActionListener(this);
-    hydrophobicityColour.setLabel(MessageManager
-            .getString("label.hydrophobicity"));
-    hydrophobicityColour.addActionListener(this);
-    helixColour
-            .setLabel(MessageManager.getString("label.helix_propensity"));
-    helixColour.addActionListener(this);
-    strandColour.setLabel(MessageManager
-            .getString("label.strand_propensity"));
-    strandColour.addActionListener(this);
-    turnColour.setLabel(MessageManager.getString("label.turn_propensity"));
-    turnColour.addActionListener(this);
-    buriedColour.setLabel(MessageManager.getString("label.buried_index"));
-    buriedColour.addActionListener(this);
-    purinePyrimidineColour.setLabel(MessageManager
-            .getString("label.purine_pyrimidine"));
-    purinePyrimidineColour.addActionListener(this);
-    RNAInteractionColour.setLabel(MessageManager
-            .getString("label.rna_interaction"));
-    RNAInteractionColour.addActionListener(this);
-    RNAHelixColour.setLabel(MessageManager
-            .getString("action.by_rna_helixes"));
-    RNAHelixColour.addActionListener(this);
-    userDefinedColour.setLabel(MessageManager
-            .getString("action.user_defined"));
-    userDefinedColour.addActionListener(this);
-    PIDColour.setLabel(MessageManager
-            .getString("label.percentage_identity"));
-    PIDColour.addActionListener(this);
-    BLOSUM62Colour.setLabel(MessageManager
-            .getString("label.blosum62_score"));
-    BLOSUM62Colour.addActionListener(this);
-    tcoffeeColour
-            .setLabel(MessageManager.getString("label.tcoffee_scores"));
-    tcoffeeColour.setEnabled(false); // it will enabled only if a score file is
-                                     // provided
-    tcoffeeColour.addActionListener(this);
-    avDistanceTreeBlosumMenuItem.setLabel(MessageManager
-            .getString("label.average_distance_bloslum62"));
-    avDistanceTreeBlosumMenuItem.addActionListener(this);
-    njTreeBlosumMenuItem.setLabel(MessageManager
-            .getString("label.neighbour_blosum62"));
-    njTreeBlosumMenuItem.addActionListener(this);
-    annotationPanelMenuItem.setLabel(MessageManager
-            .getString("label.show_annotations"));
-    annotationPanelMenuItem.addItemListener(this);
-    colourTextMenuItem.setLabel(MessageManager
-            .getString("label.colour_text"));
-    colourTextMenuItem.addItemListener(this);
-    displayNonconservedMenuItem.setLabel(MessageManager
-            .getString("label.show_non_conversed"));
-    displayNonconservedMenuItem.addItemListener(this);
-    alProperties.addActionListener(this);
-    overviewMenuItem.setLabel(MessageManager
-            .getString("label.overview_window"));
-    overviewMenuItem.addActionListener(this);
-    undoMenuItem.setEnabled(false);
-    undoMenuItem.setLabel(MessageManager.getString("action.undo"));
-    undoMenuItem.addActionListener(this);
-    redoMenuItem.setEnabled(false);
-    redoMenuItem.setLabel(MessageManager.getString("action.redo"));
-    redoMenuItem.addActionListener(this);
-    conservationMenuItem.setLabel(MessageManager
-            .getString("action.by_conservation"));
-    conservationMenuItem.addItemListener(this);
-    noColourmenuItem.setLabel(MessageManager.getString("label.none"));
-    noColourmenuItem.addActionListener(this);
-    wrapMenuItem.setLabel(MessageManager.getString("action.wrap"));
-    wrapMenuItem.addItemListener(this);
-    renderGapsMenuItem.setLabel(MessageManager
-            .getString("action.show_gaps"));
-    renderGapsMenuItem.setState(true);
-    renderGapsMenuItem.addItemListener(this);
+    /*
+     * Configure Select menu items and actions
+     */
     findMenuItem.setLabel(MessageManager.getString("action.find"));
     findMenuItem.addActionListener(this);
-    abovePIDThreshold.setLabel(MessageManager
-            .getString("label.above_identity_threshold"));
-    abovePIDThreshold.addItemListener(this);
-    nucleotideColour.setLabel(MessageManager.getString("label.nucleotide"));
-    nucleotideColour.addActionListener(this);
+    selectAllSequenceMenuItem.addActionListener(this);
+    deselectAllSequenceMenuItem.addActionListener(this);
+    invertSequenceMenuItem.setLabel(MessageManager
+            .getString("action.invert_sequence_selection"));
+    invertSequenceMenuItem.addActionListener(this);
+    invertColSel.setLabel(MessageManager
+            .getString("action.invert_column_selection"));
+    invertColSel.addActionListener(this);
     deleteGroups.setLabel(MessageManager
             .getString("action.undefine_groups"));
     deleteGroups.addActionListener(this);
@@ -3207,88 +3245,18 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     grpsFromSelection.addActionListener(this);
     createGroup.setLabel(MessageManager.getString("action.create_group"));
     unGroup.setLabel(MessageManager.getString("action.remove_group"));
-    copy.setLabel(MessageManager.getString("action.copy"));
-    copy.addActionListener(this);
-    cut.setLabel(MessageManager.getString("action.cut"));
-    cut.addActionListener(this);
-    delete.setLabel(MessageManager.getString("action.delete"));
-    delete.addActionListener(this);
-    pasteMenu.setLabel(MessageManager.getString("action.paste"));
-    pasteNew.setLabel(MessageManager.getString("label.to_new_alignment"));
-    pasteNew.addActionListener(this);
-    pasteThis.setLabel(MessageManager.getString("label.to_this_alignment"));
-    pasteThis.addActionListener(this);
-    applyToAllGroups.setLabel(MessageManager
-            .getString("label.apply_colour_to_all_groups"));
-    applyToAllGroups.setState(true);
-    applyToAllGroups.addItemListener(this);
-    font.setLabel(MessageManager.getString("action.font"));
-    font.addActionListener(this);
-    scaleAbove.setLabel(MessageManager.getString("action.scale_above"));
-    scaleAbove.setState(true);
-    scaleAbove.setEnabled(false);
-    scaleAbove.addItemListener(this);
-    scaleLeft.setEnabled(false);
-    scaleLeft.setState(true);
-    scaleLeft.setLabel(MessageManager.getString("action.scale_left"));
-    scaleLeft.addItemListener(this);
-    scaleRight.setEnabled(false);
-    scaleRight.setState(true);
-    scaleRight.setLabel(MessageManager.getString("action.scale_right"));
-    scaleRight.addItemListener(this);
-    modifyPID.setLabel(MessageManager
-            .getString("label.modify_identity_thereshold"));
-    modifyPID.addActionListener(this);
-    modifyConservation.setLabel(MessageManager
-            .getString("label.modify_conservation_thereshold"));
-    modifyConservation.addActionListener(this);
-    sortByTreeMenu.setLabel(MessageManager
-            .getString("action.by_tree_order"));
-    sort.setLabel(MessageManager.getString("action.sort"));
-    calculate.setLabel(MessageManager.getString("action.calculate_tree"));
-    autoCalculate.addItemListener(this);
-    sortByTree.addItemListener(this);
-    inputText
-            .setLabel(MessageManager.getString("label.input_from_textbox"));
-    inputText.addActionListener(this);
-    centreColumnLabelFlag.setLabel(MessageManager
-            .getString("label.centre_column_labels"));
-    centreColumnLabelFlag.addItemListener(this);
-    followMouseOverFlag.setLabel(MessageManager
-            .getString("label.automatic_scrolling"));
-    followMouseOverFlag.addItemListener(this);
-    helpMenu.setLabel(MessageManager.getString("action.help"));
-    documentation.setLabel(MessageManager.getString("label.documentation"));
-    documentation.addActionListener(this);
-
-    about.setLabel(MessageManager.getString("label.about"));
-    about.addActionListener(this);
-    seqLimits.setState(true);
-    seqLimits.setLabel(MessageManager
-            .getString("label.show_sequence_limits"));
-    seqLimits.addItemListener(this);
-    featureSettings.setLabel(MessageManager
-            .getString("label.feature_settings"));
-    featureSettings.addActionListener(this);
-    sequenceFeatures.setLabel(MessageManager
-            .getString("label.sequence_features"));
-    sequenceFeatures.addItemListener(this);
-    sequenceFeatures.setState(false);
-    annotationColour.setLabel(MessageManager
-            .getString("action.by_annotation"));
-    annotationColour.addActionListener(this);
-
     annotationColumnSelection.setLabel("Select by Annotation");
     annotationColumnSelection.addActionListener(this);
 
-    invertSequenceMenuItem.setLabel(MessageManager
-            .getString("action.invert_sequence_selection"));
-    invertColSel.setLabel(MessageManager
-            .getString("action.invert_column_selection"));
-    menu1.setLabel(MessageManager.getString("action.show"));
+    /*
+     * Configure View menu items and actions
+     */
+    newView.setLabel(MessageManager.getString("action.new_view"));
+    newView.addActionListener(this);
+    Menu showMenu = new Menu(MessageManager.getString("action.show"));
     showColumns.setLabel(MessageManager.getString("label.all_columns"));
     showSeqs.setLabel(MessageManager.getString("label.all_sequences"));
-    menu2.setLabel(MessageManager.getString("action.hide"));
+    Menu hideMenu = new Menu(MessageManager.getString("action.hide"));
     hideColumns
             .setLabel(MessageManager.getString("label.selected_columns"));
     hideSequences.setLabel(MessageManager
@@ -3299,6 +3267,34 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
             .getString("label.selected_region"));
     showAllHidden.setLabel(MessageManager
             .getString("label.all_sequences_columns"));
+    showColumns.addActionListener(this);
+    showSeqs.addActionListener(this);
+    hideColumns.addActionListener(this);
+    hideSequences.addActionListener(this);
+    hideAllButSelection.addActionListener(this);
+    hideAllSelection.addActionListener(this);
+    showAllHidden.addActionListener(this);
+    featureSettings.setLabel(MessageManager
+            .getString("label.feature_settings"));
+    featureSettings.addActionListener(this);
+    sequenceFeatures.setLabel(MessageManager
+            .getString("label.show_sequence_features"));
+    sequenceFeatures.addItemListener(this);
+    sequenceFeatures.setState(false);
+    followMouseOverFlag.setLabel(MessageManager
+            .getString("label.automatic_scrolling"));
+    followMouseOverFlag.addItemListener(this);
+    alProperties.addActionListener(this);
+    overviewMenuItem.setLabel(MessageManager
+            .getString("label.overview_window"));
+    overviewMenuItem.addActionListener(this);
+
+    /*
+     * Configure Annotations menu items and actions
+     */
+    annotationPanelMenuItem.setLabel(MessageManager
+            .getString("label.show_annotations"));
+    annotationPanelMenuItem.addItemListener(this);
     showGroupConsensus.setLabel(MessageManager
             .getString("label.group_consensus"));
     showGroupConservation.setLabel(MessageManager
@@ -3312,58 +3308,243 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     applyAutoAnnotationSettings.setLabel(MessageManager
             .getString("label.apply_all_groups"));
     applyAutoAnnotationSettings.setState(true);
-    autoAnnMenu.setLabel(MessageManager
-            .getString("label.autocalculated_annotation"));
-
-    invertColSel.addActionListener(this);
-    showColumns.addActionListener(this);
-    showSeqs.addActionListener(this);
-    hideColumns.addActionListener(this);
-    hideSequences.addActionListener(this);
-    hideAllButSelection.addActionListener(this);
-    hideAllSelection.addActionListener(this);
-    showAllHidden.addActionListener(this);
+    Menu autoAnnMenu = new Menu(
+            MessageManager.getString("label.autocalculated_annotation"));
     showGroupConsensus.addItemListener(this);
     showGroupConservation.addItemListener(this);
     showConsensusHistogram.addItemListener(this);
     showSequenceLogo.addItemListener(this);
     normSequenceLogo.addItemListener(this);
-
     applyAutoAnnotationSettings.addItemListener(this);
-    formatMenu.setLabel(MessageManager.getString("action.format"));
-    selectMenu.setLabel(MessageManager.getString("action.select"));
-    newView.setLabel(MessageManager.getString("action.new_view"));
-    newView.addActionListener(this);
+    showAlignmentAnnotations = new CheckboxMenuItem(
+            MessageManager.getString("label.show_all_al_annotations"));
+    showSequenceAnnotations = new CheckboxMenuItem(
+            MessageManager.getString("label.show_all_seq_annotations"));
+    sortAnnBySequence = new CheckboxMenuItem(
+            MessageManager.getString("label.sort_annotations_by_sequence"));
+    sortAnnByLabel = new CheckboxMenuItem(
+            MessageManager.getString("label.sort_annotations_by_label"));
+    showAutoFirst = new CheckboxMenuItem(
+            MessageManager.getString("label.show_first"));
+    showAutoLast = new CheckboxMenuItem(
+            MessageManager.getString("label.show_last"));
+    showAlignmentAnnotations.addItemListener(this);
+    showSequenceAnnotations.addItemListener(this);
+    sortAnnBySequence.addItemListener(this);
+    sortAnnByLabel.addItemListener(this);
+    showAutoFirst.addItemListener(this);
+    showAutoLast.addItemListener(this);
+
+    /*
+     * Configure Format menu items and actions
+     */
+    font.setLabel(MessageManager.getString("action.font"));
+    font.addActionListener(this);
+    scaleAbove.setLabel(MessageManager.getString("action.scale_above"));
+    scaleAbove.setState(true);
+    scaleAbove.setEnabled(false);
+    scaleAbove.addItemListener(this);
+    scaleLeft.setEnabled(false);
+    scaleLeft.setState(true);
+    scaleLeft.setLabel(MessageManager.getString("action.scale_left"));
+    scaleLeft.addItemListener(this);
+    scaleRight.setEnabled(false);
+    scaleRight.setState(true);
+    scaleRight.setLabel(MessageManager.getString("action.scale_right"));
+    scaleRight.addItemListener(this);
+    viewBoxesMenuItem.setLabel(MessageManager.getString("action.boxes"));
+    viewBoxesMenuItem.setState(true);
+    viewBoxesMenuItem.addItemListener(this);
+    viewTextMenuItem.setLabel(MessageManager.getString("action.text"));
+    viewTextMenuItem.setState(true);
+    viewTextMenuItem.addItemListener(this);
+    colourTextMenuItem.setLabel(MessageManager
+            .getString("label.colour_text"));
+    colourTextMenuItem.addItemListener(this);
+    displayNonconservedMenuItem.setLabel(MessageManager
+            .getString("label.show_non_conversed"));
+    displayNonconservedMenuItem.addItemListener(this);
+    wrapMenuItem.setLabel(MessageManager.getString("action.wrap"));
+    wrapMenuItem.addItemListener(this);
+    renderGapsMenuItem.setLabel(MessageManager
+            .getString("action.show_gaps"));
+    renderGapsMenuItem.setState(true);
+    renderGapsMenuItem.addItemListener(this);
+    centreColumnLabelFlag.setLabel(MessageManager
+            .getString("label.centre_column_labels"));
+    centreColumnLabelFlag.addItemListener(this);
+    seqLimits.setState(true);
+    seqLimits.setLabel(MessageManager
+            .getString("label.show_sequence_limits"));
+    seqLimits.addItemListener(this);
+
+    /*
+     * Configure Colour menu items and actions
+     */
+    applyToAllGroups.setLabel(MessageManager
+            .getString("label.apply_colour_to_all_groups"));
+    applyToAllGroups.setState(true);
+    applyToAllGroups.addItemListener(this);
+    clustalColour.setLabel(MessageManager.getString("label.clustalx"));
+    clustalColour.addActionListener(this);
+    zappoColour.setLabel(MessageManager.getString("label.zappo"));
+    zappoColour.addActionListener(this);
+    taylorColour.setLabel(MessageManager.getString("label.taylor"));
+    taylorColour.addActionListener(this);
+    hydrophobicityColour.setLabel(MessageManager
+            .getString("label.hydrophobicity"));
+    hydrophobicityColour.addActionListener(this);
+    helixColour
+            .setLabel(MessageManager.getString("label.helix_propensity"));
+    helixColour.addActionListener(this);
+    strandColour.setLabel(MessageManager
+            .getString("label.strand_propensity"));
+    strandColour.addActionListener(this);
+    turnColour.setLabel(MessageManager.getString("label.turn_propensity"));
+    turnColour.addActionListener(this);
+    buriedColour.setLabel(MessageManager.getString("label.buried_index"));
+    buriedColour.addActionListener(this);
+    purinePyrimidineColour.setLabel(MessageManager
+            .getString("label.purine_pyrimidine"));
+    purinePyrimidineColour.addActionListener(this);
+    RNAInteractionColour.setLabel(MessageManager
+            .getString("label.rna_interaction"));
+    RNAInteractionColour.addActionListener(this);
+    RNAHelixColour.setLabel(MessageManager
+            .getString("action.by_rna_helixes"));
+    RNAHelixColour.addActionListener(this);
+    userDefinedColour.setLabel(MessageManager
+            .getString("action.user_defined"));
+    userDefinedColour.addActionListener(this);
+    PIDColour.setLabel(MessageManager
+            .getString("label.percentage_identity"));
+    PIDColour.addActionListener(this);
+    BLOSUM62Colour.setLabel(MessageManager
+            .getString("label.blosum62_score"));
+    BLOSUM62Colour.addActionListener(this);
+    tcoffeeColour
+            .setLabel(MessageManager.getString("label.tcoffee_scores"));
+    // it will be enabled only if a score file is provided
+    tcoffeeColour.setEnabled(false);
+    tcoffeeColour.addActionListener(this);
+    conservationMenuItem.setLabel(MessageManager
+            .getString("action.by_conservation"));
+    conservationMenuItem.addItemListener(this);
+    noColourmenuItem.setLabel(MessageManager.getString("label.none"));
+    noColourmenuItem.addActionListener(this);
+    abovePIDThreshold.setLabel(MessageManager
+            .getString("label.above_identity_threshold"));
+    abovePIDThreshold.addItemListener(this);
+    nucleotideColour.setLabel(MessageManager.getString("label.nucleotide"));
+    nucleotideColour.addActionListener(this);
+    modifyPID.setLabel(MessageManager
+            .getString("label.modify_identity_thereshold"));
+    modifyPID.addActionListener(this);
+    modifyConservation.setLabel(MessageManager
+            .getString("label.modify_conservation_thereshold"));
+    modifyConservation.addActionListener(this);
+    annotationColour.setLabel(MessageManager
+            .getString("action.by_annotation"));
+    annotationColour.addActionListener(this);
+
+    /*
+     * Configure Calculate menu items and actions
+     */
+    sortPairwiseMenuItem.setLabel(MessageManager
+            .getString("action.by_pairwise_id"));
+    sortPairwiseMenuItem.addActionListener(this);
+    sortIDMenuItem.setLabel(MessageManager.getString("action.by_id"));
+    sortIDMenuItem.addActionListener(this);
+    sortLengthMenuItem.setLabel(MessageManager
+            .getString("action.by_length"));
+    sortLengthMenuItem.addActionListener(this);
+    sortGroupMenuItem.setLabel(MessageManager.getString("action.by_group"));
+    sortGroupMenuItem.addActionListener(this);
+    pairwiseAlignmentMenuItem.setLabel(MessageManager
+            .getString("action.pairwise_alignment"));
+    pairwiseAlignmentMenuItem.addActionListener(this);
+    PCAMenuItem.setLabel(MessageManager
+            .getString("label.principal_component_analysis"));
+    PCAMenuItem.addActionListener(this);
+    autoCalculate = new CheckboxMenuItem(
+            MessageManager.getString("label.autocalculate_consensus"), true);
+    averageDistanceTreeMenuItem.setLabel(MessageManager
+            .getString("label.average_distance_identity"));
+    averageDistanceTreeMenuItem.addActionListener(this);
+    neighbourTreeMenuItem.setLabel(MessageManager
+            .getString("label.neighbour_joining_identity"));
+    neighbourTreeMenuItem.addActionListener(this);
+    avDistanceTreeBlosumMenuItem.setLabel(MessageManager
+            .getString("label.average_distance_bloslum62"));
+    avDistanceTreeBlosumMenuItem.addActionListener(this);
+    njTreeBlosumMenuItem.setLabel(MessageManager
+            .getString("label.neighbour_blosum62"));
+    njTreeBlosumMenuItem.addActionListener(this);
+    sortByTreeMenu.setLabel(MessageManager
+            .getString("action.by_tree_order"));
+    Menu sortMenu = new Menu(MessageManager.getString("action.sort"));
+    Menu calculateTreeMenu = new Menu(
+            MessageManager.getString("action.calculate_tree"));
+    autoCalculate.addItemListener(this);
+    sortByTree.addItemListener(this);
+
+    /*
+     * Configure Help menu items and actions
+     */
+    Menu helpMenu = new Menu(MessageManager.getString("action.help"));
+    documentation.setLabel(MessageManager.getString("label.documentation"));
+    documentation.addActionListener(this);
+    about.setLabel(MessageManager.getString("label.about"));
+    about.addActionListener(this);
+
+    /*
+     * Add top level menus to frame
+     */
     alignFrameMenuBar.add(fileMenu);
+    Menu editMenu = new Menu(MessageManager.getString("action.edit"));
     alignFrameMenuBar.add(editMenu);
+    Menu selectMenu = new Menu(MessageManager.getString("action.select"));
     alignFrameMenuBar.add(selectMenu);
+    Menu viewMenu = new Menu(MessageManager.getString("action.view"));
     alignFrameMenuBar.add(viewMenu);
+    Menu annotationsMenu = new Menu(
+            MessageManager.getString("action.annotations"));
+    alignFrameMenuBar.add(annotationsMenu);
+    Menu formatMenu = new Menu(MessageManager.getString("action.format"));
     alignFrameMenuBar.add(formatMenu);
+    Menu colourMenu = new Menu(MessageManager.getString("action.colour"));
     alignFrameMenuBar.add(colourMenu);
+    Menu calculateMenu = new Menu(
+            MessageManager.getString("action.calculate"));
     alignFrameMenuBar.add(calculateMenu);
     alignFrameMenuBar.add(helpMenu);
 
+    /*
+     * File menu
+     */
     fileMenu.add(inputText);
     fileMenu.add(loadTree);
     fileMenu.add(loadAnnotations);
-
     fileMenu.addSeparator();
     fileMenu.add(outputTextboxMenu);
     fileMenu.add(outputFeatures);
     fileMenu.add(outputAnnotations);
-
     if (jalviewServletURL != null)
     {
       fileMenu.add(loadApplication);
     }
-
     fileMenu.addSeparator();
     fileMenu.add(closeMenuItem);
 
+    /*
+     * Edit menu
+     */
     editMenu.add(undoMenuItem);
     editMenu.add(redoMenuItem);
     editMenu.add(cut);
     editMenu.add(copy);
+    pasteMenu.add(pasteNew);
+    pasteMenu.add(pasteThis);
     editMenu.add(pasteMenu);
     editMenu.add(delete);
     editMenu.addSeparator();
@@ -3372,21 +3553,38 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     editMenu.add(removeGappedColumnMenuItem);
     editMenu.add(removeAllGapsMenuItem);
     editMenu.add(removeRedundancyMenuItem);
+
+    /*
+     * Select menu
+     */
+    selectMenu.add(findMenuItem);
+    selectMenu.addSeparator();
+    selectMenu.add(selectAllSequenceMenuItem);
+    selectMenu.add(deselectAllSequenceMenuItem);
+    selectMenu.add(invertSequenceMenuItem);
+    selectMenu.add(invertColSel);
+    selectMenu.add(createGroup);
+    selectMenu.add(unGroup);
+    selectMenu.add(grpsFromSelection);
+    selectMenu.add(deleteGroups);
+    selectMenu.add(annotationColumnSelection);
+
+    /*
+     * View menu
+     */
     viewMenu.add(newView);
     viewMenu.addSeparator();
-    viewMenu.add(menu1);
-    viewMenu.add(menu2);
+    showMenu.add(showColumns);
+    showMenu.add(showSeqs);
+    showMenu.add(showAllHidden);
+    viewMenu.add(showMenu);
+    hideMenu.add(hideColumns);
+    hideMenu.add(hideSequences);
+    hideMenu.add(hideAllSelection);
+    hideMenu.add(hideAllButSelection);
+    viewMenu.add(hideMenu);
     viewMenu.addSeparator();
     viewMenu.add(followMouseOverFlag);
-    viewMenu.add(annotationPanelMenuItem);
-    autoAnnMenu.add(applyAutoAnnotationSettings);
-    autoAnnMenu.add(showConsensusHistogram);
-    autoAnnMenu.add(showSequenceLogo);
-    autoAnnMenu.add(normSequenceLogo);
-    autoAnnMenu.addSeparator();
-    autoAnnMenu.add(showGroupConservation);
-    autoAnnMenu.add(showGroupConsensus);
-    viewMenu.add(autoAnnMenu);
     viewMenu.addSeparator();
     viewMenu.add(sequenceFeatures);
     viewMenu.add(featureSettings);
@@ -3394,6 +3592,48 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewMenu.add(alProperties);
     viewMenu.addSeparator();
     viewMenu.add(overviewMenuItem);
+
+    /*
+     * Annotations menu
+     */
+    // annotationsMenu.add(annotationPanelMenuItem);
+    // annotationsMenu.addSeparator();
+    annotationsMenu.add(showAlignmentAnnotations);
+    annotationsMenu.add(showSequenceAnnotations);
+    annotationsMenu.add(sortAnnBySequence);
+    annotationsMenu.add(sortAnnByLabel);
+    annotationsMenu.addSeparator();
+    autoAnnMenu.add(showAutoFirst);
+    autoAnnMenu.add(showAutoLast);
+    autoAnnMenu.addSeparator();
+    autoAnnMenu.add(applyAutoAnnotationSettings);
+    autoAnnMenu.add(showConsensusHistogram);
+    autoAnnMenu.add(showSequenceLogo);
+    autoAnnMenu.add(normSequenceLogo);
+    autoAnnMenu.addSeparator();
+    autoAnnMenu.add(showGroupConservation);
+    autoAnnMenu.add(showGroupConsensus);
+    annotationsMenu.add(autoAnnMenu);
+
+    /*
+     * Format menu
+     */
+    formatMenu.add(font);
+    formatMenu.add(seqLimits);
+    formatMenu.add(wrapMenuItem);
+    formatMenu.add(scaleAbove);
+    formatMenu.add(scaleLeft);
+    formatMenu.add(scaleRight);
+    formatMenu.add(viewBoxesMenuItem);
+    formatMenu.add(viewTextMenuItem);
+    formatMenu.add(colourTextMenuItem);
+    formatMenu.add(displayNonconservedMenuItem);
+    formatMenu.add(renderGapsMenuItem);
+    formatMenu.add(centreColumnLabelFlag);
+
+    /*
+     * Colour menu
+     */
     colourMenu.add(applyToAllGroups);
     colourMenu.addSeparator();
     colourMenu.add(noColourmenuItem);
@@ -3419,58 +3659,40 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     colourMenu.add(modifyPID);
     colourMenu.add(annotationColour);
     colourMenu.add(RNAHelixColour);
-    calculateMenu.add(sort);
-    calculateMenu.add(calculate);
+
+    /*
+     * Calculate menu
+     */
+    sortMenu.add(sortIDMenuItem);
+    sortMenu.add(sortLengthMenuItem);
+    sortMenu.add(sortByTreeMenu);
+    sortMenu.add(sortGroupMenuItem);
+    sortMenu.add(sortPairwiseMenuItem);
+    calculateMenu.add(sortMenu);
+    calculateTreeMenu.add(averageDistanceTreeMenuItem);
+    calculateTreeMenu.add(neighbourTreeMenuItem);
+    calculateTreeMenu.add(avDistanceTreeBlosumMenuItem);
+    calculateTreeMenu.add(njTreeBlosumMenuItem);
+    calculateMenu.add(calculateTreeMenu);
     calculateMenu.addSeparator();
     calculateMenu.add(pairwiseAlignmentMenuItem);
     calculateMenu.add(PCAMenuItem);
     calculateMenu.add(autoCalculate);
     calculateMenu.add(sortByTree);
-    this.add(statusBar, BorderLayout.SOUTH);
-    pasteMenu.add(pasteNew);
-    pasteMenu.add(pasteThis);
-    sort.add(sortIDMenuItem);
-    sort.add(sortLengthMenuItem);
-    sort.add(sortByTreeMenu);
-    sort.add(sortGroupMenuItem);
-    sort.add(sortPairwiseMenuItem);
-    calculate.add(averageDistanceTreeMenuItem);
-    calculate.add(neighbourTreeMenuItem);
-    calculate.add(avDistanceTreeBlosumMenuItem);
-    calculate.add(njTreeBlosumMenuItem);
+
+    /*
+     * Help menu
+     */
     helpMenu.add(documentation);
     helpMenu.add(about);
-    menu1.add(showColumns);
-    menu1.add(showSeqs);
-    menu1.add(showAllHidden);
-    menu2.add(hideColumns);
-    menu2.add(hideSequences);
-    menu2.add(hideAllSelection);
-    menu2.add(hideAllButSelection);
-    formatMenu.add(font);
-    formatMenu.add(seqLimits);
-    formatMenu.add(wrapMenuItem);
-    formatMenu.add(scaleAbove);
-    formatMenu.add(scaleLeft);
-    formatMenu.add(scaleRight);
-    formatMenu.add(viewBoxesMenuItem);
-    formatMenu.add(viewTextMenuItem);
-    formatMenu.add(colourTextMenuItem);
-    formatMenu.add(displayNonconservedMenuItem);
-    formatMenu.add(renderGapsMenuItem);
-    formatMenu.add(centreColumnLabelFlag);
-    selectMenu.add(findMenuItem);
-    selectMenu.addSeparator();
-    selectMenu.add(selectAllSequenceMenuItem);
-    selectMenu.add(deselectAllSequenceMenuItem);
-    selectMenu.add(invertSequenceMenuItem);
-    selectMenu.add(invertColSel);
-    selectMenu.add(createGroup);
-    selectMenu.add(unGroup);
-    selectMenu.add(grpsFromSelection);
-    selectMenu.add(deleteGroups);
-    selectMenu.add(annotationColumnSelection);
 
+    /*
+     * Status bar
+     */
+    statusBar.setBackground(Color.white);
+    statusBar.setFont(new java.awt.Font("Verdana", 0, 11));
+    statusBar.setText(MessageManager.getString("label.status_bar"));
+    this.add(statusBar, BorderLayout.SOUTH);
   }
 
   public void setStatus(String string)
@@ -3488,14 +3710,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   MenuItem invertColSel = new MenuItem();
 
-  Menu menu1 = new Menu();
-
   MenuItem showColumns = new MenuItem();
 
   MenuItem showSeqs = new MenuItem();
 
-  Menu menu2 = new Menu();
-
   MenuItem hideColumns = new MenuItem();
 
   MenuItem hideSequences = new MenuItem();
@@ -3506,11 +3724,19 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   MenuItem showAllHidden = new MenuItem();
 
-  Menu formatMenu = new Menu();
+  MenuItem newView = new MenuItem();
 
-  Menu selectMenu = new Menu();
+  private CheckboxMenuItem showAlignmentAnnotations;
 
-  MenuItem newView = new MenuItem();
+  private CheckboxMenuItem showSequenceAnnotations;
+
+  private CheckboxMenuItem sortAnnBySequence;
+
+  private CheckboxMenuItem sortAnnByLabel;
+
+  private CheckboxMenuItem showAutoFirst;
+
+  private CheckboxMenuItem showAutoLast;
 
   /**
    * Attach the alignFrame panels after embedding menus, if necessary. This used
@@ -3521,46 +3747,11 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
    *          true to attach the view to the applet area on the page rather than
    *          in a new window
    */
-  public void createAlignFrameWindow(boolean reallyEmbedded, String title)
+  public void createAlignFrameWindow(boolean reallyEmbedded)
   {
     if (reallyEmbedded)
     {
-      // ////
-      // Explicly build the embedded menu panel for the on-page applet
-      //
-      // view cannot be closed if its actually on the page
-      fileMenu.remove(closeMenuItem);
-      fileMenu.remove(3); // Remove Seperator
-      embeddedMenu = makeEmbeddedPopupMenu(alignFrameMenuBar, "Arial",
-              Font.PLAIN, 11, false); // use our own fonts.
-      // and actually add the components to the applet area
-      viewport.applet.setLayout(new BorderLayout());
-      viewport.applet.add(embeddedMenu, BorderLayout.NORTH);
-      viewport.applet.add(statusBar, BorderLayout.SOUTH);
-      alignPanel.setSize(viewport.applet.getSize().width,
-              viewport.applet.getSize().height - embeddedMenu.HEIGHT
-                      - statusBar.HEIGHT);
-      viewport.applet.add(alignPanel, BorderLayout.CENTER);
-      final AlignFrame me = this;
-      viewport.applet.addFocusListener(new FocusListener()
-      {
-
-        @Override
-        public void focusLost(FocusEvent e)
-        {
-          if (me.viewport.applet.currentAlignFrame == me)
-          {
-            me.viewport.applet.currentAlignFrame = null;
-          }
-        }
-
-        @Override
-        public void focusGained(FocusEvent e)
-        {
-          me.viewport.applet.currentAlignFrame = me;
-        }
-      });
-      viewport.applet.validate();
+      embedAlignFrameInApplet(viewport.applet);
     }
     else
     {
@@ -3569,19 +3760,69 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       //
       if (embedMenuIfNeeded(alignPanel))
       {
-        // adjust for status bar height too
-        alignPanel.setSize(getSize().width, getSize().height
-                - statusBar.HEIGHT);
+        /*
+         * adjust for status bar height too. ? pointless as overridden by layout
+         * manager
+         */
+        alignPanel.setSize(getSize().width,
+                getSize().height - statusBar.getHeight());
       }
       add(statusBar, BorderLayout.SOUTH);
       add(alignPanel, BorderLayout.CENTER);
       // and register with the applet so it can pass external API calls to us
-      jalview.bin.JalviewLite.addFrame(this, title, DEFAULT_WIDTH,
+      jalview.bin.JalviewLite.addFrame(this, this.getTitle(),
+              DEFAULT_WIDTH,
               DEFAULT_HEIGHT);
     }
   }
 
   /**
+   * Add the components of this AlignFrame to the applet container.
+   * 
+   * @param theApplet
+   */
+  public void embedAlignFrameInApplet(final JalviewLite theApplet)
+  {
+    // ////
+    // Explicitly build the embedded menu panel for the on-page applet
+    //
+    // view cannot be closed if its actually on the page
+    fileMenu.remove(closeMenuItem);
+    fileMenu.remove(3); // Remove Separator
+    // construct embedded menu, using default font
+    embeddedMenu = makeEmbeddedPopupMenu(alignFrameMenuBar, false, false);
+    // and actually add the components to the applet area
+    theApplet.setLayout(new BorderLayout());
+    theApplet.add(embeddedMenu, BorderLayout.NORTH);
+    theApplet.add(statusBar, BorderLayout.SOUTH);
+    // TODO should size be left to the layout manager?
+    alignPanel.setSize(theApplet.getSize().width,
+            theApplet.getSize().height - embeddedMenu.getHeight()
+                    - statusBar.getHeight());
+    theApplet.add(alignPanel, BorderLayout.CENTER);
+    final AlignFrame me = this;
+    theApplet.addFocusListener(new FocusListener()
+    {
+
+      @Override
+      public void focusLost(FocusEvent e)
+      {
+        if (theApplet.currentAlignFrame == me)
+        {
+          theApplet.currentAlignFrame = null;
+        }
+      }
+
+      @Override
+      public void focusGained(FocusEvent e)
+      {
+        theApplet.currentAlignFrame = me;
+      }
+    });
+    theApplet.validate();
+  }
+
+  /**
    * create a new binding between structures in an existing jmol viewer instance
    * and an alignpanel with sequences that have existing PDBFile entries. Note,
    * this does not open a new Jmol window, or modify the display of the
index 7cf7be0..2d38008 100644 (file)
  */
 package jalview.appletgui;
 
+import java.awt.Font;
+
 import jalview.analysis.NJTree;
 import jalview.api.AlignViewportI;
 import jalview.bin.JalviewLite;
+import jalview.commands.CommandI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.Sequence;
@@ -30,15 +33,14 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 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 java.awt.Font;
-import java.util.Stack;
-
 public class AlignViewport extends AlignmentViewport implements
-        AlignViewportI, SelectionSource, VamsasSource
+        AlignViewportI, SelectionSource, VamsasSource, CommandListener
 {
   int startRes;
 
@@ -50,54 +52,16 @@ public class AlignViewport extends AlignmentViewport implements
 
   boolean cursorMode = false;
 
-  boolean showJVSuffix = true;
-
-  boolean showText = true;
-
-  boolean showColourText = false;
-
-  boolean showBoxes = true;
-
-  boolean wrapAlignment = false;
-
-  boolean renderGaps = true;
-
-  boolean showAnnotation = true;
-
-  boolean upperCasebold = false;
-
-  int charHeight;
-
-  int charWidth;
-
-  int wrappedWidth;
-
   Font font = new Font("SansSerif", Font.PLAIN, 10);
 
   boolean validCharWidth = true;
 
-  int threshold;
-
-  int increment;
-
   NJTree currentTree = null;
 
-  boolean scaleAboveWrapped = true;
-
-  boolean scaleLeftWrapped = true;
-
-  boolean scaleRightWrapped = true;
-
-  boolean showHiddenMarkers = true;
-
   public jalview.bin.JalviewLite applet;
 
   boolean MAC = false;
 
-  Stack historyList = new Stack();
-
-  Stack redoList = new Stack();
-
   private AnnotationColumnChooser annotationColumnSelectionState;
 
   public void finalize()
@@ -110,9 +74,10 @@ public class AlignViewport extends AlignmentViewport implements
 
   public AlignViewport(AlignmentI al, JalviewLite applet)
   {
+    super();
     calculator = new jalview.workers.AlignCalcManager();
     this.applet = applet;
-    setAlignment(al);
+    alignment = al;
     // we always pad gaps
     this.setPadGaps(true);
     this.startRes = 0;
@@ -175,10 +140,11 @@ public class AlignViewport extends AlignmentViewport implements
 
     if (applet != null)
     {
-      showJVSuffix = applet.getDefaultParameter("showFullId", showJVSuffix);
+      setShowJVSuffix(applet.getDefaultParameter("showFullId",
+              getShowJVSuffix()));
 
-      showAnnotation = applet.getDefaultParameter("showAnnotation",
-              showAnnotation);
+      setShowAnnotation(applet.getDefaultParameter("showAnnotation",
+              isShowAnnotation()));
 
       showConservation = applet.getDefaultParameter("showConservation",
               showConservation);
@@ -188,15 +154,15 @@ public class AlignViewport extends AlignmentViewport implements
       showConsensus = applet.getDefaultParameter("showConsensus",
               showConsensus);
 
-      showUnconserved = applet.getDefaultParameter("showUnconserved",
-              showUnconserved);
+      setShowUnconserved(applet.getDefaultParameter("showUnconserved",
+              getShowUnconserved()));
 
       String param = applet.getParameter("upperCase");
       if (param != null)
       {
         if (param.equalsIgnoreCase("bold"))
         {
-          upperCasebold = true;
+          setUpperCasebold(true);
         }
       }
       sortByTree = applet.getDefaultParameter("sortByTree", sortByTree);
@@ -272,7 +238,7 @@ public class AlignViewport extends AlignmentViewport implements
     {
       return null;
     }
-    StringBuffer seqs = new StringBuffer();
+    StringBuilder seqs = new StringBuilder(consensus.annotations.length);
     for (int i = 0; i < consensus.annotations.length; i++)
     {
       if (consensus.annotations[i] != null)
@@ -368,13 +334,13 @@ public class AlignViewport extends AlignmentViewport implements
 
     java.awt.FontMetrics fm = nullFrame.getGraphics().getFontMetrics(font);
     setCharHeight((int) (heightScale * fm.getHeight()));
-    charWidth = (int) (widthScale * fm.charWidth('M'));
+    setCharWidth((int) (widthScale * fm.charWidth('M')));
 
-    if (upperCasebold)
+    if (isUpperCasebold())
     {
       Font f2 = new Font(f.getName(), Font.BOLD, f.getSize());
       fm = nullFrame.getGraphics().getFontMetrics(f2);
-      charWidth = (int) (widthScale * (fm.stringWidth("MMMMMMMMMMM") / 10));
+      setCharWidth((int) (widthScale * (fm.stringWidth("MMMMMMMMMMM") / 10)));
     }
   }
 
@@ -383,98 +349,6 @@ public class AlignViewport extends AlignmentViewport implements
     return font;
   }
 
-  public int getCharWidth()
-  {
-    return charWidth;
-  }
-
-  public void setCharHeight(int h)
-  {
-    this.charHeight = h;
-  }
-
-  public int getCharHeight()
-  {
-    return charHeight;
-  }
-
-  public void setWrappedWidth(int w)
-  {
-    this.wrappedWidth = w;
-  }
-
-  public int getwrappedWidth()
-  {
-    return wrappedWidth;
-  }
-
-  public AlignmentI getAlignment()
-  {
-    return alignment;
-  }
-
-  public void setAlignment(AlignmentI align)
-  {
-    this.alignment = align;
-  }
-
-  public void setWrapAlignment(boolean state)
-  {
-    wrapAlignment = state;
-  }
-
-  public void setShowText(boolean state)
-  {
-    showText = state;
-  }
-
-  public void setRenderGaps(boolean state)
-  {
-    renderGaps = state;
-  }
-
-  public boolean getColourText()
-  {
-    return showColourText;
-  }
-
-  public void setColourText(boolean state)
-  {
-    showColourText = state;
-  }
-
-  public void setShowBoxes(boolean state)
-  {
-    showBoxes = state;
-  }
-
-  public boolean getWrapAlignment()
-  {
-    return wrapAlignment;
-  }
-
-  public boolean getShowText()
-  {
-    return showText;
-  }
-
-  public boolean getShowBoxes()
-  {
-    return showBoxes;
-  }
-
-  public char getGapCharacter()
-  {
-    return getAlignment().getGapCharacter();
-  }
-
-  public void setGapCharacter(char gap)
-  {
-    if (getAlignment() != null)
-    {
-      getAlignment().setGapCharacter(gap);
-    }
-  }
 
   public void resetSeqLimits(int height)
   {
@@ -491,77 +365,6 @@ public class AlignViewport extends AlignmentViewport implements
     return currentTree;
   }
 
-  public boolean getShowJVSuffix()
-  {
-    return showJVSuffix;
-  }
-
-  public void setShowJVSuffix(boolean b)
-  {
-    showJVSuffix = b;
-  }
-
-  public boolean getShowAnnotation()
-  {
-    return showAnnotation;
-  }
-
-  public void setShowAnnotation(boolean b)
-  {
-    showAnnotation = b;
-  }
-
-  public boolean getScaleAboveWrapped()
-  {
-    return scaleAboveWrapped;
-  }
-
-  public boolean getScaleLeftWrapped()
-  {
-    return scaleLeftWrapped;
-  }
-
-  public boolean getScaleRightWrapped()
-  {
-    return scaleRightWrapped;
-  }
-
-  public void setScaleAboveWrapped(boolean b)
-  {
-    scaleAboveWrapped = b;
-  }
-
-  public void setScaleLeftWrapped(boolean b)
-  {
-    scaleLeftWrapped = b;
-  }
-
-  public void setScaleRightWrapped(boolean b)
-  {
-    scaleRightWrapped = b;
-  }
-
-  public void setIgnoreGapsConsensus(boolean b)
-  {
-    ignoreGapsInConsensusCalculation = b;
-    updateConsensus(null);
-    if (globalColourScheme != null)
-    {
-      globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
-              ignoreGapsInConsensusCalculation);
-
-    }
-  }
-
-  public boolean getShowHiddenMarkers()
-  {
-    return showHiddenMarkers;
-  }
-
-  public void setShowHiddenMarkers(boolean show)
-  {
-    showHiddenMarkers = show;
-  }
 
   boolean centreColumnLabels;
 
@@ -590,13 +393,25 @@ public class AlignViewport extends AlignmentViewport implements
 
   public void sendSelection()
   {
-    jalview.structure.StructureSelectionManager
-            .getStructureSelectionManager(applet).sendSelection(
+    getStructureSelectionManager().sendSelection(
                     new SequenceGroup(getSelectionGroup()),
                     new ColumnSelection(getColumnSelection()), this);
   }
 
   /**
+   * Returns an instance of the StructureSelectionManager scoped to this applet
+   * instance.
+   * 
+   * @return
+   */
+  @Override
+  public StructureSelectionManager getStructureSelectionManager()
+  {
+    return jalview.structure.StructureSelectionManager
+            .getStructureSelectionManager(applet);
+  }
+
+  /**
    * synthesize a column selection if none exists so it covers the given
    * selection group. if wholewidth is false, no column selection is made if the
    * selection group covers the whole alignment width.
@@ -659,4 +474,41 @@ public class AlignViewport extends AlignmentViewport implements
     this.annotationColumnSelectionState = annotationColumnSelectionState;
   }
 
+  @Override
+  public void mirrorCommand(CommandI command, boolean undo,
+          StructureSelectionManager ssm, VamsasSource source)
+  {
+    // TODO refactor so this can be pulled up to superclass or controller
+    /*
+     * Do nothing unless we are a 'complement' of the source. May replace this
+     * with direct calls not via SSM.
+     */
+    if (source instanceof AlignViewportI
+            && ((AlignViewportI) source).getCodingComplement() == this)
+    {
+      // ok to continue;
+    }
+    else
+    {
+      return;
+    }
+
+    CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(),
+            getGapCharacter());
+    if (mappedCommand != null)
+    {
+      mappedCommand.doCommand(null);
+      firePropertyChange("alignment", null, getAlignment().getSequences());
+
+      // ap.scalePanelHolder.repaint();
+      // ap.repaint();
+    }
+  }
+
+  @Override
+  public VamsasSource getVamsasSource()
+  {
+    return this;
+  }
+
 }
index 7c4882a..c74914f 100644 (file)
  */
 package jalview.appletgui;
 
-import jalview.api.AlignmentViewPanel;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
-import jalview.datamodel.SequenceI;
-import jalview.structure.StructureSelectionManager;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
@@ -39,6 +33,14 @@ import java.awt.event.AdjustmentListener;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
 
+import jalview.analysis.AnnotationSorter;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SequenceI;
+import jalview.structure.StructureSelectionManager;
+
 public class AlignmentPanel extends Panel implements AdjustmentListener,
         AlignmentViewPanel
 {
@@ -100,7 +102,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     sequenceHolderPanel.add(annotationPanelHolder, BorderLayout.SOUTH);
     alabels = new AnnotationLabels(this);
 
-    setAnnotationVisible(av.showAnnotation);
+    setAnnotationVisible(av.isShowAnnotation());
 
     idPanelHolder.add(idPanel, BorderLayout.CENTER);
     idSpaceFillerPanel1.add(idwidthAdjuster, BorderLayout.CENTER);
@@ -153,6 +155,11 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     });
   }
 
+  @Override
+  public AlignViewportI getAlignViewport()
+  {
+    return av;
+  }
   public SequenceRenderer getSequenceRenderer()
   {
     return seqPanel.seqCanvas.sr;
@@ -190,8 +197,9 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     idPanel.idCanvas.image = null;
     FontMetrics fm = getFontMetrics(av.getFont());
 
-    scalePanel.setSize(new Dimension(10, av.charHeight + fm.getDescent()));
-    idwidthAdjuster.setSize(new Dimension(10, av.charHeight
+    scalePanel.setSize(new Dimension(10, av.getCharHeight()
+            + fm.getDescent()));
+    idwidthAdjuster.setSize(new Dimension(10, av.getCharHeight()
             + fm.getDescent()));
     av.updateSequenceIdColours();
     annotationPanel.image = null;
@@ -375,7 +383,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     {
       start = ostart;
     }
-    if (!av.wrapAlignment)
+    if (!av.getWrapAlignment())
     {
       /*
        * int spos=av.getStartRes(),sqpos=av.getStartSeq(); if ((startv =
@@ -449,7 +457,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
 
   public void setAnnotationVisible(boolean b)
   {
-    if (!av.wrapAlignment)
+    if (!av.getWrapAlignment())
     {
       annotationSpaceFillerHolder.setVisible(b);
       annotationPanelHolder.setVisible(b);
@@ -473,7 +481,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     // this is called after loading new annotation onto alignment
     if (alignFrame.getSize().height == 0)
     {
-      System.out.println("NEEDS FIXING");
+      System.out
+              .println("adjustAnnotationHeight frame size zero NEEDS FIXING");
     }
     fontChanged();
     validateAnnotationDimensions(true);
@@ -566,7 +575,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       annotationPanelHolder.setVisible(false);
       annotationSpaceFillerHolder.setVisible(false);
     }
-    else if (av.showAnnotation)
+    else if (av.isShowAnnotation())
     {
       annotationPanelHolder.setVisible(true);
       annotationSpaceFillerHolder.setVisible(true);
@@ -648,8 +657,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     }
     ;
 
-    hextent = seqPanel.seqCanvas.getSize().width / av.charWidth;
-    vextent = seqPanel.seqCanvas.getSize().height / av.charHeight;
+    hextent = seqPanel.seqCanvas.getSize().width / av.getCharWidth();
+    vextent = seqPanel.seqCanvas.getSize().height / av.getCharHeight();
 
     if (hextent > width)
     {
@@ -694,7 +703,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
 
     av.setEndSeq(endSeq);
     av.setStartRes(x);
-    av.setEndRes((x + (seqPanel.seqCanvas.getSize().width / av.charWidth)) - 1);
+    av.setEndRes((x + (seqPanel.seqCanvas.getSize().width / av
+            .getCharWidth())) - 1);
 
     hscroll.setValues(x, hextent, 0, width);
     vscroll.setValues(y, vextent, 0, height);
@@ -776,7 +786,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       seqPanel.seqCanvas.fastPaint(scrollX, scrollY);
 
       scalePanel.repaint();
-      if (av.getShowAnnotation())
+      if (av.isShowAnnotation())
       {
         annotationPanel.fastPaint(av.getStartRes() - oldX);
       }
@@ -792,8 +802,15 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
                     av.endSeq);
   }
 
+  /**
+   * Repaint the alignment and annotations, and, optionally, any overview window
+   */
   public void paintAlignment(boolean updateOverview)
   {
+    final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
+            av.isShowAutocalculatedAbove());
+    sorter.sort(getAlignment().getAlignmentAnnotation(),
+            av.getSortAnnotationsBy());
     repaint();
 
     if (updateOverview)
@@ -847,9 +864,9 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
 
     seqPanel.seqCanvas.repaint();
     idPanel.idCanvas.repaint();
-    if (!av.wrapAlignment)
+    if (!av.getWrapAlignment())
     {
-      if (av.showAnnotation)
+      if (av.isShowAnnotation())
       {
         alabels.repaint();
         annotationPanel.repaint();
index f288188..67faf11 100755 (executable)
  */
 package jalview.appletgui;
 
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.Annotation;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.util.MessageManager;
-import jalview.util.ParseHtmlBodyAndLinks;
-
 import java.awt.Checkbox;
 import java.awt.CheckboxMenuItem;
 import java.awt.Color;
@@ -48,8 +41,17 @@ import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Vector;
 
+import jalview.analysis.AlignmentUtils;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.util.MessageManager;
+import jalview.util.ParseHtmlBodyAndLinks;
+
 public class AnnotationLabels extends Panel implements ActionListener,
         MouseListener, MouseMotionListener
 {
@@ -211,6 +213,14 @@ public class AnnotationLabels extends Panel implements ActionListener,
       }
 
     }
+    refresh();
+  }
+
+  /**
+   * Adjust size and repaint
+   */
+  protected void refresh()
+  {
     ap.annotationPanel.adjustPanelHeight();
     setSize(getSize().width, ap.annotationPanel.getSize().height);
     ap.validate();
@@ -311,8 +321,8 @@ public class AnnotationLabels extends Panel implements ActionListener,
               .getSize(), f = ap.seqPanelHolder.getSize();
       int dif = evt.getY() - oldY;
 
-      dif /= ap.av.charHeight;
-      dif *= ap.av.charHeight;
+      dif /= ap.av.getCharHeight();
+      dif *= ap.av.getCharHeight();
 
       if ((d.height - dif) > 20 && (f.height + dif) > 20)
       {
@@ -461,6 +471,32 @@ public class AnnotationLabels extends Panel implements ActionListener,
       item = new MenuItem(HIDE);
       item.addActionListener(this);
       popup.add(item);
+
+      /*
+       * Hide all <label>:
+       */
+      if (selectedRow < aa.length)
+      {
+        if (aa[selectedRow].sequenceRef != null)
+        {
+          final String label = aa[selectedRow].label;
+          MenuItem hideType = new MenuItem(
+                  MessageManager.getString("label.hide_all") + " " + label);
+          hideType.addActionListener(new ActionListener()
+          {
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+              AlignmentUtils.showOrHideSequenceAnnotations(
+                      ap.av.getAlignment(), Collections.singleton(label),
+                      null, false, false);
+              refresh();
+            }
+          });
+          popup.add(hideType);
+        }
+      }
+
       if (hasHiddenRows)
       {
         item = new MenuItem(SHOWALL);
@@ -482,7 +518,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
                     MessageManager.getString("label.ignore_gaps_consensus"),
                     (aa[selectedRow].groupRef != null) ? aa[selectedRow].groupRef
                             .getIgnoreGapsConsensus() : ap.av
-                            .getIgnoreGapsConsensus());
+                            .isIgnoreGapsConsensus());
             final AlignmentAnnotation aaa = aa[selectedRow];
             cbmi.addItemListener(new ItemListener()
             {
@@ -495,7 +531,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
                 }
                 else
                 {
-                  ap.av.setIgnoreGapsConsensus(cbmi.getState());
+                  ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
                 }
                 ap.paintAlignment(true);
               }
@@ -813,7 +849,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
               dragEvent.getY());
     }
 
-    if (!av.wrapAlignment && ((aa == null) || (aa.length < 1)))
+    if (!av.getWrapAlignment() && ((aa == null) || (aa.length < 1)))
     {
       g.setColor(Color.black);
       g.drawString(MessageManager.getString("label.right_click"), 2, 8);
index 1361430..32d528c 100755 (executable)
  */
 package jalview.appletgui;
 
-import java.util.*;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import jalview.datamodel.*;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
 import jalview.renderer.AnnotationRenderer;
 import jalview.renderer.AwtRenderPanelI;
 import jalview.util.MessageManager;
 
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.MenuItem;
+import java.awt.Panel;
+import java.awt.PopupMenu;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
 public class AnnotationPanel extends Panel implements AwtRenderPanelI,
         AdjustmentListener, ActionListener, MouseListener,
         MouseMotionListener
@@ -40,8 +54,6 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
 
   int activeRow = -1;
 
-  Vector activeRes;
-
   final String HELIX = "Helix";
 
   final String SHEET = "Sheet";
@@ -140,7 +152,9 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
     if (av.getColumnSelection() != null
             && av.getColumnSelection().size() > 0
             && anot[av.getColumnSelection().getMin()] != null)
+    {
       label = anot[av.getColumnSelection().getMin()].displayCharacter;
+    }
 
     if (evt.getActionCommand().equals(REMOVE))
     {
@@ -168,7 +182,9 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
         int index = av.getColumnSelection().columnAt(i);
 
         if (!av.getColumnSelection().isVisible(index))
+        {
           continue;
+        }
 
         if (anot[index] == null)
         {
@@ -190,7 +206,9 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
         int index = av.getColumnSelection().columnAt(i);
 
         if (!av.getColumnSelection().isVisible(index))
+        {
           continue;
+        }
 
         if (anot[index] == null)
         {
@@ -249,14 +267,15 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
         int index = av.getColumnSelection().columnAt(i);
 
         if (!av.getColumnSelection().isVisible(index))
+        {
           continue;
+        }
 
         if (anot[index] == null)
         {
           anot[index] = new Annotation(label, "", type, 0);
         }
 
-        
         anot[index].secondaryStructure = type != 'S' ? type : label
                 .length() == 0 ? ' ' : label.charAt(0);
         anot[index].displayCharacter = label;
@@ -278,9 +297,13 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
             ap.alignFrame, "Enter Label", 400, 200, true);
 
     if (dialog.accept)
+    {
       return dialog.getName();
+    }
     else
+    {
       return null;
+    }
   }
 
   @Override
@@ -363,11 +386,6 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
       return;
     }
 
-    if (aa == null)
-    {
-      return;
-    }
-
     ap.scalePanel.mousePressed(evt);
   }
 
@@ -513,15 +531,6 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
         }
       }
     }
-
-    if (activeRes == null)
-    {
-      activeRes = new Vector();
-      activeRes.addElement(String.valueOf(i));
-      return;
-    }
-
-    activeRes.addElement(String.valueOf(i));
   }
 
   @Override
@@ -574,13 +583,13 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
       return;
     }
 
-    gg.copyArea(0, 0, imgWidth, getSize().height, -horizontal
-            * av.charWidth, 0);
+    gg.copyArea(0, 0, imgWidth, getSize().height,
+            -horizontal * av.getCharWidth(), 0);
     int sr = av.startRes, er = av.endRes + 1, transX = 0;
 
     if (horizontal > 0) // scrollbar pulled right, image to the left
     {
-      transX = (er - sr - horizontal) * av.charWidth;
+      transX = (er - sr - horizontal) * av.getCharWidth();
       sr = er - horizontal;
     }
     else if (horizontal < 0)
@@ -614,7 +623,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
     g.setFont(ofont);
 
     g.setColor(Color.white);
-    g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getSize().height);
+    g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(),
+            getSize().height);
 
     if (fm == null)
     {
@@ -683,6 +693,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
       return bounds;
     }
     else
+    {
       return null;
+    }
   }
 }
index 21af301..036bbe3 100644 (file)
  */
 package jalview.appletgui;
 
-import jalview.datamodel.Alignment;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.Sequence;
-import jalview.io.AnnotationFile;
-import jalview.io.AppletFormatAdapter;
-import jalview.io.IdentifyFile;
-import jalview.io.TCoffeeScoreFile;
-import jalview.schemes.TCoffeeColourScheme;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Button;
 import java.awt.Dialog;
 import java.awt.Font;
 import java.awt.Frame;
+import java.awt.Label;
 import java.awt.Panel;
 import java.awt.TextArea;
 import java.awt.event.ActionEvent;
@@ -42,6 +33,20 @@ import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 
+import jalview.analysis.AlignmentUtils;
+import jalview.bin.JalviewLite;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.io.AnnotationFile;
+import jalview.io.AppletFormatAdapter;
+import jalview.io.IdentifyFile;
+import jalview.io.NewickFile;
+import jalview.io.TCoffeeScoreFile;
+import jalview.schemes.TCoffeeColourScheme;
+import jalview.util.MessageManager;
+
 public class CutAndPasteTransfer extends Panel implements ActionListener,
         MouseListener
 {
@@ -51,7 +56,7 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
 
   boolean annotationImport = false;
 
-  Sequence seq;
+  SequenceI seq;
 
   AlignFrame alignFrame;
 
@@ -83,7 +88,7 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
     textarea.setText(text);
   }
 
-  public void setPDBImport(Sequence seq)
+  public void setPDBImport(SequenceI seq)
   {
     this.seq = seq;
     accept.setLabel(MessageManager.getString("action.accept"));
@@ -142,149 +147,261 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
 
     if (pdbImport)
     {
-      PDBEntry pdb = new PDBEntry();
-      pdb.setFile(text);
+      openPdbViewer(text);
 
-      if (alignFrame.alignPanel.av.applet.jmolAvailable)
-      {
-        new jalview.appletgui.AppletJmol(pdb, new Sequence[]
-        { seq }, null, alignFrame.alignPanel, AppletFormatAdapter.PASTE);
-      }
-      else
+    }
+    else if (treeImport)
+    {
+      if (!loadTree())
       {
-        new MCview.AppletPDBViewer(pdb, new Sequence[]
-        { seq }, null, alignFrame.alignPanel, AppletFormatAdapter.PASTE);
+        return;
       }
+    }
+    else if (annotationImport)
+    {
+      loadAnnotations();
+    }
+    else if (alignFrame != null)
+    {
+      loadAlignment(text, newWindow);
+    }
 
+    // TODO: dialog should indicate if data was parsed correctly or not - see
+    // JAL-1102
+    if (this.getParent() instanceof Frame)
+    {
+      ((Frame) this.getParent()).setVisible(false);
     }
-    else if (treeImport)
+    else
     {
-      try
-      {
-        jalview.io.NewickFile fin = new jalview.io.NewickFile(
-                textarea.getText(), "Paste");
+      ((Dialog) this.getParent()).setVisible(false);
+    }
+  }
 
-        fin.parse();
-        if (fin.getTree() != null)
-        {
-          alignFrame.loadTree(fin, "Pasted tree file");
-        }
+  /**
+   * Parses text as Newick Tree format, and loads on to the alignment. Returns
+   * true if successful, else false.
+   */
+  protected boolean loadTree()
+  {
+    try
+    {
+      NewickFile fin = new NewickFile(textarea.getText(), "Paste");
 
-      } catch (Exception ex)
+      fin.parse();
+      if (fin.getTree() != null)
       {
-        // TODO: JAL-1102 - should have a warning message in dialog, not simply
-        // overwrite the broken input data with the exception
-        textarea.setText(MessageManager.formatMessage(
-                "label.could_not_parse_newick_file", new String[]
-                { ex.getMessage() }));
-        return;
+        alignFrame.loadTree(fin, "Pasted tree file");
+        return true;
       }
+    } catch (Exception ex)
+    {
+      // TODO: JAL-1102 - should have a warning message in dialog, not simply
+      // overwrite the broken input data with the exception
+      textarea.setText(MessageManager.formatMessage(
+              "label.could_not_parse_newick_file", new Object[]
+              { ex.getMessage() }));
+      return false;
     }
-    else if (annotationImport)
+    return false;
+  }
+
+  /**
+   * Parse text as an alignment file and add to the current or a new window.
+   * 
+   * @param text
+   * @param newWindow
+   */
+  protected void loadAlignment(String text, boolean newWindow)
+  {
+    Alignment al = null;
+
+    String format = new IdentifyFile().Identify(text,
+            AppletFormatAdapter.PASTE);
+    try
+    {
+      al = new AppletFormatAdapter().readFile(text,
+              AppletFormatAdapter.PASTE, format);
+    } catch (java.io.IOException ex)
     {
-      TCoffeeScoreFile tcf = null;
-      try
+      ex.printStackTrace();
+    }
+
+    if (al != null)
+    {
+      al.setDataset(null); // set dataset on alignment/sequences
+      if (openSplitFrame(al, format))
       {
-        tcf = new TCoffeeScoreFile(textarea.getText(),
-                jalview.io.AppletFormatAdapter.PASTE);
-        if (tcf.isValid())
-        {
-          if (tcf.annotateAlignment(alignFrame.viewport.getAlignment(),
-                  true))
-          {
-            alignFrame.tcoffeeColour.setEnabled(true);
-            alignFrame.alignPanel.fontChanged();
-            alignFrame.changeColour(new TCoffeeColourScheme(
-                    alignFrame.viewport.getAlignment()));
-            alignFrame.statusBar
-                    .setText(MessageManager
-                            .getString("label.successfully_pasted_tcoffee_scores_to_alignment"));
-          }
-          else
-          {
-            // file valid but didn't get added to alignment for some reason
-            alignFrame.statusBar.setText(MessageManager.formatMessage(
-                    "label.failed_add_tcoffee_scores",
-                    new String[]
-                    { (tcf.getWarningMessage() != null ? tcf
-                            .getWarningMessage() : "") }));
-          }
-        }
-        else
-        {
-          tcf = null;
-        }
-      } catch (Exception x)
+        return;
+      }
+      if (newWindow)
       {
-        tcf = null;
+        AlignFrame af = new AlignFrame(al, alignFrame.viewport.applet,
+                "Cut & Paste input - " + format, false);
+        af.statusBar
+                .setText(MessageManager
+                        .getString("label.successfully_pasted_annotation_to_alignment"));
       }
-      if (tcf == null)
+      else
       {
-        if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
-                textarea.getText(),
-                jalview.io.AppletFormatAdapter.PASTE))
+        alignFrame.addSequences(al.getSequencesArray());
+        alignFrame.statusBar.setText(MessageManager
+                .getString("label.successfully_pasted_alignment_file"));
+      }
+    }
+  }
+
+  /**
+   * Check whether the new alignment could be mapped to the current one as
+   * cDNA/protein, if so offer the option to open as split frame view. Returns
+   * true if a split frame view is opened, false if not.
+   * 
+   * @param al
+   * @return
+   */
+  protected boolean openSplitFrame(Alignment al, String format)
+  {
+    final AlignmentI thisAlignment = this.alignFrame.getAlignViewport().getAlignment();
+    if (thisAlignment.isNucleotide() == al.isNucleotide())
+    {
+      // both nucleotide or both protein
+      return false;
+    }
+    AlignmentI protein = thisAlignment.isNucleotide() ? al : thisAlignment;
+    AlignmentI dna = thisAlignment.isNucleotide() ? thisAlignment : al;
+    boolean mapped = AlignmentUtils.mapProteinToCdna(protein, dna);
+    if (!mapped)
+    {
+      return false;
+    }
+
+    /*
+     * A mapping is possible; ask user if they want a split frame.
+     */
+    String title = MessageManager.getString("label.open_split_window");
+    final JVDialog dialog = new JVDialog((Frame) this.getParent(), title,
+            true, 100, 400);
+    dialog.ok.setLabel(MessageManager.getString("action.yes"));
+    dialog.cancel.setLabel(MessageManager.getString("action.no"));
+    Panel question = new Panel(new BorderLayout());
+    final String text = MessageManager
+            .getString("label.open_split_window?");
+    question.add(new Label(text, Label.CENTER), BorderLayout.CENTER);
+    dialog.setMainPanel(question);
+    dialog.setVisible(true);
+    dialog.toFront();
+    
+    if (!dialog.accept)
+    {
+      return false;
+    }
+
+    /*
+     * Open SplitFrame with DNA above and protein below, including the alignment
+     * from textbox and a copy of the original.
+     */
+    final JalviewLite applet = this.alignFrame.viewport.applet;
+    AlignFrame copyFrame = new AlignFrame(
+            this.alignFrame.viewport.getAlignment(), applet,
+            alignFrame.getTitle(), false, false);
+    AlignFrame newFrame = new AlignFrame(al, alignFrame.viewport.applet,
+            "Cut & Paste input - " + format, false, false);
+    AlignFrame dnaFrame = al.isNucleotide() ? newFrame : copyFrame;
+    AlignFrame proteinFrame = al.isNucleotide() ? copyFrame
+            : newFrame;
+    SplitFrame sf = new SplitFrame(dnaFrame, proteinFrame);
+    sf.addToDisplay(false, applet);
+    return true;
+  }
+
+  /**
+   * Parse the text as a TCoffee score file, if successful add scores as
+   * alignment annotations.
+   */
+  protected void loadAnnotations()
+  {
+    TCoffeeScoreFile tcf = null;
+    try
+    {
+      tcf = new TCoffeeScoreFile(textarea.getText(),
+              jalview.io.AppletFormatAdapter.PASTE);
+      if (tcf.isValid())
+      {
+        if (tcf.annotateAlignment(alignFrame.viewport.getAlignment(),
+                true))
         {
+          alignFrame.tcoffeeColour.setEnabled(true);
           alignFrame.alignPanel.fontChanged();
-          alignFrame.alignPanel.setScrollValues(0, 0);
+          alignFrame.changeColour(new TCoffeeColourScheme(
+                  alignFrame.viewport.getAlignment()));
           alignFrame.statusBar
                   .setText(MessageManager
-                          .getString("label.successfully_pasted_annotation_to_alignment"));
-
+                          .getString("label.successfully_pasted_tcoffee_scores_to_alignment"));
         }
         else
         {
-          if (!alignFrame.parseFeaturesFile(textarea.getText(),
-                  jalview.io.AppletFormatAdapter.PASTE))
-          {
-            alignFrame.statusBar
-                    .setText(MessageManager
-                            .getString("label.couldnt_parse_pasted_text_as_valid_annotation_feature_GFF_tcoffee_file"));
-          }
+          // file valid but didn't get added to alignment for some reason
+          alignFrame.statusBar.setText(MessageManager.formatMessage(
+                  "label.failed_add_tcoffee_scores",
+                  new Object[]
+                  { (tcf.getWarningMessage() != null ? tcf
+                          .getWarningMessage() : "") }));
         }
       }
+      else
+      {
+        tcf = null;
+      }
+    } catch (Exception x)
+    {
+      tcf = null;
     }
-    else if (alignFrame != null)
+    if (tcf == null)
     {
-      Alignment al = null;
-
-      String format = new IdentifyFile().Identify(text,
-              AppletFormatAdapter.PASTE);
-      try
-      {
-        al = new AppletFormatAdapter().readFile(text,
-                AppletFormatAdapter.PASTE, format);
-      } catch (java.io.IOException ex)
+      if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
+              textarea.getText(),
+              jalview.io.AppletFormatAdapter.PASTE))
       {
-        ex.printStackTrace();
-      }
+        alignFrame.alignPanel.fontChanged();
+        alignFrame.alignPanel.setScrollValues(0, 0);
+        alignFrame.statusBar
+                .setText(MessageManager
+                        .getString("label.successfully_pasted_annotation_to_alignment"));
 
-      if (al != null)
+      }
+      else
       {
-        if (newWindow)
+        if (!alignFrame.parseFeaturesFile(textarea.getText(),
+                jalview.io.AppletFormatAdapter.PASTE))
         {
-          AlignFrame af = new AlignFrame(al, alignFrame.viewport.applet,
-                  "Cut & Paste input - " + format, false);
-          af.statusBar
+          alignFrame.statusBar
                   .setText(MessageManager
-                          .getString("label.successfully_pasted_annotation_to_alignment"));
-        }
-        else
-        {
-          alignFrame.addSequences(al.getSequencesArray());
-          alignFrame.statusBar.setText(MessageManager
-                  .getString("label.successfully_pasted_alignment_file"));
+                          .getString("label.couldnt_parse_pasted_text_as_valid_annotation_feature_GFF_tcoffee_file"));
         }
       }
     }
-    // TODO: dialog should indicate if data was parsed correctly or not - see
-    // JAL-1102
-    if (this.getParent() instanceof Frame)
+  }
+
+  /**
+   * Open a Jmol viewer (if available), failing that the built-in PDB viewer,
+   * passing the input text as the PDB file data.
+   * 
+   * @param text
+   */
+  protected void openPdbViewer(String text)
+  {
+    PDBEntry pdb = new PDBEntry();
+    pdb.setFile(text);
+
+    if (alignFrame.alignPanel.av.applet.jmolAvailable)
     {
-      ((Frame) this.getParent()).setVisible(false);
+      new jalview.appletgui.AppletJmol(pdb, new SequenceI[]
+      { seq }, null, alignFrame.alignPanel, AppletFormatAdapter.PASTE);
     }
     else
     {
-      ((Dialog) this.getParent()).setVisible(false);
+      new MCview.AppletPDBViewer(pdb, new SequenceI[]
+      { seq }, null, alignFrame.alignPanel, AppletFormatAdapter.PASTE);
     }
   }
 
index bea9e13..be4eab6 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.appletgui;
 
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.FlowLayout;
@@ -35,12 +33,12 @@ import java.awt.Panel;
 import java.awt.PopupMenu;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
-import java.util.Enumeration;
-import java.util.Hashtable;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
- * This class implements a pattern form embedding toolbars as a panel with
- * popups for situations where the system menu bar is either invisible or
+ * This class implements a pattern for embedding toolbars as a panel with popups
+ * for situations where the system menu bar is either invisible or
  * inappropriate. It was derived from the code for embedding the jalview applet
  * alignFrame as a component on the web-page, which requires the local
  * alignFrame menu to be attached to that panel rather than placed on the parent
@@ -53,10 +51,15 @@ import java.util.Hashtable;
  */
 public class EmbmenuFrame extends Frame implements MouseListener
 {
+  protected static final Font FONT_ARIAL_PLAIN_11 = new Font(
+            "Arial", Font.PLAIN, 11);
+
+  public static final Font DEFAULT_MENU_FONT = FONT_ARIAL_PLAIN_11;
+
   /**
    * map from labels to popup menus for the embedded menubar
    */
-  protected Hashtable embeddedPopup;
+  protected Map<Label, PopupMenu> embeddedPopup = new HashMap<Label, PopupMenu>();
 
   /**
    * the embedded menu is built on this and should be added to the frame at the
@@ -94,77 +97,43 @@ public class EmbmenuFrame extends Frame implements MouseListener
     // DEBUG Hint: can test embedded menus by inserting true here.
     if (new jalview.util.Platform().isAMac())
     {
-      // Build the embedded menu panel
-      embeddedMenu = makeEmbeddedPopupMenu(topMenuBar, "Arial", Font.PLAIN,
-              11, true); // try to pickup system font.
+      // Build the embedded menu panel, allowing override with system font
+      embeddedMenu = makeEmbeddedPopupMenu(topMenuBar, true, false);
       setMenuBar(null);
-      // add the components to the TreePanel area.
+      // add the components to the Panel area.
       add(embeddedMenu, BorderLayout.NORTH);
-      tobeAdjusted.setSize(getSize().width, getSize().height
-              - embeddedMenu.HEIGHT);
+      tobeAdjusted.setSize(getSize().width,
+              getSize().height - embeddedMenu.getHeight());
       return true;
     }
     return false;
   }
 
   /**
-   * move all menus on menuBar onto embeddedMenu. embeddedPopup is used to store
-   * the popups for each menu removed from the menuBar and added to the panel.
-   * NOTE: it is up to the caller to remove menuBar from the Frame if it is
-   * already attached.
-   * 
-   * @param menuBar
-   * @param fn
-   * @param fstyle
-   * @param fsz
-   * @param overrideFonts
-   *          true if we take the menuBar fonts in preference to the supplied
-   *          defaults
-   * @return the embedded menu instance to be added to the frame.
-   */
-  protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, String fn,
-          int fstyle, int fsz, boolean overrideFonts)
-  {
-    return makeEmbeddedPopupMenu(menuBar, fn, fstyle, fsz, overrideFonts,
-            false);
-  }
-
-  /**
    * Create or add elements to the embedded menu from menuBar. This removes all
    * menu from menuBar and it is up to the caller to remove the now useless
    * menuBar from the Frame if it is already attached.
    * 
    * @param menuBar
-   * @param fn
-   * @param fstyle
-   * @param fsz
    * @param overrideFonts
    * @param append
    *          true means existing menu will be emptied before adding new
    *          elements
    * @return
    */
-  protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, String fn,
-          int fstyle, int fsz, boolean overrideFonts, boolean append)
+  protected Panel makeEmbeddedPopupMenu(MenuBar menuBar,
+          boolean overrideFonts, boolean append)
   {
     if (!append)
     {
-      if (embeddedPopup != null)
-      {
-        embeddedPopup.clear(); // TODO: check if j1.1
-      }
+      embeddedPopup.clear(); // TODO: check if j1.1
       if (embeddedMenu != null)
       {
         embeddedMenu.removeAll();
       }
     }
-    if (embeddedPopup == null)
-    {
-      embeddedPopup = new Hashtable();
-    }
-
-    embeddedMenu = makeEmbeddedPopupMenu(menuBar, fn, fstyle, fsz,
-            overrideFonts, embeddedPopup, new Panel(), this);
+    embeddedMenu = makeEmbeddedPopupMenu(menuBar, DEFAULT_MENU_FONT,
+            overrideFonts, new Panel(), this);
     return embeddedMenu;
   }
 
@@ -177,12 +146,8 @@ public class EmbmenuFrame extends Frame implements MouseListener
    * 
    * @param menuBar
    *          must be non-null
-   * @param fn
-   * @param fstyle
-   * @param fsz
+   * @param font
    * @param overrideFonts
-   * @param embeddedPopup
-   *          must be non-null
    * @param embeddedMenu
    *          if null, a new panel will be created and returned
    * @param clickHandler
@@ -190,27 +155,23 @@ public class EmbmenuFrame extends Frame implements MouseListener
    *          embeddedPopup and embeddedMenu
    * @return the panel instance for convenience.
    */
-  protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, String fn,
-          int fstyle, int fsz, boolean overrideFonts,
-          Hashtable embeddedPopup, Panel embeddedMenu,
+  protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, Font font,
+          boolean overrideFonts,
+          Panel embeddedMenu,
           MouseListener clickHandler)
   {
-    if (embeddedPopup == null)
-    {
-      throw new Error(MessageManager.getString("error.implementation_error_embeddedpopup_not_null"));
-    }
     if (overrideFonts)
     {
       Font mbf = menuBar.getFont();
       if (mbf != null)
       {
-        fn = mbf.getName();
-        fstyle = mbf.getStyle();
-        fsz = mbf.getSize();
+        font = mbf;
       }
     }
     if (embeddedMenu == null)
+    {
       embeddedMenu = new Panel();
+    }
     FlowLayout flowLayout1 = new FlowLayout();
     embeddedMenu.setBackground(Color.lightGray);
     embeddedMenu.setLayout(flowLayout1);
@@ -219,7 +180,7 @@ public class EmbmenuFrame extends Frame implements MouseListener
     {
       Menu mi = menuBar.getMenu(mbi);
       Label elab = new Label(mi.getLabel());
-      elab.setFont(new java.awt.Font(fn, fstyle, fsz));
+      elab.setFont(font);
       // add the menu entries
       PopupMenu popup = new PopupMenu();
       int m, mSize = mi.getItemCount();
@@ -260,7 +221,7 @@ public class EmbmenuFrame extends Frame implements MouseListener
    */
   PopupMenu getPopupMenu(Label source)
   {
-    return (PopupMenu) embeddedPopup.get(source);
+    return embeddedPopup.get(source);
   }
 
   public void mouseClicked(MouseEvent evt)
@@ -287,10 +248,8 @@ public class EmbmenuFrame extends Frame implements MouseListener
   {
     if (embeddedPopup != null)
     {
-      Enumeration e = embeddedPopup.keys();
-      while (e.hasMoreElements())
+      for (Label lb : embeddedPopup.keySet())
       {
-        Label lb = (Label) e.nextElement();
         lb.removeMouseListener(this);
       }
       embeddedPopup.clear();
index 3a50c32..48f11b5 100644 (file)
  */
 package jalview.appletgui;
 
+import jalview.api.ViewStyleI;
 import jalview.util.MessageManager;
 
-import java.awt.*;
-import java.awt.event.*;
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.Choice;
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Frame;
+import java.awt.Label;
+import java.awt.Panel;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 
 public class FontChooser extends Panel implements ActionListener,
         ItemListener
@@ -34,6 +48,8 @@ public class FontChooser extends Panel implements ActionListener,
 
   Font oldFont;
 
+  int oldCharWidth = 0;
+
   boolean init = true;
 
   Frame frame;
@@ -65,6 +81,7 @@ public class FontChooser extends Panel implements ActionListener,
 
     this.ap = ap;
     oldFont = ap.av.getFont();
+    oldCharWidth = ap.av.getViewStyle().getCharWidth();
     init();
   }
 
@@ -144,6 +161,12 @@ public class FontChooser extends Panel implements ActionListener,
     if (ap != null)
     {
       ap.av.setFont(oldFont);
+      ViewStyleI style = ap.av.getViewStyle();
+      if (style.getCharWidth() != oldCharWidth)
+      {
+        style.setCharWidth(oldCharWidth);
+        ap.av.setViewStyle(style);
+      }
       ap.paintAlignment(true);
     }
     else if (tp != null)
index bacd14f..7f9eba9 100755 (executable)
  */
 package jalview.appletgui;
 
-import java.awt.*;
-import java.util.List;
+import jalview.datamodel.SequenceI;
 
-import jalview.datamodel.*;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Panel;
+import java.util.List;
 
 public class IdCanvas extends Panel
 {
@@ -84,7 +88,7 @@ public class IdCanvas extends Panel
             ((i - starty) * charHeight) + ypos + charHeight
                     - (charHeight / 5));
 
-    if (av.hasHiddenRows() && av.showHiddenMarkers)
+    if (av.hasHiddenRows() && av.getShowHiddenMarkers())
     {
       drawMarker(i, starty, ypos);
     }
@@ -99,8 +103,8 @@ public class IdCanvas extends Panel
       return;
     }
 
-    gg.copyArea(0, 0, getSize().width, imgHeight, 0, -vertical
-            * av.charHeight);
+    gg.copyArea(0, 0, getSize().width, imgHeight, 0,
+            -vertical * av.getCharHeight());
 
     int ss = av.startSeq, es = av.endSeq, transY = 0;
     if (vertical > 0) // scroll down
@@ -112,7 +116,7 @@ public class IdCanvas extends Panel
       }
       else
       {
-        transY = imgHeight - vertical * av.charHeight;
+        transY = imgHeight - vertical * av.getCharHeight();
       }
     }
     else if (vertical < 0)
@@ -153,7 +157,7 @@ public class IdCanvas extends Panel
     }
 
     imgHeight = getSize().height;
-    imgHeight -= imgHeight % av.charHeight;
+    imgHeight -= imgHeight % av.getCharHeight();
 
     if (imgHeight < 1)
     {
@@ -178,10 +182,17 @@ public class IdCanvas extends Panel
     g.drawImage(image, 0, 0, this);
   }
 
+  /**
+   * local copy of av.getCharHeight set at top of drawIds
+   */
+  private int avcharHeight;
   void drawIds(int starty, int endy)
   {
+    // hardwired italic IDs in applet currently
     Font italic = new Font(av.getFont().getName(), Font.ITALIC, av
             .getFont().getSize());
+    // temp variable for speed
+    avcharHeight = av.getCharHeight();
 
     gg.setFont(italic);
 
@@ -201,20 +212,19 @@ public class IdCanvas extends Panel
       int annotationHeight = 0;
       AnnotationLabels labels = null;
 
-      if (av.showAnnotation)
+      if (av.isShowAnnotation())
       {
         AnnotationPanel ap = new AnnotationPanel(av);
         annotationHeight = ap.adjustPanelHeight();
         labels = new AnnotationLabels(av);
       }
-
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      int hgap = avcharHeight;
+      if (av.getScaleAboveWrapped())
       {
-        hgap += av.charHeight;
+        hgap += avcharHeight;
       }
 
-      int cHeight = alheight * av.charHeight + hgap + annotationHeight;
+      int cHeight = alheight * avcharHeight + hgap + annotationHeight;
 
       int rowSize = av.getEndRes() - av.getStartRes();
 
@@ -236,9 +246,9 @@ public class IdCanvas extends Panel
 
         if (labels != null)
         {
-          gg.translate(0, ypos + (alheight * av.charHeight));
+          gg.translate(0, ypos + (alheight * avcharHeight));
           labels.drawComponent(gg, getSize().width);
-          gg.translate(0, -ypos - (alheight * av.charHeight));
+          gg.translate(0, -ypos - (alheight * avcharHeight));
         }
 
       }
@@ -284,15 +294,15 @@ public class IdCanvas extends Panel
         gg.setColor(currentColor);
         // TODO: isrep could be used to highlight the representative in a
         // different way
-        gg.fillRect(0, (i - starty) * av.charHeight, getSize().width,
-                av.charHeight);
+        gg.fillRect(0, (i - starty) * avcharHeight, getSize().width,
+                avcharHeight);
         gg.setColor(currentTextColor);
 
         gg.drawString(seq.getDisplayId(av.getShowJVSuffix()), 0,
-                (((i - starty) * av.charHeight) + av.charHeight)
-                        - (av.charHeight / 5));
+                (((i - starty) * avcharHeight) + avcharHeight)
+                        - (avcharHeight / 5));
 
-        if (av.hasHiddenRows() && av.showHiddenMarkers)
+        if (av.hasHiddenRows() && av.getShowHiddenMarkers())
         {
           drawMarker(i, starty, 0);
         }
@@ -343,20 +353,20 @@ public class IdCanvas extends Panel
     if (below)
     {
       gg.fillPolygon(new int[]
-      { getSize().width - av.charHeight, getSize().width - av.charHeight,
+      { getSize().width - avcharHeight, getSize().width - avcharHeight,
           getSize().width }, new int[]
-      { (i - starty) * av.charHeight + yoffset,
-          (i - starty) * av.charHeight + yoffset + av.charHeight / 4,
-          (i - starty) * av.charHeight + yoffset }, 3);
+      { (i - starty) * avcharHeight + yoffset,
+          (i - starty) * avcharHeight + yoffset + avcharHeight / 4,
+          (i - starty) * avcharHeight + yoffset }, 3);
     }
     if (above)
     {
       gg.fillPolygon(new int[]
-      { getSize().width - av.charHeight, getSize().width - av.charHeight,
+      { getSize().width - avcharHeight, getSize().width - avcharHeight,
           getSize().width }, new int[]
-      { (i - starty + 1) * av.charHeight + yoffset,
-          (i - starty + 1) * av.charHeight + yoffset - av.charHeight / 4,
-          (i - starty + 1) * av.charHeight + yoffset }, 3);
+      { (i - starty + 1) * avcharHeight + yoffset,
+          (i - starty + 1) * avcharHeight + yoffset - avcharHeight / 4,
+          (i - starty + 1) * avcharHeight + yoffset }, 3);
 
     }
   }
index 6b34a40..6cf245f 100755 (executable)
  */
 package jalview.appletgui;
 
-import java.awt.*;
-import java.awt.event.*;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.util.UrlLink;
+
+import java.awt.BorderLayout;
+import java.awt.Panel;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
 import java.util.List;
 import java.util.Vector;
 
-import jalview.datamodel.*;
-import jalview.util.UrlLink;
-
 public class IdPanel extends Panel implements MouseListener,
         MouseMotionListener
 {
@@ -303,7 +310,7 @@ public class IdPanel extends Panel implements MouseListener,
     int y = e.getY();
     if (av.getWrapAlignment())
     {
-      y -= 2 * av.charHeight;
+      y -= 2 * av.getCharHeight();
     }
 
     int seq = alignPanel.seqPanel.findSeq(e);
index 4361445..c4a6989 100644 (file)
@@ -233,9 +233,9 @@ public class RedundancyPanel extends SliderPanel implements Runnable,
     CommandI command = (CommandI) historyList.pop();
     command.undoCommand(null);
 
-    if (ap.av.historyList.contains(command))
+    if (ap.av.getHistoryList().contains(command))
     {
-      ap.av.historyList.removeElement(command);
+      ap.av.getHistoryList().remove(command);
       ap.alignFrame.updateEditMenuBar();
       ap.av.firePropertyChange("alignment", null, ap.av.getAlignment().getSequences());
     }
index d625711..3bb676f 100755 (executable)
@@ -362,7 +362,6 @@ public class ScalePanel extends Panel implements MouseMotionListener,
           int height)
   {
     gg.setFont(av.getFont());
-
     // Fill in the background
     gg.setColor(Color.white);
     gg.fillRect(0, 0, width, height);
@@ -371,7 +370,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     // Fill the selected columns
     ColumnSelection cs = av.getColumnSelection();
     gg.setColor(new Color(220, 0, 0));
-
+    int avcharWidth = av.getCharWidth(), avcharHeight = av.getCharHeight();
     for (int i = 0; i < cs.size(); i++)
     {
       int sel = cs.columnAt(i);
@@ -382,7 +381,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
 
       if ((sel >= startx) && (sel <= endx))
       {
-        gg.fillRect((sel - startx) * av.charWidth, 0, av.charWidth,
+        gg.fillRect((sel - startx) * avcharWidth, 0, avcharWidth,
                 getSize().height);
       }
     }
@@ -393,7 +392,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     int scalestartx = (startx / 10) * 10;
 
     FontMetrics fm = gg.getFontMetrics(av.getFont());
-    int y = av.charHeight - fm.getDescent();
+    int y = avcharHeight - fm.getDescent();
 
     if ((scalestartx % 10) == 0)
     {
@@ -409,25 +408,26 @@ public class ScalePanel extends Panel implements MouseMotionListener,
       {
         string = String.valueOf(av.getColumnSelection()
                 .adjustForHiddenColumns(i));
-        if ((i - startx - 1) * av.charWidth > maxX)
+        if ((i - startx - 1) * avcharWidth > maxX)
         {
-          gg.drawString(string, (i - startx - 1) * av.charWidth, y);
-          maxX = (i - startx + 1) * av.charWidth + fm.stringWidth(string);
+          gg.drawString(string, (i - startx - 1) * avcharWidth, y);
+          maxX = (i - startx + 1) * avcharWidth + fm.stringWidth(string);
         }
 
         gg.drawLine(
-                ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
+((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
                 y + 2,
-                ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
+                ((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
                 y + (fm.getDescent() * 2));
 
       }
       else
       {
         gg.drawLine(
-                ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
+((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
                 y + fm.getDescent(),
-                ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
+ ((i - startx - 1) * avcharWidth)
+                + (avcharWidth / 2),
                 y + (fm.getDescent() * 2));
       }
     }
@@ -451,10 +451,10 @@ public class ScalePanel extends Panel implements MouseMotionListener,
           }
 
           gg.fillPolygon(new int[]
-          { res * av.charWidth - av.charHeight / 4,
-              res * av.charWidth + av.charHeight / 4, res * av.charWidth },
+          { res * avcharWidth - avcharHeight / 4,
+              res * avcharWidth + avcharHeight / 4, res * avcharWidth },
                   new int[]
-                  { y - av.charHeight / 2, y - av.charHeight / 2, y + 8 },
+                  { y - avcharHeight / 2, y - avcharHeight / 2, y + 8 },
                   3);
 
         }
@@ -463,7 +463,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
       if (reveal != null && reveal[0] > startx && reveal[0] < endx)
       {
         gg.drawString(MessageManager.getString("label.reveal_columns"),
-                reveal[0] * av.charWidth, 0);
+                reveal[0] * avcharWidth, 0);
       }
     }
 
index eaa3a50..bf54c66 100755 (executable)
@@ -61,6 +61,15 @@ public class SeqCanvas extends Panel
     fr = new FeatureRenderer(av);
     sr = new SequenceRenderer(av);
     PaintRefresher.Register(this, av.getSequenceSetId());
+    updateViewport();
+  }
+
+  int avcharHeight = 0, avcharWidth = 0;
+
+  private void updateViewport()
+  {
+    avcharHeight = av.getCharHeight();
+    avcharWidth = av.getCharWidth();
   }
 
   public AlignViewport getViewport()
@@ -78,7 +87,7 @@ public class SeqCanvas extends Panel
     return sr;
   }
 
-  void drawNorthScale(Graphics g, int startx, int endx, int ypos)
+  private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
   {
     int scalestartx = startx - startx % 10 + 10;
 
@@ -93,20 +102,20 @@ public class SeqCanvas extends Panel
         value = av.getColumnSelection().adjustForHiddenColumns(value);
       }
 
-      g.drawString(String.valueOf(value), (i - startx - 1) * av.charWidth,
-              ypos - (av.charHeight / 2));
+      g.drawString(String.valueOf(value), (i - startx - 1) * avcharWidth,
+              ypos - (avcharHeight / 2));
 
-      g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
-              (ypos + 2) - (av.charHeight / 2),
-              ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
+      g.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
+              (ypos + 2) - (avcharHeight / 2),
+              ((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
               ypos - 2);
     }
   }
 
-  void drawWestScale(Graphics g, int startx, int endx, int ypos)
+  private void drawWestScale(Graphics g, int startx, int endx, int ypos)
   {
     FontMetrics fm = getFontMetrics(av.getFont());
-    ypos += av.charHeight;
+    ypos += avcharHeight;
     if (av.hasHiddenColumns())
     {
       startx = av.getColumnSelection().adjustForHiddenColumns(startx);
@@ -143,16 +152,16 @@ public class SeqCanvas extends Panel
       if (value != -1)
       {
         int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))
-                - av.charWidth / 2;
-        g.drawString(value + "", x, (ypos + (i * av.charHeight))
-                - (av.charHeight / 5));
+                - avcharWidth / 2;
+        g.drawString(value + "", x, (ypos + (i * avcharHeight))
+                - (avcharHeight / 5));
       }
     }
   }
 
-  void drawEastScale(Graphics g, int startx, int endx, int ypos)
+  private void drawEastScale(Graphics g, int startx, int endx, int ypos)
   {
-    ypos += av.charHeight;
+    ypos += avcharHeight;
 
     if (av.hasHiddenColumns())
     {
@@ -183,8 +192,8 @@ public class SeqCanvas extends Panel
 
       if (value != -1)
       {
-        g.drawString(String.valueOf(value), 0, (ypos + (i * av.charHeight))
-                - (av.charHeight / 5));
+        g.drawString(String.valueOf(value), 0, (ypos + (i * avcharHeight))
+                - (avcharHeight / 5));
       }
     }
   }
@@ -198,6 +207,8 @@ public class SeqCanvas extends Panel
       return;
     }
 
+    updateViewport();
+
     // Its possible on certain browsers that the call to fastpaint
     // is faster than it can paint, so this check here catches
     // this possibility
@@ -209,16 +220,16 @@ public class SeqCanvas extends Panel
     lastsr = av.startRes;
 
     fastPaint = true;
-    gg.copyArea(horizontal * av.charWidth, vertical * av.charHeight,
-            imgWidth - horizontal * av.charWidth, imgHeight - vertical
-                    * av.charHeight, -horizontal * av.charWidth, -vertical
-                    * av.charHeight);
+    gg.copyArea(horizontal * avcharWidth, vertical * avcharHeight, imgWidth
+            - horizontal * avcharWidth,
+            imgHeight - vertical * avcharHeight, -horizontal * avcharWidth,
+            -vertical * avcharHeight);
 
     int sr = av.startRes, er = av.endRes, ss = av.startSeq, es = av.endSeq, transX = 0, transY = 0;
 
     if (horizontal > 0) // scrollbar pulled right, image to the left
     {
-      transX = (er - sr - horizontal) * av.charWidth;
+      transX = (er - sr - horizontal) * avcharWidth;
       sr = er - horizontal;
     }
     else if (horizontal < 0)
@@ -235,7 +246,7 @@ public class SeqCanvas extends Panel
       }
       else
       {
-        transY = imgHeight - vertical * av.charHeight;
+        transY = imgHeight - vertical * avcharHeight;
       }
     }
     else if (vertical < 0)
@@ -269,6 +280,7 @@ public class SeqCanvas extends Panel
     paint(g);
   }
 
+  @Override
   public void paint(Graphics g)
   {
 
@@ -288,12 +300,13 @@ public class SeqCanvas extends Panel
       return;
     }
 
+    updateViewport();
     // this draws the whole of the alignment
     imgWidth = this.getSize().width;
     imgHeight = this.getSize().height;
 
-    imgWidth -= imgWidth % av.charWidth;
-    imgHeight -= imgHeight % av.charHeight;
+    imgWidth -= imgWidth % avcharWidth;
+    imgHeight -= imgHeight % avcharHeight;
 
     if (imgWidth < 1 || imgHeight < 1)
     {
@@ -328,24 +341,24 @@ public class SeqCanvas extends Panel
 
   public int getWrappedCanvasWidth(int cwidth)
   {
-    cwidth -= cwidth % av.charWidth;
+    cwidth -= cwidth % av.getCharWidth();
 
     FontMetrics fm = getFontMetrics(av.getFont());
 
     LABEL_EAST = 0;
     LABEL_WEST = 0;
 
-    if (av.scaleRightWrapped)
+    if (av.getScaleRightWrapped())
     {
       LABEL_EAST = fm.stringWidth(getMask());
     }
 
-    if (av.scaleLeftWrapped)
+    if (av.getScaleLeftWrapped())
     {
       LABEL_WEST = fm.stringWidth(getMask());
     }
 
-    return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
+    return (cwidth - LABEL_EAST - LABEL_WEST) / av.getCharWidth();
   }
 
   /**
@@ -375,31 +388,31 @@ public class SeqCanvas extends Panel
     return mask;
   }
 
-  public void drawWrappedPanel(Graphics g, int canvasWidth,
+  private void drawWrappedPanel(Graphics g, int canvasWidth,
           int canvasHeight, int startRes)
   {
     AlignmentI al = av.getAlignment();
 
     FontMetrics fm = getFontMetrics(av.getFont());
 
-    if (av.scaleRightWrapped)
+    if (av.getScaleRightWrapped())
     {
       LABEL_EAST = fm.stringWidth(getMask());
     }
 
-    if (av.scaleLeftWrapped)
+    if (av.getScaleLeftWrapped())
     {
       LABEL_WEST = fm.stringWidth(getMask());
     }
 
-    int hgap = av.charHeight;
-    if (av.scaleAboveWrapped)
+    int hgap = avcharHeight;
+    if (av.getScaleAboveWrapped())
     {
-      hgap += av.charHeight;
+      hgap += avcharHeight;
     }
 
-    int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
-    int cHeight = av.getAlignment().getHeight() * av.charHeight;
+    int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / avcharWidth;
+    int cHeight = av.getAlignment().getHeight() * avcharHeight;
 
     av.setWrappedWidth(cWidth);
 
@@ -426,12 +439,12 @@ public class SeqCanvas extends Panel
 
       g.setColor(Color.black);
 
-      if (av.scaleLeftWrapped)
+      if (av.getScaleLeftWrapped())
       {
         drawWestScale(g, startRes, endx, ypos);
       }
 
-      if (av.scaleRightWrapped)
+      if (av.getScaleRightWrapped())
       {
         g.translate(canvasWidth - LABEL_EAST, 0);
         drawEastScale(g, startRes, endx, ypos);
@@ -440,11 +453,11 @@ public class SeqCanvas extends Panel
 
       g.translate(LABEL_WEST, 0);
 
-      if (av.scaleAboveWrapped)
+      if (av.getScaleAboveWrapped())
       {
         drawNorthScale(g, startRes, endx, ypos);
       }
-      if (av.hasHiddenColumns() && av.showHiddenMarkers)
+      if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
       {
         g.setColor(Color.blue);
         int res;
@@ -460,24 +473,24 @@ public class SeqCanvas extends Panel
           }
 
           gg.fillPolygon(new int[]
-          { res * av.charWidth - av.charHeight / 4,
-              res * av.charWidth + av.charHeight / 4, res * av.charWidth },
+          { res * avcharWidth - avcharHeight / 4,
+              res * avcharWidth + avcharHeight / 4, res * avcharWidth },
                   new int[]
-                  { ypos - (av.charHeight / 2), ypos - (av.charHeight / 2),
-                      ypos - (av.charHeight / 2) + 8 }, 3);
+                  { ypos - (avcharHeight / 2), ypos - (avcharHeight / 2),
+                      ypos - (avcharHeight / 2) + 8 }, 3);
 
         }
       }
 
       if (g.getClip() == null)
       {
-        g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
+        g.setClip(0, 0, cWidth * avcharWidth, canvasHeight);
       }
 
       drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
       g.setClip(null);
 
-      if (av.showAnnotation)
+      if (av.isShowAnnotation())
       {
         g.translate(0, cHeight + ypos + 4);
         if (annotations == null)
@@ -501,7 +514,7 @@ public class SeqCanvas extends Panel
 
   int getAnnotationHeight()
   {
-    if (!av.showAnnotation)
+    if (!av.isShowAnnotation())
     {
       return 0;
     }
@@ -514,7 +527,8 @@ public class SeqCanvas extends Panel
     return annotations.adjustPanelHeight();
   }
 
-  void drawPanel(Graphics g1, int startRes, int endRes, int startSeq,
+  private void drawPanel(Graphics g1, int startRes, int endRes,
+          int startSeq,
           int endSeq, int offset)
   {
 
@@ -545,20 +559,20 @@ public class SeqCanvas extends Panel
 
           blockEnd = hideStart - 1;
 
-          g1.translate(screenY * av.charWidth, 0);
+          g1.translate(screenY * avcharWidth, 0);
 
           draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
 
           if (av.getShowHiddenMarkers())
           {
             g1.setColor(Color.blue);
-            g1.drawLine((blockEnd - blockStart + 1) * av.charWidth - 1,
-                    0 + offset, (blockEnd - blockStart + 1) * av.charWidth
-                            - 1, (endSeq - startSeq) * av.charHeight
+            g1.drawLine((blockEnd - blockStart + 1) * avcharWidth - 1,
+                    0 + offset, (blockEnd - blockStart + 1) * avcharWidth
+                            - 1, (endSeq - startSeq) * avcharHeight
                             + offset);
           }
 
-          g1.translate(-screenY * av.charWidth, 0);
+          g1.translate(-screenY * avcharWidth, 0);
           screenY += blockEnd - blockStart + 1;
           blockStart = hideEnd + 1;
         }
@@ -566,10 +580,10 @@ public class SeqCanvas extends Panel
       if (screenY <= (endRes - startRes))
       {
         blockEnd = blockStart + (endRes - startRes) - screenY;
-        g1.translate(screenY * av.charWidth, 0);
+        g1.translate(screenY * avcharWidth, 0);
         draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
 
-        g1.translate(-screenY * av.charWidth, 0);
+        g1.translate(-screenY * avcharWidth, 0);
       }
     }
 
@@ -581,8 +595,8 @@ public class SeqCanvas extends Panel
           int offset)
   {
     g.setFont(av.getFont());
-    sr.prepare(g, av.renderGaps);
-
+    sr.prepare(g, av.isRenderGaps());
+    updateViewport();
     SequenceI nextSeq;
 
     // / First draw the sequences
@@ -597,12 +611,12 @@ public class SeqCanvas extends Panel
       }
 
       sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
-              startRes, endRes, offset + ((i - startSeq) * av.charHeight));
+              startRes, endRes, offset + ((i - startSeq) * avcharHeight));
 
       if (av.isShowSequenceFeatures())
       {
         fr.drawSequence(g, nextSeq, startRes, endRes, offset
-                + ((i - startSeq) * av.charHeight));
+                + ((i - startSeq) * avcharHeight));
       }
 
       // / Highlight search Results once all sequences have been drawn
@@ -617,8 +631,8 @@ public class SeqCanvas extends Panel
           {
             sr.drawHighlightedText(nextSeq, visibleResults[r],
                     visibleResults[r + 1], (visibleResults[r] - startRes)
-                            * av.charWidth, offset
-                            + ((i - startSeq) * av.charHeight));
+                            * avcharWidth, offset
+                            + ((i - startSeq) * avcharHeight));
           }
         }
       }
@@ -626,9 +640,8 @@ public class SeqCanvas extends Panel
       if (av.cursorMode && cursorY == i && cursorX >= startRes
               && cursorX <= endRes)
       {
-        sr.drawCursor(nextSeq, cursorX,
-                (cursorX - startRes) * av.charWidth, offset
-                        + ((i - startSeq) * av.charHeight));
+        sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * avcharWidth,
+                offset + ((i - startSeq) * avcharHeight));
       }
     }
 
@@ -640,7 +653,7 @@ public class SeqCanvas extends Panel
 
   }
 
-  void drawGroupsBoundaries(Graphics g, int startRes, int endRes,
+  private void drawGroupsBoundaries(Graphics g, int startRes, int endRes,
           int startSeq, int endSeq, int offset)
   {
     //
@@ -673,16 +686,16 @@ public class SeqCanvas extends Panel
 
         for (i = startSeq; i < endSeq; i++)
         {
-          sx = (group.getStartRes() - startRes) * av.charWidth;
-          sy = offset + ((i - startSeq) * av.charHeight);
-          ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) - 1;
+          sx = (group.getStartRes() - startRes) * avcharWidth;
+          sy = offset + ((i - startSeq) * avcharHeight);
+          ex = (((group.getEndRes() + 1) - group.getStartRes()) * avcharWidth) - 1;
 
           if (sx + ex < 0 || sx > imgWidth)
           {
             continue;
           }
 
-          if ((sx <= (endRes - startRes) * av.charWidth)
+          if ((sx <= (endRes - startRes) * avcharWidth)
                   && group.getSequences(null).contains(
                           av.getAlignment().getSequenceAt(i)))
           {
@@ -691,7 +704,7 @@ public class SeqCanvas extends Panel
                             .contains(
                                     av.getAlignment().getSequenceAt(i + 1))))
             {
-              bottom = sy + av.charHeight;
+              bottom = sy + avcharHeight;
             }
 
             if (!inGroup)
@@ -741,9 +754,9 @@ public class SeqCanvas extends Panel
                 ex = imgWidth;
               }
 
-              else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
+              else if (sx + ex >= (endRes - startRes + 1) * avcharWidth)
               {
-                ex = (endRes - startRes + 1) * av.charWidth;
+                ex = (endRes - startRes + 1) * avcharWidth;
               }
 
               if (top != -1)
@@ -765,7 +778,7 @@ public class SeqCanvas extends Panel
 
         if (inGroup)
         {
-          sy = offset + ((i - startSeq) * av.charHeight);
+          sy = offset + ((i - startSeq) * avcharHeight);
           if (sx >= 0 && sx < imgWidth)
           {
             g.drawLine(sx, oldY, sx, sy);
@@ -786,9 +799,9 @@ public class SeqCanvas extends Panel
           {
             ex = imgWidth;
           }
-          else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
+          else if (sx + ex >= (endRes - startRes + 1) * avcharWidth)
           {
-            ex = (endRes - startRes + 1) * av.charWidth;
+            ex = (endRes - startRes + 1) * avcharWidth;
           }
 
           if (top != -1)
index e488a32..ae4ae10 100644 (file)
  */
 package jalview.appletgui;
 
+import java.awt.BorderLayout;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.util.List;
+import java.util.Vector;
+
+import jalview.api.AlignViewportI;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResults.Match;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ResidueProperties;
+import jalview.structure.SelectionListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.SequenceListener;
 import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasSource;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 
-import java.awt.BorderLayout;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Panel;
-import java.awt.Point;
-import java.awt.event.InputEvent;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.MouseMotionListener;
-import java.util.Vector;
-
 public class SeqPanel extends Panel implements MouseMotionListener,
-        MouseListener, SequenceListener
+        MouseListener, SequenceListener, SelectionListener
 {
 
   public SeqCanvas seqCanvas;
@@ -109,6 +115,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     seqCanvas.addMouseListener(this);
     ssm = StructureSelectionManager.getStructureSelectionManager(av.applet);
     ssm.addStructureViewerListener(this);
+    ssm.addSelectionListener(this);
 
     seqCanvas.repaint();
   }
@@ -159,8 +166,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   void setCursorPosition()
   {
-    SequenceI sequence = av.getAlignment().getSequenceAt(
-            seqCanvas.cursorY);
+    SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
 
     seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
     scrollToVisible();
@@ -212,7 +218,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     }
 
     endEditing();
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
       ap.scrollToWrappedVisible(seqCanvas.cursorX);
     }
@@ -252,8 +258,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   void setSelectionAreaAtCursor(boolean topLeft)
   {
-    SequenceI sequence = av.getAlignment().getSequenceAt(
-            seqCanvas.cursorY);
+    SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
 
     if (av.getSelectionGroup() != null)
     {
@@ -393,41 +398,107 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     return 1;
   }
 
+  /**
+   * Set status message in alignment panel
+   * 
+   * @param sequence
+   *          aligned sequence object
+   * @param res
+   *          alignment column
+   * @param seq
+   *          index of sequence in alignment
+   * @return position of res in sequence
+   */
   void setStatusMessage(SequenceI sequence, int res, int seq)
   {
-    StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
-            + sequence.getName());
-
-    Object obj = null;
+    // TODO remove duplication of identical gui method
+    StringBuilder text = new StringBuilder(32);
+    String seqno = seq == -1 ? "" : " " + (seq + 1);
+    text.append("Sequence" + seqno + " ID: " + sequence.getName());
+
+    String residue = null;
+    /*
+     * Try to translate the display character to residue name (null for gap).
+     */
+    final String displayChar = String.valueOf(sequence.getCharAt(res));
     if (av.getAlignment().isNucleotide())
     {
-      obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
-              + "");
-      if (obj != null)
+      residue = ResidueProperties.nucleotideName.get(displayChar);
+      if (residue != null)
       {
-        text.append(" Nucleotide: ");
+        text.append(" Nucleotide: ").append(residue);
       }
     }
     else
     {
-      obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
-      if (obj != null)
-      {
-        text.append("  Residue: ");
-      }
-    }
+      residue = "X".equalsIgnoreCase(displayChar) ? "X"
+              : ResidueProperties.aa2Triplet.get(displayChar);
+      if (residue != null)
+      {
+        text.append(" Residue: ").append(residue);
+      }
+    }
+
+    int pos = -1;
+    if (residue != null)
+    {
+      pos = sequence.findPosition(res);
+      text.append(" (").append(Integer.toString(pos)).append(")");
+    }
+    // Object obj = null;
+    // if (av.getAlignment().isNucleotide())
+    // {
+    // obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
+    // + "");
+    // if (obj != null)
+    // {
+    // text.append(" Nucleotide: ");
+    // }
+    // }
+    // else
+    // {
+    // obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
+    // if (obj != null)
+    // {
+    // text.append("  Residue: ");
+    // }
+    // }
+    //
+    // if (obj != null)
+    // {
+    //
+    // if (obj != "")
+    // {
+    // text.append(obj + " (" + sequence.findPosition(res) + ")");
+    // }
+    // }
 
-    if (obj != null)
-    {
+    ap.alignFrame.statusBar.setText(text.toString());
 
-      if (obj != "")
-      {
-        text.append(obj + " (" + sequence.findPosition(res) + ")");
-      }
-    }
+  }
 
-    ap.alignFrame.statusBar.setText(text.toString());
+  /**
+   * Set the status bar message to highlight the first matched position in
+   * search results.
+   * 
+   * @param results
+   */
+  private void setStatusMessage(SearchResults results)
+  {
+    List<Match> matches = results.getResults();
+    if (!matches.isEmpty())
+    {
+      Match m = matches.get(0);
+      SequenceI seq = m.getSequence();
+      int sequenceIndex = this.av.getAlignment().findIndex(seq);
 
+      /*
+       * Convert position in sequence (base 1) to sequence character array index
+       * (base 0)
+       */
+      int start = m.getStart() - 1;
+      setStatusMessage(seq, start, sequenceIndex);
+    }
   }
 
   public void mousePressed(MouseEvent evt)
@@ -538,16 +609,17 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     int res = 0;
     int x = evt.getX();
 
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
 
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      int hgap = av.getCharHeight();
+      if (av.getScaleAboveWrapped())
       {
-        hgap += av.charHeight;
+        hgap += av.getCharHeight();
       }
 
-      int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
+      int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
+              + hgap
               + seqCanvas.getAnnotationHeight();
 
       int y = evt.getY();
@@ -596,15 +668,16 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     int seq = 0;
     int y = evt.getY();
 
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      int hgap = av.getCharHeight();
+      if (av.getScaleAboveWrapped())
       {
-        hgap += av.charHeight;
+        hgap += av.getCharHeight();
       }
 
-      int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
+      int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
+              + hgap
               + seqCanvas.getAnnotationHeight();
 
       y -= hgap;
@@ -676,10 +749,17 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         ap.alignFrame.repaint();
       }
     }
+    setStatusMessage(results);
     seqCanvas.highlightSearchResults(results);
 
   }
 
+  @Override
+  public VamsasSource getVamsasSource()
+  {
+    return this.ap == null ? null : this.ap.av;
+  }
+
   public void updateColours(SequenceI seq, int index)
   {
     System.out.println("update the seqPanel colours");
@@ -716,39 +796,38 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       mouseOverSequence(sequence, res, respos);
     }
 
-    StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
-            + sequence.getName());
+    StringBuilder text = new StringBuilder();
+    text.append("Sequence ").append(Integer.toString(seq + 1))
+            .append(" ID: ").append(sequence.getName());
 
-    Object obj = null;
+    String obj = null;
+    final String ch = String.valueOf(sequence.getCharAt(res));
     if (av.getAlignment().isNucleotide())
     {
-      obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
-              + "");
+      obj = ResidueProperties.nucleotideName.get(ch);
       if (obj != null)
       {
-        text.append(" Nucleotide: ");
+        text.append(" Nucleotide: ").append(obj);
       }
     }
     else
     {
-      obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
+      obj = "X".equalsIgnoreCase(ch) ? "X"
+              : ResidueProperties.aa2Triplet.get(ch);
       if (obj != null)
       {
-        text.append("  Residue: ");
+        text.append(" Residue: ").append(obj);
       }
     }
 
     if (obj != null)
     {
-      if (obj != "")
-      {
-        text.append(obj + " (" + respos + ")");
-      }
+      text.append(" (").append(Integer.toString(respos)).append(")");
     }
 
     ap.alignFrame.statusBar.setText(text.toString());
 
-    StringBuffer tooltipText = new StringBuffer();
+    StringBuilder tooltipText = new StringBuilder();
     SequenceGroup[] groups = av.getAlignment().findAllGroups(sequence);
     if (groups != null)
     {
@@ -759,7 +838,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
           if (!groups[g].getName().startsWith("JTreeGroup")
                   && !groups[g].getName().startsWith("JGroup"))
           {
-            tooltipText.append(groups[g].getName() + " ");
+            tooltipText.append(groups[g].getName()).append(" ");
           }
           if (groups[g].getDescription() != null)
           {
@@ -849,7 +928,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   {
     if (mouseWheelPressed)
     {
-      int oldWidth = av.charWidth;
+      int oldWidth = av.getCharWidth();
 
       // Which is bigger, left-right or up-down?
       if (Math.abs(evt.getY() - lastMousePress.y) > Math.abs(evt.getX()
@@ -857,7 +936,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       {
         int fontSize = av.font.getSize();
 
-        if (evt.getY() < lastMousePress.y && av.charHeight > 1)
+        if (evt.getY() < lastMousePress.y && av.getCharHeight() > 1)
         {
           fontSize--;
         }
@@ -872,29 +951,29 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
 
         av.setFont(new Font(av.font.getName(), av.font.getStyle(), fontSize));
-        av.charWidth = oldWidth;
+        av.setCharWidth(oldWidth);
       }
       else
       {
-        if (evt.getX() < lastMousePress.x && av.charWidth > 1)
+        if (evt.getX() < lastMousePress.x && av.getCharWidth() > 1)
         {
-          av.charWidth--;
+          av.setCharWidth(av.getCharWidth() - 1);
         }
         else if (evt.getX() > lastMousePress.x)
         {
-          av.charWidth++;
+          av.setCharWidth(av.getCharWidth() + 1);
         }
 
-        if (av.charWidth < 1)
+        if (av.getCharWidth() < 1)
         {
-          av.charWidth = 1;
+          av.setCharWidth(1);
         }
       }
 
       ap.fontChanged();
 
       FontMetrics fm = getFontMetrics(av.getFont());
-      av.validCharWidth = fm.charWidth('M') <= av.charWidth;
+      av.validCharWidth = fm.charWidth('M') <= av.getCharWidth();
 
       lastMousePress = evt.getPoint();
 
@@ -960,15 +1039,18 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     StringBuffer message = new StringBuffer();
     if (groupEditing)
     {
-      message.append(MessageManager.getString("action.edit_group")).append(":");
+      message.append(MessageManager.getString("action.edit_group")).append(
+              ":");
       if (editCommand == null)
       {
-        editCommand = new EditCommand(MessageManager.getString("action.edit_group"));
+        editCommand = new EditCommand(
+                MessageManager.getString("action.edit_group"));
       }
     }
     else
     {
-      message.append(MessageManager.getString("label.edit_sequence")).append(" " + seq.getName());
+      message.append(MessageManager.getString("label.edit_sequence"))
+              .append(" " + seq.getName());
       String label = seq.getName();
       if (label.length() > 10)
       {
@@ -976,7 +1058,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       }
       if (editCommand == null)
       {
-        editCommand = new EditCommand(MessageManager.formatMessage("label.edit_params", new String[]{label}));
+        editCommand = new EditCommand(MessageManager.formatMessage(
+                "label.edit_params", new String[]
+                { label }));
       }
     }
 
@@ -1178,8 +1262,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
         else
         {
-          editCommand.appendEdit(Action.INSERT_GAP, groupSeqs,
-                  startres, startres - lastres, av.getAlignment(), true);
+          editCommand.appendEdit(Action.INSERT_GAP, groupSeqs, startres,
+                  startres - lastres, av.getAlignment(), true);
         }
       }
       else
@@ -1194,8 +1278,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
         else
         {
-          editCommand.appendEdit(Action.DELETE_GAP, groupSeqs,
-                  startres, lastres - startres, av.getAlignment(), true);
+          editCommand.appendEdit(Action.DELETE_GAP, groupSeqs, startres,
+                  lastres - startres, av.getAlignment(), true);
         }
 
       }
@@ -1290,16 +1374,16 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1,
             av.getAlignment(), true);
 
-    editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1,
-            av.getAlignment(), true);
+    editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1, av.getAlignment(),
+            true);
 
   }
 
   void deleteChar(int j, SequenceI[] seq, int fixedColumn)
   {
 
-    editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1,
-            av.getAlignment(), true);
+    editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1, av.getAlignment(),
+            true);
 
     editCommand.appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1,
             av.getAlignment(), true);
@@ -1378,7 +1462,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       SequenceFeature[] allFeatures = findFeaturesAtRes(sequence,
               sequence.findPosition(res));
 
-      Vector links = null;
+      Vector<String> links = null;
       if (allFeatures != null)
       {
         for (int i = 0; i < allFeatures.length; i++)
@@ -1387,7 +1471,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
           {
             if (links == null)
             {
-              links = new Vector();
+              links = new Vector<String>();
             }
             for (int j = 0; j < allFeatures[i].links.size(); j++)
             {
@@ -1718,6 +1802,16 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     {
       return;
     }
+
+    /*
+     * Check for selection in a view of which this one is a dna/protein
+     * complement.
+     */
+    if (selectionFromTranslation(seqsel, colsel, source))
+    {
+      return;
+    }
+
     // do we want to thread this ? (contention with seqsel and colsel locks, I
     // suspect)
     // rules are: colsel is copied if there is a real intersection between
@@ -1835,4 +1929,46 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     ap.scrollTo(column, column, ap.av.startSeq, true, true);
   }
 
+  /**
+   * If this panel is a cdna/protein translation view of the selection source,
+   * tries to map the source selection to a local one, and returns true. Else
+   * returns false.
+   * 
+   * @param seqsel
+   * @param colsel
+   * @param source
+   */
+  protected boolean selectionFromTranslation(SequenceGroup seqsel,
+          ColumnSelection colsel, SelectionSource source)
+  {
+    if (!(source instanceof AlignViewportI)) {
+      return false;
+    }
+    final AlignViewportI sourceAv = (AlignViewportI) source;
+    if (sourceAv.getCodingComplement() != av && av.getCodingComplement() != sourceAv)
+    {
+      return false;
+    }
+  
+    /*
+     * Map sequence selection
+     */
+    SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av);
+    av.setSelectionGroup(sg);
+    av.isSelectionGroupChanged(true);
+  
+    /*
+     * Map column selection
+     */
+    ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
+            av);
+    av.setColumnSelection(cs);
+    av.isColSelChanged(true);
+  
+    ap.scalePanelHolder.repaint();
+    ap.repaint();
+  
+    return true;
+  }
+
 }
index 40b51b1..feb2705 100755 (executable)
@@ -21,7 +21,6 @@
 package jalview.appletgui;
 
 import jalview.api.FeatureRenderer;
-import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
@@ -161,7 +160,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     int length = seq.getLength();
 
     int curStart = -1;
-    int curWidth = av.charWidth;
+    int curWidth = av.getCharWidth(), avCharWidth = av.getCharWidth(), avCharHeight = av
+            .getCharHeight();
 
     Color tempColour = null;
     while (i <= end)
@@ -186,40 +186,41 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
       {
         if (tempColour != null)
         {
-          graphics.fillRect(av.charWidth * (curStart - start), y1,
-                  curWidth, av.charHeight);
+          graphics.fillRect(avCharWidth * (curStart - start), y1, curWidth,
+                  avCharHeight);
         }
         graphics.setColor(resBoxColour);
 
         curStart = i;
-        curWidth = av.charWidth;
+        curWidth = avCharWidth;
         tempColour = resBoxColour;
 
       }
       else
       {
-        curWidth += av.charWidth;
+        curWidth += avCharWidth;
       }
 
       i++;
     }
 
-    graphics.fillRect(av.charWidth * (curStart - start), y1, curWidth,
-            av.charHeight);
+    graphics.fillRect(avCharWidth * (curStart - start), y1, curWidth,
+            avCharHeight);
   }
 
   public void drawText(SequenceI seq, int start, int end, int y1)
   {
+    int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
     Font boldFont = null;
     boolean bold = false;
-    if (av.upperCasebold)
+    if (av.isUpperCasebold())
     {
-      boldFont = new Font(av.getFont().getName(), Font.BOLD, av.charHeight);
+      boldFont = new Font(av.getFont().getName(), Font.BOLD, avCharHeight);
 
       graphics.setFont(av.getFont());
     }
 
-    y1 += av.charHeight - av.charHeight / 5; // height/5 replaces pady
+    y1 += avCharHeight - avCharHeight / 5; // height/5 replaces pady
 
     int charOffset = 0;
 
@@ -286,7 +287,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
         }
       }
 
-      if (av.upperCasebold)
+      if (av.isUpperCasebold())
       {
         fm = graphics.getFontMetrics();
         if ('A' <= s && s <= 'Z')
@@ -306,8 +307,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
       }
 
-      charOffset = (av.charWidth - fm.charWidth(s)) / 2;
-      graphics.drawString(String.valueOf(s), charOffset + av.charWidth
+      charOffset = (avCharWidth - fm.charWidth(s)) / 2;
+      graphics.drawString(String.valueOf(s), charOffset + avCharWidth
               * (i - start), y1);
     }
 
@@ -350,11 +351,11 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   public void drawHighlightedText(SequenceI seq, int start, int end,
           int x1, int y1)
   {
-    int pady = av.charHeight / 5;
+    int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
+    int pady = avCharHeight / 5;
     int charOffset = 0;
     graphics.setColor(Color.black);
-    graphics.fillRect(x1, y1, av.charWidth * (end - start + 1),
-            av.charHeight);
+    graphics.fillRect(x1, y1, avCharWidth * (end - start + 1), avCharHeight);
     graphics.setColor(Color.white);
 
     char s = '~';
@@ -368,19 +369,19 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           s = seq.getCharAt(i);
         }
 
-        charOffset = (av.charWidth - fm.charWidth(s)) / 2;
+        charOffset = (avCharWidth - fm.charWidth(s)) / 2;
         graphics.drawString(String.valueOf(s), charOffset + x1
-                + av.charWidth * (i - start), y1 + av.charHeight - pady);
+                + avCharWidth * (i - start), y1 + avCharHeight - pady);
       }
     }
   }
 
   public void drawCursor(SequenceI seq, int res, int x1, int y1)
   {
-    int pady = av.charHeight / 5;
+    int pady = av.getCharHeight() / 5;
     int charOffset = 0;
     graphics.setColor(Color.black);
-    graphics.fillRect(x1, y1, av.charWidth, av.charHeight);
+    graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
     graphics.setColor(Color.white);
 
     graphics.setColor(Color.white);
@@ -389,9 +390,9 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     if (av.validCharWidth)
     {
 
-      charOffset = (av.charWidth - fm.charWidth(s)) / 2;
+      charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
       graphics.drawString(String.valueOf(s), charOffset + x1,
-              (y1 + av.charHeight) - pady);
+              (y1 + av.getCharHeight()) - pady);
     }
   }
 
index 390aa31..65622aa 100644 (file)
  */
 package jalview.appletgui;
 
-import java.util.*;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import jalview.datamodel.*;
-import jalview.schemes.*;
+import jalview.datamodel.SequenceGroup;
+import jalview.schemes.ColourSchemeI;
 import jalview.util.MessageManager;
 
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.Checkbox;
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.Label;
+import java.awt.Panel;
+import java.awt.Scrollbar;
+import java.awt.TextField;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.Iterator;
+
 public class SliderPanel extends Panel implements ActionListener,
         AdjustmentListener, MouseListener
 {
@@ -215,13 +230,15 @@ public class SliderPanel extends Panel implements ActionListener,
       }
       else
       {
-        toChange.setThreshold(i, ap.av.getIgnoreGapsConsensus());
+        toChange.setThreshold(i, ap.av.isIgnoreGapsConsensus());
       }
       if (allGroups != null && allGroups.hasNext())
       {
         while ((toChange = allGroups.next().cs) == null
                 && allGroups.hasNext())
+        {
           ;
+        }
       }
       else
       {
diff --git a/src/jalview/appletgui/SplitFrame.java b/src/jalview/appletgui/SplitFrame.java
new file mode 100644 (file)
index 0000000..cdd1605
--- /dev/null
@@ -0,0 +1,188 @@
+package jalview.appletgui;
+
+import jalview.analysis.AlignmentUtils;
+import jalview.api.ViewStyleI;
+import jalview.bin.JalviewLite;
+import jalview.datamodel.AlignmentI;
+import jalview.structure.StructureSelectionManager;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.Panel;
+
+public class SplitFrame extends EmbmenuFrame
+{
+  private static final long serialVersionUID = 1L;
+
+  private AlignFrame topFrame;
+
+  private AlignFrame bottomFrame;
+
+  private Panel outermost;
+
+  /**
+   * Constructor
+   */
+  public SplitFrame(AlignFrame af1, AlignFrame af2)
+  {
+    topFrame = af1;
+    bottomFrame = af2;
+    init();
+  }
+
+  /**
+   * Creates a Panel containing two Panels, and adds the first and second
+   * AlignFrame's components to each. At this stage we have not yet committed to
+   * whether the enclosing panel will be added to this frame, for display as a
+   * separate frame, or added to the applet (embedded mode).
+   */
+  public void init()
+  {
+    constructSplit();
+
+    /*
+     * Try to make and add dna/protein sequence mappings
+     */
+    final AlignViewport topViewport = topFrame.viewport;
+    final AlignViewport bottomViewport = bottomFrame.viewport;
+    final AlignmentI topAlignment = topViewport.getAlignment();
+    final AlignmentI bottomAlignment = bottomViewport.getAlignment();
+    AlignViewport cdna = topAlignment.isNucleotide() ? topViewport
+            : (bottomAlignment.isNucleotide() ? bottomViewport : null);
+    AlignViewport protein = !topAlignment.isNucleotide() ? topViewport
+            : (!bottomAlignment.isNucleotide() ? bottomViewport : null);
+
+    boolean mapped = AlignmentUtils.mapProteinToCdna(
+            protein.getAlignment(), cdna.getAlignment());
+    if (mapped)
+    {
+      final StructureSelectionManager ssm = StructureSelectionManager
+              .getStructureSelectionManager(topViewport.applet);
+      ssm.addMappings(protein.getAlignment().getCodonFrames());
+      topViewport.setCodingComplement(bottomViewport);
+      ssm.addCommandListener(cdna);
+      ssm.addCommandListener(protein);
+    }
+
+    adjustLayout();
+  }
+
+  /**
+   * 
+   */
+  protected void constructSplit()
+  {
+    setMenuBar(null);
+    outermost = new Panel(new GridLayout(2, 1));
+
+    Panel topPanel = new Panel();
+    Panel bottomPanel = new Panel();
+    outermost.add(topPanel);
+    outermost.add(bottomPanel);
+
+    addAlignFrameComponents(topFrame, topPanel);
+    addAlignFrameComponents(bottomFrame, bottomPanel);
+  }
+
+  /**
+   * Make any adjustments to the layout
+   */
+  protected void adjustLayout()
+  {
+    AlignViewport cdna = topFrame.getAlignViewport().getAlignment()
+            .isNucleotide() ? topFrame.viewport : bottomFrame.viewport;
+    AlignViewport protein = cdna == topFrame.viewport ? bottomFrame.viewport
+            : topFrame.viewport;
+
+    /*
+     * Ensure sequence ids are the same width for good alignment.
+     */
+    // TODO should do this via av.getViewStyle/setViewStyle
+    // however at present av.viewStyle is not set in IdPanel.fontChanged
+    int w1 = topFrame.alignPanel.idPanel.idCanvas.getWidth();
+    int w2 = bottomFrame.alignPanel.idPanel.idCanvas.getWidth();
+    int w3 = Math.max(w1, w2);
+    if (w1 != w3)
+    {
+      Dimension d = topFrame.alignPanel.idPanel.idCanvas.getSize();
+      topFrame.alignPanel.idPanel.idCanvas.setSize(new Dimension(w3,
+              d.height));
+    }
+    if (w2 != w3)
+    {
+      Dimension d = bottomFrame.alignPanel.idPanel.idCanvas.getSize();
+      bottomFrame.alignPanel.idPanel.idCanvas.setSize(new Dimension(w3,
+              d.height));
+    }
+
+    /*
+     * Expand protein to 3 times character width of dna
+     */
+    if (protein != null && cdna != null)
+    {
+      ViewStyleI vs = protein.getViewStyle();
+      vs.setCharWidth(3 * vs.getCharWidth());
+      protein.setViewStyle(vs);
+    }
+  }
+
+  /**
+   * Add the menu bar, alignment panel and status bar from the AlignFrame to the
+   * panel. The menu bar is a panel 'reconstructed' from the AlignFrame's frame
+   * menu bar. This allows each half of the SplitFrame to have its own menu bar.
+   * 
+   * @param af
+   * @param panel
+   */
+  private void addAlignFrameComponents(AlignFrame af, Panel panel)
+  {
+    panel.setLayout(new BorderLayout());
+    Panel menuPanel = af
+            .makeEmbeddedPopupMenu(af.getMenuBar(), true, false);
+    panel.add(menuPanel, BorderLayout.NORTH);
+    panel.add(af.statusBar, BorderLayout.SOUTH);
+    panel.add(af.alignPanel, BorderLayout.CENTER);
+  }
+
+  /**
+   * Display the content panel either as a new frame or embedded in the applet.
+   * 
+   * @param embedded
+   * @param applet
+   */
+  public void addToDisplay(boolean embedded, JalviewLite applet)
+  {
+    createSplitFrameWindow(embedded, applet);
+    validate();
+    topFrame.alignPanel.adjustAnnotationHeight();
+    topFrame.alignPanel.paintAlignment(true);
+    bottomFrame.alignPanel.adjustAnnotationHeight();
+    bottomFrame.alignPanel.paintAlignment(true);
+  }
+
+  /**
+   * Either show the content panel in this frame as a new frame, or (if
+   * embed=true) add it to the applet container instead.
+   * 
+   * @param embed
+   * @param applet
+   */
+  protected void createSplitFrameWindow(boolean embed, JalviewLite applet)
+  {
+    if (embed)
+    {
+      applet.add(outermost);
+      applet.validate();
+    }
+    else
+    {
+      this.add(outermost);
+      int width = Math.max(topFrame.DEFAULT_WIDTH,
+              bottomFrame.DEFAULT_WIDTH);
+      int height = topFrame.DEFAULT_HEIGHT + bottomFrame.DEFAULT_HEIGHT;
+      jalview.bin.JalviewLite
+              .addFrame(this, this.getTitle(), width, height);
+    }
+  }
+}
index 44b55a7..7ba9977 100755 (executable)
  */
 package jalview.appletgui;
 
-import java.util.*;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import jalview.analysis.*;
-import jalview.datamodel.*;
-import jalview.schemes.*;
-import jalview.util.*;
+import jalview.analysis.Conservation;
+import jalview.analysis.NJTree;
+import jalview.api.AlignViewportI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequenceNode;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.ResidueProperties;
+import jalview.schemes.UserColourScheme;
+import jalview.util.Format;
+import jalview.util.MappingUtils;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.ScrollPane;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
 
 public class TreeCanvas extends Panel implements MouseListener,
         MouseMotionListener
@@ -582,6 +602,13 @@ public class TreeCanvas extends Panel implements MouseListener,
         av.setSelectionGroup(null);
         av.getAlignment().deleteAllGroups();
         av.clearSequenceColours();
+        final AlignViewportI codingComplement = av.getCodingComplement();
+        if (codingComplement != null)
+        {
+          codingComplement.setSelectionGroup(null);
+          codingComplement.getAlignment().deleteAllGroups();
+          codingComplement.clearSequenceColours();
+        }
 
         colourGroups();
 
@@ -639,7 +666,7 @@ public class TreeCanvas extends Panel implements MouseListener,
         if (cs != null)
         {
           cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
-                  av.getIgnoreGapsConsensus());
+                  av.isIgnoreGapsConsensus());
         }
       }
       // TODO: cs used to be initialized with a sequence collection and
@@ -666,9 +693,31 @@ public class TreeCanvas extends Panel implements MouseListener,
 
       av.getAlignment().addGroup(sg);
 
+      // TODO this is duplicated with gui TreeCanvas - refactor
+      av.getAlignment().addGroup(sg);
+      final AlignViewportI codingComplement = av.getCodingComplement();
+      if (codingComplement != null)
+      {
+        SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av,
+                codingComplement);
+        if (mappedGroup.getSequences().size() > 0)
+        {
+          codingComplement.getAlignment().addGroup(mappedGroup);
+          for (SequenceI seq : mappedGroup.getSequences())
+          {
+            // TODO why does gui require col.brighter() here??
+            codingComplement.setSequenceColour(seq, col);
+          }
+        }
+      }
+
     }
     ap.updateAnnotation();
-
+    if (av.getCodingComplement() != null)
+    {
+      ((AlignViewport) av.getCodingComplement()).firePropertyChange(
+              "alignment", null, ap.av.getAlignment().getSequences());
+    }
   }
 
   public void setShowDistances(boolean state)
index 4b13f98..342c065 100644 (file)
@@ -427,7 +427,9 @@ public class UserDefinedColours extends Panel implements ActionListener,
   {
     applyButton_actionPerformed();
     if (dialog != null)
+    {
       dialog.setVisible(false);
+    }
 
     frame.setVisible(false);
   }
@@ -489,7 +491,7 @@ public class UserDefinedColours extends Panel implements ActionListener,
     UserColourScheme ucs = new UserColourScheme(newColours);
     if (ap != null)
     {
-      ucs.setThreshold(0, ap.av.getIgnoreGapsConsensus());
+      ucs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
     }
 
     if (ap != null)
@@ -557,7 +559,9 @@ public class UserDefinedColours extends Panel implements ActionListener,
         }
       }
       if (dialog != null)
+      {
         dialog.setVisible(false);
+      }
 
       frame.setVisible(false);
       return;
index ac1f9ac..c0a8d73 100644 (file)
@@ -25,6 +25,7 @@ import jalview.appletgui.AlignFrame;
 import jalview.appletgui.AlignViewport;
 import jalview.appletgui.EmbmenuFrame;
 import jalview.appletgui.FeatureSettings;
+import jalview.appletgui.SplitFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
@@ -37,7 +38,9 @@ import jalview.io.AnnotationFile;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.FileParse;
 import jalview.io.IdentifyFile;
+import jalview.io.JPredFile;
 import jalview.io.JnetAnnotationMaker;
+import jalview.io.NewickFile;
 import jalview.javascript.JSFunctionExec;
 import jalview.javascript.JalviewLiteJsApi;
 import jalview.javascript.JsCallBack;
@@ -57,9 +60,12 @@ import java.awt.event.ActionEvent;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URL;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
@@ -76,6 +82,10 @@ public class JalviewLite extends Applet implements
         StructureSelectionManagerProvider, JalviewLiteJsApi
 {
 
+  private static final String TRUE = "true";
+
+  private static final String FALSE = "false";
+
   public StructureSelectionManager getStructureSelectionManager()
   {
     return StructureSelectionManager.getStructureSelectionManager(this);
@@ -447,12 +457,11 @@ public class JalviewLite extends Applet implements
         end = rs.findIndex(end);
         if (csel != null)
         {
-          Vector cs = csel.getSelected();
+          List<Integer> cs = csel.getSelected();
           csel.clear();
-          for (int csi = 0, csiS = cs.size(); csi < csiS; csi++)
+          for (Integer selectedCol : cs)
           {
-            csel.addElement(rs.findIndex(((Integer) cs.elementAt(csi))
-                    .intValue()));
+            csel.addElement(rs.findIndex(selectedCol));
           }
         }
       }
@@ -494,7 +503,7 @@ public class JalviewLite extends Applet implements
   {
     try
     {
-      boolean seqlimits = suffix.equalsIgnoreCase("true");
+      boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
       if (alf.viewport.getSelectionGroup() != null)
       {
         // JBPNote: getSelectionAsNewSequence behaviour has changed - this
@@ -630,7 +639,7 @@ public class JalviewLite extends Applet implements
     final String _undoName = undoName;
     // TODO: deal with synchronization here: cannot raise any events until after
     // this has returned.
-    return alf.sortBy(aorder, _undoName) ? "true" : "";
+    return alf.sortBy(aorder, _undoName) ? TRUE : "";
   }
 
   /*
@@ -640,7 +649,7 @@ public class JalviewLite extends Applet implements
    */
   public String getAlignment(String format)
   {
-    return getAlignmentFrom(getDefaultTargetFrame(), format, "true");
+    return getAlignmentFrom(getDefaultTargetFrame(), format, TRUE);
   }
 
   /*
@@ -652,7 +661,7 @@ public class JalviewLite extends Applet implements
    */
   public String getAlignmentFrom(AlignFrame alf, String format)
   {
-    return getAlignmentFrom(alf, format, "true");
+    return getAlignmentFrom(alf, format, TRUE);
   }
 
   /*
@@ -678,7 +687,7 @@ public class JalviewLite extends Applet implements
   {
     try
     {
-      boolean seqlimits = suffix.equalsIgnoreCase("true");
+      boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
 
       String reply = new AppletFormatAdapter().formatSequences(format,
               alf.viewport.getAlignment(), seqlimits);
@@ -1233,7 +1242,10 @@ public class JalviewLite extends Applet implements
 
   String file = "No file";
 
-  Button launcher = new Button("Start Jalview");
+  String file2 = "No file";
+
+  Button launcher = new Button(
+          MessageManager.getString("label.start_jalview"));
 
   /**
    * The currentAlignFrame is static, it will change if and when the user
@@ -1361,11 +1373,7 @@ public class JalviewLite extends Applet implements
     /**
      * turn on extra applet debugging
      */
-    String dbg = getParameter("debug");
-    if (dbg != null)
-    {
-      debug = dbg.toLowerCase().equals("true");
-    }
+    debug = TRUE.equalsIgnoreCase(getParameter("debug"));
     if (debug)
     {
 
@@ -1378,7 +1386,7 @@ public class JalviewLite extends Applet implements
     if (externalsviewer != null)
     {
       useXtrnalSviewer = externalsviewer.trim().toLowerCase()
-              .equals("true");
+              .equals(TRUE);
     }
     /**
      * if true disable the check for jmol
@@ -1386,7 +1394,7 @@ public class JalviewLite extends Applet implements
     String chkforJmol = getParameter("nojmol");
     if (chkforJmol != null)
     {
-      checkForJmol = !chkforJmol.equals("true");
+      checkForJmol = !chkforJmol.equals(TRUE);
     }
     /**
      * get the separator parameter if present
@@ -1451,38 +1459,37 @@ public class JalviewLite extends Applet implements
         file = data.toString();
       }
     }
+    file2 = getParameter("file2");
 
-    final JalviewLite jvapplet = this;
-    if (getParameter("embedded") != null
-            && getParameter("embedded").equalsIgnoreCase("true"))
+    embedded = TRUE.equalsIgnoreCase(getParameter("embedded"));
+    if (embedded)
     {
-      // Launch as embedded applet in page
-      embedded = true;
-      LoadingThread loader = new LoadingThread(file, jvapplet);
+      LoadingThread loader = new LoadingThread(file, file2, this);
       loader.start();
     }
     else if (file != null)
     {
-      if (getParameter("showbutton") == null
-              || !getParameter("showbutton").equalsIgnoreCase("false"))
+      /*
+       * Start the applet immediately or show a button to start it
+       */
+      if (FALSE.equalsIgnoreCase(getParameter("showbutton")))
+      {
+        LoadingThread loader = new LoadingThread(file, file2, this);
+        loader.start();
+      }
+      else
       {
-        // Add the JalviewLite 'Button' to the page
         add(launcher);
         launcher.addActionListener(new java.awt.event.ActionListener()
         {
           public void actionPerformed(ActionEvent e)
           {
-            LoadingThread loader = new LoadingThread(file, jvapplet);
+            LoadingThread loader = new LoadingThread(file, file2,
+                    JalviewLite.this);
             loader.start();
           }
         });
       }
-      else
-      {
-        // Open jalviewLite immediately.
-        LoadingThread loader = new LoadingThread(file, jvapplet);
-        loader.start();
-      }
     }
     else
     {
@@ -1756,27 +1763,19 @@ public class JalviewLite extends Applet implements
   class LoadingThread extends Thread
   {
     /**
-     * State variable: File source
-     */
-    String file;
-
-    /**
      * State variable: protocol for access to file source
      */
     String protocol;
 
-    /**
-     * State variable: format of file source
-     */
-    String format;
+    String _file; // alignment file or URL spec
 
-    String _file;
+    String _file2; // second alignment file or URL spec
 
     JalviewLite applet;
 
     private void dbgMsg(String msg)
     {
-      if (applet.debug)
+      if (JalviewLite.debug)
       {
         System.err.println(msg);
       }
@@ -1809,9 +1808,10 @@ public class JalviewLite extends Applet implements
       return file;
     }
 
-    public LoadingThread(String _file, JalviewLite _applet)
+    public LoadingThread(String file, String file2, JalviewLite _applet)
     {
-      this._file = _file;
+      this._file = file;
+      this._file2 = file2;
       applet = _applet;
     }
 
@@ -1828,25 +1828,112 @@ public class JalviewLite extends Applet implements
         } catch (Exception e)
         {
         }
-        ;
       }
       startLoading();
       // applet.callInitCallback();
     }
 
+    /**
+     * Load the alignment and any related files as specified by applet
+     * parameters
+     */
     private void startLoading()
     {
-      AlignFrame newAlignFrame;
       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
-      file = setProtocolState(_file);
 
-      format = new jalview.io.IdentifyFile().Identify(file, protocol);
-      dbgMsg("File identified as '" + format + "'");
       dbgMsg("Loading started.");
-      Alignment al = null;
+
+      AlignFrame newAlignFrame = readAlignment(_file);
+      AlignFrame newAlignFrame2 = readAlignment(_file2);
+      if (newAlignFrame != null)
+      {
+        addToDisplay(newAlignFrame, newAlignFrame2);
+        loadTree(newAlignFrame);
+
+        loadScoreFile(newAlignFrame);
+
+        loadFeatures(newAlignFrame);
+
+        loadAnnotations(newAlignFrame);
+
+        loadJnetFile(newAlignFrame);
+
+        loadPdbFiles(newAlignFrame);
+      }
+      else
+      {
+        fileFound = false;
+        applet.remove(launcher);
+        applet.repaint();
+      }
+      callInitCallback();
+    }
+
+    /**
+     * Add an AlignFrame to the display; or if two are provided, a SplitFrame.
+     * 
+     * @param af
+     * @param af2
+     */
+    public void addToDisplay(AlignFrame af, AlignFrame af2)
+    {
+      if (af2 == null)
+      {
+        af.addToDisplay(embedded);
+      }
+      else
+      {
+        SplitFrame sf = new SplitFrame(af, af2);
+        sf.addToDisplay(embedded, JalviewLite.this);
+      }
+    }
+
+    /**
+     * Read the alignment file (from URL, text 'paste', or archive by
+     * classloader).
+     * 
+     * @return
+     */
+    protected AlignFrame readAlignment(String fileParam)
+    {
+      if (fileParam == null)
+      {
+        return null;
+      }
+      String resolvedFile = setProtocolState(fileParam);
+      String format = new IdentifyFile().Identify(resolvedFile, protocol);
+      dbgMsg("File identified as '" + format + "'");
+      AlignmentI al = null;
       try
       {
-        al = new AppletFormatAdapter().readFile(file, protocol, format);
+        al = new AppletFormatAdapter().readFile(resolvedFile, protocol, format);
+        if ((al != null) && (al.getHeight() > 0))
+        {
+          dbgMsg("Successfully loaded file.");
+          al.setDataset(null);
+          AlignFrame newAlignFrame = new AlignFrame(al, applet,
+                  resolvedFile, embedded, false);
+          newAlignFrame.setTitle(resolvedFile);
+          if (initialAlignFrame == null)
+          {
+            initialAlignFrame = newAlignFrame;
+          }
+          // update the focus.
+          currentAlignFrame = newAlignFrame;
+
+          if (protocol == AppletFormatAdapter.PASTE)
+          {
+            newAlignFrame.setTitle(MessageManager.formatMessage(
+                    "label.sequences_from", new Object[]
+                    { applet.getDocumentBase().toString() }));
+          }
+
+          newAlignFrame.statusBar.setText(MessageManager.formatMessage(
+                  "label.successfully_loaded_file", new Object[]
+                  { resolvedFile }));
+
+          return newAlignFrame;
+        }
       } catch (java.io.IOException ex)
       {
         dbgMsg("File load exception.");
@@ -1855,9 +1942,9 @@ public class JalviewLite extends Applet implements
         {
           try
           {
-            FileParse fp = new FileParse(file, protocol);
+            FileParse fp = new FileParse(resolvedFile, protocol);
             String ln = null;
-            dbgMsg(">>>Dumping contents of '" + file + "' " + "("
+            dbgMsg(">>>Dumping contents of '" + resolvedFile + "' " + "("
                     + protocol + ")");
             while ((ln = fp.nextLine()) != null)
             {
@@ -1872,350 +1959,378 @@ public class JalviewLite extends Applet implements
           }
         }
       }
-      if ((al != null) && (al.getHeight() > 0))
+      return null;
+    }
+
+    /**
+     * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
+     * else false.
+     * 
+     * @param alignFrame
+     * @return
+     */
+    protected boolean loadPdbFiles(AlignFrame alignFrame)
+    {
+      boolean result = false;
+      /*
+       * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
+       * related to JAL-434
+       */
+
+      applet.setAlignPdbStructures(getDefaultParameter("alignpdbfiles",
+              false));
+      /*
+       * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
+       * PDB|1GAQ|1GAQ|C">
+       * 
+       * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
+       * 
+       * <param name="PDBfile3" value="1q0o Q45135_9MICO">
+       */
+
+      int pdbFileCount = 0;
+      // Accumulate pdbs here if they are heading for the same view (if
+      // alignPdbStructures is true)
+      Vector pdbs = new Vector();
+      // create a lazy matcher if we're asked to
+      jalview.analysis.SequenceIdMatcher matcher = (applet
+              .getDefaultParameter("relaxedidmatch", false)) ? new jalview.analysis.SequenceIdMatcher(
+              alignFrame.getAlignViewport().getAlignment()
+                      .getSequencesArray()) : null;
+
+      String param;
+      do
       {
-        dbgMsg("Successfully loaded file.");
-        newAlignFrame = new AlignFrame(al, applet, file, embedded);
-        if (initialAlignFrame == null)
+        if (pdbFileCount > 0)
         {
-          initialAlignFrame = newAlignFrame;
+          param = applet.getParameter("PDBFILE" + pdbFileCount);
         }
-        // update the focus.
-        currentAlignFrame = newAlignFrame;
-
-        if (protocol == jalview.io.AppletFormatAdapter.PASTE)
+        else
         {
-          newAlignFrame.setTitle(MessageManager.formatMessage(
-                  "label.sequences_from", new String[]
-                  { applet.getDocumentBase().toString() }));
+          param = applet.getParameter("PDBFILE");
         }
 
-        newAlignFrame.statusBar.setText(MessageManager.formatMessage(
-                "label.successfully_loaded_file", new String[]
-                { file }));
-
-        String treeFile = applet.getParameter("tree");
-        if (treeFile == null)
+        if (param != null)
         {
-          treeFile = applet.getParameter("treeFile");
-        }
+          PDBEntry pdb = new PDBEntry();
 
-        if (treeFile != null)
-        {
-          try
+          String seqstring;
+          SequenceI[] seqs = null;
+          String[] chains = null;
+
+          StringTokenizer st = new StringTokenizer(param, " ");
+
+          if (st.countTokens() < 2)
           {
-            treeFile = setProtocolState(treeFile);
-            /*
-             * if (inArchive(treeFile)) { protocol =
-             * AppletFormatAdapter.CLASSLOADER; } else { protocol =
-             * AppletFormatAdapter.URL; treeFile = addProtocol(treeFile); }
-             */
-            jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
-                    protocol);
-
-            fin.parse();
-
-            if (fin.getTree() != null)
+            String sequence = applet.getParameter("PDBSEQ");
+            if (sequence != null)
             {
-              newAlignFrame.loadTree(fin, treeFile);
-              dbgMsg("Successfuly imported tree.");
+              seqs = new SequenceI[]
+              { matcher == null ? (Sequence) alignFrame.getAlignViewport()
+                      .getAlignment().findName(sequence) : matcher
+                      .findIdMatch(sequence) };
             }
-            else
+
+          }
+          else
+          {
+            param = st.nextToken();
+            Vector tmp = new Vector();
+            Vector tmp2 = new Vector();
+
+            while (st.hasMoreTokens())
             {
-              dbgMsg("Tree parameter did not resolve to a valid tree.");
+              seqstring = st.nextToken();
+              StringTokenizer st2 = new StringTokenizer(seqstring, "=");
+              if (st2.countTokens() > 1)
+              {
+                // This is the chain
+                tmp2.addElement(st2.nextToken());
+                seqstring = st2.nextToken();
+              }
+              tmp.addElement(matcher == null ? (Sequence) alignFrame
+                      .getAlignViewport().getAlignment()
+                      .findName(seqstring) : matcher.findIdMatch(seqstring));
+            }
+
+            seqs = new SequenceI[tmp.size()];
+            tmp.copyInto(seqs);
+            if (tmp2.size() == tmp.size())
+            {
+              chains = new String[tmp2.size()];
+              tmp2.copyInto(chains);
             }
-          } catch (Exception ex)
+          }
+          param = setProtocolState(param);
+
+          if (// !jmolAvailable
+          // &&
+          protocol == AppletFormatAdapter.CLASSLOADER && !useXtrnalSviewer)
           {
-            ex.printStackTrace();
+            // Re: JAL-357 : the bug isn't a problem if we are using an
+            // external viewer!
+            // TODO: verify this Re:
+            // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
+            // This exception preserves the current behaviour where, even if
+            // the local pdb file was identified in the class loader
+            protocol = AppletFormatAdapter.URL; // this is probably NOT
+            // CORRECT!
+            param = addProtocol(param); //
           }
-        }
 
-        /*
-         * Try to load T-Coffee score file
-         */
-        String sScoreFile = applet.getParameter("scoreFile");
-        if (sScoreFile != null && !"".equals(sScoreFile))
-        {
-          try
+          pdb.setFile(param);
+
+          if (seqs != null)
           {
-            if (debug)
+            for (int i = 0; i < seqs.length; i++)
             {
-              System.err
-                      .println("Attempting to load T-COFFEE score file from the scoreFile parameter");
+              if (seqs[i] != null)
+              {
+                ((Sequence) seqs[i]).addPDBId(pdb);
+                StructureSelectionManager.getStructureSelectionManager(
+                        applet).registerPDBEntry(pdb);
+              }
+              else
+              {
+                if (JalviewLite.debug)
+                {
+                  // this may not really be a problem but we give a warning
+                  // anyway
+                  System.err
+                          .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
+                                  + i + ")");
+                }
+              }
             }
-            if (!newAlignFrame.loadScoreFile(sScoreFile))
+
+            if (!alignPdbStructures)
             {
-              System.err
-                      .println("Failed to parse T-COFFEE parameter as a valid score file ('"
-                              + sScoreFile + "')");
+              alignFrame.newStructureView(applet, pdb, seqs, chains,
+                      protocol);
+            }
+            else
+            {
+              pdbs.addElement(new Object[]
+              { pdb, seqs, chains, new String(protocol) });
             }
-          } catch (Exception e)
-          {
-            System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
-                    sScoreFile, e.getMessage());
           }
         }
 
-        // ///////////////////////////
-        // modify display of features
-        // we do this before any features have been loaded, ensuring any hidden
-        // groups are hidden when features first displayed
-        //
-        // hide specific groups
-        //
-        String param = applet.getParameter("hidefeaturegroups");
-        if (param != null)
-        {
-          newAlignFrame.setFeatureGroupState(separatorListToArray(param),
-                  false);
-          // applet.setFeatureGroupStateOn(newAlignFrame, param, false);
-        }
-        // show specific groups
-        param = applet.getParameter("showfeaturegroups");
-        if (param != null)
-        {
-          newAlignFrame.setFeatureGroupState(separatorListToArray(param),
-                  true);
-          // applet.setFeatureGroupStateOn(newAlignFrame, param, true);
+        pdbFileCount++;
+      } while (param != null || pdbFileCount < 10);
+      if (pdbs.size() > 0)
+      {
+        SequenceI[][] seqs = new SequenceI[pdbs.size()][];
+        PDBEntry[] pdb = new PDBEntry[pdbs.size()];
+        String[][] chains = new String[pdbs.size()][];
+        String[] protocols = new String[pdbs.size()];
+        for (int pdbsi = 0, pdbsiSize = pdbs.size(); pdbsi < pdbsiSize; pdbsi++)
+        {
+          Object[] o = (Object[]) pdbs.elementAt(pdbsi);
+          pdb[pdbsi] = (PDBEntry) o[0];
+          seqs[pdbsi] = (SequenceI[]) o[1];
+          chains[pdbsi] = (String[]) o[2];
+          protocols[pdbsi] = (String) o[3];
         }
-        // and now load features
-        param = applet.getParameter("features");
-        if (param != null)
+        alignFrame.alignedStructureView(applet, pdb, seqs, chains,
+                protocols);
+        result = true;
+      }
+      return result;
+    }
+
+    /**
+     * Load in a Jnetfile if specified by parameter. Returns true if loaded,
+     * else false.
+     * 
+     * @param alignFrame
+     * @return
+     */
+    protected boolean loadJnetFile(AlignFrame alignFrame)
+    {
+      boolean result = false;
+      String param = applet.getParameter("jnetfile");
+      if (param != null)
+      {
+        try
         {
           param = setProtocolState(param);
-
-          newAlignFrame.parseFeaturesFile(param, protocol);
+          JPredFile predictions = new JPredFile(param, protocol);
+          JnetAnnotationMaker.add_annotation(predictions,
+                  alignFrame.viewport.getAlignment(), 0, false);
+          // false == do not add sequence profile from concise output
+          SequenceI repseq = alignFrame.viewport.getAlignment()
+                  .getSequenceAt(0);
+          alignFrame.viewport.getAlignment().setSeqrep(repseq);
+          ColumnSelection cs = new ColumnSelection();
+          cs.hideInsertionsFor(repseq);
+          alignFrame.viewport.setColumnSelection(cs);
+          alignFrame.alignPanel.fontChanged();
+          alignFrame.alignPanel.setScrollValues(0, 0);
+          result = true;
+        } catch (Exception ex)
+        {
+          ex.printStackTrace();
         }
+      }
+      return result;
+    }
 
-        param = applet.getParameter("showFeatureSettings");
-        if (param != null && param.equalsIgnoreCase("true"))
+    /**
+     * Load annotations if specified by parameter. Returns true if loaded, else
+     * false.
+     * 
+     * @param alignFrame
+     * @return
+     */
+    protected boolean loadAnnotations(AlignFrame alignFrame)
+    {
+      boolean result = false;
+      String param = applet.getParameter("annotations");
+      if (param != null)
+      {
+        param = setProtocolState(param);
+
+        if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
+                param, protocol))
         {
-          newAlignFrame.viewport.setShowSequenceFeatures(true);
-          new FeatureSettings(newAlignFrame.alignPanel);
+          alignFrame.alignPanel.fontChanged();
+          alignFrame.alignPanel.setScrollValues(0, 0);
+          result = true;
         }
-
-        param = applet.getParameter("annotations");
-        if (param != null)
+        else
         {
-          param = setProtocolState(param);
+          System.err
+                  .println("Annotations were not added from annotation file '"
+                          + param + "'");
+        }
+      }
+      return result;
+    }
 
-          if (new AnnotationFile().annotateAlignmentView(
-                  newAlignFrame.viewport, param, protocol))
-          {
-            newAlignFrame.alignPanel.fontChanged();
-            newAlignFrame.alignPanel.setScrollValues(0, 0);
-          }
-          else
-          {
-            System.err
-                    .println("Annotations were not added from annotation file '"
-                            + param + "'");
-          }
+    /**
+     * Load features file and view settings as specified by parameters. Returns
+     * true if features were loaded, else false.
+     * 
+     * @param alignFrame
+     * @return
+     */
+    protected boolean loadFeatures(AlignFrame alignFrame)
+    {
+      boolean result = false;
+      // ///////////////////////////
+      // modify display of features
+      // we do this before any features have been loaded, ensuring any hidden
+      // groups are hidden when features first displayed
+      //
+      // hide specific groups
+      //
+      String param = applet.getParameter("hidefeaturegroups");
+      if (param != null)
+      {
+        alignFrame.setFeatureGroupState(separatorListToArray(param), false);
+        // applet.setFeatureGroupStateOn(newAlignFrame, param, false);
+      }
+      // show specific groups
+      param = applet.getParameter("showfeaturegroups");
+      if (param != null)
+      {
+        alignFrame.setFeatureGroupState(separatorListToArray(param), true);
+        // applet.setFeatureGroupStateOn(newAlignFrame, param, true);
+      }
+      // and now load features
+      param = applet.getParameter("features");
+      if (param != null)
+      {
+        param = setProtocolState(param);
 
-        }
+        result = alignFrame.parseFeaturesFile(param, protocol);
+      }
 
-        param = applet.getParameter("jnetfile");
-        if (param != null)
+      param = applet.getParameter("showFeatureSettings");
+      if (param != null && param.equalsIgnoreCase(TRUE))
+      {
+        alignFrame.viewport.setShowSequenceFeatures(true);
+        new FeatureSettings(alignFrame.alignPanel);
+      }
+      return result;
+    }
+
+    /**
+     * Load a score file if specified by parameter. Returns true if file was
+     * loaded, else false.
+     * 
+     * @param alignFrame
+     */
+    protected boolean loadScoreFile(AlignFrame alignFrame)
+    {
+      boolean result = false;
+      String sScoreFile = applet.getParameter("scoreFile");
+      if (sScoreFile != null && !"".equals(sScoreFile))
+      {
+        try
         {
-          try
+          if (debug)
           {
-            param = setProtocolState(param);
-            jalview.io.JPredFile predictions = new jalview.io.JPredFile(
-                    param, protocol);
-            JnetAnnotationMaker.add_annotation(predictions,
-                    newAlignFrame.viewport.getAlignment(), 0, false); // false==do
-            SequenceI repseq = newAlignFrame.viewport.getAlignment()
-                    .getSequenceAt(0);
-            newAlignFrame.viewport.getAlignment().setSeqrep(repseq);
-            ColumnSelection cs = new ColumnSelection();
-            cs.hideInsertionsFor(repseq);
-            newAlignFrame.viewport.setColumnSelection(cs);
-            // not
-            // add
-            // sequence
-            // profile
-            // from
-            // concise
-            // output
-            newAlignFrame.alignPanel.fontChanged();
-            newAlignFrame.alignPanel.setScrollValues(0, 0);
-          } catch (Exception ex)
+            System.err
+                    .println("Attempting to load T-COFFEE score file from the scoreFile parameter");
+          }
+          result = alignFrame.loadScoreFile(sScoreFile);
+          if (!result)
           {
-            ex.printStackTrace();
+            System.err
+                    .println("Failed to parse T-COFFEE parameter as a valid score file ('"
+                            + sScoreFile + "')");
           }
+        } catch (Exception e)
+        {
+          System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
+                  sScoreFile, e.getMessage());
         }
-        /*
-         * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6
-         * - related to JAL-434
-         */
-        applet.setAlignPdbStructures(getDefaultParameter("alignpdbfiles",
-                false));
-        /*
-         * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
-         * PDB|1GAQ|1GAQ|C">
-         * 
-         * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
-         * 
-         * <param name="PDBfile3" value="1q0o Q45135_9MICO">
-         */
+      }
+      return result;
+    }
 
-        int pdbFileCount = 0;
-        // Accumulate pdbs here if they are heading for the same view (if
-        // alignPdbStructures is true)
-        Vector pdbs = new Vector();
-        // create a lazy matcher if we're asked to
-        jalview.analysis.SequenceIdMatcher matcher = (applet
-                .getDefaultParameter("relaxedidmatch", false)) ? new jalview.analysis.SequenceIdMatcher(
-                newAlignFrame.getAlignViewport().getAlignment()
-                        .getSequencesArray()) : null;
+    /**
+     * Load a tree for the alignment if specified by parameter. Returns true if
+     * a tree was loaded, else false.
+     * 
+     * @param alignFrame
+     * @return
+     */
+    protected boolean loadTree(AlignFrame alignFrame)
+    {
+      boolean result = false;
+      String treeFile = applet.getParameter("tree");
+      if (treeFile == null)
+      {
+        treeFile = applet.getParameter("treeFile");
+      }
 
-        do
+      if (treeFile != null)
+      {
+        try
         {
-          if (pdbFileCount > 0)
+          treeFile = setProtocolState(treeFile);
+          NewickFile fin = new NewickFile(treeFile, protocol);
+          fin.parse();
+
+          if (fin.getTree() != null)
           {
-            param = applet.getParameter("PDBFILE" + pdbFileCount);
+            alignFrame.loadTree(fin, treeFile);
+            result = true;
+            dbgMsg("Successfully imported tree.");
           }
           else
           {
-            param = applet.getParameter("PDBFILE");
+            dbgMsg("Tree parameter did not resolve to a valid tree.");
           }
-
-          if (param != null)
-          {
-            PDBEntry pdb = new PDBEntry();
-
-            String seqstring;
-            SequenceI[] seqs = null;
-            String[] chains = null;
-
-            StringTokenizer st = new StringTokenizer(param, " ");
-
-            if (st.countTokens() < 2)
-            {
-              String sequence = applet.getParameter("PDBSEQ");
-              if (sequence != null)
-              {
-                seqs = new SequenceI[]
-                { matcher == null ? (Sequence) newAlignFrame
-                        .getAlignViewport().getAlignment()
-                        .findName(sequence) : matcher.findIdMatch(sequence) };
-              }
-
-            }
-            else
-            {
-              param = st.nextToken();
-              Vector tmp = new Vector();
-              Vector tmp2 = new Vector();
-
-              while (st.hasMoreTokens())
-              {
-                seqstring = st.nextToken();
-                StringTokenizer st2 = new StringTokenizer(seqstring, "=");
-                if (st2.countTokens() > 1)
-                {
-                  // This is the chain
-                  tmp2.addElement(st2.nextToken());
-                  seqstring = st2.nextToken();
-                }
-                tmp.addElement(matcher == null ? (Sequence) newAlignFrame
-                        .getAlignViewport().getAlignment()
-                        .findName(seqstring) : matcher
-                        .findIdMatch(seqstring));
-              }
-
-              seqs = new SequenceI[tmp.size()];
-              tmp.copyInto(seqs);
-              if (tmp2.size() == tmp.size())
-              {
-                chains = new String[tmp2.size()];
-                tmp2.copyInto(chains);
-              }
-            }
-            param = setProtocolState(param);
-
-            if (// !jmolAvailable
-            // &&
-            protocol == AppletFormatAdapter.CLASSLOADER
-                    && !useXtrnalSviewer)
-            {
-              // Re: JAL-357 : the bug isn't a problem if we are using an
-              // external viewer!
-              // TODO: verify this Re:
-              // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
-              // This exception preserves the current behaviour where, even if
-              // the local pdb file was identified in the class loader
-              protocol = AppletFormatAdapter.URL; // this is probably NOT
-              // CORRECT!
-              param = addProtocol(param); //
-            }
-
-            pdb.setFile(param);
-
-            if (seqs != null)
-            {
-              for (int i = 0; i < seqs.length; i++)
-              {
-                if (seqs[i] != null)
-                {
-                  ((Sequence) seqs[i]).addPDBId(pdb);
-                  StructureSelectionManager.getStructureSelectionManager(
-                          applet).registerPDBEntry(pdb);
-                }
-                else
-                {
-                  if (JalviewLite.debug)
-                  {
-                    // this may not really be a problem but we give a warning
-                    // anyway
-                    System.err
-                            .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
-                                    + i + ")");
-                  }
-                }
-              }
-
-              if (!alignPdbStructures)
-              {
-                newAlignFrame.newStructureView(applet, pdb, seqs, chains,
-                        protocol);
-              }
-              else
-              {
-                pdbs.addElement(new Object[]
-                { pdb, seqs, chains, new String(protocol) });
-              }
-            }
-          }
-
-          pdbFileCount++;
-        } while (param != null || pdbFileCount < 10);
-        if (pdbs.size() > 0)
+        } catch (Exception ex)
         {
-          SequenceI[][] seqs = new SequenceI[pdbs.size()][];
-          PDBEntry[] pdb = new PDBEntry[pdbs.size()];
-          String[][] chains = new String[pdbs.size()][];
-          String[] protocols = new String[pdbs.size()];
-          for (int pdbsi = 0, pdbsiSize = pdbs.size(); pdbsi < pdbsiSize; pdbsi++)
-          {
-            Object[] o = (Object[]) pdbs.elementAt(pdbsi);
-            pdb[pdbsi] = (PDBEntry) o[0];
-            seqs[pdbsi] = (SequenceI[]) o[1];
-            chains[pdbsi] = (String[]) o[2];
-            protocols[pdbsi] = (String) o[3];
-          }
-          newAlignFrame.alignedStructureView(applet, pdb, seqs, chains,
-                  protocols);
-
+          ex.printStackTrace();
         }
       }
-      else
-      {
-        fileFound = false;
-        applet.remove(launcher);
-        applet.repaint();
-      }
-      callInitCallback();
+      return result;
     }
 
     /**
@@ -2246,48 +2361,88 @@ public class JalviewLite extends Applet implements
       }
     }
 
-    String addProtocol(String file)
+    /**
+     * If the file is not already in URL format, tries to locate it by resolving
+     * as a URL.
+     * 
+     * @param file
+     * @return
+     */
+    String addProtocol(final String file)
     {
       if (file.indexOf("://") == -1)
       {
-        String fl = applet.resolveUrlForLocalOrAbsolute(file,
+        /*
+         * Try relative to document base
+         */
+        String url = applet.resolveUrlForLocalOrAbsolute(file,
                 getDocumentBase());
-        try
+        if (urlExists(url))
         {
-          if (new java.net.URL(fl).openStream() != null)
+          if (debug)
           {
-            if (debug)
-            {
-              System.err.println("Prepended document base for resource: '"
-                      + file + "'");
-            }
-            return fl;
+            System.err.println("Prepended document base for resource: '"
+                    + file + "'");
           }
-        } catch (Exception x)
-        {
+          return url;
         }
-        ;
-        fl = applet.resolveUrlForLocalOrAbsolute(file, getCodeBase());
-        try
+
+        /*
+         * Try relative to codebase
+         */
+        url = applet.resolveUrlForLocalOrAbsolute(file, getCodeBase());
+        if (urlExists(url))
         {
-          if (new java.net.URL(fl).openStream() != null)
+          if (debug)
           {
-            if (debug)
-            {
-              System.err.println("Prepended codebase for resource: '"
-                      + file + "'");
-            }
-            return fl;
+            System.err.println("Prepended codebase for resource: '" + file
+                    + "'");
           }
-        } catch (Exception x)
-        {
+          return url;
         }
-        ;
-
       }
 
+      /*
+       * Not resolved, leave unchanged
+       */
       return file;
     }
+
+    /**
+     * Returns true if an input stream can be opened on the specified URL, else
+     * false.
+     * 
+     * @param url
+     * @return
+     */
+    private boolean urlExists(String url)
+    {
+      InputStream is = null;
+      try
+      {
+        is = new URL(url).openStream();
+        if (is != null)
+        {
+          return true;
+        }
+      } catch (Exception x)
+      {
+        // ignore
+      } finally
+      {
+        if (is != null)
+        {
+          try
+          {
+            is.close();
+          } catch (IOException e)
+          {
+            // ignore
+          }
+        }
+      }
+      return false;
+    }
   }
 
   /**
@@ -2566,7 +2721,7 @@ public class JalviewLite extends Applet implements
     {
       return def;
     }
-    if (stn.toLowerCase().equals("true"))
+    if (stn.toLowerCase().equals(TRUE))
     {
       return true;
     }
index d06b0f2..7f7142f 100644 (file)
  */
 package jalview.commands;
 
-import jalview.datamodel.*;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+
+import java.util.List;
 
 public class ChangeCaseCommand implements CommandI
 {
@@ -36,10 +39,10 @@ public class ChangeCaseCommand implements CommandI
 
   SequenceI[] seqs;
 
-  int[][] regions;
+  List<int[]> regions;
 
   public ChangeCaseCommand(String description, SequenceI[] seqs,
-          int[][] regions, int caseChange)
+          List<int[]> regions, int caseChange)
   {
     this.description = description;
     this.seqs = seqs;
@@ -73,21 +76,21 @@ public class ChangeCaseCommand implements CommandI
     String sequence;
     int start, end;
     char nextChar;
-    for (int r = 0; r < regions.length; r++)
+    for (int[] r : regions)
     {
-      start = regions[r][0];
+      start = r[0];
       for (int s = 0; s < seqs.length; s++)
       {
         sequence = seqs[s].getSequenceAsString();
         StringBuffer newSeq = new StringBuffer();
 
-        if (regions[r][1] > sequence.length())
+        if (r[1] > sequence.length())
         {
           end = sequence.length();
         }
         else
         {
-          end = regions[r][1];
+          end = r[1];
         }
 
         if (start > 0)
index db837db..2d3a8d8 100644 (file)
@@ -26,11 +26,16 @@ import jalview.datamodel.Annotation;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.util.ReverseListIterator;
+import jalview.util.StringUtils;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Map;
 
 /**
  * 
@@ -58,7 +63,55 @@ public class EditCommand implements CommandI
 {
   public enum Action
   {
-    INSERT_GAP, DELETE_GAP, CUT, PASTE, REPLACE, INSERT_NUC
+    INSERT_GAP
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return DELETE_GAP;
+      }
+    },
+    DELETE_GAP
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return INSERT_GAP;
+      }
+    },
+    CUT
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return PASTE;
+      }
+    },
+    PASTE
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return CUT;
+      }
+    },
+    REPLACE
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return REPLACE;
+      }
+    },
+    INSERT_NUC
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return null;
+      }
+    };
+    public abstract Action getUndoAction();
   };
 
   private List<Edit> edits = new ArrayList<Edit>();
@@ -110,13 +163,88 @@ public class EditCommand implements CommandI
   }
 
   /**
-   * Add the given edit command to the stored list of commands.
+   * Add the given edit command to the stored list of commands. If simply
+   * expanding the range of the last command added, then modify it instead of
+   * adding a new command.
    * 
    * @param e
    */
-  protected void addEdit(Edit e)
+  public void addEdit(Edit e)
   {
-    edits.add(e);
+    if (!expandEdit(edits, e))
+    {
+      edits.add(e);
+    }
+  }
+
+  /**
+   * Returns true if the new edit is incorporated by updating (expanding the
+   * range of) the last edit on the list, else false. We can 'expand' the last
+   * edit if the new one is the same action, on the same sequences, and acts on
+   * a contiguous range. This is the case where a mouse drag generates a series
+   * of contiguous gap insertions or deletions.
+   * 
+   * @param edits
+   * @param e
+   * @return
+   */
+  protected static boolean expandEdit(List<Edit> edits, Edit e)
+  {
+    if (edits == null || edits.isEmpty())
+    {
+      return false;
+    }
+    Edit lastEdit = edits.get(edits.size() - 1);
+    Action action = e.command;
+    if (lastEdit.command != action)
+    {
+      return false;
+    }
+
+    /*
+     * Both commands must act on the same sequences - compare the underlying
+     * dataset sequences, rather than the aligned sequences, which change as
+     * they are edited.
+     */
+    if (lastEdit.seqs.length != e.seqs.length)
+    {
+      return false;
+    }
+    for (int i = 0; i < e.seqs.length; i++)
+    {
+      if (lastEdit.seqs[i].getDatasetSequence() != e.seqs[i]
+              .getDatasetSequence())
+      {
+        return false;
+      }
+    }
+
+    /**
+     * Check a contiguous edit; either
+     * <ul>
+     * <li>a new Insert <n> positions to the right of the last <insert n>, or</li>
+     * <li>a new Delete <n> gaps which is <n> positions to the left of the last
+     * delete.</li>
+     * </ul>
+     */
+    boolean contiguous = (action == Action.INSERT_GAP && e.position == lastEdit.position
+            + lastEdit.number)
+            || (action == Action.DELETE_GAP && e.position + e.number == lastEdit.position);
+    if (contiguous)
+    {
+      /*
+       * We are just expanding the range of the last edit. For delete gap, also
+       * moving the start position left.
+       */
+      lastEdit.number += e.number;
+      lastEdit.seqs = e.seqs;
+      if (action == Action.DELETE_GAP)
+      {
+        lastEdit.position--;
+      }
+      return true;
+    }
+    return false;
   }
 
   /**
@@ -209,7 +337,32 @@ public class EditCommand implements CommandI
       edit.fullAlignmentHeight = true;
     }
 
-    edits.add(edit);
+    addEdit(edit);
+
+    if (performEdit)
+    {
+      performEdit(edit, views);
+    }
+  }
+
+  /**
+   * Overloaded method that accepts an Edit object with additional parameters.
+   * 
+   * @param edit
+   * @param al
+   * @param performEdit
+   * @param views
+   */
+  final public void appendEdit(Edit edit, AlignmentI al,
+          boolean performEdit, AlignmentI[] views)
+  {
+    if (al.getHeight() == edit.seqs.length)
+    {
+      edit.al = al;
+      edit.fullAlignmentHeight = true;
+    }
+
+    addEdit(edit);
 
     if (performEdit)
     {
@@ -223,7 +376,7 @@ public class EditCommand implements CommandI
    * @param commandIndex
    * @param views
    */
-  final void performEdit(int commandIndex, AlignmentI[] views)
+  public final void performEdit(int commandIndex, AlignmentI[] views)
   {
     ListIterator<Edit> iterator = edits.listIterator(commandIndex);
     while (iterator.hasNext())
@@ -239,7 +392,7 @@ public class EditCommand implements CommandI
    * @param edit
    * @param views
    */
-  protected void performEdit(Edit edit, AlignmentI[] views)
+  protected static void performEdit(Edit edit, AlignmentI[] views)
   {
     switch (edit.command)
     {
@@ -316,13 +469,13 @@ public class EditCommand implements CommandI
    * 
    * @param command
    */
-  final private void insertGap(Edit command)
+  final private static void insertGap(Edit command)
   {
 
     for (int s = 0; s < command.seqs.length; s++)
     {
-      command.seqs[s].insertCharAt(command.position, command.number,
-              command.gapChar);
+      command.seqs[s].insertCharAt(command.position,
+              command.number, command.gapChar);
       // System.out.println("pos: "+command.position+" number: "+command.number);
     }
 
@@ -348,7 +501,7 @@ public class EditCommand implements CommandI
    * 
    * @param command
    */
-  final private void deleteGap(Edit command)
+  final static private void deleteGap(Edit command)
   {
     for (int s = 0; s < command.seqs.length; s++)
     {
@@ -366,7 +519,7 @@ public class EditCommand implements CommandI
    * @param command
    * @param views
    */
-  void cut(Edit command, AlignmentI[] views)
+  static void cut(Edit command, AlignmentI[] views)
   {
     boolean seqDeleted = false;
     command.string = new char[command.seqs.length][];
@@ -430,7 +583,7 @@ public class EditCommand implements CommandI
    * @param command
    * @param views
    */
-  void paste(Edit command, AlignmentI[] views)
+  static void paste(Edit command, AlignmentI[] views)
   {
     StringBuffer tmp;
     boolean newDSNeeded;
@@ -446,7 +599,7 @@ public class EditCommand implements CommandI
       if (command.seqs[i].getLength() < 1)
       {
         // ie this sequence was deleted, we need to
-        // read it to the alignment
+        // readd it to the alignment
         if (command.alIndex[i] < command.al.getHeight())
         {
           List<SequenceI> sequences;
@@ -548,7 +701,7 @@ public class EditCommand implements CommandI
     command.string = null;
   }
 
-  void replace(Edit command)
+  static void replace(Edit command)
   {
     StringBuffer tmp;
     String oldstring;
@@ -625,7 +778,7 @@ public class EditCommand implements CommandI
     }
   }
 
-  final void adjustAnnotations(Edit command, boolean insert,
+  final static void adjustAnnotations(Edit command, boolean insert,
           boolean modifyVisibility, AlignmentI[] views)
   {
     AlignmentAnnotation[] annotations = null;
@@ -949,7 +1102,7 @@ public class EditCommand implements CommandI
     }
   }
 
-  final void adjustFeatures(Edit command, int index, int i, int j,
+  final static void adjustFeatures(Edit command, int index, int i, int j,
           boolean insert)
   {
     SequenceI seq = command.seqs[index];
@@ -1027,7 +1180,112 @@ public class EditCommand implements CommandI
 
   }
 
-  class Edit
+  /**
+   * Returns the list of edit commands wrapped by this object.
+   * 
+   * @return
+   */
+  public List<Edit> getEdits()
+  {
+    return this.edits;
+  }
+
+  /**
+   * Returns a map whose keys are the dataset sequences, and values their
+   * aligned sequences before the command edit list was applied. The aligned
+   * sequences are copies, which may be updated without affecting the originals.
+   * 
+   * The command holds references to the aligned sequences (after editing). If
+   * the command is an 'undo',then the prior state is simply the aligned state.
+   * Otherwise, we have to derive the prior state by working backwards through
+   * the edit list to infer the aligned sequences before editing.
+   * 
+   * Note: an alternative solution would be to cache the 'before' state of each
+   * edit, but this would be expensive in space in the common case that the
+   * original is never needed (edits are not mirrored).
+   * 
+   * @return
+   * @throws IllegalStateException
+   *           on detecting an edit command of a type that can't be unwound
+   */
+  public Map<SequenceI, SequenceI> priorState(boolean forUndo)
+  {
+    Map<SequenceI, SequenceI> result = new HashMap<SequenceI, SequenceI>();
+    if (getEdits() == null)
+    {
+      return result;
+    }
+    if (forUndo)
+    {
+      for (Edit e : getEdits())
+      {
+        for (SequenceI seq : e.getSequences())
+        {
+          SequenceI ds = seq.getDatasetSequence();
+          SequenceI preEdit = result.get(ds);
+          if (preEdit == null)
+          {
+            preEdit = new Sequence("", seq.getSequenceAsString());
+            preEdit.setDatasetSequence(ds);
+            result.put(ds, preEdit);
+          }
+        }
+      }
+      return result;
+    }
+
+    /*
+     * Work backwards through the edit list, deriving the sequences before each
+     * was applied. The final result is the sequence set before any edits.
+     */
+    Iterator<Edit> edits = new ReverseListIterator<Edit>(getEdits());
+    while (edits.hasNext())
+    {
+      Edit oldEdit = edits.next();
+      Action action = oldEdit.getAction();
+      int position = oldEdit.getPosition();
+      int number = oldEdit.getNumber();
+      final char gap = oldEdit.getGapCharacter();
+      for (SequenceI seq : oldEdit.getSequences())
+      {
+        SequenceI ds = seq.getDatasetSequence();
+        SequenceI preEdit = result.get(ds);
+        if (preEdit == null)
+        {
+          preEdit = new Sequence("", seq.getSequenceAsString());
+          preEdit.setDatasetSequence(ds);
+          result.put(ds, preEdit);
+        }
+        /*
+         * 'Undo' this edit action on the sequence (updating the value in the
+         * map).
+         */
+        if (ds != null)
+        {
+          if (action == Action.DELETE_GAP)
+          {
+            preEdit.setSequence(new String(StringUtils.insertCharAt(
+                    preEdit.getSequence(), position,
+                    number, gap)));
+          }
+          else if (action == Action.INSERT_GAP)
+          {
+            preEdit.setSequence(new String(StringUtils.deleteChars(
+                    preEdit.getSequence(), position, position + number)));
+          }
+          else
+          {
+            System.err.println("Can't undo edit action " + action);
+            // throw new IllegalStateException("Can't undo edit action " +
+            // action);
+          }
+        }
+      }
+    }
+    return result;
+  }
+
+  public class Edit
   {
     public SequenceI[] oldds;
 
@@ -1053,7 +1311,7 @@ public class EditCommand implements CommandI
 
     char gapChar;
 
-    Edit(Action command, SequenceI[] seqs, int position, int number,
+    public Edit(Action command, SequenceI[] seqs, int position, int number,
             char gapChar)
     {
       this.command = command;
@@ -1099,5 +1357,49 @@ public class EditCommand implements CommandI
 
       fullAlignmentHeight = (al.getHeight() == seqs.length);
     }
+
+    public SequenceI[] getSequences()
+    {
+      return seqs;
+    }
+
+    public int getPosition()
+    {
+      return position;
+    }
+
+    public Action getAction()
+    {
+      return command;
+    }
+
+    public int getNumber()
+    {
+      return number;
+    }
+
+    public char getGapCharacter()
+    {
+      return gapChar;
+    }
+  }
+
+  /**
+   * Returns an iterator over the list of edit commands which traverses the list
+   * either forwards or backwards.
+   * 
+   * @param forwards
+   * @return
+   */
+  public Iterator<Edit> getEditIterator(boolean forwards)
+  {
+    if (forwards)
+    {
+      return getEdits().iterator();
+    }
+    else
+    {
+      return new ReverseListIterator<Edit>(getEdits());
+    }
   }
 }
index bc72406..5758e19 100644 (file)
  */
 package jalview.commands;
 
-import jalview.analysis.*;
-import jalview.datamodel.*;
+import jalview.analysis.AlignmentSorter;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
 
+/**
+ * An undoable command to reorder the sequences in an alignment.
+ * 
+ * @author gmcarstairs
+ *
+ */
 public class OrderCommand implements CommandI
 {
   String description;
 
+  /*
+   * The sequence order before sorting (target order for an undo)
+   */
   SequenceI[] seqs;
 
+  /*
+   * The sequence order specified by this command
+   */
   SequenceI[] seqs2;
 
+  /*
+   * The alignment the command acts on
+   */
   AlignmentI al;
 
+  /**
+   * Constructor given the 'undo' sequence order, and the (already) sorted
+   * alignment.
+   * 
+   * @param description
+   *          a text label for the 'undo' menu option
+   * @param seqs
+   *          the sequence order for undo
+   * @param al
+   *          the alignment as ordered by this command
+   */
   public OrderCommand(String description, SequenceI[] seqs, AlignmentI al)
   {
     this.description = description;
@@ -61,4 +88,15 @@ public class OrderCommand implements CommandI
   {
     AlignmentSorter.setOrder(al, seqs);
   }
+
+  /**
+   * Returns the sequence order used to sort, or before sorting if undo=true.
+   * 
+   * @param undo
+   * @return
+   */
+  public SequenceI[] getSequenceOrder(boolean undo)
+  {
+    return undo ? seqs : seqs2;
+  }
 }
index 2d74590..972b6ab 100644 (file)
@@ -179,13 +179,7 @@ public class AlignViewController implements AlignViewControllerI
       int tfeat = 0;
       if (sq != null)
       {
-        SequenceI dsq = sq.getDatasetSequence();
-        while (dsq.getDatasetSequence() != null)
-        {
-          dsq = dsq.getDatasetSequence();
-        }
-        ;
-        SequenceFeature[] sf = dsq.getSequenceFeatures();
+        SequenceFeature[] sf = sq.getSequenceFeatures();
         if (sf != null)
         {
           int ist = sq.findIndex(sq.getStart());
diff --git a/src/jalview/datamodel/AlignedCodon.java b/src/jalview/datamodel/AlignedCodon.java
new file mode 100644 (file)
index 0000000..0daa3fb
--- /dev/null
@@ -0,0 +1,86 @@
+package jalview.datamodel;
+
+/**
+ * Holds the aligned column positions (base 0) for one codon in a nucleotide
+ * sequence, and (optionally) its peptide translation. The object is immutable
+ * once created.
+ * 
+ * Example: in "G-AT-C-GA" the aligned codons are (0, 2, 3) and (5, 7, 8).
+ * 
+ * @author gmcarstairs
+ *
+ */
+public final class AlignedCodon
+{
+  public final int pos1;
+
+  public final int pos2;
+
+  public final int pos3;
+
+  public final String product;
+
+  public AlignedCodon(int i, int j, int k)
+  {
+    this(i, j, k, null);
+  }
+
+  public AlignedCodon(int i, int j, int k, String prod)
+  {
+    pos1 = i;
+    pos2 = j;
+    pos3 = k;
+    product = prod;
+  }
+
+  /**
+   * Returns the column position for the given base (1, 2, 3).
+   * 
+   * @param base
+   * @return
+   * @throws IllegalArgumentException
+   *           if an argument value other than 1, 2 or 3 is supplied
+   */
+  public int getBaseColumn(int base)
+  {
+    if (base < 1 || base > 3)
+    {
+      throw new IllegalArgumentException(Integer.toString(base));
+    }
+    return base == 1 ? pos1 : (base == 2 ? pos2 : pos3);
+  }
+
+  /**
+   * Two aligned codons are equal if all their base positions are the same. We
+   * don't care about the protein product. This test is required for correct
+   * alignment of translated gapped dna alignments (the same codon positions in
+   * different sequences occupy the same column in the translated alignment).
+   */
+  @Override
+  public boolean equals(Object o)
+  {
+    /*
+     * Equality with null value required for consistency with
+     * Dna.compareCodonPos
+     */
+    if (o == null)
+    {
+      return true;
+    }
+    if (!(o instanceof AlignedCodon))
+    {
+      return false;
+    }
+    AlignedCodon ac = (AlignedCodon) o;
+    return (pos1 == ac.pos1 && pos2 == ac.pos2 && pos3 == ac.pos3);
+  }
+
+  @Override
+  public String toString()
+  {
+    StringBuilder sb = new StringBuilder();
+    sb.append("[").append(pos1).append(", ").append(pos2).append(", ")
+            .append(pos3).append("]");
+    return sb.toString();
+  }
+}
index 55df1af..1f5d827 100644 (file)
  */
 package jalview.datamodel;
 
-import java.util.Enumeration;
-import java.util.Vector;
-
 import jalview.util.MapList;
+import jalview.util.MappingUtils;
 
 /**
  * Stores mapping between the columns of a protein alignment and a DNA alignment
  * and a list of individual codon to amino acid mappings between sequences.
  */
-
 public class AlignedCodonFrame
 {
-  /**
-   * array of nucleotide positions for aligned codons at column of aligned
-   * proteins.
-   */
-  public int[][] codons = null;
-
-  /**
-   * width of protein sequence alignement implicit assertion that codons.length
-   * >= aaWidth
-   */
-  public int aaWidth = 0;
 
-  /**
-   * initialise codon frame with a nominal alignment width
-   * 
-   * @param aWidth
+  /*
+   * tied array of na Sequence objects.
    */
-  public AlignedCodonFrame(int aWidth)
-  {
-    if (aWidth <= 0)
-    {
-      codons = null;
-      return;
-    }
-    codons = new int[aWidth][];
-    for (int res = 0; res < aWidth; res++)
-      codons[res] = null;
-  }
+  private SequenceI[] dnaSeqs = null;
 
-  /**
-   * ensure that codons array is at least as wide as aslen residues
-   * 
-   * @param aslen
-   * @return (possibly newly expanded) codon array
+  /*
+   * tied array of Mappings to protein sequence Objects and SequenceI[]
+   * aaSeqs=null; MapLists where each maps from the corresponding dnaSeqs
+   * element to corresponding aaSeqs element
    */
-  public int[][] checkCodonFrameWidth(int aslen)
-  {
-    if (codons.length <= aslen + 1)
-    {
-      // probably never have to do this ?
-      int[][] c = new int[codons.length + 10][];
-      for (int i = 0; i < codons.length; i++)
-      {
-        c[i] = codons[i];
-        codons[i] = null;
-      }
-      codons = c;
-    }
-    return codons;
-  }
+  private Mapping[] dnaToProt = null;
 
   /**
-   * @return width of aligned translated amino acid residues
+   * Constructor
    */
-  public int getaaWidth()
+  public AlignedCodonFrame()
   {
-    return aaWidth;
   }
 
   /**
-   * TODO: not an ideal solution - we reference the aligned amino acid sequences
-   * in order to make insertions on them Better would be dnaAlignment and
-   * aaAlignment reference....
-   */
-  Vector a_aaSeqs = new Vector();
-
-  /**
-   * increase aaWidth by one and insert a new aligned codon position space at
-   * aspos.
-   * 
-   * @param aspos
-   */
-  public void insertAAGap(int aspos, char gapCharacter)
-  {
-    // this aa appears before the aligned codons at aspos - so shift them in
-    // each pair of mapped sequences
-    aaWidth++;
-    if (a_aaSeqs != null)
-    {
-      // we actually have to modify the aligned sequences here, so use the
-      // a_aaSeqs vector
-      Enumeration sq = a_aaSeqs.elements();
-      while (sq.hasMoreElements())
-      {
-        ((SequenceI) sq.nextElement()).insertCharAt(aspos, gapCharacter);
-      }
-    }
-    checkCodonFrameWidth(aspos);
-    if (aspos < aaWidth)
-    {
-      aaWidth++;
-      System.arraycopy(codons, aspos, codons, aspos + 1, codons.length
-              - aspos - 1);
-      codons[aspos] = null; // clear so new codon position can be marked.
-    }
-  }
-
-  public void setAaWidth(int aapos)
-  {
-    aaWidth = aapos;
-  }
-
-  /**
-   * tied array of na Sequence objects.
-   */
-  SequenceI[] dnaSeqs = null;
-
-  /**
-   * tied array of Mappings to protein sequence Objects and SequenceI[]
-   * aaSeqs=null; MapLists where eac maps from the corresponding dnaSeqs element
-   * to corresponding aaSeqs element
-   */
-  Mapping[] dnaToProt = null;
-
-  /**
-   * add a mapping between the dataset sequences for the associated dna and
+   * Adds a mapping between the dataset sequences for the associated dna and
    * protein sequence objects
    * 
    * @param dnaseq
@@ -179,7 +82,6 @@ public class AlignedCodonFrame
     // aaseq.transferAnnotation(dnaseq, new Mapping(map.getInverse()));
     mp.to = (aaseq.getDatasetSequence() == null) ? aaseq : aaseq
             .getDatasetSequence();
-    a_aaSeqs.addElement(aaseq);
     dnaToProt[nlen] = mp;
   }
 
@@ -191,7 +93,9 @@ public class AlignedCodonFrame
   public SequenceI[] getAaSeqs()
   {
     if (dnaToProt == null)
+    {
       return null;
+    }
     SequenceI[] sqs = new SequenceI[dnaToProt.length];
     for (int sz = 0; sz < dnaToProt.length; sz++)
     {
@@ -203,7 +107,9 @@ public class AlignedCodonFrame
   public MapList[] getdnaToProt()
   {
     if (dnaToProt == null)
+    {
       return null;
+    }
     MapList[] sqs = new MapList[dnaToProt.length];
     for (int sz = 0; sz < dnaToProt.length; sz++)
     {
@@ -218,9 +124,37 @@ public class AlignedCodonFrame
   }
 
   /**
+   * Returns the first mapping found which is to or from the given sequence, or
+   * null.
+   * 
+   * @param seq
+   * @return
+   */
+  public Mapping getMappingForSequence(SequenceI seq)
+  {
+    if (dnaSeqs == null)
+    {
+      return null;
+    }
+    SequenceI seqDs = seq.getDatasetSequence();
+    seqDs = seqDs != null ? seqDs : seq;
+
+    for (int ds = 0; ds < dnaSeqs.length; ds++)
+    {
+      if (dnaSeqs[ds] == seqDs || dnaToProt[ds].to == seqDs)
+      {
+        return dnaToProt[ds];
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the corresponding aligned or dataset aa sequence for given dna
+   * sequence, null if not found.
    * 
    * @param sequenceRef
-   * @return null or corresponding aaSeq entry for dnaSeq entry
+   * @return
    */
   public SequenceI getAaForDnaSeq(SequenceI dnaSeqRef)
   {
@@ -232,7 +166,9 @@ public class AlignedCodonFrame
     for (int ds = 0; ds < dnaSeqs.length; ds++)
     {
       if (dnaSeqs[ds] == dnaSeqRef || dnaSeqs[ds] == dnads)
+      {
         return dnaToProt[ds].to;
+      }
     }
     return null;
   }
@@ -252,7 +188,9 @@ public class AlignedCodonFrame
     for (int as = 0; as < dnaToProt.length; as++)
     {
       if (dnaToProt[as].to == aaSeqRef || dnaToProt[as].to == aads)
+      {
         return dnaSeqs[as];
+      }
     }
     return null;
   }
@@ -319,4 +257,170 @@ public class AlignedCodonFrame
       }
     }
   }
+
+  /**
+   * Returns the DNA codon positions (base 1) for the given position (base 1) in
+   * a mapped protein sequence, or null if no mapping is found.
+   * 
+   * Intended for use in aligning cDNA to match aligned protein. Only the first
+   * mapping found is returned, so not suitable for use if multiple protein
+   * sequences are mapped to the same cDNA (but aligning cDNA as protein is
+   * ill-defined for this case anyway).
+   * 
+   * @param seq
+   *          the DNA dataset sequence
+   * @param aaPos
+   *          residue position (base 1) in a protein sequence
+   * @return
+   */
+  public int[] getDnaPosition(SequenceI seq, int aaPos)
+  {
+    /*
+     * Adapted from markMappedRegion().
+     */
+    MapList ml = null;
+    for (int i = 0; i < dnaToProt.length; i++)
+    {
+      if (dnaSeqs[i] == seq)
+      {
+        ml = getdnaToProt()[i];
+        break;
+      }
+    }
+    return ml == null ? null : ml.locateInFrom(aaPos, aaPos);
+  }
+
+  /**
+   * Convenience method to return the first aligned sequence in the given
+   * alignment whose dataset has a mapping with the given dataset sequence.
+   * 
+   * @param seq
+   * 
+   * @param al
+   * @return
+   */
+  public SequenceI findAlignedSequence(SequenceI seq, AlignmentI al)
+  {
+    /*
+     * Search mapped protein ('to') sequences first.
+     */
+    if (this.dnaToProt != null)
+    {
+      for (int i = 0; i < dnaToProt.length; i++)
+      {
+        if (this.dnaSeqs[i] == seq)
+        {
+          for (SequenceI sourceAligned : al.getSequences())
+          {
+            if (this.dnaToProt[i].to == sourceAligned.getDatasetSequence())
+            {
+              return sourceAligned;
+            }
+          }
+        }
+      }
+    }
+
+    /*
+     * Then try mapped dna sequences.
+     */
+    if (this.dnaToProt != null)
+    {
+      for (int i = 0; i < dnaToProt.length; i++)
+      {
+        if (this.dnaToProt[i].to == seq)
+        {
+          for (SequenceI sourceAligned : al.getSequences())
+          {
+            if (this.dnaSeqs[i] == sourceAligned.getDatasetSequence())
+            {
+              return sourceAligned;
+            }
+          }
+        }
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Returns the region in the 'mappedFrom' sequence's dataset that is mapped to
+   * position 'pos' (base 1) in the 'mappedTo' sequence's dataset. The region is
+   * a set of start/end position pairs.
+   * 
+   * @param mappedFrom
+   * @param mappedTo
+   * @param pos
+   * @return
+   */
+  public int[] getMappedRegion(SequenceI mappedFrom, SequenceI mappedTo,
+          int pos)
+  {
+    SequenceI targetDs = mappedFrom.getDatasetSequence() == null ? mappedFrom
+            : mappedFrom.getDatasetSequence();
+    SequenceI sourceDs = mappedTo.getDatasetSequence() == null ? mappedTo
+            : mappedTo.getDatasetSequence();
+    if (targetDs == null || sourceDs == null || dnaToProt == null)
+    {
+      return null;
+    }
+    for (int mi = 0; mi < dnaToProt.length; mi++)
+    {
+      if (dnaSeqs[mi] == targetDs && dnaToProt[mi].to == sourceDs)
+      {
+        int[] codon = dnaToProt[mi].map.locateInFrom(pos, pos);
+        if (codon != null) {
+          return codon;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Returns the DNA codon for the given position (base 1) in a mapped protein
+   * sequence, or null if no mapping is found.
+   * 
+   * @param protein
+   *          the peptide dataset sequence
+   * @param aaPos
+   *          residue position (base 1) in the peptide sequence
+   * @return
+   */
+  public char[] getMappedCodon(SequenceI protein, int aaPos)
+  {
+    if (dnaToProt == null)
+    {
+      return null;
+    }
+    MapList ml = null;
+    char[] dnaSeq = null;
+    for (int i = 0; i < dnaToProt.length; i++)
+    {
+      if (dnaToProt[i].to == protein)
+      {
+        ml = getdnaToProt()[i];
+        dnaSeq = dnaSeqs[i].getSequence();
+        break;
+      }
+    }
+    if (ml == null)
+    {
+      return null;
+    }
+    int[] codonPos = ml.locateInFrom(aaPos, aaPos);
+    if (codonPos == null)
+    {
+      return null;
+    }
+
+    /*
+     * Read off the mapped nucleotides (converting to position base 0)
+     */
+    codonPos = MappingUtils.flattenRanges(codonPos);
+    return new char[]
+    { dnaSeq[codonPos[0] - 1], dnaSeq[codonPos[1] - 1],
+        dnaSeq[codonPos[2] - 1] };
+  }
 }
index ace4f30..482df7f 100755 (executable)
  */
 package jalview.datamodel;
 
+import jalview.analysis.AlignmentUtils;
+import jalview.io.FastaFile;
 import jalview.util.MessageManager;
 
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.Vector;
 
 /**
@@ -62,6 +67,8 @@ public class Alignment implements AlignmentI
 
   public Hashtable alignmentProperties;
 
+  private Set<AlignedCodonFrame> codonFrameList = new LinkedHashSet<AlignedCodonFrame>();
+
   private void initAlignment(SequenceI[] seqs)
   {
     int i = 0;
@@ -86,6 +93,27 @@ public class Alignment implements AlignmentI
   }
 
   /**
+   * Make a 'copy' alignment - sequences have new copies of features and
+   * annotations, but share the original dataset sequences.
+   */
+  public Alignment(AlignmentI al)
+  {
+    SequenceI[] seqs = al.getSequencesArray();
+    for (int i = 0; i < seqs.length; i++)
+    {
+      seqs[i] = new Sequence(seqs[i]);
+    }
+
+    /*
+     * Share the same dataset sequence mappings (if any). TODO: find a better
+     * place for these to live (alignment dataset?).
+     */
+    this.codonFrameList = ((Alignment) al).codonFrameList;
+
+    initAlignment(seqs);
+  }
+
+  /**
    * Make an alignment from an array of Sequences.
    * 
    * @param sequences
@@ -123,11 +151,6 @@ public class Alignment implements AlignmentI
     // this(compactAlignment.refCigars);
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
   @Override
   public List<SequenceI> getSequences()
   {
@@ -157,6 +180,17 @@ public class Alignment implements AlignmentI
   }
 
   /**
+   * Returns a map of lists of sequences keyed by sequence name.
+   * 
+   * @return
+   */
+  @Override
+  public Map<String, List<SequenceI>> getSequencesByName()
+  {
+    return AlignmentUtils.getSequencesByName(this);
+  }
+
+  /**
    * DOCUMENT ME!
    * 
    * @param i
@@ -226,10 +260,9 @@ public class Alignment implements AlignmentI
   @Override
   public void setSequenceAt(int i, SequenceI snew)
   {
-    SequenceI oldseq = getSequenceAt(i);
-    deleteSequence(i);
     synchronized (sequences)
     {
+      deleteSequence(i);
       sequences.set(i, snew);
     }
   }
@@ -298,8 +331,8 @@ public class Alignment implements AlignmentI
       synchronized (sequences)
       {
         sequences.remove(i);
+        hiddenSequences.adjustHeightSequenceDeleted(i);
       }
-      hiddenSequences.adjustHeightSequenceDeleted(i);
     }
   }
 
@@ -343,14 +376,14 @@ public class Alignment implements AlignmentI
       for (int i = 0; i < gSize; i++)
       {
         SequenceGroup sg = groups.get(i);
-        if (sg == null || sg.getSequences(null) == null)
+        if (sg == null || sg.getSequences() == null)
         {
           this.deleteGroup(sg);
           gSize--;
           continue;
         }
 
-        if (sg.getSequences(null).contains(s))
+        if (sg.getSequences().contains(s))
         {
           temp.add(sg);
         }
@@ -720,6 +753,28 @@ public class Alignment implements AlignmentI
     return true;
   }
 
+  /**
+   * Delete all annotations, including auto-calculated if the flag is set true.
+   * Returns true if at least one annotation was deleted, else false.
+   * 
+   * @param includingAutoCalculated
+   * @return
+   */
+  @Override
+  public boolean deleteAllAnnotations(boolean includingAutoCalculated)
+  {
+    boolean result = false;
+    for (AlignmentAnnotation alan : getAlignmentAnnotation())
+    {
+      if (!alan.autoCalculated || includingAutoCalculated)
+      {
+        deleteAnnotation(alan);
+        result = true;
+      }
+    }
+    return result;
+  }
+
   /*
    * (non-Javadoc)
    * 
@@ -1205,8 +1260,6 @@ public class Alignment implements AlignmentI
     return alignmentProperties;
   }
 
-  AlignedCodonFrame[] codonFrameList = null;
-
   /*
    * (non-Javadoc)
    * 
@@ -1217,31 +1270,10 @@ public class Alignment implements AlignmentI
   @Override
   public void addCodonFrame(AlignedCodonFrame codons)
   {
-    if (codons == null)
+    if (codons != null)
     {
-      return;
-    }
-    if (codonFrameList == null)
-    {
-      codonFrameList = new AlignedCodonFrame[]
-      { codons };
-      return;
+      codonFrameList.add(codons);
     }
-    AlignedCodonFrame[] t = new AlignedCodonFrame[codonFrameList.length + 1];
-    System.arraycopy(codonFrameList, 0, t, 0, codonFrameList.length);
-    t[codonFrameList.length] = codons;
-    codonFrameList = t;
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.datamodel.AlignmentI#getCodonFrame(int)
-   */
-  @Override
-  public AlignedCodonFrame getCodonFrame(int index)
-  {
-    return codonFrameList[index];
   }
 
   /*
@@ -1251,36 +1283,42 @@ public class Alignment implements AlignmentI
    * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI)
    */
   @Override
-  public AlignedCodonFrame[] getCodonFrame(SequenceI seq)
+  public List<AlignedCodonFrame> getCodonFrame(SequenceI seq)
   {
-    if (seq == null || codonFrameList == null)
+    if (seq == null)
     {
       return null;
     }
-    Vector cframes = new Vector();
-    for (int f = 0; f < codonFrameList.length; f++)
+    List<AlignedCodonFrame> cframes = new ArrayList<AlignedCodonFrame>();
+    for (AlignedCodonFrame acf : codonFrameList)
     {
-      if (codonFrameList[f].involvesSequence(seq))
+      if (acf.involvesSequence(seq))
       {
-        cframes.addElement(codonFrameList[f]);
+        cframes.add(acf);
       }
     }
-    if (cframes.size() == 0)
-    {
-      return null;
-    }
-    AlignedCodonFrame[] cfr = new AlignedCodonFrame[cframes.size()];
-    cframes.copyInto(cfr);
-    return cfr;
+    return cframes;
   }
 
-  /*
-   * (non-Javadoc)
+  /**
+   * Sets the codon frame mappings (replacing any existing mappings).
+   * 
+   * @see jalview.datamodel.AlignmentI#setCodonFrames()
+   */
+  @Override
+  public void setCodonFrames(Set<AlignedCodonFrame> acfs)
+  {
+    this.codonFrameList = acfs;
+  }
+
+  /**
+   * Returns the set of codon frame mappings. Any changes to the returned set
+   * will affect the alignment.
    * 
    * @see jalview.datamodel.AlignmentI#getCodonFrames()
    */
   @Override
-  public AlignedCodonFrame[] getCodonFrames()
+  public Set<AlignedCodonFrame> getCodonFrames()
   {
     return codonFrameList;
   }
@@ -1298,26 +1336,7 @@ public class Alignment implements AlignmentI
     {
       return false;
     }
-    boolean removed = false;
-    int i = 0, iSize = codonFrameList.length;
-    while (i < iSize)
-    {
-      if (codonFrameList[i] == codons)
-      {
-        removed = true;
-        if (i + 1 < iSize)
-        {
-          System.arraycopy(codonFrameList, i + 1, codonFrameList, i, iSize
-                  - i - 1);
-        }
-        iSize--;
-      }
-      else
-      {
-        i++;
-      }
-    }
-    return removed;
+    return codonFrameList.remove(codons);
   }
 
   @Override
@@ -1362,11 +1381,9 @@ public class Alignment implements AlignmentI
     {
       addAnnotation(alan[a]);
     }
-    AlignedCodonFrame[] acod = toappend.getCodonFrames();
-    for (int a = 0; acod != null && a < acod.length; a++)
-    {
-      this.addCodonFrame(acod[a]);
-    }
+
+    this.codonFrameList.addAll(toappend.getCodonFrames());
+
     List<SequenceGroup> sg = toappend.getGroups();
     if (sg != null)
     {
@@ -1627,4 +1644,91 @@ public class Alignment implements AlignmentI
   {
     return dataset;
   }
+
+  /**
+   * Align this alignment like the given (mapped) one.
+   */
+  @Override
+  public int alignAs(AlignmentI al)
+  {
+    /*
+     * Currently retains unmapped gaps (in introns), regaps mapped regions
+     * (exons)
+     */
+    return alignAs(al, false, true);
+  }
+
+  /**
+   * Align this alignment 'the same as' the given one. Mapped sequences only are
+   * realigned. If both of the same type (nucleotide/protein) then align both
+   * identically. If this is nucleotide and the other is protein, make 3 gaps
+   * for each gap in the protein sequences. If this is protein and the other is
+   * nucleotide, insert a gap for each 3 gaps (or part thereof) between
+   * nucleotide bases. Does nothing if alignment of protein from cDNA is
+   * requested (not yet implemented).
+   * 
+   * Parameters control whether gaps in exon (mapped) and intron (unmapped)
+   * regions are preserved. Gaps that connect introns to exons are treated
+   * conservatively, i.e. only preserved if both intron and exon gaps are
+   * preserved.
+   * 
+   * @param al
+   * @param preserveMappedGaps
+   *          if true, gaps within and between mapped codons are preserved
+   * @param preserveUnmappedGaps
+   *          if true, gaps within and between unmapped codons are preserved
+   */
+//  @Override
+  public int alignAs(AlignmentI al, boolean preserveMappedGaps,
+          boolean preserveUnmappedGaps)
+  {
+    // TODO should this method signature be the one in the interface?
+    int count = 0;
+    boolean thisIsNucleotide = this.isNucleotide();
+    boolean thatIsProtein = !al.isNucleotide();
+    if (!thatIsProtein && !thisIsNucleotide)
+    {
+      return AlignmentUtils.alignProteinAsDna(this, al);
+    }
+
+    char thisGapChar = this.getGapCharacter();
+    String gap = thisIsNucleotide && thatIsProtein ? String
+            .valueOf(new char[]
+            { thisGapChar, thisGapChar, thisGapChar }) : String
+            .valueOf(thisGapChar);
+
+    /*
+     * Get mappings from 'that' alignment's sequences to this.
+     */
+    for (SequenceI alignTo : getSequences())
+    {
+      count += AlignmentUtils.alignSequenceAs(alignTo, al, gap, preserveMappedGaps,
+              preserveUnmappedGaps) ? 1 : 0;
+    }
+    return count;
+  }
+
+  /**
+   * Returns the alignment in Fasta format. Behaviour of this method is not
+   * guaranteed between versions.
+   */
+  @Override
+  public String toString()
+  {
+    return new FastaFile().print(getSequencesArray());
+  }
+
+  /**
+   * Returns the set of distinct sequence names. No ordering is guaranteed.
+   */
+  @Override
+  public Set<String> getSequenceNames()
+  {
+    Set<String> names = new HashSet<String>();
+    for (SequenceI seq : getSequences())
+    {
+      names.add(seq.getName());
+    }
+    return names;
+  }
 }
index 09d9ee0..1bbe81e 100755 (executable)
@@ -27,9 +27,8 @@ import jalview.analysis.WUSSParseException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
 
@@ -41,6 +40,15 @@ import java.util.Map.Entry;
  */
 public class AlignmentAnnotation
 {
+  /*
+   * Identifers for different types of profile data
+   */
+  public static final int SEQUENCE_PROFILE = 0;
+
+  public static final int STRUCTURE_PROFILE = 1;
+
+  public static final int CDNA_PROFILE = 2;
+
   /**
    * If true, this annotations is calculated every edit, eg consensus, quality
    * or conservation graphs
@@ -155,7 +163,7 @@ public class AlignmentAnnotation
   /**
    * map of positions in the associated annotation
    */
-  public java.util.Hashtable<Integer, Annotation> sequenceMapping;
+  private Map<Integer, Annotation> sequenceMapping;
 
   /** DOCUMENT ME!! */
   public float graphMin;
@@ -499,6 +507,7 @@ public class AlignmentAnnotation
               : annotations[index + offset].secondaryStructure);
     }
 
+    @Override
     public String toString()
     {
       char[] string = new char[max - offset];
@@ -710,12 +719,13 @@ public class AlignmentAnnotation
       if (annotation.sequenceMapping != null)
       {
         Integer p = null;
-        sequenceMapping = new Hashtable();
-        Enumeration pos = annotation.sequenceMapping.keys();
-        while (pos.hasMoreElements())
+        sequenceMapping = new HashMap<Integer, Annotation>();
+        Iterator<Integer> pos = annotation.sequenceMapping.keySet()
+                .iterator();
+        while (pos.hasNext())
         {
           // could optimise this!
-          p = (Integer) pos.nextElement();
+          p = pos.next();
           Annotation a = annotation.sequenceMapping.get(p);
           if (a == null)
           {
@@ -789,11 +799,11 @@ public class AlignmentAnnotation
       int epos = sequenceRef.findPosition(endRes);
       if (sequenceMapping != null)
       {
-        Hashtable newmapping = new Hashtable();
-        Enumeration e = sequenceMapping.keys();
-        while (e.hasMoreElements())
+        Map<Integer, Annotation> newmapping = new HashMap<Integer, Annotation>();
+        Iterator<Integer> e = sequenceMapping.keySet().iterator();
+        while (e.hasNext())
         {
-          Integer pos = (Integer) e.nextElement();
+          Integer pos = e.next();
           if (pos.intValue() >= spos && pos.intValue() <= epos)
           {
             newmapping.put(pos, sequenceMapping.get(pos));
@@ -836,9 +846,10 @@ public class AlignmentAnnotation
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public String toString()
   {
-    StringBuffer buffer = new StringBuffer();
+    StringBuilder buffer = new StringBuilder(256);
 
     for (int i = 0; i < annotations.length; i++)
     {
@@ -911,7 +922,7 @@ public class AlignmentAnnotation
     {
       return;
     }
-    sequenceMapping = new java.util.Hashtable();
+    sequenceMapping = new HashMap<Integer, Annotation>();
 
     int seqPos;
 
@@ -1223,7 +1234,7 @@ public class AlignmentAnnotation
             .getTo() == sq.getDatasetSequence()) : false;
 
     // TODO build a better annotation element map and get rid of annotations[]
-    Hashtable<Integer, Annotation> mapForsq = new Hashtable();
+    Map<Integer, Annotation> mapForsq = new HashMap<Integer, Annotation>();
     if (sequenceMapping != null)
     {
       if (sp2sq != null)
@@ -1275,7 +1286,8 @@ public class AlignmentAnnotation
   {
     if (mapping != null)
     {
-      Hashtable<Integer, Annotation> old = sequenceMapping, remap = new Hashtable<Integer, Annotation>();
+      Map<Integer, Annotation> old = sequenceMapping;
+      Map<Integer, Annotation> remap = new HashMap<Integer, Annotation>();
       int index = -1;
       for (int mp[] : mapping)
       {
index ea4dad4..67e5743 100755 (executable)
@@ -5,16 +5,16 @@
  * 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 
+ * modify it under the terms of the GNU General 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.
+ * PURPOSE.  See the GNU General License for more details.
  * 
- * You should have received a copy of the GNU General Public License
+ * You should have received a copy of the GNU General License
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
@@ -23,6 +23,7 @@ package jalview.datamodel;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Data structure to hold and manipulate a multiple sequence alignment
@@ -34,15 +35,16 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * @return Number of sequences in alignment
    */
-  public int getHeight();
+  int getHeight();
 
   /**
+   * 
    * Calculates the maximum width of the alignment, including gaps.
    * 
    * @return Greatest sequence length within alignment.
    */
   @Override
-  public int getWidth();
+  int getWidth();
 
   /**
    * Calculates if this set of sequences (visible and invisible) are all the
@@ -50,7 +52,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * @return true if all sequences in alignment are the same length
    */
-  public boolean isAligned();
+  boolean isAligned();
 
   /**
    * Calculates if this set of sequences is all the same length
@@ -59,7 +61,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    *          optionally exclude hidden sequences from test
    * @return true if all (or just visible) sequences are the same length
    */
-  public boolean isAligned(boolean includeHidden);
+  boolean isAligned(boolean includeHidden);
 
   /**
    * Gets sequences as a Synchronized collection
@@ -67,14 +69,14 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @return All sequences in alignment.
    */
   @Override
-  public List<SequenceI> getSequences();
+  List<SequenceI> getSequences();
 
   /**
    * Gets sequences as a SequenceI[]
    * 
    * @return All sequences in alignment.
    */
-  public SequenceI[] getSequencesArray();
+  SequenceI[] getSequencesArray();
 
   /**
    * Find a specific sequence in this alignment.
@@ -84,7 +86,14 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * @return SequenceI at given index.
    */
-  public SequenceI getSequenceAt(int i);
+  SequenceI getSequenceAt(int i);
+
+  /**
+   * Returns a map of lists of sequences keyed by sequence name.
+   * 
+   * @return
+   */
+  Map<String, List<SequenceI>> getSequencesByName();
 
   /**
    * Add a new sequence to this alignment.
@@ -92,7 +101,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param seq
    *          New sequence will be added at end of alignment.
    */
-  public void addSequence(SequenceI seq);
+  void addSequence(SequenceI seq);
 
   /**
    * Used to set a particular index of the alignment with the given sequence.
@@ -102,7 +111,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param seq
    *          New sequence to be inserted.
    */
-  public void setSequenceAt(int i, SequenceI seq);
+  void setSequenceAt(int i, SequenceI seq);
 
   /**
    * Deletes a sequence from the alignment
@@ -110,7 +119,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param s
    *          Sequence to be deleted.
    */
-  public void deleteSequence(SequenceI s);
+  void deleteSequence(SequenceI s);
 
   /**
    * Deletes a sequence from the alignment.
@@ -118,7 +127,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param i
    *          Index of sequence to be deleted.
    */
-  public void deleteSequence(int i);
+  void deleteSequence(int i);
 
   /**
    * Finds sequence in alignment using sequence name as query.
@@ -128,9 +137,9 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * @return Sequence matching query, if found. If not found returns null.
    */
-  public SequenceI findName(String name);
+  SequenceI findName(String name);
 
-  public SequenceI[] findSequenceMatch(String name);
+  SequenceI[] findSequenceMatch(String name);
 
   /**
    * Finds index of a given sequence in the alignment.
@@ -140,7 +149,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * @return Index of sequence within the alignment or -1 if not found
    */
-  public int findIndex(SequenceI s);
+  int findIndex(SequenceI s);
 
   /**
    * Finds group that given sequence is part of.
@@ -151,7 +160,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @return First group found for sequence. WARNING : Sequences may be members
    *         of several groups. This method is incomplete.
    */
-  public SequenceGroup findGroup(SequenceI s);
+  SequenceGroup findGroup(SequenceI s);
 
   /**
    * Finds all groups that a given sequence is part of.
@@ -161,7 +170,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * @return All groups containing given sequence.
    */
-  public SequenceGroup[] findAllGroups(SequenceI s);
+  SequenceGroup[] findAllGroups(SequenceI s);
 
   /**
    * Adds a new SequenceGroup to this alignment.
@@ -169,7 +178,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param sg
    *          New group to be added.
    */
-  public void addGroup(SequenceGroup sg);
+  void addGroup(SequenceGroup sg);
 
   /**
    * Deletes a specific SequenceGroup
@@ -177,19 +186,19 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param g
    *          Group will be deleted from alignment.
    */
-  public void deleteGroup(SequenceGroup g);
+  void deleteGroup(SequenceGroup g);
 
   /**
    * Get all the groups associated with this alignment.
    * 
    * @return All groups as a list.
    */
-  public List<SequenceGroup> getGroups();
+  List<SequenceGroup> getGroups();
 
   /**
    * Deletes all groups from this alignment.
    */
-  public void deleteAllGroups();
+  void deleteAllGroups();
 
   /**
    * Adds a new AlignmentAnnotation to this alignment
@@ -197,7 +206,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @note Care should be taken to ensure that annotation is at least as wide as
    *       the longest sequence in the alignment for rendering purposes.
    */
-  public void addAnnotation(AlignmentAnnotation aa);
+  void addAnnotation(AlignmentAnnotation aa);
 
   /**
    * moves annotation to a specified index in alignment annotation display stack
@@ -207,7 +216,16 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param index
    *          the destination position
    */
-  public void setAnnotationIndex(AlignmentAnnotation aa, int index);
+  void setAnnotationIndex(AlignmentAnnotation aa, int index);
+
+  /**
+   * Delete all annotations, including auto-calculated if the flag is set true.
+   * Returns true if at least one annotation was deleted, else false.
+   * 
+   * @param includingAutoCalculated
+   * @return
+   */
+  boolean deleteAllAnnotations(boolean includingAutoCalculated);
 
   /**
    * Deletes a specific AlignmentAnnotation from the alignment, and removes its
@@ -219,7 +237,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    *          the annotation to delete
    * @return true if annotation was deleted from this alignment.
    */
-  public boolean deleteAnnotation(AlignmentAnnotation aa);
+  boolean deleteAnnotation(AlignmentAnnotation aa);
 
   /**
    * Deletes a specific AlignmentAnnotation from the alignment, and optionally
@@ -235,7 +253,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    *          into the alignment
    * @return true if annotation was deleted from this alignment.
    */
-  public boolean deleteAnnotation(AlignmentAnnotation aa, boolean unhook);
+  boolean deleteAnnotation(AlignmentAnnotation aa, boolean unhook);
 
   /**
    * Get the annotation associated with this alignment (this can be null if no
@@ -244,7 +262,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @return array of AlignmentAnnotation objects
    */
   @Override
-  public AlignmentAnnotation[] getAlignmentAnnotation();
+  AlignmentAnnotation[] getAlignmentAnnotation();
 
   /**
    * Change the gap character used in this alignment to 'gc'
@@ -252,34 +270,34 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param gc
    *          the new gap character.
    */
-  public void setGapCharacter(char gc);
+  void setGapCharacter(char gc);
 
   /**
    * Get the gap character used in this alignment
    * 
    * @return gap character
    */
-  public char getGapCharacter();
+  char getGapCharacter();
 
   /**
    * Test for all nucleotide alignment
    * 
    * @return true if alignment is nucleotide sequence
    */
-  public boolean isNucleotide();
+  boolean isNucleotide();
 
   /**
    * Test if alignment contains RNA structure
    * 
    * @return true if RNA structure AligmnentAnnotation was added to alignment
    */
-  public boolean hasRNAStructure();
+  boolean hasRNAStructure();
 
   /**
    * Set alignment to be a nucleotide sequence
    * 
    */
-  public void setNucleotide(boolean b);
+  void setNucleotide(boolean b);
 
   /**
    * Get the associated dataset for the alignment.
@@ -287,7 +305,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @return Alignment containing dataset sequences or null of this is a
    *         dataset.
    */
-  public Alignment getDataset();
+  Alignment getDataset();
 
   /**
    * Set the associated dataset for the alignment, or create one.
@@ -295,23 +313,23 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param dataset
    *          The dataset alignment or null to construct one.
    */
-  public void setDataset(Alignment dataset);
+  void setDataset(Alignment dataset);
 
   /**
    * pads sequences with gaps (to ensure the set looks like an alignment)
    * 
    * @return boolean true if alignment was modified
    */
-  public boolean padGaps();
+  boolean padGaps();
 
-  public HiddenSequences getHiddenSequences();
+  HiddenSequences getHiddenSequences();
 
   /**
    * Compact representation of alignment
    * 
    * @return CigarArray
    */
-  public CigarArray getCompactAlignment();
+  CigarArray getCompactAlignment();
 
   /**
    * Set an arbitrary key value pair for an alignment. Note: both key and value
@@ -320,7 +338,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param key
    * @param value
    */
-  public void setProperty(Object key, Object value);
+  void setProperty(Object key, Object value);
 
   /**
    * Get a named property from the alignment.
@@ -328,21 +346,21 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param key
    * @return value of property
    */
-  public Object getProperty(Object key);
+  Object getProperty(Object key);
 
   /**
    * Get the property hashtable.
    * 
    * @return hashtable of alignment properties (or null if none are defined)
    */
-  public Hashtable getProperties();
+  Hashtable getProperties();
 
   /**
    * add a reference to a frame of aligned codons for this alignment
    * 
    * @param codons
    */
-  public void addCodonFrame(AlignedCodonFrame codons);
+  void addCodonFrame(AlignedCodonFrame codons);
 
   /**
    * remove a particular codon frame reference from this alignment
@@ -350,27 +368,24 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param codons
    * @return true if codon frame was removed.
    */
-  public boolean removeCodonFrame(AlignedCodonFrame codons);
+  boolean removeCodonFrame(AlignedCodonFrame codons);
 
   /**
    * get all codon frames associated with this alignment
    * 
    * @return
    */
-  public AlignedCodonFrame[] getCodonFrames();
+  Set<AlignedCodonFrame> getCodonFrames();
 
   /**
-   * get a particular codon frame
-   * 
-   * @param index
-   * @return
+   * Set the codon frame mappings (replacing any existing set).
    */
-  public AlignedCodonFrame getCodonFrame(int index);
+  void setCodonFrames(Set<AlignedCodonFrame> acfs);
 
   /**
    * get codon frames involving sequenceI
    */
-  public AlignedCodonFrame[] getCodonFrame(SequenceI seq);
+  List<AlignedCodonFrame> getCodonFrame(SequenceI seq);
 
   /**
    * find sequence with given name in alignment
@@ -382,7 +397,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    *          tried
    * @return matched sequence or null
    */
-  public SequenceI findName(String token, boolean b);
+  SequenceI findName(String token, boolean b);
 
   /**
    * find next sequence with given name in alignment starting after a given
@@ -398,7 +413,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    *          tried
    * @return matched sequence or null
    */
-  public SequenceI findName(SequenceI startAfter, String token, boolean b);
+  SequenceI findName(SequenceI startAfter, String token, boolean b);
 
   /**
    * find first sequence in alignment which is involved in the given search
@@ -407,7 +422,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param results
    * @return -1 or index of sequence in alignment
    */
-  public int findIndex(SearchResults results);
+  int findIndex(SearchResults results);
 
   /**
    * append sequences and annotation from another alignment object to this one.
@@ -420,7 +435,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param toappend
    *          - the alignment to be appended.
    */
-  public void append(AlignmentI toappend);
+  void append(AlignmentI toappend);
 
   /**
    * Justify the sequences to the left or right by deleting and inserting gaps
@@ -430,7 +445,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    *          true if alignment padded to right, false to justify to left
    * @return true if alignment was changed TODO: return undo object
    */
-  public boolean justify(boolean right);
+  boolean justify(boolean right);
 
   /**
    * add given annotation row at given position (0 is start, -1 is end)
@@ -438,7 +453,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param consensus
    * @param i
    */
-  public void addAnnotation(AlignmentAnnotation consensus, int i);
+  void addAnnotation(AlignmentAnnotation consensus, int i);
 
   /**
    * search for or create a specific annotation row on the alignment
@@ -458,7 +473,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * @return existing annotation matching the given attributes
    */
-  public AlignmentAnnotation findOrCreateAnnotation(String name,
+  AlignmentAnnotation findOrCreateAnnotation(String name,
           String calcId, boolean autoCalc, SequenceI seqRef,
           SequenceGroup groupRef);
 
@@ -472,7 +487,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param up
    * @param i
    */
-  public void moveSelectedSequencesByOne(SequenceGroup sg,
+  void moveSelectedSequencesByOne(SequenceGroup sg,
           Map<SequenceI, SequenceCollectionI> map, boolean up);
 
   /**
@@ -481,5 +496,25 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * @param alignmentAnnotation
    */
-  public void validateAnnotation(AlignmentAnnotation alignmentAnnotation);
+  void validateAnnotation(AlignmentAnnotation alignmentAnnotation);
+
+  /**
+   * Align this alignment the same as the given one. If both of the same type
+   * (nucleotide/protein) then align both identically. If this is nucleotide and
+   * the other is protein, make 3 gaps for each gap in the protein sequences. If
+   * this is protein and the other is nucleotide, insert a gap for each 3 gaps
+   * (or part thereof) between nucleotide bases. Returns the number of mapped
+   * sequences that were realigned .
+   * 
+   * @param al
+   * @return
+   */
+  int alignAs(AlignmentI al);
+
+  /**
+   * Returns the set of distinct sequence names in the alignment.
+   * 
+   * @return
+   */
+  Set<String> getSequenceNames();
 }
index 0d2a948..ccc9bfa 100755 (executable)
@@ -20,7 +20,9 @@
  */
 package jalview.datamodel;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 public class AlignmentOrder
 {
@@ -51,7 +53,7 @@ public class AlignmentOrder
 
   private String Name;
 
-  private Vector Order = null;
+  private List<SequenceI> Order = null;
 
   /**
    * Creates a new AlignmentOrder object.
@@ -64,9 +66,8 @@ public class AlignmentOrder
    * AlignmentOrder
    * 
    * @param anOrder
-   *          Vector
    */
-  public AlignmentOrder(Vector anOrder)
+  public AlignmentOrder(List<SequenceI> anOrder)
   {
     Order = anOrder;
   }
@@ -79,11 +80,11 @@ public class AlignmentOrder
    */
   public AlignmentOrder(AlignmentI orderFrom)
   {
-    Order = new Vector();
+    Order = new ArrayList<SequenceI>();
 
-    for (int i = 0, ns = orderFrom.getHeight(); i < ns; i++)
+    for (SequenceI seq : orderFrom.getSequences())
     {
-      Order.addElement(orderFrom.getSequenceAt(i));
+      Order.add(seq);
     }
   }
 
@@ -95,12 +96,7 @@ public class AlignmentOrder
    */
   public AlignmentOrder(SequenceI[] orderFrom)
   {
-    Order = new Vector();
-
-    for (int i = 0, ns = orderFrom.length; i < ns; i++)
-    {
-      Order.addElement(orderFrom[i]);
-    }
+    Order = new ArrayList<SequenceI>(Arrays.asList(orderFrom));
   }
 
   /**
@@ -151,7 +147,7 @@ public class AlignmentOrder
    * @param Order
    *          DOCUMENT ME!
    */
-  public void setOrder(Vector Order)
+  public void setOrder(List<SequenceI> Order)
   {
     this.Order = Order;
   }
@@ -161,7 +157,7 @@ public class AlignmentOrder
    * 
    * @return DOCUMENT ME!
    */
-  public Vector getOrder()
+  public List<SequenceI> getOrder()
   {
     return Order;
   }
@@ -178,7 +174,7 @@ public class AlignmentOrder
     int found = Order.indexOf(oldref);
     if (found > -1)
     {
-      Order.setElementAt(newref, found);
+      Order.set(found, newref);
     }
     return found > -1;
   }
@@ -189,9 +185,14 @@ public class AlignmentOrder
    * @param o
    * @return true if o orders the same sequenceI objects in the same way
    */
-  public boolean equals(AlignmentOrder o)
+  @Override
+  public boolean equals(Object o)
   {
-    return equals(o, true);
+    if (o == null || !(o instanceof AlignmentOrder))
+    {
+      return false;
+    }
+    return equals((AlignmentOrder) o, true);
   }
 
   /**
@@ -223,7 +224,7 @@ public class AlignmentOrder
         {
           for (int i = 0, j = o.Order.size(); i < j; i++)
           {
-            if (Order.elementAt(i) != o.Order.elementAt(i))
+            if (Order.get(i) != o.Order.get(i))
             {
               return false;
             }
@@ -271,7 +272,7 @@ public class AlignmentOrder
       }
       if (Order != null && o.Order != null)
       {
-        Vector c, s;
+        List<SequenceI> c, s;
         if (o.Order.size() > Order.size())
         {
           c = o.Order;
@@ -292,7 +293,7 @@ public class AlignmentOrder
           int last = -1;
           for (int i = 0, j = s.size(); i < j; i++)
           {
-            int pos = c.indexOf(s.elementAt(i)); // JBPNote - optimize by
+            int pos = c.indexOf(s.get(i)); // JBPNote - optimize by
             // incremental position search
             if (pos > last)
             {
index f73fb74..b36a0a5 100644 (file)
@@ -179,7 +179,9 @@ public class AlignmentView
         sgrps[g] = new ScGroup();
         sgrps[g].sg = new SequenceGroup(sg);
         addedgps[g] = false;
-        seqsets.set(g, sg.getSequences(null));
+        // can't set entry 0 in an empty list
+        // seqsets.set(g, sg.getSequences(null));
+        seqsets.add(sg.getSequences());
       }
       // seqsets now contains vectors (should be sets) for each group, so we can
       // track when we've done with the group
index b7bc40e..835d7e9 100644 (file)
@@ -25,8 +25,7 @@ import jalview.viewmodel.annotationfilter.AnnotationFilterParameter;
 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter.SearchableAnnotationField;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Enumeration;
+import java.util.Collections;
 import java.util.List;
 import java.util.Vector;
 
@@ -35,7 +34,7 @@ import java.util.Vector;
  */
 public class ColumnSelection
 {
-  Vector selected = new Vector();
+  Vector<Integer> selected = new Vector<Integer>();
 
   // Vector of int [] {startCol, endCol}
   Vector<int[]> hiddenColumns;
@@ -64,7 +63,7 @@ public class ColumnSelection
   }
 
   /**
-   * removes col from selection
+   * Removes value 'col' from the selection (not the col'th item)
    * 
    * @param col
    *          index of column to be removed
@@ -75,6 +74,8 @@ public class ColumnSelection
 
     if (selected.contains(colInt))
     {
+      // if this ever changes to List.remove(), ensure Integer not int argument
+      // as List.remove(int i) removes the i'th item which is wrong
       selected.removeElement(colInt);
     }
   }
@@ -131,7 +132,7 @@ public class ColumnSelection
    */
   public int columnAt(int i)
   {
-    return ((Integer) selected.elementAt(i)).intValue();
+    return selected.elementAt(i).intValue();
   }
 
   /**
@@ -201,6 +202,7 @@ public class ColumnSelection
 
       if (temp >= start)
       {
+        // if this ever changes to List.set(), swap parameter order!!
         selected.setElementAt(new Integer(temp - change), i);
       }
     }
@@ -258,6 +260,7 @@ public class ColumnSelection
 
       if (temp >= start)
       {
+        // if this ever changes to List.set(), swap parameter order!!
         selected.setElementAt(new Integer(temp - change), i);
       }
     }
@@ -303,13 +306,13 @@ public class ColumnSelection
   {
     if (shiftrecord != null)
     {
-      Vector shifts = shiftrecord.shifts;
+      final List<int[]> shifts = shiftrecord.getShifts();
       if (shifts != null && shifts.size() > 0)
       {
         int shifted = 0;
         for (int i = 0, j = shifts.size(); i < j; i++)
         {
-          int[] sh = (int[]) shifts.elementAt(i);
+          int[] sh = shifts.get(i);
           // compensateForEdit(shifted+sh[0], sh[1]);
           compensateForDelEdits(shifted + sh[0], sh[1]);
           shifted -= sh[1];
@@ -324,16 +327,17 @@ public class ColumnSelection
    * removes intersection of position,length ranges in deletions from the
    * start,end regions marked in intervals.
    * 
-   * @param deletions
+   * @param shifts
    * @param intervals
    * @return
    */
-  private boolean pruneIntervalVector(Vector deletions, Vector intervals)
+  private boolean pruneIntervalVector(final List<int[]> shifts,
+          Vector<int[]> intervals)
   {
     boolean pruned = false;
-    int i = 0, j = intervals.size() - 1, s = 0, t = deletions.size() - 1;
-    int hr[] = (int[]) intervals.elementAt(i);
-    int sr[] = (int[]) deletions.elementAt(s);
+    int i = 0, j = intervals.size() - 1, s = 0, t = shifts.size() - 1;
+    int hr[] = intervals.elementAt(i);
+    int sr[] = shifts.get(s);
     while (i <= j && s <= t)
     {
       boolean trailinghn = hr[1] >= sr[0];
@@ -341,7 +345,7 @@ public class ColumnSelection
       {
         if (i < j)
         {
-          hr = (int[]) intervals.elementAt(++i);
+          hr = intervals.elementAt(++i);
         }
         else
         {
@@ -354,7 +358,7 @@ public class ColumnSelection
       { // leadinghc disjoint or not a deletion
         if (s < t)
         {
-          sr = (int[]) deletions.elementAt(++s);
+          sr = shifts.get(++s);
         }
         else
         {
@@ -374,7 +378,7 @@ public class ColumnSelection
           j--;
           if (i <= j)
           {
-            hr = (int[]) intervals.elementAt(i);
+            hr = intervals.elementAt(i);
           }
           continue;
         }
@@ -400,7 +404,7 @@ public class ColumnSelection
           // sr contained in hr
           if (s < t)
           {
-            sr = (int[]) deletions.elementAt(++s);
+            sr = shifts.get(++s);
           }
           else
           {
@@ -414,15 +418,16 @@ public class ColumnSelection
     // operations.
   }
 
-  private boolean pruneColumnList(Vector deletion, Vector list)
+  private boolean pruneColumnList(final List<int[]> shifts,
+          Vector<Integer> list)
   {
-    int s = 0, t = deletion.size();
-    int[] sr = (int[]) list.elementAt(s++);
+    int s = 0, t = shifts.size();
+    int[] sr = shifts.get(s++);
     boolean pruned = false;
     int i = 0, j = list.size();
     while (i < j && s <= t)
     {
-      int c = ((Integer) list.elementAt(i++)).intValue();
+      int c = list.elementAt(i++).intValue();
       if (sr[0] <= c)
       {
         if (sr[1] + sr[0] >= c)
@@ -434,7 +439,7 @@ public class ColumnSelection
         {
           if (s < t)
           {
-            sr = (int[]) deletion.elementAt(s);
+            sr = shifts.get(s);
           }
           s++;
         }
@@ -453,7 +458,7 @@ public class ColumnSelection
   {
     if (deletions != null)
     {
-      Vector shifts = deletions.shifts;
+      final List<int[]> shifts = deletions.getShifts();
       if (shifts != null && shifts.size() > 0)
       {
         // delete any intervals intersecting.
@@ -485,8 +490,8 @@ public class ColumnSelection
    */
   public List<int[]> getHiddenColumns()
   {
-    return hiddenColumns == null ? Arrays.asList(new int[]
-    {}) : hiddenColumns;
+    return hiddenColumns == null ? Collections.<int[]> emptyList()
+            : hiddenColumns;
   }
 
   /**
@@ -644,7 +649,7 @@ public class ColumnSelection
   {
     if (hiddenColumns == null)
     {
-      hiddenColumns = new Vector();
+      hiddenColumns = new Vector<int[]>();
     }
 
     boolean added = false;
@@ -780,7 +785,7 @@ public class ColumnSelection
     {
       if (copy.selected != null)
       {
-        selected = new Vector();
+        selected = new Vector<Integer>();
         for (int i = 0, j = copy.selected.size(); i < j; i++)
         {
           selected.addElement(copy.selected.elementAt(i));
@@ -788,7 +793,7 @@ public class ColumnSelection
       }
       if (copy.hiddenColumns != null)
       {
-        hiddenColumns = new Vector(copy.hiddenColumns.size());
+        hiddenColumns = new Vector<int[]>(copy.hiddenColumns.size());
         for (int i = 0, j = copy.hiddenColumns.size(); i < j; i++)
         {
           int[] rh, cp;
@@ -885,7 +890,7 @@ public class ColumnSelection
   {
     if (hiddenColumns != null && hiddenColumns.size() > 0)
     {
-      Vector visiblecontigs = new Vector();
+      List<int[]> visiblecontigs = new ArrayList<int[]>();
       List<int[]> regions = getHiddenColumns();
 
       int vstart = start;
@@ -904,7 +909,7 @@ public class ColumnSelection
         }
         if (hideStart > vstart)
         {
-          visiblecontigs.addElement(new int[]
+          visiblecontigs.add(new int[]
           { vstart, hideStart - 1 });
         }
         vstart = hideEnd + 1;
@@ -912,18 +917,18 @@ public class ColumnSelection
 
       if (vstart < end)
       {
-        visiblecontigs.addElement(new int[]
+        visiblecontigs.add(new int[]
         { vstart, end - 1 });
       }
       int[] vcontigs = new int[visiblecontigs.size() * 2];
       for (int i = 0, j = visiblecontigs.size(); i < j; i++)
       {
-        int[] vc = (int[]) visiblecontigs.elementAt(i);
-        visiblecontigs.setElementAt(null, i);
+        int[] vc = visiblecontigs.get(i);
+        visiblecontigs.set(i, null);
         vcontigs[i * 2] = vc[0];
         vcontigs[i * 2 + 1] = vc[1];
       }
-      visiblecontigs.removeAllElements();
+      visiblecontigs.clear();
       return vcontigs;
     }
     else
@@ -970,7 +975,7 @@ public class ColumnSelection
     if (hiddenColumns != null && hiddenColumns.size() > 0)
     {
       // then mangle the alignmentAnnotation annotation array
-      Vector annels = new Vector();
+      Vector<Annotation []> annels = new Vector<Annotation []>();
       Annotation[] els = null;
       List<int[]> regions = getHiddenColumns();
       int blockStart = start, blockEnd = end;
@@ -1026,12 +1031,12 @@ public class ColumnSelection
       {
         return;
       }
-      Enumeration e = annels.elements();
+
       alignmentAnnotation.annotations = new Annotation[w];
       w = 0;
-      while (e.hasMoreElements())
+
+        for (Annotation [] chnk : annels)
       {
-        Annotation[] chnk = (Annotation[]) e.nextElement();
         System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
                 chnk.length);
         w += chnk.length;
@@ -1079,15 +1084,13 @@ public class ColumnSelection
   {
     if (colsel != null && colsel.size() > 0)
     {
-      Enumeration e = colsel.getSelected().elements();
-      while (e.hasMoreElements())
+      for (Integer col : colsel.getSelected())
       {
-        Object eo = e.nextElement();
-        if (hiddenColumns != null && isVisible(((Integer) eo).intValue()))
+        if (hiddenColumns != null && isVisible(col.intValue()))
         {
-          if (!selected.contains(eo))
+          if (!selected.contains(col))
           {
-            selected.addElement(eo);
+            selected.addElement(col);
           }
         }
       }
@@ -1102,22 +1105,20 @@ public class ColumnSelection
    */
   public void setElementsFrom(ColumnSelection colsel)
   {
-    selected = new Vector();
+    selected = new Vector<Integer>();
     if (colsel.selected != null && colsel.selected.size() > 0)
     {
       if (hiddenColumns != null && hiddenColumns.size() > 0)
       {
         // only select visible columns in this columns selection
-        selected = new Vector();
         addElementsFrom(colsel);
       }
       else
       {
         // add everything regardless
-        Enumeration en = colsel.selected.elements();
-        while (en.hasMoreElements())
+        for (Integer col : colsel.getSelected())
         {
-          selected.addElement(en.nextElement());
+          addElement(col);
         }
       }
     }
index bc1d610..0581845 100755 (executable)
@@ -69,12 +69,20 @@ public class DBRefEntry
             (entry.map == null ? null : new Mapping(entry.map)));
   }
 
-  public boolean equals(DBRefEntry entry)
+  @Override
+  public boolean equals(Object o)
   {
+    // TODO should also override hashCode to ensure equal objects have equal
+    // hashcodes
+    if (o == null || !(o instanceof DBRefEntry))
+    {
+      return false;
+    }
+    DBRefEntry entry = (DBRefEntry) o;
     if (entry == this)
+    {
       return true;
-    if (entry == null)
-      return false;
+    }
     if (equalRef(entry)
             && ((map == null && entry.map == null) || (map != null
                     && entry.map != null && map.equals(entry.map))))
@@ -97,7 +105,9 @@ public class DBRefEntry
       return false;
     }
     if (entry == this)
+    {
       return true;
+    }
     if ((source != null && entry.source != null && source
             .equalsIgnoreCase(entry.source))
             && (accessionId != null && entry.accessionId != null && accessionId
@@ -183,4 +193,9 @@ public class DBRefEntry
     return ((source != null) ? source : "") + ":"
             + ((accessionId != null) ? accessionId : "");
   }
+
+  public String toString()
+  {
+    return getSrcAccString();
+  }
 }
index b1824f7..6982594 100755 (executable)
@@ -38,12 +38,15 @@ public class DBRefSource
   /**
    * UNIPROT Entry Name
    */
-  public static String UP_NAME = "UNIPROT_NAME";
+  public static String UP_NAME = "UNIPROT_NAME".toUpperCase();
 
   /**
    * Uniprot Knowledgebase/TrEMBL as served from EMBL protein products.
    */
-  public static final String UNIPROTKB = "UniProtKB/TrEMBL";
+  public static final String UNIPROTKB = "UniProtKB/TrEMBL".toUpperCase();
+  
+  public static final String EMBLCDSProduct = "EMBLCDSProtein"
+          .toUpperCase();
 
   /**
    * PDB Entry Code
@@ -73,7 +76,7 @@ public class DBRefSource
   /**
    * GeneDB ID
    */
-  public static final String GENEDB = "GeneDB";
+  public static final String GENEDB = "GeneDB".toUpperCase();
 
   /**
    * List of databases whose sequences might have coding regions annotated
@@ -85,10 +88,10 @@ public class DBRefSource
   { EMBLCDS, GENEDB };
 
   public static final String[] PROTEINDBS =
-  { UNIPROT, PDB, UNIPROTKB };
+  { UNIPROT, PDB, UNIPROTKB, EMBLCDSProduct };
 
   public static final String[] PROTEINSEQ =
-  { UNIPROT, UNIPROTKB };
+  { UNIPROT, UNIPROTKB, EMBLCDSProduct };
 
   public static final String[] PROTEINSTR =
   { PDB };
diff --git a/src/jalview/datamodel/IncompleteCodonException.java b/src/jalview/datamodel/IncompleteCodonException.java
new file mode 100644 (file)
index 0000000..f716a53
--- /dev/null
@@ -0,0 +1,13 @@
+package jalview.datamodel;
+
+/**
+ * An exception to indicate that less than 3 nucleotide bases are available when
+ * trying to form a codon.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class IncompleteCodonException extends RuntimeException
+{
+
+}
index e307143..559ae4c 100644 (file)
  */
 package jalview.datamodel;
 
-import java.util.Vector;
-
 import jalview.util.MapList;
 
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Vector;
+
 public class Mapping
 {
   /**
+   * An iterator that serves the aligned codon positions (with their protein
+   * products).
+   * 
+   * @author gmcarstairs
+   *
+   */
+  public class AlignedCodonIterator implements Iterator<AlignedCodon>
+  {
+    /*
+     * The gap character used in the aligned sequence
+     */
+    private final char gap;
+
+    /*
+     * The characters of the aligned sequence e.g. "-cGT-ACgTG-"
+     */
+    private final char[] alignedSeq;
+
+    /*
+     * Next position (base 0) in the aligned sequence
+     */
+    private int alignedColumn = 0;
+
+    /*
+     * Count of bases up to and including alignedColumn position
+     */
+    private int alignedBases = 0;
+
+    /*
+     * [start, end] from ranges (base 1)
+     */
+    private Iterator<int[]> fromRanges;
+
+    /*
+     * [start, end] to ranges (base 1)
+     */
+    private Iterator<int[]> toRanges;
+
+    /*
+     * The current [start, end] (base 1) from range
+     */
+    private int[] currentFromRange = null;
+
+    /*
+     * The current [start, end] (base 1) to range
+     */
+    private int[] currentToRange = null;
+
+    /*
+     * The next 'from' position (base 1) to process
+     */
+    private int fromPosition = 0;
+
+    /*
+     * The next 'to' position (base 1) to process
+     */
+    private int toPosition = 0;
+
+    /**
+     * Constructor
+     * 
+     * @param cs
+     *          the aligned sequence characters
+     * @param gapChar
+     */
+    public AlignedCodonIterator(char[] cs, char gapChar)
+    {
+      this.alignedSeq = cs;
+      this.gap = gapChar;
+      fromRanges = map.getFromRanges().iterator();
+      toRanges = map.getToRanges().iterator();
+      if (fromRanges.hasNext())
+      {
+        currentFromRange = fromRanges.next();
+        fromPosition = currentFromRange[0];
+      }
+      if (toRanges.hasNext())
+      {
+        currentToRange = toRanges.next();
+        toPosition = currentToRange[0];
+      }
+    }
+
+    /**
+     * Returns true unless we have already traversed the whole mapping.
+     */
+    @Override
+    public boolean hasNext()
+    {
+      if (fromRanges.hasNext())
+      {
+        return true;
+      }
+      if (currentFromRange == null || fromPosition >= currentFromRange[1])
+      {
+        return false;
+      }
+      return true;
+    }
+
+    /**
+     * Returns the next codon's aligned positions, and translated value.
+     * 
+     * @throws NoSuchElementException
+     *           if hasNext() would have returned false
+     * @throws IncompleteCodonException
+     *           if not enough mapped bases are left to make up a codon
+     */
+    @Override
+    public AlignedCodon next() throws IncompleteCodonException
+    {
+      if (!hasNext())
+      {
+        throw new NoSuchElementException();
+      }
+
+      int[] codon = getNextCodon();
+      int[] alignedCodon = getAlignedCodon(codon);
+
+      String peptide = getPeptide();
+      return new AlignedCodon(alignedCodon[0], alignedCodon[1],
+              alignedCodon[2], peptide);
+    }
+
+    /**
+     * Retrieve the translation as the 'mapped to' position in the mapped to
+     * sequence.
+     * 
+     * @return
+     */
+    private String getPeptide()
+    {
+      // TODO should ideally handle toRatio other than 1 as well...
+      // i.e. code like getNextCodon()
+      if (toPosition <= currentToRange[1]) {
+        char pep = Mapping.this.to.getSequence()[toPosition - 1];
+        toPosition++;
+        return String.valueOf(pep);
+      }
+      if (!toRanges.hasNext())
+      {
+        throw new NoSuchElementException("Ran out of peptide at position "
+                + toPosition);
+      }
+      currentToRange = toRanges.next();
+      toPosition = currentToRange[0];
+      return getPeptide();
+    }
+
+    /**
+     * Get the (base 1) dataset positions for the next codon in the mapping.
+     * 
+     * @throws IncompleteCodonException
+     *           if less than 3 remaining bases are mapped
+     */
+    private int[] getNextCodon()
+    {
+      int[] codon = new int[3];
+      int codonbase = 0;
+
+      while (codonbase < 3)
+      {
+        if (fromPosition <= currentFromRange[1])
+        {
+          /*
+           * Add next position from the current start-end range
+           */
+          codon[codonbase++] = fromPosition++;
+        }
+        else
+        {
+          /*
+           * Move to the next range - if there is one
+           */
+          if (!fromRanges.hasNext())
+          {
+            throw new IncompleteCodonException();
+          }
+          currentFromRange = fromRanges.next();
+          fromPosition = currentFromRange[0];
+        }
+      }
+      return codon;
+    }
+
+    /**
+     * Get the aligned column positions (base 0) for the given sequence
+     * positions (base 1), by counting ungapped characters in the aligned
+     * sequence.
+     * 
+     * @param codon
+     * @return
+     */
+    private int[] getAlignedCodon(int[] codon)
+    {
+      int[] aligned = new int[codon.length];
+      for (int i = 0; i < codon.length; i++)
+      {
+        aligned[i] = getAlignedColumn(codon[i]);
+      }
+      return aligned;
+    }
+
+    /**
+     * Get the aligned column position (base 0) for the given sequence position
+     * (base 1).
+     * 
+     * @param sequencePos
+     * @return
+     */
+    private int getAlignedColumn(int sequencePos)
+    {
+      while (alignedBases < sequencePos
+              && alignedColumn < alignedSeq.length)
+      {
+        if (alignedSeq[alignedColumn++] != gap)
+        {
+          alignedBases++;
+        }
+      }
+      return alignedColumn - 1;
+    }
+
+    @Override
+    public void remove()
+    {
+      // ignore
+    }
+
+  }
+
+  /**
    * Contains the start-end pairs mapping from the associated sequence to the
-   * sequence in the database coordinate system it also takes care of step
-   * difference between coordinate systems
+   * sequence in the database coordinate system. It also takes care of step
+   * difference between coordinate systems.
    */
   MapList map = null;
 
   /**
-   * The seuqence that map maps the associated seuqence to (if any).
+   * The sequence that map maps the associated sequence to (if any).
    */
   SequenceI to = null;
 
@@ -111,19 +345,33 @@ public class Mapping
    * @param other
    * @return
    */
-  public boolean equals(Mapping other)
+  @Override
+  public boolean equals(Object o)
   {
-    if (other == null)
+    // TODO should override Object.hashCode() to ensure that equal objects have
+    // equal hashcodes
+    if (o == null || !(o instanceof Mapping))
+    {
       return false;
+    }
+    Mapping other = (Mapping) o;
     if (other == this)
+    {
       return true;
+    }
     if (other.to != to)
+    {
       return false;
+    }
     if ((map != null && other.map == null)
             || (map == null && other.map != null))
+    {
       return false;
-    if (map.equals(other.map))
+    }
+    if ((map == null && other.map == null) || map.equals(other.map))
+    {
       return true;
+    }
     return false;
   }
 
@@ -251,7 +499,9 @@ public class Mapping
           vf[v].setBegin(frange[i]);
           vf[v].setEnd(frange[i + 1]);
           if (frange.length > 2)
+          {
             vf[v].setDescription(f.getDescription() + "\nPart " + (v + 1));
+          }
         }
         return vf;
       }
@@ -300,14 +550,18 @@ public class Mapping
         from = (map.getToLowest() < from) ? from : map.getToLowest();
         to = (map.getToHighest() > to) ? to : map.getToHighest();
         if (from > to)
+        {
           return null;
+        }
       }
       else
       {
         from = (map.getToHighest() > from) ? from : map.getToHighest();
         to = (map.getToLowest() < to) ? to : map.getToLowest();
         if (from < to)
+        {
           return null;
+        }
       }
       return map.locateInFrom(from, to);
     }
@@ -333,14 +587,18 @@ public class Mapping
         from = (map.getFromLowest() < from) ? from : map.getFromLowest();
         to = (map.getFromHighest() > to) ? to : map.getFromHighest();
         if (from > to)
+        {
           return null;
+        }
       }
       else
       {
         from = (map.getFromHighest() > from) ? from : map.getFromHighest();
         to = (map.getFromLowest() < to) ? to : map.getFromLowest();
         if (from < to)
+        {
           return null;
+        }
       }
       return map.locateInTo(from, to);
     }
@@ -455,4 +713,9 @@ public class Mapping
     super.finalize();
   }
 
+  public Iterator<AlignedCodon> getCodonIterator(SequenceI seq, char gapChar)
+  {
+    return new AlignedCodonIterator(seq.getSequence(), gapChar);
+  }
+
 }
index 07ceeb8..e62e58a 100755 (executable)
  */
 package jalview.datamodel;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Holds a list of search result matches, where each match is a contiguous
+ * stretch of a single sequence.
+ * 
+ * @author gmcarstairs
+ *
+ */
 public class SearchResults
 {
 
-  Match[] matches;
+  private List<Match> matches = new ArrayList<Match>();
+
+  public class Match
+  {
+    SequenceI sequence;
+
+    /**
+     * Start position of match in sequence (base 1)
+     */
+    int start;
+
+    /**
+     * End position (inclusive) (base 1)
+     */
+    int end;
+
+    /**
+     * Constructor
+     * 
+     * @param seq
+     *          a sequence
+     * @param start
+     *          start position of matched range (base 1)
+     * @param end
+     *          end of matched range (inclusive, base 1)
+     */
+    public Match(SequenceI seq, int start, int end)
+    {
+      sequence = seq;
+      this.start = start;
+      this.end = end;
+    }
+
+    public SequenceI getSequence()
+    {
+      return sequence;
+    }
+
+    public int getStart()
+    {
+      return start;
+    }
+
+    public int getEnd()
+    {
+      return end;
+    }
+
+    /**
+     * Returns the string of characters in the matched region.
+     */
+    @Override
+    public String toString()
+    {
+      char[] chars = sequence.getSequence();
+      // convert start/end to base 0 (with bounds check)
+      final int from = Math.max(start - 1, 0);
+      final int to = Math.min(end, chars.length + 1);
+      return String.valueOf(Arrays.copyOfRange(chars, from, to));
+    }
+  }
 
   /**
    * This method replaces the old search results which merely held an alignment
@@ -39,25 +110,7 @@ public class SearchResults
    */
   public void addResult(SequenceI seq, int start, int end)
   {
-    if (matches == null)
-    {
-      matches = new Match[]
-      { new Match(seq, start, end) };
-      return;
-    }
-
-    int mSize = matches.length;
-
-    Match[] tmp = new Match[mSize + 1];
-    int m;
-    for (m = 0; m < mSize; m++)
-    {
-      tmp[m] = matches[m];
-    }
-
-    tmp[m] = new Match(seq, start, end);
-
-    matches = tmp;
+    matches.add(new Match(seq, start, end));
   }
 
   /**
@@ -69,15 +122,11 @@ public class SearchResults
    */
   public boolean involvesSequence(SequenceI sequence)
   {
-    if (matches == null || matches.length == 0)
-    {
-      return false;
-    }
     SequenceI ds = sequence.getDatasetSequence();
-    for (int m = 0; m < matches.length; m++)
+    for (Match m : matches)
     {
-      if (matches[m].sequence != null
-              && (matches[m].sequence == sequence || matches[m].sequence == ds))
+      if (m.sequence != null
+              && (m.sequence == sequence || m.sequence == ds))
       {
         return true;
       }
@@ -92,7 +141,7 @@ public class SearchResults
    */
   public int[] getResults(SequenceI sequence, int start, int end)
   {
-    if (matches == null)
+    if (matches.isEmpty())
     {
       return null;
     }
@@ -101,22 +150,22 @@ public class SearchResults
     int[] tmp = null;
     int resultLength, matchStart = 0, matchEnd = 0;
     boolean mfound;
-    for (int m = 0; m < matches.length; m++)
+    for (Match m : matches)
     {
       mfound = false;
-      if (matches[m].sequence == sequence)
+      if (m.sequence == sequence)
       {
         mfound = true;
         // locate aligned position
-        matchStart = sequence.findIndex(matches[m].start) - 1;
-        matchEnd = sequence.findIndex(matches[m].end) - 1;
+        matchStart = sequence.findIndex(m.start) - 1;
+        matchEnd = sequence.findIndex(m.end) - 1;
       }
-      else if (matches[m].sequence == sequence.getDatasetSequence())
+      else if (m.sequence == sequence.getDatasetSequence())
       {
         mfound = true;
         // locate region in local context
-        matchStart = sequence.findIndex(matches[m].start) - 1;
-        matchEnd = sequence.findIndex(matches[m].end) - 1;
+        matchStart = sequence.findIndex(m.start) - 1;
+        matchEnd = sequence.findIndex(m.end) - 1;
       }
       if (mfound)
       {
@@ -160,37 +209,71 @@ public class SearchResults
 
   public int getSize()
   {
-    return matches == null ? 0 : matches.length;
+    return matches.size();
   }
 
   public SequenceI getResultSequence(int index)
   {
-    return matches[index].sequence;
+    return matches.get(index).sequence;
   }
 
-  public int getResultStart(int index)
+  /**
+   * Returns the start position of the i'th match in the search results.
+   * 
+   * @param i
+   * @return
+   */
+  public int getResultStart(int i)
   {
-    return matches[index].start;
+    return matches.get(i).start;
   }
 
-  public int getResultEnd(int index)
+  /**
+   * Returns the end position of the i'th match in the search results.
+   * 
+   * @param i
+   * @return
+   */
+  public int getResultEnd(int i)
   {
-    return matches[index].end;
+    return matches.get(i).end;
   }
 
-  class Match
+  /**
+   * Returns true if no search result matches are held.
+   * 
+   * @return
+   */
+  public boolean isEmpty()
   {
-    SequenceI sequence;
-
-    int start;
+    return matches.isEmpty();
+  }
 
-    int end;
+  /**
+   * Returns the list of matches.
+   * 
+   * @return
+   */
+  public List<Match> getResults()
+  {
+    return matches;
+  }
 
-    public Match(SequenceI seq, int start, int end)
+  /**
+   * Return the results as a string of characters. Meant for use when the
+   * context ensures that all matches are to regions of the same sequence
+   * (otherwise the result is meaningless).
+   * 
+   * @return
+   */
+  @Override
+  public String toString()
+  {
+    StringBuilder result = new StringBuilder(256);
+    for (Match m : matches)
     {
-      sequence = seq;
-      this.start = start;
-      this.end = end;
+      result.append(m.toString());
     }
+    return result.toString();
   }
 }
index 0267f45..5c1fba5 100755 (executable)
@@ -21,6 +21,7 @@
 package jalview.datamodel;
 
 import jalview.analysis.AlignSeq;
+import jalview.util.StringUtils;
 
 import java.util.ArrayList;
 import java.util.Enumeration;
@@ -326,13 +327,26 @@ public class Sequence implements SequenceI
   }
 
   /**
-   * DOCUMENT ME!
+   * Returns the sequence features (if any), looking first on the sequence, then
+   * on its dataset sequence, and so on until a non-null value is found (or
+   * none). This supports retrieval of sequence features stored on the sequence
+   * (as in the applet) or on the dataset sequence (as in the Desktop version).
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
   public SequenceFeature[] getSequenceFeatures()
   {
-    return sequenceFeatures;
+    SequenceFeature[] features = sequenceFeatures;
+
+    SequenceI seq = this;
+    int count = 0; // failsafe against loop in sequence.datasetsequence...
+    while (features == null && seq.getDatasetSequence() != null
+            && count++ < 10)
+    {
+      seq = seq.getDatasetSequence();
+      features = ((Sequence) seq).sequenceFeatures;
+    }
+    return features;
   }
 
   public void addPDBId(PDBEntry entry)
@@ -726,24 +740,12 @@ public class Sequence implements SequenceI
       return;
     }
 
-    char[] tmp;
-
-    if (j >= sequence.length)
-    {
-      tmp = new char[i];
-      System.arraycopy(sequence, 0, tmp, 0, i);
-      j = sequence.length;
-    }
-    else
-    {
-      tmp = new char[sequence.length - j + i];
-      System.arraycopy(sequence, 0, tmp, 0, i);
-      System.arraycopy(sequence, j, tmp, i, sequence.length - j);
-    }
+    char[] tmp = StringUtils.deleteChars(sequence, i, j);
     boolean createNewDs = false;
-    // TODO: take a look at the new dataset creation validation method below -
-    // this could become time comsuming for large sequences - consider making it
-    // more efficient
+    // TODO: take a (second look) at the dataset creation validation method for
+    // the very large sequence case
+    int eindex = -1, sindex = -1;
+    boolean ecalc = false, scalc = false;
     for (int s = i; s < j; s++)
     {
       if (jalview.schemes.ResidueProperties.aaIndex[sequence[s]] != 23)
@@ -754,7 +756,11 @@ public class Sequence implements SequenceI
         }
         else
         {
-          int sindex = findIndex(start) - 1;
+          if (!scalc)
+          {
+            sindex = findIndex(start) - 1;
+            scalc = true;
+          }
           if (sindex == s)
           {
             // delete characters including start of sequence
@@ -764,7 +770,11 @@ public class Sequence implements SequenceI
           else
           {
             // delete characters after start.
-            int eindex = findIndex(end) - 1;
+            if (!ecalc)
+            {
+              eindex = findIndex(end) - 1;
+              ecalc = true;
+            }
             if (eindex < j)
             {
               // delete characters at end of sequence
@@ -1024,7 +1034,7 @@ public class Sequence implements SequenceI
           AlignmentAnnotation _aa = new AlignmentAnnotation(aa);
           _aa.sequenceRef = datasetSequence;
           _aa.adjustForAlignment(); // uses annotation's own record of
-                                   // sequence-column mapping
+                                    // sequence-column mapping
           datasetSequence.addAlignmentAnnotation(_aa);
         }
       }
@@ -1245,8 +1255,10 @@ public class Sequence implements SequenceI
           String label)
   {
     List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
-    if (this.annotation != null) {
-      for (AlignmentAnnotation ann : annotation) {
+    if (this.annotation != null)
+    {
+      for (AlignmentAnnotation ann : annotation)
+      {
         if (ann.calcId != null && ann.calcId.equals(calcId)
                 && ann.label != null && ann.label.equals(label))
         {
index 48b7f88..28ab82c 100755 (executable)
@@ -20,7 +20,8 @@
  */
 package jalview.datamodel;
 
-import java.util.*;
+import java.util.Hashtable;
+import java.util.Vector;
 
 /**
  * DOCUMENT ME!
@@ -42,7 +43,7 @@ public class SequenceFeature
 
   public Hashtable otherDetails;
 
-  public java.util.Vector links;
+  public Vector<String> links;
 
   // Feature group can be set from a features file
   // as a group of features between STARTGROUP and ENDGROUP markers
@@ -90,7 +91,7 @@ public class SequenceFeature
       }
       if (cpy.links != null && cpy.links.size() > 0)
       {
-        links = new Vector();
+        links = new Vector<String>();
         for (int i = 0, iSize = cpy.links.size(); i < iSize; i++)
         {
           links.addElement(cpy.links.elementAt(i));
@@ -211,7 +212,7 @@ public class SequenceFeature
   {
     if (links == null)
     {
-      links = new java.util.Vector();
+      links = new Vector<String>();
     }
 
     links.insertElementAt(labelLink, 0);
@@ -283,7 +284,9 @@ public class SequenceFeature
     {
       String stat = (String) otherDetails.get("status");
       if (stat != null)
+      {
         return new String(stat);
+      }
     }
     return null;
   }
index 4727e5e..feb714a 100755 (executable)
@@ -1295,6 +1295,9 @@ public class SequenceGroup implements AnnotatedCollectionI
     return false;
   }
 
+  /**
+   * Remove all sequences from the group (leaving other properties unchanged).
+   */
   public void clear()
   {
     synchronized (sequences)
index 3808966..a9a7589 100755 (executable)
@@ -172,8 +172,7 @@ public interface SequenceI
   public String getDescription();
 
   /**
-   * Return the alignment column for a sequence position * Return the alignment
-   * position for a sequence position
+   * Return the alignment column for a sequence position
    * 
    * @param pos
    *          lying from start to end
@@ -220,9 +219,9 @@ public interface SequenceI
    * if necessary and adjusting start and end positions accordingly.
    * 
    * @param i
-   *          first column in range to delete
+   *          first column in range to delete (inclusive)
    * @param j
-   *          last column in range to delete
+   *          last column in range to delete (exclusive)
    */
   public void deleteChars(int i, int j);
 
@@ -238,13 +237,12 @@ public interface SequenceI
 
   /**
    * DOCUMENT ME!
-   * 
-   * @param i
+   * @param position
    *          DOCUMENT ME!
-   * @param c
+   * @param ch
    *          DOCUMENT ME!
    */
-  public void insertCharAt(int i, int length, char c);
+  public void insertCharAt(int position, int count, char ch);
 
   /**
    * DOCUMENT ME!
index 1a24415..fc57b27 100644 (file)
@@ -425,9 +425,13 @@ public class EmblEntry
       { 1, dna.getLength() }, 1, 1));
       // TODO: transform EMBL Database refs to canonical form
       if (dbRefs != null)
+      {
         for (Iterator i = dbRefs.iterator(); i.hasNext(); dna
                 .addDBRef((DBRefEntry) i.next()))
+        {
           ;
+        }
+      }
     }
     try
     {
@@ -440,7 +444,9 @@ public class EmblEntry
           {
             for (Iterator dbr = feature.dbRefs.iterator(); dbr.hasNext(); dna
                     .addDBRef((DBRefEntry) dbr.next()))
+            {
               ;
+            }
           }
         }
         if (FeatureProperties.isCodingFeature(sourceDb, feature.getName()))
@@ -456,7 +462,9 @@ public class EmblEntry
             {
               for (Iterator dbr = feature.dbRefs.iterator(); dbr.hasNext(); dna
                       .addDBRef((DBRefEntry) dbr.next()))
+              {
                 ;
+              }
             }
           }
         }
@@ -582,8 +590,10 @@ public class EmblEntry
       }
     }
     Sequence product = null;
+    DBRefEntry protEMBLCDS = null;
     exon = adjustForPrStart(prstart, exon);
-
+    boolean noProteinDbref=true;
+    
     if (prseq != null && prname != null && prid != null)
     {
       // extract proteins.
@@ -659,8 +669,14 @@ public class EmblEntry
           // { 1prstart, prstart + prseq.length() - 1 }, 3, 1);
           pcdnaref.setMap(new Mapping(mp));
           if (product != null)
+          {
             product.addDBRef(pcdnaref);
-
+            protEMBLCDS = new DBRefEntry(pcdnaref);
+            protEMBLCDS.setSource(DBRefSource.EMBLCDSProduct);
+            product.addDBRef(protEMBLCDS);
+            
+          }     
+          
         }
       }
       // add cds feature to dna seq - this may include the stop codon
@@ -671,18 +687,20 @@ public class EmblEntry
         sf.setEnd(exon[xint + 1]);
         sf.setType(feature.getName());
         sf.setFeatureGroup(sourceDb);
-        sf.setDescription("Exon " + (1 + (int) (xint / 2))
+        sf.setDescription("Exon " + (1 + xint / 2)
                 + " for protein '" + prname + "' EMBLCDS:" + prid);
         sf.setValue(FeatureProperties.EXONPOS, new Integer(1 + xint));
         sf.setValue(FeatureProperties.EXONPRODUCT, prname);
         if (vals != null && vals.size() > 0)
         {
-          Enumeration kv = vals.elements();
+          Enumeration kv = vals.keys();
           while (kv.hasMoreElements())
           {
             Object key = kv.nextElement();
             if (key != null)
+            {
               sf.setValue(key.toString(), vals.get(key));
+            }
           }
         }
         dna.addSequenceFeature(sf);
@@ -712,6 +730,7 @@ public class EmblEntry
                               + ref.getAccessionId());
             }
           }
+          noProteinDbref = false;
         }
         if (product != null)
         {
@@ -734,6 +753,33 @@ public class EmblEntry
         }
         dna.addDBRef(ref);
       }
+      if (noProteinDbref && product != null)
+      {
+        // add protein coding reference to dna sequence so xref matches
+        if (protEMBLCDS == null)
+        {
+          protEMBLCDS = new DBRefEntry();
+          protEMBLCDS.setAccessionId(prid);
+          protEMBLCDS.setSource(DBRefSource.EMBLCDSProduct);
+          protEMBLCDS.setVersion(getVersion());
+          protEMBLCDS
+                  .setMap(new Mapping(product, map.getMap().getInverse()));
+        }
+        product.addDBRef(protEMBLCDS);
+          
+        // Add converse mapping reference
+        if (map != null)
+        {
+          Mapping pmap = new Mapping(product, protEMBLCDS.getMap().getMap()
+                  .getInverse());
+          DBRefEntry ncMap = new DBRefEntry(protEMBLCDS);
+          ncMap.setMap(pmap);
+          if (map.getTo() != null)
+          {
+            dna.addDBRef(ncMap);
+          }
+        }
+      }
     }
   }
 
index 18b04ec..68cc792 100644 (file)
@@ -43,7 +43,6 @@ import java.awt.event.ComponentListener;
 import java.io.File;
 import java.net.URL;
 import java.security.AccessControlException;
-import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Map;
 import java.util.Vector;
@@ -1328,17 +1327,15 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       return;
     }
 
-    String res;
     int index;
     Color col;
     jmolHistory(false);
     // TODO: Switch between nucleotide or aa selection expressions
-    Enumeration en = ResidueProperties.aa3Hash.keys();
-    StringBuffer command = new StringBuffer("select *;color white;");
-    while (en.hasMoreElements())
+    StringBuilder command = new StringBuilder(128);
+    command.append("select *;color white;");
+    for (String res : ResidueProperties.aa3Hash.keySet())
     {
-      res = en.nextElement().toString();
-      index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
+      index = ResidueProperties.aa3Hash.get(res).intValue();
       if (index > 20)
       {
         continue;
index 80b2ab8..55c3c1d 100644 (file)
@@ -23,13 +23,13 @@ package jalview.ext.paradise;
 import jalview.util.MessageManager;
 import jalview.ws.HttpClientUtils;
 
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StringReader;
 import java.net.URL;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 
@@ -37,7 +37,6 @@ import org.apache.http.NameValuePair;
 import org.apache.http.message.BasicNameValuePair;
 import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
-import org.json.simple.JSONStreamAware;
 import org.json.simple.parser.ContentHandler;
 import org.json.simple.parser.ParseException;
 
@@ -138,7 +137,8 @@ public class Annotate3D
     // return processJsonResponseFor(HttpClientUtils.doHttpUrlPost(twoDtoolsURL,
     // vals));
     ArrayList<Reader> readers = new ArrayList<Reader>();
-    readers.add(HttpClientUtils.doHttpUrlPost(twoDtoolsURL, vals));
+    final BufferedReader postResponse = HttpClientUtils.doHttpUrlPost(twoDtoolsURL, vals, 0, 0);
+    readers.add(postResponse);
     return readers.iterator();
 
   }
index fa1b7e2..148f252 100644 (file)
  */
 package jalview.ext.rbvi.chimera;
 
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
 import jalview.datamodel.AlignmentI;
@@ -30,13 +37,6 @@ import jalview.structure.StructureSelectionManager;
 import jalview.util.ColorUtils;
 import jalview.util.Comparison;
 
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
 /**
  * Routines for generating Chimera commands for Jalview/Chimera binding
  * 
@@ -121,6 +121,7 @@ public class ChimeraCommands
         final Map<String, List<int[]>> modelData = colourData.get(model);
         for (String chain : modelData.keySet())
         {
+          boolean hasChain = !"".equals(chain.trim());
           for (int[] range : modelData.get(chain))
           {
             if (!firstPositionForModel)
@@ -135,7 +136,10 @@ public class ChimeraCommands
             {
               sb.append(range[0]).append("-").append(range[1]);
             }
-            sb.append(".").append(chain);
+            if (hasChain)
+            {
+              sb.append(".").append(chain);
+            }
             firstPositionForModel = false;
           }
         }
index 90751e3..9d1ed43 100644 (file)
@@ -39,7 +39,6 @@ import jalview.util.MessageManager;
 
 import java.awt.Color;
 import java.util.ArrayList;
-import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -87,8 +86,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
   private Map<String, String> chainFile;
 
-  private StringBuffer eval = new StringBuffer();
-
   public String fileLoadingError;
 
   /*
@@ -109,6 +106,15 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   private boolean loadedInline;
 
   /**
+   * current set of model filenames loaded
+   */
+  String[] modelFileNames = null;
+
+  String lastMousedOverAtomSpec;
+
+  private List<String> lastReply;
+
+  /**
    * Open a PDB structure file in Chimera and set up mappings from Jalview.
    * 
    * We check if the PDB model id is already loaded in Chimera, if so don't
@@ -173,16 +179,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   }
 
   /**
-   * current set of model filenames loaded
-   */
-  String[] modelFileNames = null;
-
-
-  StringBuffer resetLastRes = new StringBuffer();
-
-  private List<String> lastReply;
-
-  /**
    * Constructor
    * 
    * @param ssm
@@ -693,7 +689,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     }
     AlignmentI alignment = alignmentv.getAlignment();
 
-    for (jalview.structure.StructureMappingcommandSet cpdbbyseq : getColourBySequenceCommands(files, sr, fr, alignment))
+    for (jalview.structure.StructureMappingcommandSet cpdbbyseq : getColourBySequenceCommands(
+            files, sr, fr, alignment))
     {
       for (String command : cpdbbyseq.commands)
       {
@@ -713,10 +710,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
           String[] files, SequenceRenderer sr, FeatureRenderer fr,
           AlignmentI alignment)
   {
-    return ChimeraCommands
-            .getColourBySequenceCommand(getSsm(), files, getSequence(), sr,
-                    fr,
-                    alignment);
+    return ChimeraCommands.getColourBySequenceCommand(getSsm(), files,
+            getSequence(), sr, fr, alignment);
   }
 
   /**
@@ -740,7 +735,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     }
   }
 
-  
+
 
   // End StructureListener
   // //////////////////////////
@@ -843,44 +838,44 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   public abstract SequenceRenderer getSequenceRenderer(
           AlignmentViewPanel alignment);
 
-  // jmol/ssm only
+  /**
+   * Construct and send a command to highlight an atom.
+   * 
+   * <pre>
+   * Done by generating a command like (to 'highlight' position 44)
+   *   ~select #0:43.C;select #0:44.C
+   * Note this removes the selection from the previous position.
+   * </pre>
+   */
   public void highlightAtom(int atomIndex, int pdbResNum, String chain,
           String pdbfile)
   {
     List<ChimeraModel> cms = chimeraMaps.get(pdbfile);
     if (cms != null)
     {
-      int mdlNum = cms.get(0).getModelNumber();
-
-      viewerCommandHistory(false);
-      // viewer.stopListening();
-      if (resetLastRes.length() > 0)
+      StringBuilder sb = new StringBuilder();
+      sb.append(" #" + cms.get(0).getModelNumber());
+      sb.append(":" + pdbResNum);
+      if (!chain.equals(" "))
       {
-        eval.setLength(0);
-        eval.append(resetLastRes.toString() + ";");
+        sb.append("." + chain);
       }
+      String atomSpec = sb.toString();
 
-      eval.append("display "); // +modelNum
-
-      resetLastRes.setLength(0);
-      resetLastRes.append("~display ");
+      StringBuilder command = new StringBuilder(32);
+      if (lastMousedOverAtomSpec != null)
       {
-        eval.append(" #" + (mdlNum));
-        resetLastRes.append(" #" + (mdlNum));
+        command.append("~show " + lastMousedOverAtomSpec + ";");
       }
-      // complete select string
-
-      eval.append(":" + pdbResNum);
-      resetLastRes.append(":" + pdbResNum);
-      if (!chain.equals(" "))
+      viewerCommandHistory(false);
+      command.append("show ").append(atomSpec);
+      String cmd = command.toString();
+      if (cmd.length() > 0)
       {
-        eval.append("." + chain);
-        resetLastRes.append("." + chain);
+        viewer.sendChimeraCommand(cmd, false);
       }
-      
-      viewer.sendChimeraCommand(eval.toString(), false);
       viewerCommandHistory(true);
-      // viewer.startListening();
+      this.lastMousedOverAtomSpec = atomSpec;
     }
   }
 
@@ -1140,20 +1135,17 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       return;
     }
 
-    String res;
     int index;
     Color col;
     // Chimera expects RBG values in the range 0-1
     final double normalise = 255D;
     viewerCommandHistory(false);
     // TODO: Switch between nucleotide or aa selection expressions
-    Enumeration en = ResidueProperties.aa3Hash.keys();
     StringBuilder command = new StringBuilder(128);
     command.append("color white;");
-    while (en.hasMoreElements())
+    for (String res : ResidueProperties.aa3Hash.keySet())
     {
-      res = en.nextElement().toString();
-      index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
+      index = ResidueProperties.aa3Hash.get(res).intValue();
       if (index > 20)
       {
         continue;
index 7e451aa..c7792a5 100644 (file)
  */
 package jalview.gui;
 
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.GridLayout;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.print.PageFormat;
+import java.awt.print.PrinterJob;
+import java.beans.PropertyChangeEvent;
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JEditorPane;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+
 import jalview.analysis.AAFrequency;
 import jalview.analysis.AlignmentSorter;
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.Conservation;
 import jalview.analysis.CrossRef;
-import jalview.analysis.NJTree;
+import jalview.analysis.Dna;
 import jalview.analysis.ParseProperties;
 import jalview.analysis.SequenceIdMatcher;
 import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
+import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.SplitContainerI;
+import jalview.api.ViewStyleI;
 import jalview.api.analysis.ScoreModelI;
 import jalview.bin.Cache;
 import jalview.commands.CommandI;
@@ -53,6 +107,7 @@ import jalview.datamodel.SeqCigar;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.gui.ViewSelectionMenu.ViewSetProvider;
 import jalview.io.AlignmentProperties;
 import jalview.io.AnnotationFile;
 import jalview.io.BioJsHTMLOutput;
@@ -86,58 +141,12 @@ import jalview.schemes.TurnColourScheme;
 import jalview.schemes.UserColourScheme;
 import jalview.schemes.ZappoColourScheme;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.jws1.Discoverer;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.seqfetcher.DbSourceProxy;
 
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.GridLayout;
-import java.awt.Rectangle;
-import java.awt.Toolkit;
-import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.DataFlavor;
-import java.awt.datatransfer.StringSelection;
-import java.awt.datatransfer.Transferable;
-import java.awt.dnd.DnDConstants;
-import java.awt.dnd.DropTargetDragEvent;
-import java.awt.dnd.DropTargetDropEvent;
-import java.awt.dnd.DropTargetEvent;
-import java.awt.dnd.DropTargetListener;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.print.PageFormat;
-import java.awt.print.PrinterJob;
-import java.beans.PropertyChangeEvent;
-import java.io.File;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Vector;
-
-import javax.swing.JButton;
-import javax.swing.JCheckBoxMenuItem;
-import javax.swing.JEditorPane;
-import javax.swing.JInternalFrame;
-import javax.swing.JLabel;
-import javax.swing.JLayeredPane;
-import javax.swing.JMenu;
-import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JProgressBar;
-import javax.swing.JRadioButtonMenuItem;
-import javax.swing.JScrollPane;
-import javax.swing.SwingUtilities;
-
 /**
  * DOCUMENT ME!
  * 
@@ -148,19 +157,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         IProgressIndicator, AlignViewControllerGuiI
 {
 
-  /** DOCUMENT ME!! */
   public static final int DEFAULT_WIDTH = 700;
 
-  /** DOCUMENT ME!! */
   public static final int DEFAULT_HEIGHT = 500;
 
+  /*
+   * The currently displayed panel (selected tabbed view if more than one)
+   */
   public AlignmentPanel alignPanel;
 
   AlignViewport viewport;
 
   public AlignViewControllerI avc;
 
-  Vector alignPanels = new Vector();
+  List<AlignmentPanel> alignPanels = new ArrayList<AlignmentPanel>();
 
   /**
    * Last format used to load or save alignments in this window
@@ -339,7 +349,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     buildSortByAnnotationScoresMenu();
     buildTreeMenu();
     
-    if (viewport.wrapAlignment)
+    if (viewport.getWrapAlignment())
     {
       wrapMenuItem_actionPerformed(null);
     }
@@ -351,6 +361,59 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     addKeyListener();
 
+    final List<AlignmentPanel> selviews = new ArrayList<AlignmentPanel>();
+    final List<AlignmentPanel> origview = new ArrayList<AlignmentPanel>();
+    ViewSelectionMenu vsel = new ViewSelectionMenu("Transfer colours from",
+            new ViewSetProvider()
+            {
+
+              @Override
+              public AlignmentPanel[] getAllAlignmentPanels()
+              {
+                origview.clear();
+                origview.add(alignPanel);
+                return Desktop.getAlignmentPanels(null);
+              }
+            }, selviews, new ItemListener()
+            {
+
+              @Override
+              public void itemStateChanged(ItemEvent e)
+              {
+                if (origview.size() > 0)
+                {
+                  ViewStyleI vs = selviews.get(0).getAlignViewport()
+                          .getViewStyle();
+                  origview.get(0).getAlignViewport().setViewStyle(vs);
+                  AlignViewportI complement = origview.get(0)
+                          .getAlignViewport().getCodingComplement();
+                  if (complement != null)
+                  {
+                    AlignFrame af = Desktop.getAlignFrameFor(complement);
+                    if (complement.isNucleotide())
+                    {
+                      complement.setViewStyle(vs);
+                      vs.setCharWidth(vs.getCharWidth() / 3);
+                    }
+                    else
+                    {
+                      int rw = vs.getCharWidth();
+                      vs.setCharWidth(rw * 3);
+                      complement.setViewStyle(vs);
+                      vs.setCharWidth(rw);
+                    }
+                    af.alignPanel.updateLayout();
+                    af.setMenusForViewport();
+                  }
+                  origview.get(0).updateLayout();
+                  origview.get(0).setSelected(true);
+                  origview.get(0).alignFrame.setMenusForViewport();
+
+                }
+              }
+            });
+    formatMenu.add(vsel);
+
   }
 
   /**
@@ -365,10 +428,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void setFileName(String file, String format)
   {
     fileName = file;
-    currentFileFormat = format;
+    setFileFormat(format);
     reload.setEnabled(true);
   }
 
+  /**
+   * Add a KeyListener with handlers for various KeyPressed and KeyReleased
+   * events
+   */
   void addKeyListener()
   {
     addKeyListener(new KeyAdapter()
@@ -542,7 +609,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           break;
         }
         case KeyEvent.VK_PAGE_UP:
-          if (viewport.wrapAlignment)
+          if (viewport.getWrapAlignment())
           {
             alignPanel.scrollUp(true);
           }
@@ -553,7 +620,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           break;
         case KeyEvent.VK_PAGE_DOWN:
-          if (viewport.wrapAlignment)
+          if (viewport.getWrapAlignment())
           {
             alignPanel.scrollUp(false);
           }
@@ -597,7 +664,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     avc = new jalview.controller.AlignViewController(this, viewport,
             alignPanel);
 
-    alignPanels.addElement(ap);
+    alignPanels.add(ap);
 
     PaintRefresher.Register(ap, ap.av.getSequenceSetId());
 
@@ -640,7 +707,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     expandViews.setEnabled(true);
     gatherViews.setEnabled(true);
     tabbedPane.setVisible(true);
-    AlignmentPanel first = (AlignmentPanel) alignPanels.firstElement();
+    AlignmentPanel first = alignPanels.get(0);
     tabbedPane.addTab(first.av.viewName, first);
     this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
   }
@@ -701,6 +768,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }).start();
   }
 
+  /**
+   * Configure menu items that vary according to whether the alignment is
+   * nucleotide or protein
+   * 
+   * @param nucleotide
+   */
   public void setGUINucleotide(boolean nucleotide)
   {
     showTranslation.setVisible(nucleotide);
@@ -709,16 +782,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     showGroupConservation.setEnabled(!nucleotide);
     rnahelicesColour.setEnabled(nucleotide);
     purinePyrimidineColour.setEnabled(nucleotide);
-    // Remember AlignFrame always starts as protein
-    // if (!nucleotide)
-    // {
-    // showTr
-    // calculateMenu.remove(calculateMenu.getItemCount() - 2);
-    // }
+    showComplementMenuItem.setText(MessageManager
+            .getString(nucleotide ? "label.protein" : "label.nucleotide"));
+    setColourSelected(jalview.bin.Cache.getDefault(
+            nucleotide ? Preferences.DEFAULT_COLOUR_NUC
+                    : Preferences.DEFAULT_COLOUR_PROT, "None"));
   }
 
   /**
-   * set up menus for the currently viewport. This may be called after any
+   * set up menus for the current viewport. This may be called after any
    * operation that affects the data in the current view (selection changed,
    * etc) to update the menus to reflect the new state.
    */
@@ -737,17 +809,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   void setMenusFromViewport(AlignViewport av)
   {
     padGapsMenuitem.setSelected(av.isPadGaps());
-    colourTextMenuItem.setSelected(av.showColourText);
+    colourTextMenuItem.setSelected(av.isShowColourText());
     abovePIDThreshold.setSelected(av.getAbovePIDThreshold());
     conservationMenuItem.setSelected(av.getConservationSelected());
     seqLimits.setSelected(av.getShowJVSuffix());
     idRightAlign.setSelected(av.isRightAlignIds());
-    centreColumnLabelsMenuItem.setState(av.centreColumnLabels);
-    renderGapsMenuItem.setSelected(av.renderGaps);
-    wrapMenuItem.setSelected(av.wrapAlignment);
-    scaleAbove.setVisible(av.wrapAlignment);
-    scaleLeft.setVisible(av.wrapAlignment);
-    scaleRight.setVisible(av.wrapAlignment);
+    centreColumnLabelsMenuItem.setState(av.isCentreColumnLabels());
+    renderGapsMenuItem.setSelected(av.isRenderGaps());
+    wrapMenuItem.setSelected(av.getWrapAlignment());
+    scaleAbove.setVisible(av.getWrapAlignment());
+    scaleLeft.setVisible(av.getWrapAlignment());
+    scaleRight.setVisible(av.getWrapAlignment());
     annotationPanelMenuItem.setState(av.isShowAnnotation());
     /*
      * Show/hide annotations only enabled if annotation panel is shown
@@ -756,8 +828,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     hideAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
     showAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
     hideAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
-    viewBoxesMenuItem.setSelected(av.showBoxes);
-    viewTextMenuItem.setSelected(av.showText);
+    viewBoxesMenuItem.setSelected(av.getShowBoxes());
+    viewTextMenuItem.setSelected(av.getShowText());
     showNonconservedMenuItem.setSelected(av.getShowUnconserved());
     showGroupConsensus.setSelected(av.isShowGroupConsensus());
     showGroupConservation.setSelected(av.isShowGroupConservation());
@@ -769,10 +841,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             .getGlobalColourScheme()));
 
     showSeqFeatures.setSelected(av.isShowSequenceFeatures());
-    hiddenMarkers.setState(av.showHiddenMarkers);
+    hiddenMarkers.setState(av.getShowHiddenMarkers());
     applyToAllGroups.setState(av.getColourAppliesToAllGroups());
-    showNpFeatsMenuitem.setSelected(av.isShowNpFeats());
-    showDbRefsMenuitem.setSelected(av.isShowDbRefs());
+    showNpFeatsMenuitem.setSelected(av.isShowNPFeats());
+    showDbRefsMenuitem.setSelected(av.isShowDBRefs());
     autoCalculate.setSelected(av.autoCalculateConsensus);
     sortByTree.setSelected(av.sortByTree);
     listenToViewSelections.setSelected(av.followSelection);
@@ -862,7 +934,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         public void actionPerformed(ActionEvent e)
         {
           handler.cancelActivity(id);
-          us.setProgressBar(MessageManager.formatMessage("label.cancelled_params", new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
+          us.setProgressBar(MessageManager.formatMessage("label.cancelled_params", new Object[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
         }
       });
       progressPanel.add(cancel, BorderLayout.EAST);
@@ -1033,7 +1105,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
       currentFileFormat = chooser.getSelectedFormat();
-      if (currentFileFormat == null)
+      while (currentFileFormat == null)
       {
         JOptionPane
                 .showInternalMessageDialog(
@@ -1043,8 +1115,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         MessageManager
                                 .getString("label.file_format_not_specified"),
                         JOptionPane.WARNING_MESSAGE);
+        currentFileFormat = chooser.getSelectedFormat();
         value = chooser.showSaveDialog(this);
-        return;
+        if (value != JalviewFileChooser.APPROVE_OPTION)
+        {
+          return;
+        }
       }
 
       fileName = chooser.getSelectedFile().getPath();
@@ -1141,7 +1217,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           this.setTitle(file);
           statusBar.setText(MessageManager.formatMessage(
                   "label.successfully_saved_to_file_in_format",
-                  new String[]
+                  new Object[]
                   { fileName, format }));
         } catch (Exception ex)
         {
@@ -1154,7 +1230,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if (!success)
     {
       JOptionPane.showInternalMessageDialog(this, MessageManager
-              .formatMessage("label.couldnt_save_file", new String[]
+              .formatMessage("label.couldnt_save_file", new Object[]
               { fileName }), MessageManager
               .getString("label.error_saving_file"),
               JOptionPane.WARNING_MESSAGE);
@@ -1216,7 +1292,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               viewport.getAlignment(), omitHidden,
               viewport.getColumnSelection()));
       Desktop.addInternalFrame(cap, MessageManager.formatMessage(
-              "label.alignment_output_command", new String[]
+              "label.alignment_output_command", new Object[]
               { e.getActionCommand() }), 600, 500);
     } catch (OutOfMemoryError oom)
     {
@@ -1362,7 +1438,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             // setClosed(true) is called
             for (int i = 0; i < alignPanels.size(); i++)
             {
-              AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i);
+              AlignmentPanel ap = alignPanels.get(i);
               ap.closePanel();
             }
           }
@@ -1384,22 +1460,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * close alignPanel2 and shuffle tabs appropriately.
+   * Close the specified panel and close up tabs appropriately.
    * 
-   * @param alignPanel2
+   * @param panelToClose
    */
-  public void closeView(AlignmentPanel alignPanel2)
+  public void closeView(AlignmentPanel panelToClose)
   {
     int index = tabbedPane.getSelectedIndex();
-    int closedindex = tabbedPane.indexOfComponent(alignPanel2);
-    alignPanels.removeElement(alignPanel2);
-    // Unnecessary
-    // if (viewport == alignPanel2.av)
-    // {
-    // viewport = null;
-    // }
-    alignPanel2.closePanel();
-    alignPanel2 = null;
+    int closedindex = tabbedPane.indexOfComponent(panelToClose);
+    alignPanels.remove(panelToClose);
+    panelToClose.closePanel();
+    panelToClose = null;
 
     tabbedPane.removeTabAt(closedindex);
     tabbedPane.validate();
@@ -1419,12 +1490,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   void updateEditMenuBar()
   {
 
-    if (viewport.historyList.size() > 0)
+    if (viewport.getHistoryList().size() > 0)
     {
       undoMenuItem.setEnabled(true);
-      CommandI command = viewport.historyList.peek();
+      CommandI command = viewport.getHistoryList().peek();
       undoMenuItem.setText(MessageManager.formatMessage(
-              "label.undo_command", new String[]
+              "label.undo_command", new Object[]
               { command.getDescription() }));
     }
     else
@@ -1433,13 +1504,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       undoMenuItem.setText(MessageManager.getString("action.undo"));
     }
 
-    if (viewport.redoList.size() > 0)
+    if (viewport.getRedoList().size() > 0)
     {
       redoMenuItem.setEnabled(true);
 
-      CommandI command = viewport.redoList.peek();
+      CommandI command = viewport.getRedoList().peek();
       redoMenuItem.setText(MessageManager.formatMessage(
-              "label.redo_command", new String[]
+              "label.redo_command", new Object[]
               { command.getDescription() }));
     }
     else
@@ -1453,8 +1524,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     if (command.getSize() > 0)
     {
-      viewport.historyList.push(command);
-      viewport.redoList.clear();
+      viewport.addToHistoryList(command);
+      viewport.clearRedoList();
       updateEditMenuBar();
       viewport.updateHiddenColumns();
       // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
@@ -1472,11 +1543,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     if (alignPanels != null)
     {
-      Enumeration e = alignPanels.elements();
       AlignmentI[] als = new AlignmentI[alignPanels.size()];
-      for (int i = 0; e.hasMoreElements(); i++)
+      int i = 0;
+      for (AlignmentPanel ap : alignPanels)
       {
-        als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
+        als[i++] = ap.av.getAlignment();
       }
       return als;
     }
@@ -1497,15 +1568,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void undoMenuItem_actionPerformed(ActionEvent e)
   {
-    if (viewport.historyList.empty())
+    if (viewport.getHistoryList().isEmpty())
     {
       return;
     }
-    CommandI command = viewport.historyList.pop();
-    viewport.redoList.push(command);
+    CommandI command = viewport.getHistoryList().pop();
+    viewport.addToRedoList(command);
     command.undoCommand(getViewAlignments());
 
-    AlignViewport originalSource = getOriginatingSource(command);
+    AlignmentViewport originalSource = getOriginatingSource(command);
     updateEditMenuBar();
 
     if (originalSource != null)
@@ -1535,16 +1606,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void redoMenuItem_actionPerformed(ActionEvent e)
   {
-    if (viewport.redoList.size() < 1)
+    if (viewport.getRedoList().size() < 1)
     {
       return;
     }
 
-    CommandI command = viewport.redoList.pop();
-    viewport.historyList.push(command);
+    CommandI command = viewport.getRedoList().pop();
+    viewport.addToHistoryList(command);
     command.doCommand(getViewAlignments());
 
-    AlignViewport originalSource = getOriginatingSource(command);
+    AlignmentViewport originalSource = getOriginatingSource(command);
     updateEditMenuBar();
 
     if (originalSource != null)
@@ -1566,9 +1637,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
-  AlignViewport getOriginatingSource(CommandI command)
+  AlignmentViewport getOriginatingSource(CommandI command)
   {
-    AlignViewport originalSource = null;
+    AlignmentViewport originalSource = null;
     // For sequence removal and addition, we need to fire
     // the property change event FROM the viewport where the
     // original alignment was altered
@@ -1577,16 +1648,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       EditCommand editCommand = (EditCommand) command;
       al = editCommand.getAlignment();
-      Vector comps = (Vector) PaintRefresher.components.get(viewport
+      List<Component> comps = PaintRefresher.components.get(viewport
               .getSequenceSetId());
 
-      for (int i = 0; i < comps.size(); i++)
+      for (Component comp : comps)
       {
-        if (comps.elementAt(i) instanceof AlignmentPanel)
+        if (comp instanceof AlignmentPanel)
         {
-          if (al == ((AlignmentPanel) comps.elementAt(i)).av.getAlignment())
+          if (al == ((AlignmentPanel) comp).av.getAlignment())
           {
-            originalSource = ((AlignmentPanel) comps.elementAt(i)).av;
+            originalSource = ((AlignmentPanel) comp).av;
             break;
           }
         }
@@ -1629,7 +1700,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   synchronized void slideSequences(boolean right, int size)
   {
-    List<SequenceI> sg = new Vector();
+    List<SequenceI> sg = new ArrayList<SequenceI>();
     if (viewport.cursorMode)
     {
       sg.add(viewport.getAlignment().getSequenceAt(
@@ -1648,13 +1719,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    Vector invertGroup = new Vector();
+    List<SequenceI> invertGroup = new ArrayList<SequenceI>();
 
-    for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
+    for (SequenceI seq : viewport.getAlignment().getSequences())
     {
-      if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
+      if (!sg.contains(seq))
       {
-        invertGroup.add(viewport.getAlignment().getSequenceAt(i));
+        invertGroup.add(seq);
       }
     }
 
@@ -1663,7 +1734,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
     for (int i = 0; i < invertGroup.size(); i++)
     {
-      seqs2[i] = (SequenceI) invertGroup.elementAt(i);
+      seqs2[i] = invertGroup.get(i);
     }
 
     SlideSequencesCommand ssc;
@@ -1711,11 +1782,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
 
     boolean appendHistoryItem = false;
-    if (viewport.historyList != null && viewport.historyList.size() > 0
-            && viewport.historyList.peek() instanceof SlideSequencesCommand)
+    Deque<CommandI> historyList = viewport.getHistoryList();
+    if (historyList != null
+            && historyList.size() > 0
+            && historyList.peek() instanceof SlideSequencesCommand)
     {
       appendHistoryItem = ssc
-              .appendSlideCommand((SlideSequencesCommand) viewport.historyList
+              .appendSlideCommand((SlideSequencesCommand) historyList
                       .peek());
     }
 
@@ -1791,7 +1864,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     Desktop.jalviewClipboard = new Object[]
     { seqs, viewport.getAlignment().getDataset(), hiddenColumns };
     statusBar.setText(MessageManager.formatMessage(
-            "label.copied_sequences_to_clipboard", new String[]
+            "label.copied_sequences_to_clipboard", new Object[]
             { Integer.valueOf(seqs.length).toString() }));
   }
 
@@ -2089,7 +2162,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 alignment.getSequences());
         if (alignPanels != null)
         {
-          for (AlignmentPanel ap : ((Vector<AlignmentPanel>) alignPanels))
+          for (AlignmentPanel ap : alignPanels)
           {
             ap.validateAnnotationDimensions(false);
           }
@@ -2236,15 +2309,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    List<SequenceI> seqs = new ArrayList<SequenceI>(sg.getSize());
-    SequenceI seq;
-    for (int i = 0; i < sg.getSize(); i++)
-    {
-      seq = sg.getSequenceAt(i);
-      seqs.add(seq);
-    }
-
-    // If the cut affects all sequences, warn, remove highlighted columns
+    /*
+     * If the cut affects all sequences, warn, remove highlighted columns
+     */
     if (sg.getSize() == viewport.getAlignment().getHeight())
     {
       int confirm = JOptionPane.showConfirmDialog(this,
@@ -2261,15 +2328,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               sg.getEndRes() + 1);
     }
 
-    SequenceI[] cut = new SequenceI[seqs.size()];
-    for (int i = 0; i < seqs.size(); i++)
-    {
-      cut[i] = seqs.get(i);
-    }
+    SequenceI[] cut = sg.getSequences()
+            .toArray(new SequenceI[sg.getSize()]);
 
-    /*
-     * //ADD HISTORY ITEM
-     */
     addHistoryItem(new EditCommand(
             MessageManager.getString("label.cut_sequences"), Action.CUT,
             cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
@@ -2511,7 +2572,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     addHistoryItem(removeGapCols);
 
     statusBar.setText(MessageManager.formatMessage(
-            "label.removed_empty_columns", new String[]
+            "label.removed_empty_columns", new Object[]
             { Integer.valueOf(removeGapCols.getSize()).toString() }));
 
     // This is to maintain viewport position on first residue
@@ -2582,16 +2643,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             .getSequences());
   }
 
-  // else
-  {
-    // if (justifySeqs>0)
-    {
-      // alignment.justify(justifySeqs!=RIGHT_JUSTIFY);
-    }
-  }
-
-  // }
-
   /**
    * DOCUMENT ME!
    * 
@@ -2604,74 +2655,80 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     new Finder();
   }
 
-  @Override
-  public void newView_actionPerformed(ActionEvent e)
-  {
-    newView(true);
-  }
-
-  /**
-   * 
-   * @param copyAnnotation
-   *          if true then duplicate all annnotation, groups and settings
-   * @return new alignment panel, already displayed.
-   */
-  public AlignmentPanel newView(boolean copyAnnotation)
-  {
-    return newView(null, copyAnnotation);
-  }
-
   /**
-   * 
-   * @param viewTitle
-   *          title of newly created view
-   * @return new alignment panel, already displayed.
+   * Create a new view of the current alignment.
    */
-  public AlignmentPanel newView(String viewTitle)
+  @Override
+  public void newView_actionPerformed(ActionEvent e)
   {
-    return newView(viewTitle, true);
+    newView(null, true);
   }
 
   /**
+   * Creates and shows a new view of the current alignment.
    * 
    * @param viewTitle
-   *          title of newly created view
+   *          title of newly created view; if null, one will be generated
    * @param copyAnnotation
    *          if true then duplicate all annnotation, groups and settings
    * @return new alignment panel, already displayed.
    */
   public AlignmentPanel newView(String viewTitle, boolean copyAnnotation)
   {
+    /*
+     * Create a new AlignmentPanel (with its own, new Viewport)
+     */
     AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
             true);
     if (!copyAnnotation)
     {
-      // just remove all the current annotation except for the automatic stuff
+      /*
+       * remove all groups and annotation except for the automatic stuff
+       */
       newap.av.getAlignment().deleteAllGroups();
-      for (AlignmentAnnotation alan : newap.av.getAlignment()
-              .getAlignmentAnnotation())
-      {
-        if (!alan.autoCalculated)
-        {
-          newap.av.getAlignment().deleteAnnotation(alan);
-        }
-        ;
-      }
+      newap.av.getAlignment().deleteAllAnnotations(false);
     }
 
-    newap.av.gatherViewsHere = false;
+    newap.av.setGatherViewsHere(false);
 
     if (viewport.viewName == null)
     {
-      viewport.viewName = "Original";
+      viewport.viewName = MessageManager
+              .getString("label.view_name_original");
     }
 
-    newap.av.historyList = viewport.historyList;
-    newap.av.redoList = viewport.redoList;
+    /*
+     * Views share the same edits, undo and redo stacks, mappings.
+     */
+    newap.av.setHistoryList(viewport.getHistoryList());
+    newap.av.setRedoList(viewport.getRedoList());
+    newap.av.getAlignment().setCodonFrames(
+            viewport.getAlignment().getCodonFrames());
+
+    newap.av.viewName = getNewViewName(viewTitle);
 
+    addAlignmentPanel(newap, true);
+    newap.alignmentChanged();
+
+    if (alignPanels.size() == 2)
+    {
+      viewport.setGatherViewsHere(true);
+    }
+    tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
+    return newap;
+  }
+
+  /**
+   * Make a new name for the view, ensuring it is unique within the current
+   * sequenceSetId. (This used to be essential for Jalview Project archives, but
+   * these now use viewId. Unique view names are still desirable for usability.)
+   * 
+   * @param viewTitle
+   * @return
+   */
+  protected String getNewViewName(String viewTitle)
+  {
     int index = Desktop.getViewCount(viewport.getSequenceSetId());
-    // make sure the new view has a unique name - this is essential for Jalview
-    // 2 archives
     boolean addFirstIndex = false;
     if (viewTitle == null || viewTitle.trim().length() == 0)
     {
@@ -2683,45 +2740,55 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       index = 1;// we count from 1 if given a specific name
     }
     String newViewName = viewTitle + ((addFirstIndex) ? " " + index : "");
-    Vector comps = (Vector) PaintRefresher.components.get(viewport
+
+    List<Component> comps = PaintRefresher.components.get(viewport
             .getSequenceSetId());
-    Vector existingNames = new Vector();
-    for (int i = 0; i < comps.size(); i++)
-    {
-      if (comps.elementAt(i) instanceof AlignmentPanel)
-      {
-        AlignmentPanel ap = (AlignmentPanel) comps.elementAt(i);
-        if (!existingNames.contains(ap.av.viewName))
-        {
-          existingNames.addElement(ap.av.viewName);
-        }
-      }
-    }
+
+    List<String> existingNames = getExistingViewNames(comps);
 
     while (existingNames.contains(newViewName))
     {
       newViewName = viewTitle + " " + (++index);
     }
+    return newViewName;
+  }
 
-    newap.av.viewName = newViewName;
-
-    addAlignmentPanel(newap, true);
-    newap.alignmentChanged();
-
-    if (alignPanels.size() == 2)
+  /**
+   * Returns a list of distinct view names found in the given list of
+   * components. View names are held on the viewport of an AlignmentPanel.
+   * 
+   * @param comps
+   * @return
+   */
+  protected List<String> getExistingViewNames(List<Component> comps)
+  {
+    List<String> existingNames = new ArrayList<String>();
+    for (Component comp : comps)
     {
-      viewport.gatherViewsHere = true;
+      if (comp instanceof AlignmentPanel)
+      {
+        AlignmentPanel ap = (AlignmentPanel) comp;
+        if (!existingNames.contains(ap.av.viewName))
+        {
+          existingNames.add(ap.av.viewName);
+        }
+      }
     }
-    tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
-    return newap;
+    return existingNames;
   }
 
+  /**
+   * Explode tabbed views into separate windows.
+   */
   @Override
   public void expandViews_actionPerformed(ActionEvent e)
   {
     Desktop.instance.explodeViews(this);
   }
 
+  /**
+   * Gather views in separate windows back into a tabbed presentation.
+   */
   @Override
   public void gatherViews_actionPerformed(ActionEvent e)
   {
@@ -2766,7 +2833,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void centreColumnLabels_actionPerformed(ActionEvent e)
   {
-    viewport.centreColumnLabels = centreColumnLabelsMenuItem.getState();
+    viewport.setCentreColumnLabels(centreColumnLabelsMenuItem.getState());
     alignPanel.paintAlignment(true);
   }
 
@@ -2778,6 +2845,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void followHighlight_actionPerformed()
   {
+    /*
+     * Set the 'follow' flag on the Viewport (and scroll to position if now
+     * true).
+     */
     if (viewport.followHighlight = this.followHighlightMenuItem.getState())
     {
       alignPanel.scrollToPosition(
@@ -2811,7 +2882,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     scaleLeft.setVisible(wrapMenuItem.isSelected());
     scaleRight.setVisible(wrapMenuItem.isSelected());
     viewport.setWrapAlignment(wrapMenuItem.isSelected());
-    alignPanel.setWrapAlignment(wrapMenuItem.isSelected());
+    alignPanel.updateLayout();
   }
 
   @Override
@@ -2831,7 +2902,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void hideSelSequences_actionPerformed(ActionEvent e)
   {
     viewport.hideAllSelectedSeqs();
-    alignPanel.paintAlignment(true);
+//    alignPanel.paintAlignment(true);
   }
 
   /**
@@ -3115,11 +3186,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     final boolean setVisible = annotationPanelMenuItem.isSelected();
     viewport.setShowAnnotation(setVisible);
-    alignPanel.setAnnotationVisible(setVisible);
     this.showAllSeqAnnotations.setEnabled(setVisible);
     this.hideAllSeqAnnotations.setEnabled(setVisible);
     this.showAllAlAnnotations.setEnabled(setVisible);
     this.hideAllAlAnnotations.setEnabled(setVisible);
+    alignPanel.updateLayout();
   }
 
   @Override
@@ -3130,13 +3201,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
             .formatAsHtml();
     editPane.setText(MessageManager.formatMessage("label.html_content",
-            new String[]
+            new Object[]
             { contents.toString() }));
     JInternalFrame frame = new JInternalFrame();
     frame.getContentPane().add(new JScrollPane(editPane));
 
-    Desktop.instance.addInternalFrame(frame, MessageManager.formatMessage(
-            "label.alignment_properties", new String[]
+    Desktop.addInternalFrame(frame, MessageManager.formatMessage(
+            "label.alignment_properties", new Object[]
             { getTitle() }), 500, 400);
   }
 
@@ -3158,7 +3229,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     OverviewPanel overview = new OverviewPanel(alignPanel);
     frame.setContentPane(overview);
     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
-            "label.overview_params", new String[]
+            "label.overview_params", new Object[]
             { this.getTitle() }), frame.getWidth(), frame.getHeight());
     frame.pack();
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
@@ -3361,11 +3432,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
                 "Background");
-        cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
+        cs.setThreshold(threshold, viewport.isIgnoreGapsConsensus());
       }
       else
       {
-        cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
+        cs.setThreshold(0, viewport.isIgnoreGapsConsensus());
       }
 
       if (viewport.getConservationSelected())
@@ -3428,7 +3499,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 || cs instanceof PIDColourScheme
                 || cs instanceof Blosum62ColourScheme)
         {
-          sg.cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
+          sg.cs.setThreshold(threshold, viewport.isIgnoreGapsConsensus());
 
           sg.cs.setConsensus(AAFrequency.calculate(
                   sg.getSequences(viewport.getHiddenRepSequences()),
@@ -3436,7 +3507,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         }
         else
         {
-          sg.cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
+          sg.cs.setThreshold(0, viewport.isIgnoreGapsConsensus());
         }
 
         if (viewport.getConservationSelected())
@@ -3565,8 +3636,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
 
     Component[] menuItems = colourMenu.getMenuComponents();
-    int i, iSize = menuItems.length;
-    for (i = 0; i < iSize; i++)
+    int iSize = menuItems.length;
+    for (int i = 0; i < iSize; i++)
     {
       if (menuItems[i].getName() != null
               && menuItems[i].getName().equals("USER_DEFINED"))
@@ -3827,7 +3898,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("AV", "PID", "Average distance tree using PID");
+    newTreePanel("AV", "PID", "Average distance tree using PID");
   }
 
   /**
@@ -3839,7 +3910,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
+    newTreePanel("NJ", "PID", "Neighbour joining tree using PID");
   }
 
   /**
@@ -3851,7 +3922,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
+    newTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
   }
 
   /**
@@ -3863,7 +3934,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
+    newTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
   }
 
   /**
@@ -3876,7 +3947,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param title
    *          DOCUMENT ME!
    */
-  void NewTreePanel(String type, String pwType, String title)
+  void newTreePanel(String type, String pwType, String title)
   {
     TreePanel tp;
 
@@ -3967,7 +4038,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void addSortByOrderMenuItem(String title,
           final AlignmentOrder order)
   {
-    final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new String[]{title}));
+    final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new Object[]{title}));
     sort.add(item);
     item.addActionListener(new java.awt.event.ActionListener()
     {
@@ -4091,7 +4162,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       String treecalcnm = MessageManager.getString("label.tree_calc_"
               + type.toLowerCase());
-      for (final Object pwtype : ResidueProperties.scoreMatrices.keySet())
+      for (final String pwtype : ResidueProperties.scoreMatrices.keySet())
       {
         JMenuItem tm = new JMenuItem();
         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
@@ -4107,7 +4178,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             @Override
             public void actionPerformed(ActionEvent e)
             {
-              NewTreePanel(type, (String) pwtype, title);
+              newTreePanel(type, pwtype, title);
             }
           });
           calculateTree.add(tm);
@@ -4117,21 +4188,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     sortByTreeMenu.removeAll();
 
-    Vector comps = (Vector) PaintRefresher.components.get(viewport
+    List<Component> comps = PaintRefresher.components.get(viewport
             .getSequenceSetId());
-    Vector treePanels = new Vector();
-    int i, iSize = comps.size();
-    for (i = 0; i < iSize; i++)
+    List<TreePanel> treePanels = new ArrayList<TreePanel>();
+    for (Component comp : comps)
     {
-      if (comps.elementAt(i) instanceof TreePanel)
+      if (comp instanceof TreePanel)
       {
-        treePanels.add(comps.elementAt(i));
+        treePanels.add((TreePanel) comp);
       }
     }
 
-    iSize = treePanels.size();
-
-    if (iSize < 1)
+    if (treePanels.size() < 1)
     {
       sortByTreeMenu.setVisible(false);
       return;
@@ -4139,17 +4207,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     sortByTreeMenu.setVisible(true);
 
-    for (i = 0; i < treePanels.size(); i++)
+    for (final TreePanel tp : treePanels)
     {
-      final TreePanel tp = (TreePanel) treePanels.elementAt(i);
       final JMenuItem item = new JMenuItem(tp.getTitle());
-      final NJTree tree = ((TreePanel) treePanels.elementAt(i)).getTree();
       item.addActionListener(new java.awt.event.ActionListener()
       {
         @Override
         public void actionPerformed(ActionEvent e)
         {
-          tp.sortByTree_actionPerformed(null);
+          tp.sortByTree_actionPerformed();
           addHistoryItem(tp.sortAlignmentIn(alignPanel));
 
         }
@@ -4257,7 +4323,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    *          DOCUMENT ME!
    */
   @Override
-  protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
+  protected void loadTreeMenuItem_actionPerformed(ActionEvent e)
   {
     // Pick the tree file
     JalviewFileChooser chooser = new JalviewFileChooser(
@@ -4389,7 +4455,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       } catch (Exception e)
       {
       }
-      ;
     }
     final AlignFrame me = this;
     buildingMenu = true;
@@ -4544,14 +4609,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         .debug("Exception during web service menu building process.",
                                 e);
               }
-              ;
             }
           });
         } catch (Exception e)
         {
         }
-        ;
-
         buildingMenu = false;
       }
     }).start();
@@ -4663,7 +4725,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           public void actionPerformed(ActionEvent e)
           {
             // TODO: new thread for this call with vis-delay
-            af.showProductsFor(af.viewport.getSequenceSelection(), ds,
+            af.showProductsFor(af.viewport.getSequenceSelection(),
                     isRegSel, dna, source);
           }
 
@@ -4682,14 +4744,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     return showp;
   }
 
-  protected void showProductsFor(SequenceI[] sel, Alignment ds,
-          boolean isRegSel, boolean dna, String source)
+  protected void showProductsFor(final SequenceI[] sel,
+          final boolean isRegSel, final boolean dna, final String source)
   {
-    final boolean fisRegSel = isRegSel;
-    final boolean fdna = dna;
-    final String fsrc = source;
-    final AlignFrame ths = this;
-    final SequenceI[] fsel = sel;
     Runnable foo = new Runnable()
     {
 
@@ -4697,15 +4754,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       public void run()
       {
         final long sttime = System.currentTimeMillis();
-        ths.setProgressBar(MessageManager.formatMessage("status.searching_for_sequences_from", new String[]{fsrc}), sttime);
+        AlignFrame.this.setProgressBar(MessageManager.formatMessage(
+                "status.searching_for_sequences_from", new Object[]
+                { source }), sttime);
         try
         {
-          Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
-          // our local
-          // dataset
-          // reference
+          // update our local dataset reference
+          Alignment ds = AlignFrame.this.getViewport().getAlignment()
+                  .getDataset();
           Alignment prods = CrossRef
-                  .findXrefSequences(fsel, fdna, fsrc, ds);
+                  .findXrefSequences(sel, dna, source, ds);
           if (prods != null)
           {
             SequenceI[] sprods = new SequenceI[prods.getHeight()];
@@ -4721,25 +4779,47 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               sprods[s].updatePDBIds();
             }
             Alignment al = new Alignment(sprods);
-            AlignedCodonFrame[] cf = prods.getCodonFrames();
+            Set<AlignedCodonFrame> cf = prods.getCodonFrames();
             al.setDataset(ds);
-            for (int s = 0; cf != null && s < cf.length; s++)
+            for (AlignedCodonFrame acf : cf)
             {
-              al.addCodonFrame(cf[s]);
-              cf[s] = null;
+              al.addCodonFrame(acf);
             }
             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
                     DEFAULT_HEIGHT);
-            String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
-                    + " for " + ((fisRegSel) ? "selected region of " : "")
+            String newtitle = "" + ((dna) ? "Proteins" : "Nucleotides")
+                    + " for " + ((isRegSel) ? "selected region of " : "")
                     + getTitle();
-            Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
-                    DEFAULT_HEIGHT);
+            naf.setTitle(newtitle);
+
+            // remove this flag once confirmed we want a split view
+            boolean asSplitFrame = true;
+            if (asSplitFrame)
+            {
+              final Alignment copyAlignment = new Alignment(new Alignment(
+                      AlignFrame.this.viewport.getSequenceSelection()));
+              AlignFrame copyThis = new AlignFrame(copyAlignment,
+                      AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+              copyThis.setTitle(AlignFrame.this.getTitle());
+              // SplitFrame with dna above, protein below
+              SplitFrame sf = new SplitFrame(dna ? copyThis : naf,
+                      dna ? naf : copyThis);
+              naf.setVisible(true);
+              copyThis.setVisible(true);
+              String linkedTitle = MessageManager
+                      .getString("label.linked_view_title");
+              Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
+            }
+            else
+            {
+              Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
+                      DEFAULT_HEIGHT);
+            }
           }
           else
           {
             System.err.println("No Sequences generated for xRef type "
-                    + fsrc);
+                    + source);
           }
         } catch (Exception e)
         {
@@ -4753,7 +4833,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           jalview.bin.Cache.log.error("Error when finding crossreferences",
                   e);
         }
-        ths.setProgressBar(MessageManager.formatMessage("status.finished_searching_for_sequences_from", new String[]{fsrc}),
+        AlignFrame.this.setProgressBar(MessageManager.formatMessage(
+                "status.finished_searching_for_sequences_from",
+                new Object[]
+                { source }),
                 sttime);
       }
 
@@ -4779,96 +4862,66 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
-  @Override
-  public void showProducts_actionPerformed(ActionEvent e)
-  {
-    // /////////////////////////////
-    // Collect Data to be translated/transferred
-
-    SequenceI[] selection = viewport.getSequenceSelection();
-    AlignmentI al = null;
-    try
-    {
-      al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
-              .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
-              viewport.getAlignment().getDataset());
-    } catch (Exception ex)
-    {
-      al = null;
-      jalview.bin.Cache.log.debug("Exception during translation.", ex);
-    }
-    if (al == null)
-    {
-      JOptionPane
-              .showMessageDialog(
-                      Desktop.desktop,
-                      MessageManager
-                              .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
-                      MessageManager.getString("label.translation_failed"),
-                      JOptionPane.WARNING_MESSAGE);
-    }
-    else
-    {
-      AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-      Desktop.addInternalFrame(af, MessageManager.formatMessage(
-              "label.translation_of_params", new String[]
-              { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
-    }
-  }
-
+  /**
+   * Construct and display a new frame containing the translation of this
+   * frame's DNA sequences to their aligned protein (amino acid) equivalents.
+   */
   @Override
   public void showTranslation_actionPerformed(ActionEvent e)
   {
-    // /////////////////////////////
-    // Collect Data to be translated/transferred
-
-    SequenceI[] selection = viewport.getSequenceSelection();
-    String[] seqstring = viewport.getViewAsString(true);
     AlignmentI al = null;
     try
     {
-      al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
-              viewport.getViewAsVisibleContigs(true), viewport
-                      .getGapCharacter(), viewport.getAlignment()
-                      .getAlignmentAnnotation(), viewport.getAlignment()
-                      .getWidth(), viewport.getAlignment().getDataset());
+      Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
+
+      al = dna.translateCdna();
     } catch (Exception ex)
     {
-      al = null;
       jalview.bin.Cache.log.error(
               "Exception during translation. Please report this !", ex);
-      JOptionPane
-              .showMessageDialog(
-                      Desktop.desktop,
-                      MessageManager
-                              .getString("label.error_when_translating_sequences_submit_bug_report"),
-                      MessageManager
-                              .getString("label.implementation_error")
-                              + MessageManager
-                                      .getString("translation_failed"),
-                      JOptionPane.ERROR_MESSAGE);
+      final String msg = MessageManager
+              .getString("label.error_when_translating_sequences_submit_bug_report");
+      final String title = MessageManager
+              .getString("label.implementation_error")
+              + MessageManager.getString("translation_failed");
+      JOptionPane.showMessageDialog(Desktop.desktop, msg, title,
+              JOptionPane.ERROR_MESSAGE);
       return;
     }
-    if (al == null)
+    if (al == null || al.getHeight() == 0)
     {
-      JOptionPane
-              .showMessageDialog(
-                      Desktop.desktop,
-                      MessageManager
-                              .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
-                      MessageManager.getString("label.translation_failed"),
-                      JOptionPane.WARNING_MESSAGE);
+      final String msg = MessageManager
+              .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
+      final String title = MessageManager
+              .getString("label.translation_failed");
+      JOptionPane.showMessageDialog(Desktop.desktop, msg, title,
+              JOptionPane.WARNING_MESSAGE);
     }
     else
     {
       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-      Desktop.addInternalFrame(af, MessageManager.formatMessage(
-              "label.translation_of_params", new String[]
-              { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
+      af.setFileFormat(this.currentFileFormat);
+      final String newTitle = MessageManager.formatMessage(
+              "label.translation_of_params", new Object[]
+              { this.getTitle() });
+      af.setTitle(newTitle);
+      final SequenceI[] seqs = viewport.getSelectionAsNewSequence();
+      viewport.openSplitFrame(af, new Alignment(seqs), al.getCodonFrames());
+      // Desktop.addInternalFrame(af, newTitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
     }
   }
 
   /**
+   * Set the file format
+   * 
+   * @param fileFormat
+   */
+  public void setFileFormat(String fileFormat)
+  {
+    this.currentFileFormat = fileFormat;
+  }
+
+  /**
    * Try to load a features file onto the alignment.
    * 
    * @param file
@@ -5060,7 +5113,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                                   MessageManager
                                           .formatMessage(
                                                   "label.automatically_associate_pdb_files_with_sequences_same_name",
-                                                  new String[]
+                                                  new Object[]
                                                   { Integer.valueOf(
                                                           filesmatched
                                                                   .size())
@@ -5103,7 +5156,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                                   "<html>"+MessageManager
                                           .formatMessage(
                                                   "label.ignore_unmatched_dropped_files_info",
-                                                  new String[]
+                                                  new Object[]
                                                   { Integer.valueOf(
                                                           filesnotmatched
                                                                   .size())
@@ -5211,7 +5264,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           {
             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
                     file, protocol);
-            new JnetAnnotationMaker().add_annotation(predictions,
+            new JnetAnnotationMaker();
+            JnetAnnotationMaker.add_annotation(predictions,
                     viewport.getAlignment(), 0, false);
             SequenceI repseq = viewport.getAlignment().getSequenceAt(0);
             viewport.getAlignment().setSeqrep(repseq);
@@ -5288,31 +5342,55 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
+  /**
+   * Method invoked by the ChangeListener on the tabbed pane, in other words
+   * when a different tabbed pane is selected by the user or programmatically.
+   */
   @Override
   public void tabSelectionChanged(int index)
   {
     if (index > -1)
     {
-      alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
+      alignPanel = alignPanels.get(index);
       viewport = alignPanel.av;
       avc.setViewportAndAlignmentPanel(viewport, alignPanel);
       setMenusFromViewport(viewport);
     }
+
+    /*
+     * If there is a frame linked to this one in a SplitPane, switch it to the
+     * same view tab index. No infinite recursion of calls should happen, since
+     * tabSelectionChanged() should not get invoked on setting the selected
+     * index to an unchanged value. Guard against setting an invalid index
+     * before the new view peer tab has been created.
+     */
+    final AlignViewportI peer = viewport.getCodingComplement();
+    if (peer != null)
+    {
+      AlignFrame linkedAlignFrame = ((AlignViewport) peer).getAlignPanel().alignFrame;
+      if (linkedAlignFrame.tabbedPane.getTabCount() > index)
+      {
+        linkedAlignFrame.tabbedPane.setSelectedIndex(index);
+      }
+    }
   }
 
+  /**
+   * On right mouse click on view tab, prompt for and set new view name.
+   */
   @Override
   public void tabbedPane_mousePressed(MouseEvent e)
   {
     if (SwingUtilities.isRightMouseButton(e))
     {
-      String reply = JOptionPane.showInternalInputDialog(this,
-              MessageManager.getString("label.enter_view_name"),
-              MessageManager.getString("label.enter_view_name"),
+      String msg = MessageManager.getString("label.enter_view_name");
+      String reply = JOptionPane.showInternalInputDialog(this, msg, msg,
               JOptionPane.QUESTION_MESSAGE);
 
       if (reply != null)
       {
         viewport.viewName = reply;
+        // TODO warn if reply is in getExistingViewNames()?
         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
       }
     }
@@ -5352,7 +5430,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void showDbRefs_actionPerformed(ActionEvent e)
   {
-    viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
+    viewport.setShowDBRefs(showDbRefsMenuitem.isSelected());
   }
 
   /*
@@ -5364,7 +5442,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void showNpFeats_actionPerformed(ActionEvent e)
   {
-    viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
+    viewport.setShowNPFeats(showNpFeatsMenuitem.isSelected());
   }
 
   /**
@@ -5373,7 +5451,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param av
    */
-  public boolean closeView(AlignViewport av)
+  public boolean closeView(AlignViewportI av)
   {
     if (viewport == av)
     {
@@ -5519,7 +5597,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                   }
 
                 });
-                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{src.getDbName()})));
+                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new Object[]{src.getDbName()})));
                 dfetch.add(fetchr);
                 comp++;
               }
@@ -5530,7 +5608,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 // fetch all entry
                 DbSourceProxy src = otherdb.get(0);
                 fetchr = new JMenuItem(MessageManager.formatMessage(
-                        "label.fetch_all_param", new String[]
+                        "label.fetch_all_param", new Object[]
                         { src.getDbSource() }));
                 fetchr.addActionListener(new ActionListener()
                 {
@@ -5552,11 +5630,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                   }
                 });
 
-                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new String[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
+                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new Object[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
                 dfetch.add(fetchr);
                 comp++;
                 // and then build the rest of the individual menus
-                ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new String[]{src.getDbSource()}));
+                ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new Object[]{src.getDbSource()}));
                 icomp = 0;
                 String imname = null;
                 int i = 0;
@@ -5569,7 +5647,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                           0, 10) + "..." : dbname;
                   if (imname == null)
                   {
-                    imname = MessageManager.formatMessage("label.from_msname", new String[]{sname});
+                    imname = MessageManager.formatMessage("label.from_msname", new Object[]{sname});
                   }
                   fetchr = new JMenuItem(msname);
                   final DbSourceProxy[] dassrc =
@@ -5596,7 +5674,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
                   });
                   fetchr.setToolTipText("<html>"
-                          + MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{dbname}));
+                          + MessageManager.formatMessage("label.fetch_retrieve_from", new Object[]{dbname}));
                   ifetch.add(fetchr);
                   ++i;
                   if (++icomp >= mcomp || i == (otherdb.size()))
@@ -5802,7 +5880,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       throw new Error(MessageManager.getString("error.implementation_error_cannot_show_view_alignment_frame"));
     }
     if (tabbedPane != null
-            & alignPanels.indexOf(alignmentPanel) != tabbedPane
+            && tabbedPane.getTabCount() > 0
+            && alignPanels.indexOf(alignmentPanel) != tabbedPane
                     .getSelectedIndex())
     {
       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
@@ -5850,11 +5929,74 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   /**
    * 
-   * @return alignment panels in this alignemnt frame
+   * @return alignment panels in this alignment frame
+   */
+  public List<? extends AlignmentViewPanel> getAlignPanels()
+  {
+    return alignPanels == null ? Arrays.asList(alignPanel)
+            : alignPanels;
+  }
+
+  /**
+   * Open a new alignment window, with the cDNA associated with this (protein)
+   * alignment, aligned as is the protein.
+   */
+  protected void viewAsCdna_actionPerformed()
+  {
+    // TODO no longer a menu action - refactor as required
+    final AlignmentI alignment = getViewport().getAlignment();
+    Set<AlignedCodonFrame> mappings = alignment.getCodonFrames();
+    if (mappings == null)
+    {
+      return;
+    }
+    List<SequenceI> cdnaSeqs = new ArrayList<SequenceI>();
+    for (SequenceI aaSeq : alignment.getSequences()) {
+      for (AlignedCodonFrame acf : mappings) {
+        SequenceI dnaSeq = acf.getDnaForAaSeq(aaSeq.getDatasetSequence());
+        if (dnaSeq != null)
+        {
+          /*
+           * There is a cDNA mapping for this protein sequence - add to new
+           * alignment. It will share the same dataset sequence as other mapped
+           * cDNA (no new mappings need to be created).
+           */
+          final Sequence newSeq = new Sequence(dnaSeq);
+          newSeq.setDatasetSequence(dnaSeq);
+          cdnaSeqs.add(newSeq);
+        }
+      }
+    }
+    if (cdnaSeqs.size() == 0)
+    {
+      // show a warning dialog no mapped cDNA
+      return;
+    }
+    AlignmentI cdna = new Alignment(cdnaSeqs.toArray(new SequenceI[cdnaSeqs
+            .size()]));
+    AlignFrame alignFrame = new AlignFrame(cdna, AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
+    cdna.alignAs(alignment);
+    String newtitle = "cDNA " + MessageManager.getString("label.for") + " "
+            + this.title;
+    Desktop.addInternalFrame(alignFrame, newtitle,
+            AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
+  }
+
+  /**
+   * Set visibility of dna/protein complement view (available when shown in a
+   * split frame).
+   * 
+   * @param show
    */
-  public List<AlignmentViewPanel> getAlignPanels()
+  @Override
+  protected void showComplement_actionPerformed(boolean show)
   {
-    return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
+    SplitContainerI sf = getSplitViewContainer();
+    if (sf != null) {
+      sf.setComplementVisible(this, show);
+    }
   }
 }
 
index b088e08..e4593a3 100644 (file)
  */
 package jalview.gui;
 
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Rectangle;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import jalview.analysis.AlignmentUtils;
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.NJTree;
 import jalview.api.AlignViewportI;
+import jalview.api.ViewStyleI;
 import jalview.bin.Cache;
 import jalview.commands.CommandI;
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
@@ -51,21 +67,14 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 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.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.AutoCalcSetting;
 
-import java.awt.Color;
-import java.awt.Container;
-import java.awt.Font;
-import java.awt.Rectangle;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.Stack;
-import java.util.Vector;
-
 /**
  * DOCUMENT ME!
  * 
@@ -73,7 +82,7 @@ import java.util.Vector;
  * @version $Revision: 1.141 $
  */
 public class AlignViewport extends AlignmentViewport implements
-        SelectionSource, VamsasSource, AlignViewportI
+        SelectionSource, AlignViewportI, CommandListener
 {
   int startRes;
 
@@ -83,61 +92,26 @@ public class AlignViewport extends AlignmentViewport implements
 
   int endSeq;
 
-  boolean showJVSuffix = true;
-
-  boolean showText = true;
-
-  boolean showColourText = false;
-
-  boolean showBoxes = true;
-
-  boolean wrapAlignment = false;
-
-  boolean renderGaps = true;
-
-  SequenceAnnotationOrder sortAnnotationsBy = null;
-
-  int charHeight;
-
-  int charWidth;
-
-  boolean validCharWidth;
-
-  int wrappedWidth;
-
   Font font;
 
-  boolean seqNameItalics;
-
   NJTree currentTree = null;
 
-  boolean scaleAboveWrapped = false;
-
-  boolean scaleLeftWrapped = true;
-
-  boolean scaleRightWrapped = true;
-
-  boolean showHiddenMarkers = true;
-
   boolean cursorMode = false;
 
   boolean antiAlias = false;
 
-  Rectangle explodedPosition;
+  private Rectangle explodedGeometry;
 
   String viewName;
 
-  boolean gatherViewsHere = false;
-
-  Stack<CommandI> historyList = new Stack<CommandI>();
-
-  Stack<CommandI> redoList = new Stack<CommandI>();
-
-  int thresholdTextColour = 0;
-
-  Color textColour = Color.black;
-
-  Color textColour2 = Color.white;
+  /*
+   * Flag set true on the view that should 'gather' multiple views of the same
+   * sequence set id when a project is reloaded. Set false on all views when
+   * they are 'exploded' into separate windows. Set true on the current view
+   * when 'Gather' is performed, and also on the first tab when the first new
+   * view is created.
+   */
+  private boolean gatherViewsHere = false;
 
   private AnnotationColumnChooser annotationColumnSelectionState;
   /**
@@ -247,32 +221,46 @@ public class AlignViewport extends AlignmentViewport implements
     init();
   }
 
-  void init()
+  private void applyViewProperties()
   {
-    this.startRes = 0;
-    this.endRes = alignment.getWidth() - 1;
-    this.startSeq = 0;
-    this.endSeq = alignment.getHeight() - 1;
-
     antiAlias = Cache.getDefault("ANTI_ALIAS", false);
 
-    showJVSuffix = Cache.getDefault("SHOW_JVSUFFIX", true);
+    viewStyle.setShowJVSuffix(Cache.getDefault("SHOW_JVSUFFIX", true));
     setShowAnnotation(Cache.getDefault("SHOW_ANNOTATIONS", true));
 
     setRightAlignIds(Cache.getDefault("RIGHT_ALIGN_IDS", false));
-    centreColumnLabels = Cache.getDefault("CENTRE_COLUMN_LABELS", false);
+    setCentreColumnLabels(Cache.getDefault("CENTRE_COLUMN_LABELS", false));
     autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
 
     setPadGaps(Cache.getDefault("PAD_GAPS", true));
-    shownpfeats = Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true);
-    showdbrefs = Cache.getDefault("SHOW_DBREFS_TOOLTIP", true);
+    setShowNPFeats(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
+    setShowDBRefs(Cache.getDefault("SHOW_DBREFS_TOOLTIP", true));
+    viewStyle.setSeqNameItalics(Cache.getDefault("ID_ITALICS", true));
+    viewStyle.setWrapAlignment(Cache.getDefault("WRAP_ALIGNMENT", false));
+    viewStyle.setShowUnconserved(Cache
+            .getDefault("SHOW_UNCONSERVED", false));
+    sortByTree = Cache.getDefault("SORT_BY_TREE", false);
+    followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true);
+    sortAnnotationsBy = SequenceAnnotationOrder.valueOf(Cache.getDefault(
+            Preferences.SORT_ANNOTATIONS,
+            SequenceAnnotationOrder.NONE.name()));
+    showAutocalculatedAbove = Cache.getDefault(
+            Preferences.SHOW_AUTOCALC_ABOVE, false);
+
+  }
+
+  void init()
+  {
+    this.startRes = 0;
+    this.endRes = alignment.getWidth() - 1;
+    this.startSeq = 0;
+    this.endSeq = alignment.getHeight() - 1;
+    applyViewProperties();
 
     String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
     String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + "");
     String fontSize = Cache.getDefault("FONT_SIZE", "10");
 
-    seqNameItalics = Cache.getDefault("ID_ITALICS", true);
-
     int style = 0;
 
     if (fontStyle.equals("bold"))
@@ -284,7 +272,7 @@ public class AlignViewport extends AlignmentViewport implements
       style = 2;
     }
 
-    setFont(new Font(fontName, style, Integer.parseInt(fontSize)));
+    setFont(new Font(fontName, style, Integer.parseInt(fontSize)), true);
 
     alignment
             .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
@@ -309,16 +297,24 @@ public class AlignViewport extends AlignmentViewport implements
       showConsensus = Cache.getDefault("SHOW_IDENTITY", true);
     }
     initAutoAnnotation();
-    if (jalview.bin.Cache.getProperty("DEFAULT_COLOUR") != null)
+    String colourProperty = alignment.isNucleotide() ? Preferences.DEFAULT_COLOUR_NUC
+            : Preferences.DEFAULT_COLOUR_PROT;
+    String propertyValue = Cache.getProperty(colourProperty);
+    if (propertyValue == null)
+    {
+      // fall back on this property for backwards compatibility
+      propertyValue = Cache.getProperty(Preferences.DEFAULT_COLOUR);
+    }
+    if (propertyValue != null)
     {
       globalColourScheme = ColourSchemeProperty.getColour(alignment,
-              jalview.bin.Cache.getProperty("DEFAULT_COLOUR"));
+              propertyValue);
 
       if (globalColourScheme instanceof UserColourScheme)
       {
         globalColourScheme = UserDefinedColours.loadDefaultColours();
         ((UserColourScheme) globalColourScheme).setThreshold(0,
-                getIgnoreGapsConsensus());
+                isIgnoreGapsConsensus());
       }
 
       if (globalColourScheme != null)
@@ -326,31 +322,9 @@ public class AlignViewport extends AlignmentViewport implements
         globalColourScheme.setConsensus(hconsensus);
       }
     }
-
-    wrapAlignment = Cache.getDefault("WRAP_ALIGNMENT", false);
-    showUnconserved = Cache.getDefault("SHOW_UNCONSERVED", false);
-    sortByTree = Cache.getDefault("SORT_BY_TREE", false);
-    followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true);
-    sortAnnotationsBy = SequenceAnnotationOrder.valueOf(Cache.getDefault(
-            Preferences.SORT_ANNOTATIONS,
-            SequenceAnnotationOrder.NONE.name()));
-    showAutocalculatedAbove = Cache.getDefault(
-            Preferences.SHOW_AUTOCALC_ABOVE, false);
   }
 
   /**
-   * centre columnar annotation labels in displayed alignment annotation TODO:
-   * add to jalviewXML and annotation display settings
-   */
-  boolean centreColumnLabels = false;
-
-  private boolean showdbrefs;
-
-  private boolean shownpfeats;
-
-  // --------END Structure Conservation
-
-  /**
    * get the consensus sequence as displayed under the PID consensus annotation
    * row.
    * 
@@ -494,105 +468,52 @@ public class AlignViewport extends AlignmentViewport implements
     return endSeq;
   }
 
+  boolean validCharWidth;
+
   /**
-   * DOCUMENT ME!
+   * update view settings with the given font. You may need to call
+   * alignPanel.fontChanged to update the layout geometry
    * 
-   * @param f
-   *          DOCUMENT ME!
+   * @param setGrid
+   *          when true, charWidth/height is set according to font mentrics
    */
-  public void setFont(Font f)
+  public void setFont(Font f, boolean setGrid)
   {
     font = f;
 
     Container c = new Container();
 
     java.awt.FontMetrics fm = c.getFontMetrics(font);
-    setCharHeight(fm.getHeight());
-    setCharWidth(fm.charWidth('M'));
-    validCharWidth = true;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public Font getFont()
-  {
-    return font;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param w
-   *          DOCUMENT ME!
-   */
-  public void setCharWidth(int w)
-  {
-    this.charWidth = w;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getCharWidth()
-  {
-    return charWidth;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param h
-   *          DOCUMENT ME!
-   */
-  public void setCharHeight(int h)
-  {
-    this.charHeight = h;
-  }
+    int w = viewStyle.getCharWidth(), ww = fm.charWidth('M'), h = viewStyle
+            .getCharHeight();
+    if (setGrid)
+    {
+      setCharHeight(fm.getHeight());
+      setCharWidth(ww);
+    }
+    viewStyle.setFontName(font.getName());
+    viewStyle.setFontStyle(font.getStyle());
+    viewStyle.setFontSize(font.getSize());
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getCharHeight()
-  {
-    return charHeight;
+    validCharWidth = true;
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param w
-   *          DOCUMENT ME!
-   */
-  public void setWrappedWidth(int w)
+  @Override
+  public void setViewStyle(ViewStyleI settingsForView)
   {
-    this.wrappedWidth = w;
-  }
+    super.setViewStyle(settingsForView);
+    setFont(new Font(viewStyle.getFontName(), viewStyle.getFontStyle(),
+            viewStyle.getFontSize()), false);
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getWrappedWidth()
-  {
-    return wrappedWidth;
   }
-
   /**
    * DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
    */
-  public AlignmentI getAlignment()
+  public Font getFont()
   {
-    return alignment;
+    return font;
   }
 
   /**
@@ -619,101 +540,6 @@ public class AlignViewport extends AlignmentViewport implements
   /**
    * DOCUMENT ME!
    * 
-   * @param state
-   *          DOCUMENT ME!
-   */
-  public void setWrapAlignment(boolean state)
-  {
-    wrapAlignment = state;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param state
-   *          DOCUMENT ME!
-   */
-  public void setShowText(boolean state)
-  {
-    showText = state;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param state
-   *          DOCUMENT ME!
-   */
-  public void setRenderGaps(boolean state)
-  {
-    renderGaps = state;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getColourText()
-  {
-    return showColourText;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param state
-   *          DOCUMENT ME!
-   */
-  public void setColourText(boolean state)
-  {
-    showColourText = state;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param state
-   *          DOCUMENT ME!
-   */
-  public void setShowBoxes(boolean state)
-  {
-    showBoxes = state;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getWrapAlignment()
-  {
-    return wrapAlignment;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getShowText()
-  {
-    return showText;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getShowBoxes()
-  {
-    return showBoxes;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
    * @return DOCUMENT ME!
    */
   public char getGapCharacter()
@@ -767,110 +593,6 @@ public class AlignViewport extends AlignmentViewport implements
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getShowJVSuffix()
-  {
-    return showJVSuffix;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param b
-   *          DOCUMENT ME!
-   */
-  public void setShowJVSuffix(boolean b)
-  {
-    showJVSuffix = b;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getScaleAboveWrapped()
-  {
-    return scaleAboveWrapped;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getScaleLeftWrapped()
-  {
-    return scaleLeftWrapped;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getScaleRightWrapped()
-  {
-    return scaleRightWrapped;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param b
-   *          DOCUMENT ME!
-   */
-  public void setScaleAboveWrapped(boolean b)
-  {
-    scaleAboveWrapped = b;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param b
-   *          DOCUMENT ME!
-   */
-  public void setScaleLeftWrapped(boolean b)
-  {
-    scaleLeftWrapped = b;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param b
-   *          DOCUMENT ME!
-   */
-  public void setScaleRightWrapped(boolean b)
-  {
-    scaleRightWrapped = b;
-  }
-
-  public void setDataset(boolean b)
-  {
-    isDataset = b;
-  }
-
-  public boolean isDataset()
-  {
-    return isDataset;
-  }
-
-  public boolean getShowHiddenMarkers()
-  {
-    return showHiddenMarkers;
-  }
-
-  public void setShowHiddenMarkers(boolean show)
-  {
-    showHiddenMarkers = show;
-  }
-
-  /**
    * returns the visible column regions of the alignment
    * 
    * @param selectedRegionOnly
@@ -943,55 +665,6 @@ public class AlignViewport extends AlignmentViewport implements
     return false;
   }
 
-  public boolean getCentreColumnLabels()
-  {
-    return centreColumnLabels;
-  }
-
-  public void setCentreColumnLabels(boolean centrecolumnlabels)
-  {
-    centreColumnLabels = centrecolumnlabels;
-  }
-
-  /**
-   * enable or disable the display of Database Cross References in the sequence
-   * ID tooltip
-   */
-  public void setShowDbRefs(boolean show)
-  {
-    showdbrefs = show;
-  }
-
-  /**
-   * 
-   * @return true if Database References are to be displayed on tooltips.
-   */
-  public boolean isShowDbRefs()
-  {
-    return showdbrefs;
-  }
-
-  /**
-   * 
-   * @return true if Non-positional features are to be displayed on tooltips.
-   */
-  public boolean isShowNpFeats()
-  {
-    return shownpfeats;
-  }
-
-  /**
-   * enable or disable the display of Non-Positional sequence features in the
-   * sequence ID tooltip
-   * 
-   * @param show
-   */
-  public void setShowNpFeats(boolean show)
-  {
-    shownpfeats = show;
-  }
-
-
   /**
    * when set, view will scroll to show the highlighted position
    */
@@ -1018,6 +691,9 @@ public class AlignViewport extends AlignmentViewport implements
     return followSelection;
   }
 
+  /**
+   * Send the current selection to be broadcast to any selection listeners.
+   */
   public void sendSelection()
   {
     jalview.structure.StructureSelectionManager
@@ -1038,7 +714,6 @@ public class AlignViewport extends AlignmentViewport implements
   {
     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
             .getSequenceSetId());
-    AlignmentPanel ap = null;
     for (int p = 0; aps != null && p < aps.length; p++)
     {
       if (aps[p].av == this)
@@ -1092,6 +767,10 @@ public class AlignViewport extends AlignmentViewport implements
     }
   }
 
+  /**
+   * Returns the (Desktop) instance of the StructureSelectionManager
+   */
+  @Override
   public StructureSelectionManager getStructureSelectionManager()
   {
     return StructureSelectionManager
@@ -1159,8 +838,6 @@ public class AlignViewport extends AlignmentViewport implements
 
   private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<String, AutoCalcSetting>();
 
-  private boolean showAutocalculatedAbove;
-
   public AutoCalcSetting getCalcIdSettingsFor(String calcId)
   {
     return calcIdParams.get(calcId);
@@ -1179,24 +856,229 @@ public class AlignViewport extends AlignmentViewport implements
     }
   }
 
-  protected SequenceAnnotationOrder getSortAnnotationsBy()
-  {
-    return sortAnnotationsBy;
+  /**
+   * Method called when another alignment's edit (or possibly other) command is
+   * broadcast to here.
+   *
+   * To allow for sequence mappings (e.g. protein to cDNA), we have to first
+   * 'unwind' the command on the source sequences (in simulation, not in fact),
+   * and then for each edit in turn:
+   * <ul>
+   * <li>compute the equivalent edit on the mapped sequences</li>
+   * <li>apply the mapped edit</li>
+   * <li>'apply' the source edit to the working copy of the source sequences</li>
+   * </ul>
+   * 
+   * @param command
+   * @param undo
+   * @param ssm
+   */
+  @Override
+  public void mirrorCommand(CommandI command, boolean undo,
+          StructureSelectionManager ssm, VamsasSource source)
+  {
+    /*
+     * Do nothing unless we are a 'complement' of the source. May replace this
+     * with direct calls not via SSM.
+     */
+    if (source instanceof AlignViewportI
+            && ((AlignViewportI) source).getCodingComplement() == this)
+    {
+      // ok to continue;
+    }
+    else
+    {
+      return;
+    }
+
+    CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(),
+            getGapCharacter());
+    if (mappedCommand != null)
+    {
+      AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments();
+      mappedCommand.doCommand(views);
+      getAlignPanel().alignmentChanged();
+    }
   }
 
-  protected void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
+  /**
+   * Add the sequences from the given alignment to this viewport. Optionally,
+   * may give the user the option to open a new frame, or split panel, with cDNA
+   * and protein linked.
+   * 
+   * @param al
+   * @param title
+   */
+  public void addAlignment(AlignmentI al, String title)
   {
-    this.sortAnnotationsBy = sortAnnotationsBy;
+    // TODO: promote to AlignViewportI? applet CutAndPasteTransfer is different
+
+    // JBPComment: title is a largely redundant parameter at the moment
+    // JBPComment: this really should be an 'insert/pre/append' controller
+    // JBPComment: but the DNA/Protein check makes it a bit more complex
+
+    // refactored from FileLoader / CutAndPasteTransfer / SequenceFetcher with
+    // this comment:
+    // TODO: create undo object for this JAL-1101
+
+    /*
+     * If one alignment is protein and one nucleotide, with at least one
+     * sequence name in common, offer to open a linked alignment.
+     */
+    if (AlignmentUtils.isMappable(al, getAlignment()))
+    {
+      if (openLinkedAlignment(al, title))
+      {
+        return;
+      }
+    }
+    // TODO: JAL-407 regardless of above - identical sequences (based on ID and
+    // provenance) should share the same dataset sequence
+
+    for (int i = 0; i < al.getHeight(); i++)
+    {
+      getAlignment().addSequence(al.getSequenceAt(i));
+    }
+    // TODO this call was done by SequenceFetcher but not FileLoader or
+    // CutAndPasteTransfer. Is it needed?
+    // JBPComment: this repositions the view to show the new sequences
+    // JBPComment: so it is needed for UX
+    setEndSeq(getAlignment().getHeight());
+    firePropertyChange("alignment", null, getAlignment().getSequences());
   }
 
-  protected boolean isShowAutocalculatedAbove()
-  {
-    return showAutocalculatedAbove;
+  /**
+   * Show a dialog with the option to open and link (cDNA <-> protein) as a new
+   * alignment. Returns true if the new alignment was opened, false if not,
+   * because the user declined the offer.
+   * 
+   * @param title
+   */
+  protected boolean openLinkedAlignment(AlignmentI al, String title)
+  {
+    String[] options = new String[]
+    { MessageManager.getString("action.no"),
+        MessageManager.getString("label.split_window"),
+        MessageManager.getString("label.new_window"), };
+    final String question = JvSwingUtils.wrapTooltip(true,
+            MessageManager.getString("label.open_split_window?"));
+    int response = JOptionPane.showOptionDialog(Desktop.desktop, question,
+            MessageManager.getString("label.open_split_window"),
+            JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null,
+            options, options[0]);
+
+    if (response != 1 && response != 2)
+    {
+      return false;
+    }
+    final boolean openSplitPane = (response == 1);
+    final boolean openInNewWindow = (response == 2);
+
+    /*
+     * Create the AlignFrame first (which creates the new alignment's datasets),
+     * before attempting sequence mapping.
+     */
+    AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
+    newAlignFrame.setTitle(title);
+
+    /*
+     * Identify protein and dna alignments. Make a copy of this one if opening
+     * in a new split pane.
+     */
+    AlignmentI thisAlignment = openSplitPane ? new Alignment(getAlignment())
+            : getAlignment();
+    AlignmentI protein = al.isNucleotide() ? thisAlignment : al;
+    final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment;
+
+    newAlignFrame.statusBar.setText(MessageManager.formatMessage(
+            "label.successfully_loaded_file", new Object[]
+            { title }));
+
+    // TODO if we want this (e.g. to enable reload of the alignment from file),
+    // we will need to add parameters to the stack.
+    // if (!protocol.equals(AppletFormatAdapter.PASTE))
+    // {
+    // alignFrame.setFileName(file, format);
+    // }
+
+    if (openInNewWindow)
+    {
+      Desktop.addInternalFrame(newAlignFrame, title,
+              AlignFrame.DEFAULT_WIDTH,
+              AlignFrame.DEFAULT_HEIGHT);
+    }
+
+    /*
+     * Map sequences. At least one should get mapped as we have already passed
+     * the test for 'mappability'. Any mappings made will be added to the
+     * protein alignment.
+     */
+    AlignmentUtils.mapProteinToCdna(protein, cdna);
+
+    try
+    {
+      newAlignFrame.setMaximum(jalview.bin.Cache.getDefault(
+              "SHOW_FULLSCREEN",
+              false));
+    } catch (java.beans.PropertyVetoException ex)
+    {
+    }
+
+    if (openSplitPane)
+    {
+      protein = openSplitFrame(newAlignFrame, thisAlignment,
+              protein.getCodonFrames());
+    }
+
+    /*
+     * Register the mappings (held on the protein alignment) with the
+     * StructureSelectionManager (for mouseover linking).
+     */
+    final StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
+    ssm.addMappings(protein.getCodonFrames());
+
+    return true;
   }
 
-  protected void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
-  {
-    this.showAutocalculatedAbove = showAutocalculatedAbove;
+  /**
+   * Helper method to open a new SplitFrame holding linked dna and protein
+   * alignments.
+   * 
+   * @param newAlignFrame
+   *          containing a new alignment to be shown
+   * @param complement
+   *          cdna/protein complement alignment to show in the other split half
+   * @param mappings
+   * @return the protein alignment in the split frame
+   */
+  protected AlignmentI openSplitFrame(AlignFrame newAlignFrame,
+          AlignmentI complement, Set<AlignedCodonFrame> mappings)
+  {
+    /*
+     * Open in split pane. DNA sequence above, protein below.
+     */
+    AlignFrame copyMe = new AlignFrame(complement,
+            AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+    copyMe.setTitle(getAlignPanel().alignFrame.getTitle());
+
+    AlignmentI al = newAlignFrame.viewport.getAlignment();
+    final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
+            : newAlignFrame;
+    final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame
+            : copyMe;
+    AlignmentI protein = proteinFrame.viewport.getAlignment();
+    protein.setCodonFrames(mappings);
+
+    cdnaFrame.setVisible(true);
+    proteinFrame.setVisible(true);
+    String linkedTitle = MessageManager
+            .getString("label.linked_view_title");
+    JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame);
+    Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
+
+    return protein;
   }
 
   public AnnotationColumnChooser getAnnotationColumnSelectionState()
@@ -1209,4 +1091,39 @@ public class AlignViewport extends AlignmentViewport implements
   {
     this.annotationColumnSelectionState = currentAnnotationColumnSelectionState;
   }
+
+  @Override
+  public void setIdWidth(int i)
+  {
+    super.setIdWidth(i);
+    AlignmentPanel ap = getAlignPanel();
+    if (ap != null)
+    {
+      // modify GUI elements to reflect geometry change
+      Dimension idw = getAlignPanel().getIdPanel().getIdCanvas()
+              .getPreferredSize();
+      idw.width = i;
+      getAlignPanel().getIdPanel().getIdCanvas().setPreferredSize(idw);
+    }
+  }
+
+  public Rectangle getExplodedGeometry()
+  {
+    return explodedGeometry;
+  }
+
+  public void setExplodedGeometry(Rectangle explodedPosition)
+  {
+    this.explodedGeometry = explodedPosition;
+  }
+
+  public boolean isGatherViewsHere()
+  {
+    return gatherViewsHere;
+  }
+
+  public void setGatherViewsHere(boolean gatherViewsHere)
+  {
+    this.gatherViewsHere = gatherViewsHere;
+  }
 }
index d8b0757..beafa8c 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.gui;
 
 import jalview.analysis.AnnotationSorter;
+import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
@@ -94,9 +95,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * Creates a new AlignmentPanel object.
    * 
    * @param af
-   *          DOCUMENT ME!
    * @param av
-   *          DOCUMENT ME!
    */
   public AlignmentPanel(AlignFrame af, final AlignViewport av)
   {
@@ -122,8 +121,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     setScrollValues(0, 0);
 
-    setAnnotationVisible(av.isShowAnnotation());
-
     hscroll.addAdjustmentListener(this);
     vscroll.addAdjustmentListener(this);
 
@@ -141,9 +138,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
     });
     fontChanged();
     adjustAnnotationHeight();
-
+    updateLayout();
   }
 
+  @Override
+  public AlignViewportI getAlignViewport()
+  {
+    return av;
+  }
   public void alignmentChanged()
   {
     av.alignmentChanged(this);
@@ -163,9 +165,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
     // to prevent drawing old image
     FontMetrics fm = getFontMetrics(av.getFont());
 
-    scalePanelHolder.setPreferredSize(new Dimension(10, av.charHeight
+    scalePanelHolder.setPreferredSize(new Dimension(10, av.getCharHeight()
             + fm.getDescent()));
-    idSpaceFillerPanel1.setPreferredSize(new Dimension(10, av.charHeight
+    idSpaceFillerPanel1.setPreferredSize(new Dimension(10, av
+            .getCharHeight()
             + fm.getDescent()));
 
     getIdPanel().getIdCanvas().gg = null;
@@ -173,6 +176,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     getAnnotationPanel().adjustPanelHeight();
 
     Dimension d = calculateIdWidth();
+
     d.setSize(d.width + 4, d.height);
     getIdPanel().getIdCanvas().setPreferredSize(d);
     hscrollFillerPanel.setPreferredSize(d);
@@ -181,6 +185,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
     {
       overviewPanel.setBoxPosition();
     }
+    if (this.alignFrame.getSplitViewContainer() != null)
+    {
+      ((SplitFrame) this.alignFrame.getSplitViewContainer()).adjustLayout();
+    }
 
     repaint();
   }
@@ -195,11 +203,21 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public Dimension calculateIdWidth()
   {
     // calculate sensible default width when no preference is available
-
-    int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
-    int maxwidth = Math.max(20,
-            Math.min(afwidth - 200, 2 * afwidth / 3));
-    return calculateIdWidth(maxwidth);
+    Dimension r = null;
+    if (av.getIdWidth() < 0)
+    {
+      int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
+      int maxwidth = Math.max(20, Math.min(afwidth - 200, 2 * afwidth / 3));
+      r = calculateIdWidth(maxwidth);
+      av.setIdWidth(r.width);
+    }
+    else
+    {
+      r = new Dimension();
+      r.width = av.getIdWidth();
+      r.height = 0;
+    }
+    return r;
   }
 
   /**
@@ -339,7 +357,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
           }
         }
       }
-      if (!av.wrapAlignment)
+      if (!av.getWrapAlignment())
       {
         if ((startv = av.getStartRes()) >= start)
         {
@@ -412,7 +430,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   public void setAnnotationVisible(boolean b)
   {
-    if (!av.wrapAlignment)
+    if (!av.getWrapAlignment())
     {
       annotationSpaceFillerHolder.setVisible(b);
       annotationScroller.setVisible(b);
@@ -488,13 +506,16 @@ public class AlignmentPanel extends GAlignmentPanel implements
   }
 
   /**
-   * DOCUMENT ME!
+   * update alignment layout for viewport settings
    * 
    * @param wrap
    *          DOCUMENT ME!
    */
-  public void setWrapAlignment(boolean wrap)
+  public void updateLayout()
   {
+    fontChanged();
+    setAnnotationVisible(av.isShowAnnotation());
+    boolean wrap = av.getWrapAlignment();
     av.startSeq = 0;
     scalePanelHolder.setVisible(!wrap);
     hscroll.setVisible(!wrap);
@@ -605,10 +626,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
       width = av.getColumnSelection().findColumnPosition(width);
     }
 
-    av.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av.charWidth)) - 1);
+    av.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av
+            .getCharWidth())) - 1);
 
-    hextent = getSeqPanel().seqCanvas.getWidth() / av.charWidth;
-    vextent = getSeqPanel().seqCanvas.getHeight() / av.charHeight;
+    hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
+    vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
 
     if (hextent > width)
     {
@@ -652,7 +674,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
-
     int oldX = av.getStartRes();
     int oldY = av.getStartSeq();
 
@@ -859,7 +880,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     int idWidth = getVisibleIdWidth(false);
     FontMetrics fm = getFontMetrics(av.getFont());
-    int scaleHeight = av.charHeight + fm.getDescent();
+    int scaleHeight = av.getCharHeight() + fm.getDescent();
 
     pg.setColor(Color.white);
     pg.fillRect(0, 0, pwidth, pheight);
@@ -949,7 +970,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
 
       pg.setColor(currentColor);
-      pg.fillRect(0, (i - startSeq) * av.charHeight, idWidth,
+      pg.fillRect(0, (i - startSeq) * av.getCharHeight(), idWidth,
               av.getCharHeight());
 
       pg.setColor(currentTextColor);
@@ -966,7 +987,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       pg.drawString(
               seq.getDisplayId(av.getShowJVSuffix()),
               xPos,
-              (((i - startSeq) * av.charHeight) + av.getCharHeight())
+              (((i - startSeq) * av.getCharHeight()) + av.getCharHeight())
                       - (av.getCharHeight() / 5));
     }
 
@@ -981,7 +1002,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
       // draw annotation - need to offset for current scroll position
       int offset = -getAlabels().getScrollOffset();
       pg.translate(0, offset);
-      pg.translate(-idWidth - 3, (endSeq - startSeq) * av.charHeight + 3);
+      pg.translate(-idWidth - 3, (endSeq - startSeq) * av.getCharHeight()
+              + 3);
       getAlabels().drawComponent(pg, idWidth);
       pg.translate(idWidth + 3, 0);
       getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), av,
@@ -1012,7 +1034,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public int printWrappedAlignment(Graphics pg, int pwidth, int pheight,
           int pi) throws PrinterException
   {
-
     int annotationHeight = 0;
     AnnotationLabels labels = null;
     if (av.isShowAnnotation())
@@ -1021,13 +1042,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
       labels = new AnnotationLabels(av);
     }
 
-    int hgap = av.charHeight;
-    if (av.scaleAboveWrapped)
+    int hgap = av.getCharHeight();
+    if (av.getScaleAboveWrapped())
     {
-      hgap += av.charHeight;
+      hgap += av.getCharHeight();
     }
 
-    int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
+    int cHeight = av.getAlignment().getHeight() * av.getCharHeight() + hgap
             + annotationHeight;
 
     int idWidth = getVisibleIdWidth(false);
@@ -1071,18 +1092,19 @@ public class AlignmentPanel extends GAlignmentPanel implements
           xPos = idWidth - fm.stringWidth(string) - 4;
         }
         pg.drawString(string, xPos,
-                ((i * av.charHeight) + ypos + av.charHeight)
-                        - (av.charHeight / 5));
+                ((i * av.getCharHeight()) + ypos + av.getCharHeight())
+                        - (av.getCharHeight() / 5));
       }
       if (labels != null)
       {
         pg.translate(-3, ypos
-                + (av.getAlignment().getHeight() * av.charHeight));
+ + (av.getAlignment().getHeight() * av.getCharHeight()));
 
         pg.setFont(av.getFont());
         labels.drawComponent(pg, idWidth);
         pg.translate(+3, -ypos
-                - (av.getAlignment().getHeight() * av.charHeight));
+                        - (av.getAlignment().getHeight() * av
+                                .getCharHeight()));
       }
 
       ypos += cHeight;
@@ -1149,8 +1171,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (alignFrame != null && !headless)
     {
       alignFrame.setProgressBar(MessageManager.formatMessage(
-              "status.saving_file",
-              new String[]
+              "status.saving_file", new Object[]
               { type.getLabel() }), progress);
     }
     try
@@ -1225,9 +1246,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth);
     }
 
-    int height = ((av.getAlignment().getHeight() + 1) * av.charHeight)
+    int height = ((av.getAlignment().getHeight() + 1) * av.getCharHeight())
             + getScalePanel().getHeight();
-    int width = getVisibleIdWidth(false) + (maxwidth * av.charWidth);
+    int width = getVisibleIdWidth(false) + (maxwidth * av.getCharWidth());
 
     if (av.getWrapAlignment())
     {
@@ -1283,7 +1304,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     // ////////////////////////////////////////////
     int idWidth = getVisibleIdWidth(false);
     FontMetrics fm = getFontMetrics(av.getFont());
-    int scaleHeight = av.charHeight + fm.getDescent();
+    int scaleHeight = av.getCharHeight() + fm.getDescent();
 
     // Gen image map
     // ////////////////////////////////
@@ -1302,33 +1323,32 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
         for (s = 0; s < sSize; s++)
         {
-          sy = s * av.charHeight + scaleHeight;
+          sy = s * av.getCharHeight() + scaleHeight;
 
           SequenceI seq = av.getAlignment().getSequenceAt(s);
-          SequenceFeature[] features = seq.getDatasetSequence()
-                  .getSequenceFeatures();
+          SequenceFeature[] features = seq.getSequenceFeatures();
           SequenceGroup[] groups = av.getAlignment().findAllGroups(seq);
           for (res = 0; res < alwidth; res++)
           {
             text = new StringBuffer();
-            Object obj = null;
+            String triplet = null;
             if (av.getAlignment().isNucleotide())
             {
-              obj = ResidueProperties.nucleotideName.get(seq.getCharAt(res)
+              triplet = ResidueProperties.nucleotideName.get(seq
+                      .getCharAt(res)
                       + "");
             }
             else
             {
-              obj = ResidueProperties.aa2Triplet.get(seq.getCharAt(res)
+              triplet = ResidueProperties.aa2Triplet.get(seq.getCharAt(res)
                       + "");
             }
 
-            if (obj == null)
+            if (triplet == null)
             {
               continue;
             }
 
-            String triplet = obj.toString();
             int alIndex = seq.findPosition(res);
             gSize = groups.length;
             for (g = 0; g < gSize; g++)
@@ -1336,9 +1356,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
               if (text.length() < 1)
               {
                 text.append("<area shape=\"rect\" coords=\""
-                        + (idWidth + res * av.charWidth) + "," + sy + ","
-                        + (idWidth + (res + 1) * av.charWidth) + ","
-                        + (av.charHeight + sy) + "\""
+                        + (idWidth + res * av.getCharWidth()) + "," + sy
+                        + "," + (idWidth + (res + 1) * av.getCharWidth())
+                        + ","
+                        + (av.getCharHeight() + sy) + "\""
                         + " onMouseOver=\"toolTip('" + alIndex + " "
                         + triplet);
               }
@@ -1355,9 +1376,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
               if (text.length() < 1)
               {
                 text.append("<area shape=\"rect\" coords=\""
-                        + (idWidth + res * av.charWidth) + "," + sy + ","
-                        + (idWidth + (res + 1) * av.charWidth) + ","
-                        + (av.charHeight + sy) + "\""
+                        + (idWidth + res * av.getCharWidth()) + "," + sy
+                        + "," + (idWidth + (res + 1) * av.getCharWidth())
+                        + ","
+                        + (av.getCharHeight() + sy) + "\""
                         + " onMouseOver=\"toolTip('" + alIndex + " "
                         + triplet);
               }
@@ -1433,10 +1455,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
     int chunkWidth = getSeqPanel().seqCanvas
             .getWrappedCanvasWidth(seqPanelWidth);
 
-    int hgap = av.charHeight;
-    if (av.scaleAboveWrapped)
+    int hgap = av.getCharHeight();
+    if (av.getScaleAboveWrapped())
     {
-      hgap += av.charHeight;
+      hgap += av.getCharHeight();
     }
 
     int annotationHeight = 0;
@@ -1445,7 +1467,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       annotationHeight = getAnnotationPanel().adjustPanelHeight();
     }
 
-    int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
+    int cHeight = av.getAlignment().getHeight() * av.getCharHeight() + hgap
             + annotationHeight;
 
     int maxwidth = av.getAlignment().getWidth();
@@ -1474,6 +1496,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
               .getStructureSelectionManager();
       ssm.removeStructureViewerListener(getSeqPanel(), null);
       ssm.removeSelectionListener(getSeqPanel());
+      ssm.removeCommandListener(av);
+      ssm.removeStructureViewerListener(getSeqPanel(), null);
+      ssm.removeSelectionListener(getSeqPanel());
       av.setAlignment(null);
       av = null;
     }
index 25802e6..9b25388 100644 (file)
@@ -156,13 +156,13 @@ public class AnnotationExporter extends JPanel
       {
         text = new FeaturesFile().printGFFFormat(ap.av.getAlignment()
                 .getDataset().getSequencesArray(), ap.getFeatureRenderer()
-                .getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());// ap.av.featuresDisplayed//);
+                .getDisplayedFeatureCols(), true, ap.av.isShowNPFeats());// ap.av.featuresDisplayed//);
       }
       else
       {
         text = new FeaturesFile().printJalviewFormat(ap.av.getAlignment()
                 .getDataset().getSequencesArray(), ap.getFeatureRenderer()
-                .getDisplayedFeatureCols(), true, ap.av.isShowNpFeats()); // ap.av.featuresDisplayed);
+                .getDisplayedFeatureCols(), true, ap.av.isShowNPFeats()); // ap.av.featuresDisplayed);
       }
     }
     else
index 9c357a0..a544813 100755 (executable)
  */
 package jalview.gui;
 
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.Annotation;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.io.FormatAdapter;
-import jalview.util.MessageManager;
-
 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.Font;
@@ -49,6 +40,7 @@ import java.awt.geom.AffineTransform;
 import java.awt.image.BufferedImage;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.regex.Pattern;
 
 import javax.swing.JCheckBoxMenuItem;
@@ -58,6 +50,16 @@ import javax.swing.JPopupMenu;
 import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
 
+import jalview.analysis.AlignmentUtils;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.io.FormatAdapter;
+import jalview.util.MessageManager;
+
 /**
  * DOCUMENT ME!
  * 
@@ -67,6 +69,8 @@ import javax.swing.ToolTipManager;
 public class AnnotationLabels extends JPanel implements MouseListener,
         MouseMotionListener, ActionListener
 {
+  private static final Pattern LEFT_ANGLE_BRACKET_PATTERN = Pattern.compile("<");
+
   String TOGGLE_LABELSCALE = MessageManager.getString("label.scale_label_to_column");
 
   String ADDNEW = MessageManager.getString("label.add_new_row");
@@ -408,8 +412,8 @@ public class AnnotationLabels extends JPanel implements MouseListener,
       Dimension d = ap.annotationScroller.getPreferredSize();
       int dif = evt.getY() - oldY;
 
-      dif /= ap.av.charHeight;
-      dif *= ap.av.charHeight;
+      dif /= ap.av.getCharHeight();
+      dif *= ap.av.getCharHeight();
 
       if ((d.height - dif) > 20)
       {
@@ -460,7 +464,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
                 || (desc.substring(0, 6).toLowerCase().indexOf("<html>") < 0))
         {
           // clean the description ready for embedding in html
-          desc = new StringBuffer(Pattern.compile("<").matcher(desc)
+          desc = new StringBuffer(LEFT_ANGLE_BRACKET_PATTERN.matcher(desc)
                   .replaceAll("&lt;"));
           desc.insert(0, "<html>");
         }
@@ -621,11 +625,11 @@ public class AnnotationLabels extends JPanel implements MouseListener,
     item.addActionListener(this);
     pop.add(item);
     // JAL-1264 hide all sequence-specific annotations of this type
-    final String label = aa[selectedRow].label;
     if (selectedRow < aa.length)
     {
       if (aa[selectedRow].sequenceRef != null)
       {
+        final String label = aa[selectedRow].label;
         JMenuItem hideType = new JMenuItem();
         String text = MessageManager.getString("label.hide_all") + " " + label;
         hideType.setText(text);
@@ -634,15 +638,18 @@ public class AnnotationLabels extends JPanel implements MouseListener,
           @Override
           public void actionPerformed(ActionEvent e)
           {
-            for (AlignmentAnnotation ann : ap.av.getAlignment()
-                    .getAlignmentAnnotation())
-            {
-              if (ann.sequenceRef != null && ann.label != null
-                      && ann.label.equals(label))
-              {
-                ann.visible = false;
-              }
-            }
+            AlignmentUtils.showOrHideSequenceAnnotations(
+                    ap.av.getAlignment(), Collections.singleton(label),
+                    null, false, false);
+            // for (AlignmentAnnotation ann : ap.av.getAlignment()
+            // .getAlignmentAnnotation())
+            // {
+            // if (ann.sequenceRef != null && ann.label != null
+            // && ann.label.equals(label))
+            // {
+            // ann.visible = false;
+            // }
+            // }
             refresh();
           }
         });
@@ -665,6 +672,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
     // property methods
     if (selectedRow < aa.length)
     {
+      final String label = aa[selectedRow].label;
       if (!aa[selectedRow].autoCalculated)
       {
         if (aa[selectedRow].graph == AlignmentAnnotation.NO_GRAPH)
@@ -683,10 +691,10 @@ public class AnnotationLabels extends JPanel implements MouseListener,
         pop.addSeparator();
         // av and sequencegroup need to implement same interface for
         final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
-                       MessageManager.getString("label.ignore_gaps_consensus"),
+                        MessageManager.getString("label.ignore_gaps_consensus"),
                 (aa[selectedRow].groupRef != null) ? aa[selectedRow].groupRef
                         .getIgnoreGapsConsensus() : ap.av
-                        .getIgnoreGapsConsensus());
+                        .isIgnoreGapsConsensus());
         final AlignmentAnnotation aaa = aa[selectedRow];
         cbmi.addActionListener(new ActionListener()
         {
@@ -709,7 +717,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
         if (aaa.groupRef != null)
         {
           final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
-                         MessageManager.getString("label.show_group_histogram"),
+                          MessageManager.getString("label.show_group_histogram"),
                   aa[selectedRow].groupRef.isShowConsensusHistogram());
           chist.addActionListener(new ActionListener()
           {
@@ -728,7 +736,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
           });
           pop.add(chist);
           final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem(
-                         MessageManager.getString("label.show_group_logo"),
+                          MessageManager.getString("label.show_group_logo"),
                   aa[selectedRow].groupRef.isShowSequenceLogo());
           cprofl.addActionListener(new ActionListener()
           {
@@ -747,7 +755,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
           });
           pop.add(cprofl);
           final JCheckBoxMenuItem cproflnorm = new JCheckBoxMenuItem(
-                         MessageManager.getString("label.normalise_group_logo"),
+                          MessageManager.getString("label.normalise_group_logo"),
                   aa[selectedRow].groupRef.isNormaliseSequenceLogo());
           cproflnorm.addActionListener(new ActionListener()
           {
@@ -772,7 +780,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
         else
         {
           final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
-                         MessageManager.getString("label.show_histogram"), av.isShowConsensusHistogram());
+                          MessageManager.getString("label.show_histogram"), av.isShowConsensusHistogram());
           chist.addActionListener(new ActionListener()
           {
             public void actionPerformed(ActionEvent e)
@@ -791,7 +799,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
           });
           pop.add(chist);
           final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem(
-                         MessageManager.getString("label.show_logo"), av.isShowSequenceLogo());
+                          MessageManager.getString("label.show_logo"), av.isShowSequenceLogo());
           cprof.addActionListener(new ActionListener()
           {
             public void actionPerformed(ActionEvent e)
@@ -810,7 +818,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
           });
           pop.add(cprof);
           final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem(
-                         MessageManager.getString("label.normalise_logo"), av.isNormaliseSequenceLogo());
+                          MessageManager.getString("label.normalise_logo"), av.isNormaliseSequenceLogo());
           cprofnorm.addActionListener(new ActionListener()
           {
             public void actionPerformed(ActionEvent e)
@@ -1122,7 +1130,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
               dragEvent.getY() - getScrollOffset());
     }
 
-    if (!av.wrapAlignment && ((aa == null) || (aa.length < 1)))
+    if (!av.getWrapAlignment() && ((aa == null) || (aa.length < 1)))
     {
       g.drawString(MessageManager.getString("label.right_click"), 2, 8);
       g.drawString(MessageManager.getString("label.to_add_annotation"), 2,
index e5bed74..c952c13 100755 (executable)
@@ -757,10 +757,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
           int y1)
   {
-    int pady = av.charHeight / 5;
+    int pady = av.getCharHeight() / 5;
     int charOffset = 0;
     graphics.setColor(Color.black);
-    graphics.fillRect(x1, y1, av.charWidth, av.charHeight);
+    graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
 
     if (av.validCharWidth)
     {
@@ -768,9 +768,9 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
       char s = seq.getCharAt(res);
 
-      charOffset = (av.charWidth - fm.charWidth(s)) / 2;
+      charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
       graphics.drawString(String.valueOf(s), charOffset + x1,
-              (y1 + av.charHeight) - pady);
+              (y1 + av.getCharHeight()) - pady);
     }
 
   }
@@ -799,7 +799,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         return;
       }
     }
-    imgWidth = (av.endRes - av.startRes + 1) * av.charWidth;
+    imgWidth = (av.endRes - av.startRes + 1) * av.getCharWidth();
     if (imgWidth < 1)
     {
       return;
@@ -867,7 +867,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       return;
     }
     long stime = System.currentTimeMillis();
-    gg.copyArea(0, 0, imgWidth, getHeight(), -horizontal * av.charWidth, 0);
+    gg.copyArea(0, 0, imgWidth, getHeight(),
+            -horizontal * av.getCharWidth(), 0);
     long mtime = System.currentTimeMillis();
     int sr = av.startRes;
     int er = av.endRes + 1;
@@ -875,7 +876,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
     if (horizontal > 0) // scrollbar pulled right, image to the left
     {
-      transX = (er - sr - horizontal) * av.charWidth;
+      transX = (er - sr - horizontal) * av.getCharWidth();
       sr = er - horizontal;
     }
     else if (horizontal < 0)
@@ -960,7 +961,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     }
 
     g.setColor(Color.white);
-    g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getHeight());
+    g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
 
     g.setFont(av.getFont());
     if (fm == null)
index 822f81c..7894ee2 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.gui;
 
+import jalview.structure.AtomSpec;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -827,8 +828,6 @@ public class AppVarnaBinding extends jalview.ext.varna.JalviewVarnaBinding
 
   public void onWarningEmitted(String s)
   {
-    // TODO Auto-generated method stub
-
   }
 
   public void mouseClicked(MouseEvent e)
@@ -852,127 +851,73 @@ public class AppVarnaBinding extends jalview.ext.varna.JalviewVarnaBinding
 
   public void mouseEntered(MouseEvent arg0)
   {
-    // TODO Auto-generated method stub
-
   }
 
   public void mouseExited(MouseEvent arg0)
   {
-    // TODO Auto-generated method stub
-
   }
 
   public void mousePressed(MouseEvent arg0)
   {
-    // TODO Auto-generated method stub
-
   }
 
   public void mouseReleased(MouseEvent arg0)
   {
-    // TODO Auto-generated method stub
-
-  }
-
-  @Override
-  public Color getColour(int atomIndex, int pdbResNum, String chain,
-          String pdbId)
-  {
-    // TODO Auto-generated method stub
-    return null;
   }
 
   @Override
   public String[] getPdbFile()
   {
-    // TODO Auto-generated method stub
     return null;
   }
 
   @Override
-  public void highlightAtom(int atomIndex, int pdbResNum, String chain,
-          String pdbId)
-  {
-    // TODO Auto-generated method stub
-
-  }
-
-  @Override
-  public void mouseOverStructure(int atomIndex, String strInfo)
-  {
-    // TODO Auto-generated method stub
-
-  }
-
-  @Override
   public void releaseReferences(Object svl)
   {
-    // TODO Auto-generated method stub
-
   }
 
   @Override
   public void updateColours(Object source)
   {
-    // TODO Auto-generated method stub
-
   }
 
   @Override
   public void componentHidden(ComponentEvent e)
   {
-    // TODO Auto-generated method stub
-
   }
 
   @Override
   public void componentMoved(ComponentEvent e)
   {
-    // TODO Auto-generated method stub
-
   }
 
   @Override
   public void componentResized(ComponentEvent e)
   {
-    // TODO Auto-generated method stub
-
   }
 
   @Override
   public void componentShown(ComponentEvent e)
   {
-    // TODO Auto-generated method stub
-
   }
 
   @Override
   public void onStructureRedrawn()
   {
-    // TODO Auto-generated method stub
-
   }
 
   @Override
   public void onZoomLevelChanged()
   {
-    // TODO Auto-generated method stub
-
   }
 
   @Override
   public void onTranslationChanged()
   {
-    // TODO Auto-generated method stub
+  }
 
+  @Override
+  public void highlightAtoms(List<AtomSpec> atoms)
+  {
   }
 }
-
-/*
- * public static void main(String[] args) { JTextField str = new
- * JTextField("ATGC");
- * 
- * AppVarnaBinding vab = new AppVarnaBinding(); vab.varnagui.set_seq(str);
- * vab.varnagui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- * vab.varnagui.pack(); vab.varnagui.setVisible(true); } }
- */
index 78d256c..ea5c75e 100644 (file)
@@ -33,6 +33,7 @@ import javax.swing.event.HyperlinkEvent.EventType;
 import jalview.io.*;
 import jalview.jbgui.*;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 /**
  * Cut'n'paste files into the desktop See JAL-1105
@@ -43,7 +44,7 @@ import jalview.util.MessageManager;
 public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
 {
 
-  AlignViewport viewport;
+  AlignmentViewport viewport;
 
   public CutAndPasteHtmlTransfer()
   {
@@ -102,7 +103,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
   /**
    * DOCUMENT ME!
    */
-  public void setForInput(AlignViewport viewport)
+  public void setForInput(AlignmentViewport viewport)
   {
     this.viewport = viewport;
     if (viewport != null)
index 8b0bafd..22f0a59 100644 (file)
  */
 package jalview.gui;
 
-import java.awt.*;
-import java.awt.datatransfer.*;
-import java.awt.event.*;
-import javax.swing.*;
-
-import jalview.datamodel.*;
-import jalview.io.*;
-import jalview.jbgui.*;
+import jalview.datamodel.Alignment;
+import jalview.io.FormatAdapter;
+import jalview.io.IdentifyFile;
+import jalview.io.JalviewFileChooser;
+import jalview.io.JalviewFileView;
+import jalview.jbgui.GCutAndPasteTransfer;
 import jalview.util.MessageManager;
 
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPopupMenu;
+import javax.swing.SwingUtilities;
+
 /**
  * Cut'n'paste files into the desktop See JAL-1105
  * 
@@ -190,24 +202,19 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
 
     if (al != null)
     {
+      String title = MessageManager.formatMessage(
+              "label.input_cut_paste_params", new String[]
+              { format });
       if (viewport != null)
       {
-        for (int i = 0; i < al.getHeight(); i++)
-        {
-          viewport.getAlignment().addSequence(al.getSequenceAt(i));
-        }
-
-        viewport.firePropertyChange("alignment", null, viewport
-                .getAlignment().getSequences());
+        viewport.addAlignment(al, title);
       }
       else
       {
         AlignFrame af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
                 AlignFrame.DEFAULT_HEIGHT);
         af.currentFileFormat = format;
-        Desktop.addInternalFrame(af, MessageManager.formatMessage(
-                "label.input_cut_paste_params", new String[]
-                { format }), AlignFrame.DEFAULT_WIDTH,
+        Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
                 AlignFrame.DEFAULT_HEIGHT);
         af.statusBar.setText(MessageManager
                 .getString("label.successfully_pasted_alignment_file"));
index 4280db8..fab4cb3 100644 (file)
  */
 package jalview.gui;
 
-import jalview.jbgui.GDasSourceBrowser;
-import jalview.util.MessageManager;
-import jalview.util.TableSorter;
-import jalview.ws.dbsources.das.api.DasSourceRegistryI;
-import jalview.ws.dbsources.das.api.jalviewSourceI;
-
 import java.awt.BorderLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.MouseAdapter;
@@ -52,6 +46,12 @@ import org.biodas.jdas.schema.sources.COORDINATES;
 import org.biodas.jdas.schema.sources.PROP;
 import org.biodas.jdas.schema.sources.VERSION;
 
+import jalview.jbgui.GDasSourceBrowser;
+import jalview.util.MessageManager;
+import jalview.util.TableSorter;
+import jalview.ws.dbsources.das.api.DasSourceRegistryI;
+import jalview.ws.dbsources.das.api.jalviewSourceI;
+
 public class DasSourceBrowser extends GDasSourceBrowser implements
         Runnable, ListSelectionListener
 {
@@ -440,8 +440,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     seqs.setSelected(seqsrc);
     JPanel panel = new JPanel(new BorderLayout());
     JPanel pane12 = new JPanel(new BorderLayout());
-    pane12.add(
-            new JLabel(MessageManager.getString("label.structure_manager")),
+    pane12.add(new JLabel(MessageManager.getString("label.name")),
             BorderLayout.CENTER);
     pane12.add(nametf, BorderLayout.EAST);
     panel.add(pane12, BorderLayout.NORTH);
index 865fbbc..918fc02 100644 (file)
@@ -28,6 +28,7 @@ import jalview.io.JalviewFileChooser;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.GraduatedColor;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.dbsources.das.api.jalviewSourceI;
 
 import java.awt.BorderLayout;
@@ -428,15 +429,13 @@ public class FeatureSettings extends JPanel
     String group;
     for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
     {
-      if (af.getViewport().getAlignment().getSequenceAt(i)
-              .getDatasetSequence().getSequenceFeatures() == null)
+      tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i)
+              .getSequenceFeatures();
+      if (tmpfeatures == null)
       {
         continue;
       }
 
-      tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i)
-              .getDatasetSequence().getSequenceFeatures();
-
       int index = 0;
       while (index < tmpfeatures.length)
       {
@@ -546,7 +545,7 @@ public class FeatureSettings extends JPanel
     {
 
       tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i)
-              .getDatasetSequence().getSequenceFeatures();
+              .getSequenceFeatures();
       if (tmpfeatures == null)
       {
         continue;
@@ -1213,7 +1212,7 @@ public class FeatureSettings extends JPanel
   {
     SequenceI[] dataset, seqs;
     int iSize;
-    AlignViewport vp = af.getViewport();
+    AlignmentViewport vp = af.getViewport();
     if (vp.getSelectionGroup() != null
             && vp.getSelectionGroup().getSize() > 0)
     {
index 5a2271d..3cce82c 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.jbgui.GFinder;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.event.ActionEvent;
 import java.util.Vector;
@@ -52,7 +53,7 @@ public class Finder extends GFinder
 
   private static final int WIDTH = 340;
 
-  AlignViewport av;
+  AlignmentViewport av;
 
   AlignmentPanel ap;
 
@@ -80,7 +81,7 @@ public class Finder extends GFinder
    * @param viewport
    * @param alignPanel
    */
-  public Finder(AlignViewport viewport, AlignmentPanel alignPanel)
+  public Finder(AlignmentViewport viewport, AlignmentPanel alignPanel)
   {
     av = viewport;
     ap = alignPanel;
index 0f65354..fc66965 100755 (executable)
  */
 package jalview.gui;
 
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-
-import jalview.bin.*;
-import jalview.jbgui.*;
+import jalview.bin.Cache;
+import jalview.jbgui.GFontChooser;
 import jalview.util.MessageManager;
 
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.event.ActionEvent;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JLayeredPane;
+import javax.swing.JOptionPane;
+
 /**
  * DOCUMENT ME!
  * 
@@ -167,7 +171,7 @@ public class FontChooser extends GFontChooser
   {
     if (ap != null)
     {
-      ap.av.setFont(oldFont);
+      ap.av.setFont(oldFont, true);
       ap.paintAlignment(true);
     }
     else if (tp != null)
@@ -236,7 +240,7 @@ public class FontChooser extends GFontChooser
     }
     else if (ap != null)
     {
-      ap.av.setFont(newFont);
+      ap.av.setFont(newFont, true);
       ap.fontChanged();
     }
 
index 011edf3..a7a4e34 100755 (executable)
  */
 package jalview.gui;
 
-import java.awt.*;
-import java.awt.image.*;
+import jalview.datamodel.SequenceI;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
 import java.util.List;
 
-import javax.swing.*;
-
-import jalview.datamodel.*;
+import javax.swing.JPanel;
 
 /**
  * DOCUMENT ME!
@@ -94,7 +100,7 @@ public class IdCanvas extends JPanel
   {
     int xPos = 0;
     int panelWidth = getWidth();
-    int charHeight = av.charHeight;
+    int charHeight = av.getCharHeight();
 
     if ((searchResults != null) && searchResults.contains(s))
     {
@@ -128,7 +134,7 @@ public class IdCanvas extends JPanel
     gg.drawString(s.getDisplayId(av.getShowJVSuffix()), xPos,
             (((i - starty + 1) * charHeight) + ypos) - (charHeight / 5));
 
-    if (av.hasHiddenRows() && av.showHiddenMarkers)
+    if (av.hasHiddenRows() && av.getShowHiddenMarkers())
     {
       drawMarker(i, starty, ypos);
     }
@@ -150,7 +156,8 @@ public class IdCanvas extends JPanel
       return;
     }
 
-    gg.copyArea(0, 0, getWidth(), imgHeight, 0, -vertical * av.charHeight);
+    gg.copyArea(0, 0, getWidth(), imgHeight, 0,
+            -vertical * av.getCharHeight());
 
     int ss = av.startSeq;
     int es = av.endSeq;
@@ -166,7 +173,7 @@ public class IdCanvas extends JPanel
       }
       else
       {
-        transY = imgHeight - (vertical * av.charHeight);
+        transY = imgHeight - (vertical * av.getCharHeight());
       }
     }
     else if (vertical < 0)
@@ -211,7 +218,7 @@ public class IdCanvas extends JPanel
     int oldHeight = imgHeight;
 
     imgHeight = getHeight();
-    imgHeight -= (imgHeight % av.charHeight);
+    imgHeight -= (imgHeight % av.getCharHeight());
 
     if (imgHeight < 1)
     {
@@ -245,7 +252,7 @@ public class IdCanvas extends JPanel
    */
   void drawIds(int starty, int endy)
   {
-    if (av.seqNameItalics)
+    if (av.isSeqNameItalics())
     {
       setIdfont(new Font(av.getFont().getName(), Font.ITALIC, av.getFont()
               .getSize()));
@@ -293,13 +300,13 @@ public class IdCanvas extends JPanel
         }
       }
 
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      int hgap = av.getCharHeight();
+      if (av.getScaleAboveWrapped())
       {
-        hgap += av.charHeight;
+        hgap += av.getCharHeight();
       }
 
-      int cHeight = alheight * av.charHeight + hgap + annotationHeight;
+      int cHeight = alheight * av.getCharHeight() + hgap + annotationHeight;
 
       int rowSize = av.getEndRes() - av.getStartRes();
 
@@ -324,9 +331,9 @@ public class IdCanvas extends JPanel
 
         if (labels != null && av.isShowAnnotation())
         {
-          gg.translate(0, ypos + (alheight * av.charHeight));
+          gg.translate(0, ypos + (alheight * av.getCharHeight()));
           labels.drawComponent(gg, getWidth());
-          gg.translate(0, -ypos - (alheight * av.charHeight));
+          gg.translate(0, -ypos - (alheight * av.getCharHeight()));
         }
       }
     }
@@ -376,8 +383,8 @@ public class IdCanvas extends JPanel
 
         gg.setColor(currentColor);
 
-        gg.fillRect(0, (i - starty) * av.charHeight, getWidth(),
-                av.charHeight);
+        gg.fillRect(0, (i - starty) * av.getCharHeight(), getWidth(),
+                av.getCharHeight());
 
         gg.setColor(currentTextColor);
 
@@ -389,10 +396,10 @@ public class IdCanvas extends JPanel
         }
 
         gg.drawString(string, xPos,
-                (((i - starty) * av.charHeight) + av.charHeight)
-                        - (av.charHeight / 5));
+                (((i - starty) * av.getCharHeight()) + av.getCharHeight())
+                        - (av.getCharHeight() / 5));
 
-        if (av.hasHiddenRows() && av.showHiddenMarkers)
+        if (av.hasHiddenRows() && av.getShowHiddenMarkers())
         {
           drawMarker(i, starty, 0);
         }
@@ -441,24 +448,27 @@ public class IdCanvas extends JPanel
     {
       gg.fillPolygon(
               new int[]
-              { getWidth() - av.charHeight, getWidth() - av.charHeight,
+              { getWidth() - av.getCharHeight(),
+                  getWidth() - av.getCharHeight(),
                   getWidth() }, new int[]
               {
-                  (i - starty) * av.charHeight + yoffset,
-                  (i - starty) * av.charHeight + yoffset + av.charHeight
-                          / 4, (i - starty) * av.charHeight + yoffset }, 3);
+                  (i - starty) * av.getCharHeight() + yoffset,
+                  (i - starty) * av.getCharHeight() + yoffset
+                          + av.getCharHeight() / 4,
+                  (i - starty) * av.getCharHeight() + yoffset }, 3);
     }
     if (above)
     {
       gg.fillPolygon(
               new int[]
-              { getWidth() - av.charHeight, getWidth() - av.charHeight,
+              { getWidth() - av.getCharHeight(),
+                  getWidth() - av.getCharHeight(),
                   getWidth() }, new int[]
               {
-                  (i - starty + 1) * av.charHeight + yoffset,
-                  (i - starty + 1) * av.charHeight + yoffset
-                          - av.charHeight / 4,
-                  (i - starty + 1) * av.charHeight + yoffset }, 3);
+                  (i - starty + 1) * av.getCharHeight() + yoffset,
+                  (i - starty + 1) * av.getCharHeight() + yoffset
+                          - av.getCharHeight() / 4,
+                  (i - starty + 1) * av.getCharHeight() + yoffset }, 3);
 
     }
   }
index 6aad6c5..4ea35ca 100755 (executable)
@@ -110,7 +110,7 @@ public class IdPanel extends JPanel implements MouseListener,
       StringBuffer tip = new StringBuffer(64);
       seqAnnotReport
               .createSequenceAnnotationReport(tip, sequence,
-                      av.isShowDbRefs(), av.isShowNpFeats(),
+              av.isShowDBRefs(), av.isShowNPFeats(),
                       sp.seqCanvas.fr.getMinMax());
       setToolTipText("<html>" + sequence.getDisplayId(true) + " "
               + tip.toString() + "</html>");
@@ -323,8 +323,7 @@ public class IdPanel extends JPanel implements MouseListener,
       // build a new links menu based on the current links + any non-positional
       // features
       Vector nlinks = new Vector(Preferences.sequenceURLLinks);
-      SequenceFeature sf[] = sq == null ? null : sq.getDatasetSequence()
-              .getSequenceFeatures();
+      SequenceFeature sf[] = sq == null ? null : sq.getSequenceFeatures();
       for (int sl = 0; sf != null && sl < sf.length; sl++)
       {
         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
@@ -363,6 +362,9 @@ public class IdPanel extends JPanel implements MouseListener,
     {
       selectSeq(seq);
     }
+    // TODO is this addition ok here?
+    av.isSelectionGroupChanged(true);
+
     alignPanel.paintAlignment(true);
   }
 
index e3252d6..71e2040 100755 (executable)
  */
 package jalview.gui;
 
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
+import javax.swing.JPanel;
 
 /**
  * DOCUMENT ME!
@@ -119,13 +124,13 @@ public class IdwidthAdjuster extends JPanel implements MouseListener,
   {
     active = true;
 
-    Dimension d = ap.getIdPanel().getIdCanvas().getPreferredSize();
+    int curwidth = ap.getAlignViewport().getIdWidth();
     int dif = evt.getX() - oldX;
 
-    if (((d.width + dif) > 20) || (dif > 0))
+    if (((curwidth + dif) > 20) || (dif > 0))
     {
-      ap.getIdPanel().getIdCanvas().setPreferredSize(new Dimension(d.width + dif,
-              d.height));
+      ap.getAlignViewport().setIdWidth(curwidth + dif);
+
       ap.paintAlignment(true);
     }
 
index fb53900..8beb26f 100644 (file)
  */
 package jalview.gui;
 
+import java.awt.Rectangle;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.exolab.castor.xml.Unmarshaller;
+
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
+import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
@@ -30,7 +69,6 @@ import jalview.datamodel.SequenceI;
 import jalview.datamodel.StructureViewerModel;
 import jalview.datamodel.StructureViewerModel.StructureData;
 import jalview.schemabinding.version2.AlcodMap;
-import jalview.schemabinding.version2.Alcodon;
 import jalview.schemabinding.version2.AlcodonFrame;
 import jalview.schemabinding.version2.Annotation;
 import jalview.schemabinding.version2.AnnotationColours;
@@ -83,44 +121,6 @@ import jalview.ws.params.ArgumentI;
 import jalview.ws.params.AutoCalcSetting;
 import jalview.ws.params.WsParamSetI;
 
-import java.awt.Rectangle;
-import java.io.BufferedReader;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.Vector;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-import java.util.jar.JarOutputStream;
-
-import javax.swing.JInternalFrame;
-import javax.swing.JOptionPane;
-import javax.swing.SwingUtilities;
-
-import org.exolab.castor.xml.Unmarshaller;
-
 /**
  * Write out the current jalview desktop state as a Jalview XML stream.
  * 
@@ -150,6 +150,12 @@ public class Jalview2XML
 
   boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
 
+  /*
+   * Map of reconstructed AlignFrame objects that appear to have come from
+   * SplitFrame objects (have a dna/protein complement view).
+   */
+  private Map<Viewport, AlignFrame> splitFrameCandidates = new HashMap<Viewport, AlignFrame>();
+
   /**
    * create/return unique hash string for sq
    * 
@@ -298,12 +304,12 @@ public class Jalview2XML
   }
 
   /**
-   * This maintains a list of viewports, the key being the seqSetId. Important
-   * to set historyItem and redoList for multiple views
+   * This maintains a map of viewports, the key being the seqSetId. Important to
+   * set historyItem and redoList for multiple views
    */
-  Hashtable viewportsAdded;
+  Map<String, AlignViewport> viewportsAdded = new HashMap<String, AlignViewport>();
 
-  Hashtable annotationIds = new Hashtable();
+  Map<String, AlignmentAnnotation> annotationIds = new HashMap<String, AlignmentAnnotation>();
 
   String uniqueSetSuffix = "";
 
@@ -359,7 +365,7 @@ public class Jalview2XML
    */
   public void saveState(JarOutputStream jout)
   {
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    AlignFrame[] frames = Desktop.getAlignFrames(); // Desktop.desktop.getAllFrames();
 
     if (frames == null)
     {
@@ -379,67 +385,61 @@ public class Jalview2XML
       // REVERSE ORDER
       for (int i = frames.length - 1; i > -1; i--)
       {
-        if (frames[i] instanceof AlignFrame)
+        AlignFrame af = frames[i];
+        // skip ?
+        if (skipList != null
+                && skipList
+                        .containsKey(af.getViewport().getSequenceSetId()))
         {
-          AlignFrame af = (AlignFrame) frames[i];
-          // skip ?
-          if (skipList != null
-                  && skipList.containsKey(af.getViewport()
-                          .getSequenceSetId()))
-          {
-            continue;
-          }
+          continue;
+        }
+
+        String shortName = af.getTitle();
+
+        if (shortName.indexOf(File.separatorChar) > -1)
+        {
+          shortName = shortName.substring(shortName
+                  .lastIndexOf(File.separatorChar) + 1);
+        }
 
-          String shortName = af.getTitle();
+        int count = 1;
 
-          if (shortName.indexOf(File.separatorChar) > -1)
+        while (shortNames.contains(shortName))
+        {
+          if (shortName.endsWith("_" + (count - 1)))
           {
-            shortName = shortName.substring(shortName
-                    .lastIndexOf(File.separatorChar) + 1);
+            shortName = shortName.substring(0, shortName.lastIndexOf("_"));
           }
 
-          int count = 1;
+          shortName = shortName.concat("_" + count);
+          count++;
+        }
 
-          while (shortNames.contains(shortName))
-          {
-            if (shortName.endsWith("_" + (count - 1)))
-            {
-              shortName = shortName
-                      .substring(0, shortName.lastIndexOf("_"));
-            }
+        shortNames.addElement(shortName);
 
-            shortName = shortName.concat("_" + count);
-            count++;
-          }
+        if (!shortName.endsWith(".xml"))
+        {
+          shortName = shortName + ".xml";
+        }
 
-          shortNames.addElement(shortName);
+        int ap, apSize = af.alignPanels.size();
 
-          if (!shortName.endsWith(".xml"))
+        for (ap = 0; ap < apSize; ap++)
+        {
+          AlignmentPanel apanel = af.alignPanels.get(ap);
+          String fileName = apSize == 1 ? shortName : ap + shortName;
+          if (!fileName.endsWith(".xml"))
           {
-            shortName = shortName + ".xml";
+            fileName = fileName + ".xml";
           }
 
-          int ap, apSize = af.alignPanels.size();
+          saveState(apanel, fileName, jout);
 
-          for (ap = 0; ap < apSize; ap++)
+          String dssid = getDatasetIdRef(af.getViewport().getAlignment()
+                  .getDataset());
+          if (!dsses.containsKey(dssid))
           {
-            AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
-                    .elementAt(ap);
-            String fileName = apSize == 1 ? shortName : ap + shortName;
-            if (!fileName.endsWith(".xml"))
-            {
-              fileName = fileName + ".xml";
-            }
-
-            saveState(apanel, fileName, jout);
-
-            String dssid = getDatasetIdRef(af.getViewport().getAlignment()
-                    .getDataset());
-            if (!dsses.containsKey(dssid))
-            {
-              dsses.put(dssid, af);
-            }
-
+            dsses.put(dssid, af);
           }
         }
       }
@@ -473,15 +473,15 @@ public class Jalview2XML
   {
     try
     {
-      int ap, apSize = af.alignPanels.size();
+      int ap = 0;
+      int apSize = af.alignPanels.size();
       FileOutputStream fos = new FileOutputStream(jarFile);
       JarOutputStream jout = new JarOutputStream(fos);
       Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
-      for (ap = 0; ap < apSize; ap++)
+      for (AlignmentPanel apanel : af.alignPanels)
       {
-        AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
-                .elementAt(ap);
         String jfileName = apSize == 1 ? fileName : fileName + ap;
+        ap++;
         if (!jfileName.endsWith(".xml"))
         {
           jfileName = jfileName + ".xml";
@@ -682,9 +682,9 @@ public class Jalview2XML
         }
       }
 
-      if (jdatasq.getSequenceFeatures() != null)
+      if (jds.getSequenceFeatures() != null)
       {
-        jalview.datamodel.SequenceFeature[] sf = jdatasq
+        jalview.datamodel.SequenceFeature[] sf = jds
                 .getSequenceFeatures();
         int index = 0;
         while (index < sf.length)
@@ -780,8 +780,7 @@ public class Jalview2XML
                 {
                   byte[] data = new byte[(int) file.length()];
                   jout.putNextEntry(new JarEntry(entry.getId()));
-                  dis = new DataInputStream(
-                          new FileInputStream(file));
+                  dis = new DataInputStream(new FileInputStream(file));
                   dis.readFully(data);
 
                   DataOutputStream dout = new DataOutputStream(jout);
@@ -837,31 +836,18 @@ public class Jalview2XML
       jal = av.getAlignment();
     }
     // SAVE MAPPINGS
-    if (jal.getCodonFrames() != null && jal.getCodonFrames().length > 0)
+    if (jal.getCodonFrames() != null)
     {
-      jalview.datamodel.AlignedCodonFrame[] jac = jal.getCodonFrames();
-      for (int i = 0; i < jac.length; i++)
+      Set<AlignedCodonFrame> jac = jal.getCodonFrames();
+      for (AlignedCodonFrame acf : jac)
       {
         AlcodonFrame alc = new AlcodonFrame();
         vamsasSet.addAlcodonFrame(alc);
-        for (int p = 0; p < jac[i].aaWidth; p++)
+        if (acf.getProtMappings() != null
+                && acf.getProtMappings().length > 0)
         {
-          Alcodon cmap = new Alcodon();
-          if (jac[i].codons[p] != null)
-          {
-            // Null codons indicate a gapped column in the translated peptide
-            // alignment.
-            cmap.setPos1(jac[i].codons[p][0]);
-            cmap.setPos2(jac[i].codons[p][1]);
-            cmap.setPos3(jac[i].codons[p][2]);
-          }
-          alc.addAlcodon(cmap);
-        }
-        if (jac[i].getProtMappings() != null
-                && jac[i].getProtMappings().length > 0)
-        {
-          SequenceI[] dnas = jac[i].getdnaSeqs();
-          jalview.datamodel.Mapping[] pmaps = jac[i].getProtMappings();
+          SequenceI[] dnas = acf.getdnaSeqs();
+          jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
           for (int m = 0; m < pmaps.length; m++)
           {
             AlcodMap alcmap = new AlcodMap();
@@ -871,6 +857,37 @@ public class Jalview2XML
             alc.addAlcodMap(alcmap);
           }
         }
+
+//      {
+//        AlcodonFrame alc = new AlcodonFrame();
+//        vamsasSet.addAlcodonFrame(alc);
+//        for (int p = 0; p < acf.aaWidth; p++)
+//        {
+//          Alcodon cmap = new Alcodon();
+//          if (acf.codons[p] != null)
+//          {
+//            // Null codons indicate a gapped column in the translated peptide
+//            // alignment.
+//            cmap.setPos1(acf.codons[p][0]);
+//            cmap.setPos2(acf.codons[p][1]);
+//            cmap.setPos3(acf.codons[p][2]);
+//          }
+//          alc.addAlcodon(cmap);
+//        }
+//        if (acf.getProtMappings() != null
+//                && acf.getProtMappings().length > 0)
+//        {
+//          SequenceI[] dnas = acf.getdnaSeqs();
+//          jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
+//          for (int m = 0; m < pmaps.length; m++)
+//          {
+//            AlcodMap alcmap = new AlcodMap();
+//            alcmap.setDnasq(seqHash(dnas[m]));
+//            alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
+//                    false));
+//            alc.addAlcodMap(alcmap);
+//          }
+//        }
       }
     }
 
@@ -1031,23 +1048,22 @@ public class Jalview2XML
       view.setSequenceSetId(makeHashCode(av.getSequenceSetId(),
               av.getSequenceSetId()));
       view.setId(av.getViewId());
-      view.setViewName(av.viewName);
-      view.setGatheredViews(av.gatherViewsHere);
-
-      if (ap.av.explodedPosition != null)
+      if (av.getCodingComplement() != null)
       {
-        view.setXpos(av.explodedPosition.x);
-        view.setYpos(av.explodedPosition.y);
-        view.setWidth(av.explodedPosition.width);
-        view.setHeight(av.explodedPosition.height);
+        view.setComplementId(av.getCodingComplement().getViewId());
       }
-      else
+      view.setViewName(av.viewName);
+      view.setGatheredViews(av.isGatherViewsHere());
+
+      Rectangle position = ap.av.getExplodedGeometry();
+      if (position == null)
       {
-        view.setXpos(ap.alignFrame.getBounds().x);
-        view.setYpos(ap.alignFrame.getBounds().y);
-        view.setWidth(ap.alignFrame.getBounds().width);
-        view.setHeight(ap.alignFrame.getBounds().height);
+        position = ap.alignFrame.getBounds();
       }
+      view.setXpos(position.x);
+      view.setYpos(position.y);
+      view.setWidth(position.width);
+      view.setHeight(position.height);
 
       view.setStartRes(av.startRes);
       view.setStartSeq(av.startSeq);
@@ -1097,7 +1113,7 @@ public class Jalview2XML
       view.setFontName(av.font.getName());
       view.setFontSize(av.font.getSize());
       view.setFontStyle(av.font.getStyle());
-      view.setRenderGaps(av.renderGaps);
+      view.setRenderGaps(av.isRenderGaps());
       view.setShowAnnotation(av.isShowAnnotation());
       view.setShowBoxes(av.getShowBoxes());
       view.setShowColourText(av.getColourText());
@@ -1107,25 +1123,26 @@ public class Jalview2XML
       view.setShowText(av.getShowText());
       view.setShowUnconserved(av.getShowUnconserved());
       view.setWrapAlignment(av.getWrapAlignment());
-      view.setTextCol1(av.textColour.getRGB());
-      view.setTextCol2(av.textColour2.getRGB());
-      view.setTextColThreshold(av.thresholdTextColour);
+      view.setTextCol1(av.getTextColour().getRGB());
+      view.setTextCol2(av.getTextColour2().getRGB());
+      view.setTextColThreshold(av.getThresholdTextColour());
       view.setShowConsensusHistogram(av.isShowConsensusHistogram());
       view.setShowSequenceLogo(av.isShowSequenceLogo());
       view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo());
       view.setShowGroupConsensus(av.isShowGroupConsensus());
       view.setShowGroupConservation(av.isShowGroupConservation());
-      view.setShowNPfeatureTooltip(av.isShowNpFeats());
-      view.setShowDbRefTooltip(av.isShowDbRefs());
+      view.setShowNPfeatureTooltip(av.isShowNPFeats());
+      view.setShowDbRefTooltip(av.isShowDBRefs());
       view.setFollowHighlight(av.followHighlight);
       view.setFollowSelection(av.followSelection);
-      view.setIgnoreGapsinConsensus(av.getIgnoreGapsConsensus());
+      view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus());
       if (av.getFeaturesDisplayed() != null)
       {
         jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
 
-        String[] renderOrder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                .getRenderOrder().toArray(new String[0]);
+        String[] renderOrder = ap.getSeqPanel().seqCanvas
+                .getFeatureRenderer().getRenderOrder()
+                .toArray(new String[0]);
 
         Vector settingsAdded = new Vector();
         Object gstyle = null;
@@ -1152,7 +1169,8 @@ public class Jalview2XML
             }
             else
             {
-              setting.setColour(ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+              setting.setColour(ap.getSeqPanel().seqCanvas
+                      .getFeatureRenderer()
                       .getColour(renderOrder[ro]).getRGB());
             }
 
@@ -1196,8 +1214,8 @@ public class Jalview2XML
           settingsAdded.addElement(key);
         }
         // is groups actually supposed to be a map here ?
-        en = ap.getSeqPanel().seqCanvas.getFeatureRenderer().getFeatureGroups()
-                .iterator();
+        en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                .getFeatureGroups().iterator();
         Vector groupsAdded = new Vector();
         while (en.hasNext())
         {
@@ -1230,8 +1248,8 @@ public class Jalview2XML
           for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
                   .size(); c++)
           {
-            int[] region = av.getColumnSelection()
-                    .getHiddenColumns().get(c);
+            int[] region = av.getColumnSelection().getHiddenColumns()
+                    .get(c);
             HiddenColumns hc = new HiddenColumns();
             hc.setStart(region[0]);
             hc.setEnd(region[1]);
@@ -1302,17 +1320,14 @@ public class Jalview2XML
           Pdbids pdb, PDBEntry entry, List<String> viewIds,
           String matchedFile, StructureViewerBase viewFrame)
   {
-    final AAStructureBindingModel bindingModel = viewFrame
-            .getBinding();
-    for (int peid = 0; peid < bindingModel
-            .getPdbCount(); peid++)
+    final AAStructureBindingModel bindingModel = viewFrame.getBinding();
+    for (int peid = 0; peid < bindingModel.getPdbCount(); peid++)
     {
       final PDBEntry pdbentry = bindingModel.getPdbEntry(peid);
       final String pdbId = pdbentry.getId();
       if (!pdbId.equals(entry.getId())
               && !(entry.getId().length() > 4 && entry.getId()
-                      .toLowerCase()
-                      .startsWith(pdbId.toLowerCase())))
+                      .toLowerCase().startsWith(pdbId.toLowerCase())))
       {
         continue;
       }
@@ -1320,8 +1335,7 @@ public class Jalview2XML
       {
         matchedFile = pdbentry.getFile();
       }
-      else if (!matchedFile.equals(pdbentry
-              .getFile()))
+      else if (!matchedFile.equals(pdbentry.getFile()))
       {
         Cache.log
                 .warn("Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
@@ -1334,8 +1348,7 @@ public class Jalview2XML
       // 1QIP==1qipA)
       String statestring = viewFrame.getStateInfo();
 
-      for (int smap = 0; smap < viewFrame.getBinding()
-              .getSequence()[peid].length; smap++)
+      for (int smap = 0; smap < viewFrame.getBinding().getSequence()[peid].length; smap++)
       {
         // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
         if (jds == viewFrame.getBinding().getSequence()[peid][smap])
@@ -1349,8 +1362,7 @@ public class Jalview2XML
           final String viewId = viewFrame.getViewId();
           state.setViewId(viewId);
           state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap));
-          state.setColourwithAlignPanel(viewFrame
-                  .isUsedforcolourby(ap));
+          state.setColourwithAlignPanel(viewFrame.isUsedforcolourby(ap));
           state.setColourByJmol(viewFrame.isColouredByViewer());
           /*
            * Only store each structure viewer's state once in each XML document.
@@ -1645,7 +1657,9 @@ public class Jalview2XML
         return false;
       }
     }
-    throw new Error(MessageManager.formatMessage("error.unsupported_version_calcIdparam", new String[]{calcIdParam.toString()}));
+    throw new Error(MessageManager.formatMessage(
+            "error.unsupported_version_calcIdparam", new Object[]
+            { calcIdParam.toString() }));
   }
 
   /**
@@ -1767,20 +1781,20 @@ public class Jalview2XML
       mp = new Mapping();
 
       jalview.util.MapList mlst = jmp.getMap();
-      int r[] = mlst.getFromRanges();
-      for (int s = 0; s < r.length; s += 2)
+      List<int[]> r = mlst.getFromRanges();
+      for (int[] range : r)
       {
         MapListFrom mfrom = new MapListFrom();
-        mfrom.setStart(r[s]);
-        mfrom.setEnd(r[s + 1]);
+        mfrom.setStart(range[0]);
+        mfrom.setEnd(range[1]);
         mp.addMapListFrom(mfrom);
       }
       r = mlst.getToRanges();
-      for (int s = 0; s < r.length; s += 2)
+      for (int[] range : r)
       {
         MapListTo mto = new MapListTo();
-        mto.setStart(r[s]);
-        mto.setEnd(r[s + 1]);
+        mto.setStart(range[0]);
+        mto.setEnd(range[1]);
         mp.addMapListTo(mto);
       }
       mp.setMapFromUnit(mlst.getFromRatio());
@@ -1977,7 +1991,7 @@ public class Jalview2XML
     errorMessage = null;
     uniqueSetSuffix = null;
     seqRefIds = null;
-    viewportsAdded = null;
+    viewportsAdded.clear();
     frefedSequence = null;
 
     if (file.startsWith("http://"))
@@ -2029,17 +2043,13 @@ public class Jalview2XML
     {
       seqRefIds = new HashMap<String, SequenceI>();
     }
-    if (viewportsAdded == null)
-    {
-      viewportsAdded = new Hashtable();
-    }
     if (frefedSequence == null)
     {
       frefedSequence = new Vector();
     }
 
-    jalview.gui.AlignFrame af = null, _af = null;
-    Hashtable gatherToThisFrame = new Hashtable();
+    AlignFrame af = null, _af = null;
+    Map<String, AlignFrame> gatherToThisFrame = new HashMap<String, AlignFrame>();
     final String file = jprovider.getFilename();
     try
     {
@@ -2069,7 +2079,7 @@ public class Jalview2XML
             if (object.getJalviewModelSequence().getViewportCount() > 0)
             {
               af = _af;
-              if (af.viewport.gatherViewsHere)
+              if (af.viewport.isGatherViewsHere())
               {
                 gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
               }
@@ -2139,11 +2149,20 @@ public class Jalview2XML
       Desktop.instance.stopLoading();
     }
 
-    Enumeration en = gatherToThisFrame.elements();
-    while (en.hasMoreElements())
+    /*
+     * Regather multiple views (with the same sequence set id) to the frame (if
+     * any) that is flagged as the one to gather to, i.e. convert them to tabbed
+     * views instead of separate frames. Note this doesn't restore a state where
+     * some expanded views in turn have tabbed views - the last "first tab" read
+     * in will play the role of gatherer for all.
+     */
+    for (AlignFrame fr : gatherToThisFrame.values())
     {
-      Desktop.instance.gatherViews((AlignFrame) en.nextElement());
+      Desktop.instance.gatherViews(fr);
     }
+
+    restoreSplitFrames();
+
     if (errorMessage != null)
     {
       reportErrors();
@@ -2152,6 +2171,102 @@ public class Jalview2XML
   }
 
   /**
+   * Try to reconstruct and display SplitFrame windows, where each contains
+   * complementary dna and protein alignments. Done by pairing up AlignFrame
+   * objects (created earlier) which have complementary viewport ids associated.
+   */
+  protected void restoreSplitFrames()
+  {
+    List<SplitFrame> gatherTo = new ArrayList<SplitFrame>();
+    List<AlignFrame> addedToSplitFrames = new ArrayList<AlignFrame>();
+    Map<String, AlignFrame> dna = new HashMap<String, AlignFrame>();
+
+    /*
+     * Identify the DNA alignments
+     */
+    for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
+            .entrySet())
+    {
+      AlignFrame af = candidate.getValue();
+      if (af.getViewport().getAlignment().isNucleotide())
+      {
+        dna.put(candidate.getKey().getId(), af);
+      }
+    }
+
+    /*
+     * Try to match up the protein complements
+     */
+    for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
+            .entrySet())
+    {
+      AlignFrame af = candidate.getValue();
+      if (!af.getViewport().getAlignment().isNucleotide())
+      {
+        String complementId = candidate.getKey().getComplementId();
+        // only non-null complements should be in the Map
+        if (complementId != null && dna.containsKey(complementId))
+        {
+          final AlignFrame dnaFrame = dna.get(complementId);
+          SplitFrame sf = createSplitFrame(dnaFrame, af);
+          addedToSplitFrames.add(dnaFrame);
+          addedToSplitFrames.add(af);
+          if (af.viewport.isGatherViewsHere())
+          {
+            gatherTo.add(sf);
+          }
+        }
+      }
+    }
+
+    /*
+     * Open any that we failed to pair up (which shouldn't happen!) as
+     * standalone AlignFrame's.
+     */
+    for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
+            .entrySet())
+    {
+      AlignFrame af = candidate.getValue();
+      if (!addedToSplitFrames.contains(af)) {
+        Viewport view = candidate.getKey();
+        Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
+                view.getHeight());
+        System.err.println("Failed to restore view " + view.getTitle()
+                + " to split frame");
+      }
+    }
+
+    /*
+     * Gather back into tabbed views as flagged.
+     */
+    for (SplitFrame sf : gatherTo)
+    {
+      Desktop.instance.gatherViews(sf);
+    }
+
+    splitFrameCandidates.clear();
+  }
+
+  /**
+   * Construct and display one SplitFrame holding DNA and protein alignments.
+   * 
+   * @param dnaFrame
+   * @param proteinFrame
+   * @return
+   */
+  protected SplitFrame createSplitFrame(AlignFrame dnaFrame,
+          AlignFrame proteinFrame)
+  {
+    SplitFrame splitFrame = new SplitFrame(dnaFrame, proteinFrame);
+    String title = MessageManager.getString("label.linked_view_title");
+    int width = (int) dnaFrame.getBounds().getWidth();
+    int height = (int) (dnaFrame.getBounds().getHeight()
+            + proteinFrame.getBounds().getHeight() + 50);
+    Desktop.addInternalFrame(splitFrame, title, width, height);
+    return splitFrame;
+  }
+
+  /**
    * check errorMessage for a valid error message and raise an error box in the
    * GUI or write the current errorMessage to stderr and then clear the error
    * state.
@@ -2188,7 +2303,7 @@ public class Jalview2XML
     errorMessage = null;
   }
 
-  Hashtable<String, String> alreadyLoadedPDB;
+  Map<String, String> alreadyLoadedPDB = new HashMap<String, String>();
 
   /**
    * when set, local views will be updated from view stored in JalviewXML
@@ -2199,11 +2314,6 @@ public class Jalview2XML
 
   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
   {
-    if (alreadyLoadedPDB == null)
-    {
-      alreadyLoadedPDB = new Hashtable();
-    }
-
     if (alreadyLoadedPDB.containsKey(pdbId))
     {
       return alreadyLoadedPDB.get(pdbId).toString();
@@ -2305,10 +2415,10 @@ public class Jalview2XML
     // ////////////////////////////////
     // LOAD SEQUENCES
 
-    Vector hiddenSeqs = null;
+    List<SequenceI> hiddenSeqs = null;
     jalview.datamodel.Sequence jseq;
 
-    ArrayList tmpseqs = new ArrayList();
+    List<SequenceI> tmpseqs = new ArrayList<SequenceI>();
 
     boolean multipleView = false;
 
@@ -2340,10 +2450,10 @@ public class Jalview2XML
       {
         if (hiddenSeqs == null)
         {
-          hiddenSeqs = new Vector();
+          hiddenSeqs = new ArrayList<SequenceI>();
         }
 
-        hiddenSeqs.addElement(seqRefIds.get(seqId));
+        hiddenSeqs.add(seqRefIds.get(seqId));
       }
 
     }
@@ -2351,13 +2461,10 @@ public class Jalview2XML
     // /
     // Create the alignment object from the sequence set
     // ///////////////////////////////
-    jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
-            .size()];
-
-    tmpseqs.toArray(orderedSeqs);
+    SequenceI[] orderedSeqs = tmpseqs
+            .toArray(new SequenceI[tmpseqs.size()]);
 
-    jalview.datamodel.Alignment al = new jalview.datamodel.Alignment(
-            orderedSeqs);
+    Alignment al = new Alignment(orderedSeqs);
 
     // / Add the alignment properties
     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
@@ -2384,7 +2491,7 @@ public class Jalview2XML
     }
     // ///////////////////////////////
 
-    Hashtable pdbloaded = new Hashtable();
+    Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
     if (!multipleView)
     {
       // load sequence features, database references and any associated PDB
@@ -2443,8 +2550,7 @@ public class Jalview2XML
               }
             }
             StructureSelectionManager.getStructureSelectionManager(
-                    Desktop.instance)
-                    .registerPDBEntry(entry);
+                    Desktop.instance).registerPDBEntry(entry);
             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
           }
         }
@@ -2461,35 +2567,13 @@ public class Jalview2XML
       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
       for (int i = 0; i < alc.length; i++)
       {
-        jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
-                alc[i].getAlcodonCount());
-        if (alc[i].getAlcodonCount() > 0)
-        {
-          Alcodon[] alcods = alc[i].getAlcodon();
-          for (int p = 0; p < cf.codons.length; p++)
-          {
-            if (alcods[p].hasPos1() && alcods[p].hasPos2()
-                    && alcods[p].hasPos3())
-            {
-              // translated codons require three valid positions
-              cf.codons[p] = new int[3];
-              cf.codons[p][0] = (int) alcods[p].getPos1();
-              cf.codons[p][1] = (int) alcods[p].getPos2();
-              cf.codons[p][2] = (int) alcods[p].getPos3();
-            }
-            else
-            {
-              cf.codons[p] = null;
-            }
-          }
-        }
+        AlignedCodonFrame cf = new AlignedCodonFrame();
         if (alc[i].getAlcodMapCount() > 0)
         {
           AlcodMap[] maps = alc[i].getAlcodMap();
           for (int m = 0; m < maps.length; m++)
           {
-            SequenceI dnaseq = seqRefIds
-                    .get(maps[m].getDnasq());
+            SequenceI dnaseq = seqRefIds.get(maps[m].getDnasq());
             // Load Mapping
             jalview.datamodel.Mapping mapping = null;
             // attach to dna sequence reference.
@@ -2511,12 +2595,11 @@ public class Jalview2XML
         }
         al.addCodonFrame(cf);
       }
-
     }
 
     // ////////////////////////////////
     // LOAD ANNOTATIONS
-    ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
+    List<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
     /**
      * store any annotations which forward reference a group's ID
      */
@@ -2555,8 +2638,7 @@ public class Jalview2XML
         if (an[i].getId() != null
                 && annotationIds.containsKey(an[i].getId()))
         {
-          jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
-                  .get(an[i].getId());
+          AlignmentAnnotation jda = annotationIds.get(an[i].getId());
           // in principle Visible should always be true for annotation displayed
           // in multiple views
           if (an[i].hasVisible())
@@ -2766,8 +2848,7 @@ public class Jalview2XML
         for (int s = 0; s < groups[i].getSeqCount(); s++)
         {
           String seqId = groups[i].getSeq(s) + "";
-          jalview.datamodel.SequenceI ts = seqRefIds
-                  .get(seqId);
+          jalview.datamodel.SequenceI ts = seqRefIds.get(seqId);
 
           if (ts != null)
           {
@@ -3057,10 +3138,10 @@ public class Jalview2XML
           for (int s = 0; s < structureStateCount; s++)
           {
             // check to see if we haven't already created this structure view
-            final StructureState structureState = ids[p].getStructureState(s);
+            final StructureState structureState = ids[p]
+                    .getStructureState(s);
             String sviewid = (structureState.getViewId() == null) ? null
-                    : structureState.getViewId()
-                            + uniqueSetSuffix;
+                    : structureState.getViewId() + uniqueSetSuffix;
             jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
             // Originally : ids[p].getFile()
             // : TODO: verify external PDB file recovery still works in normal
@@ -3077,8 +3158,8 @@ public class Jalview2XML
             // Desktop.desktop.getComponentAt(x, y);
             // TODO: NOW: check that this recovers the PDB file correctly.
             String pdbFile = loadPDBFile(jprovider, ids[p].getId());
-            jalview.datamodel.SequenceI seq = seqRefIds
-                    .get(jseqs[i].getId() + "");
+            jalview.datamodel.SequenceI seq = seqRefIds.get(jseqs[i]
+                    .getId() + "");
             if (sviewid == null)
             {
               sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
@@ -3086,8 +3167,8 @@ public class Jalview2XML
             }
             if (!structureViewers.containsKey(sviewid))
             {
-              structureViewers.put(sviewid, new StructureViewerModel(x, y, width, height,
-                      false, false, true));
+              structureViewers.put(sviewid, new StructureViewerModel(x, y,
+                      width, height, false, false, true));
               // Legacy pre-2.7 conversion JAL-823 :
               // do not assume any view has to be linked for colour by
               // sequence
@@ -3117,8 +3198,7 @@ public class Jalview2XML
              * pre-2.7 projects)
              */
             boolean colourByViewer = jmoldat.isColourByViewer();
-            colourByViewer &= structureState
-                    .hasColourByJmol() ? structureState
+            colourByViewer &= structureState.hasColourByJmol() ? structureState
                     .getColourByJmol() : true;
             jmoldat.setColourByViewer(colourByViewer);
 
@@ -3155,11 +3235,12 @@ public class Jalview2XML
         }
       }
     }
-      // Instantiate the associated structure views
-      for (Entry<String, StructureViewerModel> entry : structureViewers.entrySet())
+    // Instantiate the associated structure views
+    for (Entry<String, StructureViewerModel> entry : structureViewers
+            .entrySet())
       {
-        createOrLinkStructureViewer(entry, af, ap);
-      }
+      createOrLinkStructureViewer(entry, af, ap);
+    }
   }
 
   /**
@@ -3206,8 +3287,8 @@ public class Jalview2XML
    * @param viewerData
    * @param af
    */
-  protected void createChimeraViewer(Entry<String, StructureViewerModel> viewerData,
-          AlignFrame af)
+  protected void createChimeraViewer(
+          Entry<String, StructureViewerModel> viewerData, AlignFrame af)
   {
     final StructureViewerModel data = viewerData.getValue();
     String chimeraSession = data.getStateData();
@@ -3232,12 +3313,11 @@ public class Jalview2XML
       boolean colourBySequence = data.isColourWithAlignPanel();
 
       // TODO can/should this be done via StructureViewer (like Jmol)?
-      final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs
-              .size()]);
-      final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs.size()][]);
+      final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]);
+      final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs
+              .size()][]);
       new ChimeraViewFrame(chimeraSession, af.alignPanel, pdbArray,
-              seqsArray,
-              colourByChimera, colourBySequence);
+              seqsArray, colourByChimera, colourBySequence);
     }
     else
     {
@@ -3255,7 +3335,8 @@ public class Jalview2XML
    * @param af
    */
   protected void createJmolViewer(
-          final Entry<String, StructureViewerModel> viewerData, AlignFrame af)
+          final Entry<String, StructureViewerModel> viewerData,
+          AlignFrame af)
   {
     final StructureViewerModel svattrib = viewerData.getValue();
     String state = svattrib.getStateData();
@@ -3282,8 +3363,7 @@ public class Jalview2XML
         newFileLoc.append(Platform.escapeString(filedat.getFilePath()));
         pdbfilenames.add(filedat.getFilePath());
         pdbids.add(filedat.getPdbId());
-        seqmaps.add(filedat.getSeqList()
-                .toArray(new SequenceI[0]));
+        seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
         newFileLoc.append("\"");
         cp = ecp + 1; // advance beyond last \" and set cursor so we can
                       // look for next file statement.
@@ -3307,8 +3387,7 @@ public class Jalview2XML
         newFileLoc.append(filedat.getFilePath());
         pdbfilenames.add(filedat.getFilePath());
         pdbids.add(filedat.getPdbId());
-        seqmaps.add(filedat.getSeqList()
-                .toArray(new SequenceI[0]));
+        seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
         newFileLoc.append(" \"");
         newFileLoc.append(filedat.getFilePath());
         newFileLoc.append("\"");
@@ -3414,8 +3493,8 @@ public class Jalview2XML
          * Post jalview 2.4 schema includes structure view id
          */
         if (sviewid != null
-                && ((StructureViewerBase) frame).getViewId().equals(
-                        sviewid))
+                && ((StructureViewerBase) frame).getViewId()
+                        .equals(sviewid))
         {
           comp = (AppJmol) frame;
           // todo: break?
@@ -3600,10 +3679,10 @@ public class Jalview2XML
     }
   }
 
-  AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
-          Alignment al, JalviewModelSequence jms, Viewport view,
-          String uniqueSeqSetId, String viewId,
-          ArrayList<JvAnnotRow> autoAlan)
+  AlignFrame loadViewport(String file, JSeq[] JSEQ,
+          List<SequenceI> hiddenSeqs, Alignment al,
+          JalviewModelSequence jms, Viewport view, String uniqueSeqSetId,
+          String viewId, List<JvAnnotRow> autoAlan)
   {
     AlignFrame af = null;
     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
@@ -3617,19 +3696,18 @@ public class Jalview2XML
               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
     }
 
-    af.viewport.gatherViewsHere = view.getGatheredViews();
+    af.viewport.setGatherViewsHere(view.getGatheredViews());
 
     if (view.getSequenceSetId() != null)
     {
-      jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
-              .get(uniqueSeqSetId);
+      AlignmentViewport av = viewportsAdded.get(uniqueSeqSetId);
 
       af.viewport.setSequenceSetId(uniqueSeqSetId);
       if (av != null)
       {
         // propagate shared settings to this new view
-        af.viewport.historyList = av.historyList;
-        af.viewport.redoList = av.redoList;
+        af.viewport.setHistoryList(av.getHistoryList());
+        af.viewport.setRedoList(av.getRedoList());
       }
       else
       {
@@ -3654,14 +3732,17 @@ public class Jalview2XML
         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
       }
 
-      jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
-              .size()];
-
-      for (int s = 0; s < hiddenSeqs.size(); s++)
-      {
-        hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
-      }
+      // jalview.datamodel.SequenceI[] hseqs = new
+      // jalview.datamodel.SequenceI[hiddenSeqs
+      // .size()];
+      //
+      // for (int s = 0; s < hiddenSeqs.size(); s++)
+      // {
+      // hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
+      // }
 
+      SequenceI[] hseqs = hiddenSeqs.toArray(new SequenceI[hiddenSeqs
+              .size()]);
       af.viewport.hideSequence(hseqs);
 
     }
@@ -3682,27 +3763,27 @@ public class Jalview2XML
     af.viewport.setConservationSelected(view.getConservationSelected());
     af.viewport.setShowJVSuffix(view.getShowFullId());
     af.viewport.setRightAlignIds(view.getRightAlignIds());
-    af.viewport.setFont(new java.awt.Font(view.getFontName(), view
-            .getFontStyle(), view.getFontSize()));
-    af.alignPanel.fontChanged();
+    af.viewport.setFont(
+            new java.awt.Font(view.getFontName(), view.getFontStyle(), view
+                    .getFontSize()), true);
+    // TODO: allow custom charWidth/Heights to be restored by updating them
+    // after setting font - which means set above to false
     af.viewport.setRenderGaps(view.getRenderGaps());
     af.viewport.setWrapAlignment(view.getWrapAlignment());
-    af.alignPanel.setWrapAlignment(view.getWrapAlignment());
     af.viewport.setShowAnnotation(view.getShowAnnotation());
-    af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
 
     af.viewport.setShowBoxes(view.getShowBoxes());
 
     af.viewport.setShowText(view.getShowText());
 
-    af.viewport.textColour = new java.awt.Color(view.getTextCol1());
-    af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
-    af.viewport.thresholdTextColour = view.getTextColThreshold();
+    af.viewport.setTextColour(new java.awt.Color(view.getTextCol1()));
+    af.viewport.setTextColour2(new java.awt.Color(view.getTextCol2()));
+    af.viewport.setThresholdTextColour(view.getTextColThreshold());
     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
             .isShowUnconserved() : false);
     af.viewport.setStartRes(view.getStartRes());
     af.viewport.setStartSeq(view.getStartSeq());
-
+    af.alignPanel.updateLayout();
     ColourSchemeI cs = null;
     // apply colourschemes
     if (view.getBgColour() != null)
@@ -3785,11 +3866,11 @@ public class Jalview2XML
     }
     if (view.hasShowDbRefTooltip())
     {
-      af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
+      af.viewport.setShowDBRefs(view.getShowDbRefTooltip());
     }
     if (view.hasShowNPfeatureTooltip())
     {
-      af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
+      af.viewport.setShowNPFeats(view.hasShowNPfeatureTooltip());
     }
     if (view.hasShowGroupConsensus())
     {
@@ -3910,12 +3991,27 @@ public class Jalview2XML
       }
     }
     af.setMenusFromViewport(af.viewport);
+    
     // TODO: we don't need to do this if the viewport is aready visible.
-    Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
-            view.getHeight());
-    af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
-    reorderAutoannotation(af, al, autoAlan);
-    af.alignPanel.alignmentChanged();
+    /*
+     * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it
+     * has a 'cdna/protein complement' view, in which case save it in order to
+     * populate a SplitFrame once all views have been read in.
+     */
+    String complementaryViewId = view.getComplementId();
+    if (complementaryViewId == null)
+    {
+      Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
+              view.getHeight());
+      // recompute any autoannotation
+      af.alignPanel.updateAnnotation(false, true);
+      reorderAutoannotation(af, al, autoAlan);
+      af.alignPanel.alignmentChanged();
+    }
+    else
+    {
+      splitFrameCandidates.put(view, af);
+    }
     return af;
   }
 
@@ -4045,7 +4141,7 @@ public class Jalview2XML
   }
 
   private void reorderAutoannotation(AlignFrame af, Alignment al,
-          ArrayList<JvAnnotRow> autoAlan)
+          List<JvAnnotRow> autoAlan)
   {
     // copy over visualization settings for autocalculated annotation in the
     // view
@@ -4069,11 +4165,11 @@ public class Jalview2XML
                         + auan.template.getCalcId()), auan);
       }
       int hSize = al.getAlignmentAnnotation().length;
-      ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
+      List<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
       // work through any autoCalculated annotation already on the view
       // removing it if it should be placed in a different location on the
       // annotation panel.
-      List<String> remains = new ArrayList(visan.keySet());
+      List<String> remains = new ArrayList<String>(visan.keySet());
       for (int h = 0; h < hSize; h++)
       {
         jalview.datamodel.AlignmentAnnotation jalan = al
@@ -4245,9 +4341,8 @@ public class Jalview2XML
   {
     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
     // xRef Codon Maps
-    jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
-            .get(vamsasSeq.getId());
-    jalview.datamodel.SequenceI dsq = null;
+    SequenceI sq = seqRefIds.get(vamsasSeq.getId());
+    SequenceI dsq = null;
     if (sq != null && sq.getDatasetSequence() != null)
     {
       dsq = sq.getDatasetSequence();
@@ -4322,7 +4417,7 @@ public class Jalview2XML
     // if (pre || post)
     if (sq != dsq)
     {
-      StringBuffer sb = new StringBuffer();
+      // StringBuffer sb = new StringBuffer();
       String newres = jalview.analysis.AlignSeq.extractGaps(
               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
@@ -4472,14 +4567,14 @@ public class Jalview2XML
          * local sequence definition
          */
         Sequence ms = mc.getSequence();
-        jalview.datamodel.Sequence djs = null;
+        SequenceI djs = null;
         String sqid = ms.getDsseqid();
         if (sqid != null && sqid.length() > 0)
         {
           /*
            * recover dataset sequence
            */
-          djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
+          djs = seqRefIds.get(sqid);
         }
         else
         {
@@ -4538,7 +4633,7 @@ public class Jalview2XML
       frefedSequence = new Vector();
     }
 
-    viewportsAdded = new Hashtable();
+    viewportsAdded.clear();
 
     AlignFrame af = loadFromObject(jm, null, false, null);
     af.alignPanels.clear();
@@ -4685,13 +4780,9 @@ public class Jalview2XML
       }
       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
       {
-        if (annotationIds == null)
-        {
-          annotationIds = new Hashtable();
-        }
         String anid;
-        annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
-        jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
+        AlignmentAnnotation jvann = (AlignmentAnnotation) jvobj;
+        annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvann);
         if (jvann.annotationId == null)
         {
           jvann.annotationId = anid;
index c83f1aa..ef01a50 100755 (executable)
@@ -371,15 +371,14 @@ public class Jalview2XML_V1
     af.viewport.setColourText(view.getShowColourText());
     af.viewport.setConservationSelected(view.getConservationSelected());
     af.viewport.setShowJVSuffix(view.getShowFullId());
-    af.viewport.setFont(new java.awt.Font(view.getFontName(), view
-            .getFontStyle(), view.getFontSize()));
-    af.alignPanel.fontChanged();
+    af.viewport.setFont(
+            new java.awt.Font(view.getFontName(), view.getFontStyle(), view
+                    .getFontSize()), true);
 
     af.viewport.setRenderGaps(view.getRenderGaps());
     af.viewport.setWrapAlignment(view.getWrapAlignment());
-    af.alignPanel.setWrapAlignment(view.getWrapAlignment());
-    af.viewport.setShowAnnotation(view.getShowAnnotation());
-    af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
+
+    af.viewport.setShowAnnotation(view.isShowAnnotation());
     af.viewport.setShowBoxes(view.getShowBoxes());
     af.viewport.setShowText(view.getShowText());
 
@@ -405,6 +404,7 @@ public class Jalview2XML_V1
 
     af.viewport.setGlobalColourScheme(cs);
     af.viewport.setColourAppliesToAllGroups(false);
+    af.alignPanel.updateLayout();
     af.changeColour(cs);
     if (view.getConservationSelected() && cs != null)
     {
index c5dd7b6..a0bc7c9 100644 (file)
@@ -22,8 +22,10 @@ package jalview.gui;
 
 import jalview.util.MessageManager;
 
+import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Font;
+import java.awt.GridLayout;
 import java.awt.Rectangle;
 import java.awt.event.ActionListener;
 
@@ -34,6 +36,7 @@ import javax.swing.JLabel;
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import javax.swing.JPanel;
+import javax.swing.JScrollBar;
 import javax.swing.SwingConstants;
 
 /**
@@ -122,14 +125,16 @@ public final class JvSwingUtils
   public static JPanel addtoLayout(JPanel panel, String tooltip,
           JComponent label, JComponent valBox)
   {
-    JPanel laypanel = new JPanel(), labPanel = new JPanel(), valPanel = new JPanel();
+    JPanel laypanel = new JPanel(new GridLayout(1, 2));
+    JPanel labPanel = new JPanel(new BorderLayout());
+    JPanel valPanel = new JPanel();
     // laypanel.setSize(panel.getPreferredSize());
     // laypanel.setLayout(null);
     labPanel.setBounds(new Rectangle(7, 7, 158, 23));
     valPanel.setBounds(new Rectangle(172, 7, 270, 23));
     // labPanel.setLayout(new GridLayout(1,1));
     // valPanel.setLayout(new GridLayout(1,1));
-    labPanel.add(label);
+    labPanel.add(label, BorderLayout.WEST);
     valPanel.add(valBox);
     laypanel.add(labPanel);
     laypanel.add(valPanel);
@@ -212,6 +217,53 @@ public final class JvSwingUtils
     }
   }
 
+  /**
+   * Returns the proportion of its range that a scrollbar's position represents,
+   * as a value between 0 and 1. For example if the whole range is from 0 to
+   * 200, then a position of 40 gives proportion = 0.2.
+   * 
+   * @see http://www.javalobby.org/java/forums/t33050.html#91885334
+   * 
+   * @param scroll
+   * @return
+   */
+  public static float getScrollBarProportion(JScrollBar scroll)
+  {
+    /*
+     * The extent (scroll handle width) deduction gives the true operating range
+     * of possible positions.
+     */
+    int possibleRange = scroll.getMaximum() - scroll.getMinimum()
+            - scroll.getModel().getExtent();
+    float valueInRange = scroll.getValue()
+            - (scroll.getModel().getExtent() / 2f);
+    float proportion = valueInRange / possibleRange;
+    return proportion;
+  }
+
+  /**
+   * Returns the scroll bar position in its range that would match the given
+   * proportion (between 0 and 1) of the whole. For example if the whole range
+   * is from 0 to 200, then a proportion of 0.25 gives position 50.
+   * 
+   * @param scrollbar
+   * @param proportion
+   * @return
+   */
+  public static int getScrollValueForProportion(JScrollBar scrollbar,
+          float proportion)
+  {
+    /*
+     * The extent (scroll handle width) deduction gives the true operating range
+     * of possible positions.
+     */
+    float fraction = proportion
+            * (scrollbar.getMaximum() - scrollbar.getMinimum() - scrollbar
+                    .getModel().getExtent())
+            + (scrollbar.getModel().getExtent() / 2f);
+    return Math.min(Math.round(fraction), scrollbar.getMaximum());
+  }
+
   public static void jvInitComponent(AbstractButton comp, String i18nString)
   {
     setColorAndFont(comp);
index bed9de1..39a232a 100755 (executable)
@@ -22,10 +22,17 @@ package jalview.gui;
 
 import jalview.renderer.AnnotationRenderer;
 
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.*;
-import javax.swing.*;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionAdapter;
+import java.awt.image.BufferedImage;
+
+import javax.swing.JPanel;
 
 /**
  * DOCUMENT ME!
@@ -135,7 +142,7 @@ public class OverviewPanel extends JPanel implements Runnable
       @Override
       public void mouseDragged(MouseEvent evt)
       {
-        if (!av.wrapAlignment)
+        if (!av.getWrapAlignment())
         {
           // TODO: feature: jv2.5 detect shift drag and update selection from
           // it.
@@ -151,7 +158,7 @@ public class OverviewPanel extends JPanel implements Runnable
       @Override
       public void mousePressed(MouseEvent evt)
       {
-        if (!av.wrapAlignment)
+        if (!av.getWrapAlignment())
         {
           boxX = evt.getX();
           boxY = evt.getY();
index b879092..2674617 100644 (file)
@@ -28,6 +28,7 @@ import jalview.datamodel.SequenceI;
 import jalview.jbgui.GPCAPanel;
 import jalview.schemes.ResidueProperties;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.PCAModel;
 
 import java.awt.BorderLayout;
@@ -67,7 +68,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
 
   AlignmentPanel ap;
 
-  AlignViewport av;
+  AlignmentViewport av;
 
   PCAModel pcaModel;
 
diff --git a/src/jalview/gui/PDBSearchPanel.java b/src/jalview/gui/PDBSearchPanel.java
new file mode 100644 (file)
index 0000000..2d46714
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 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 jalview.jbgui.GPDBSearchPanel;
+import jalview.jbgui.PDBDocFieldPreferences;
+import jalview.util.MessageManager;
+import jalview.ws.dbsources.PDBRestClient;
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+import jalview.ws.uimodel.PDBRestRequest;
+import jalview.ws.uimodel.PDBRestResponse;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.swing.table.DefaultTableModel;
+
+@SuppressWarnings("serial")
+public class PDBSearchPanel extends GPDBSearchPanel
+{
+  private SequenceFetcher seqFetcher;
+
+  private IProgressIndicator progressIdicator;
+
+  private Collection<PDBDocField> wantedFields;
+
+  public PDBSearchPanel(SequenceFetcher seqFetcher)
+  {
+    this.seqFetcher = seqFetcher;
+    this.progressIdicator = (seqFetcher == null) ? null : seqFetcher
+            .getProgressIndicator();
+  }
+
+
+  /**
+   * Action performed when an input is detected on txt_search field.
+   */
+  @Override
+  public void txt_search_ActionPerformed()
+  {
+    boolean allowEmptySequence = false;
+    mainFrame.setTitle(MessageManager
+            .getString("label.pdb_sequence_getcher"));
+    tbl_summary.setModel(new DefaultTableModel());
+    if (txt_search.getText().trim().length() > 0)
+    {
+      long startTime = System.currentTimeMillis();
+
+      String searchTarget = ((PDBDocField) cmb_searchTarget
+              .getSelectedItem()).getCode();
+
+      wantedFields = PDBDocFieldPreferences
+              .getSearchSummaryFields();
+
+      PDBRestRequest request = new PDBRestRequest();
+      request.setAllowEmptySeq(allowEmptySequence);
+      request.setResponseSize(100);
+      request.setFieldToSearchBy(searchTarget + ":");
+      request.setSearchTerm(txt_search.getText());
+      request.setWantedFields(wantedFields);
+
+      PDBRestClient pdbRestCleint = new PDBRestClient();
+      PDBRestResponse resultList = pdbRestCleint.executeRequest(request);
+      if (resultList.getSearchSummary() != null)
+      {
+        tbl_summary.setModel(PDBRestResponse.getTableModel(request,
+                resultList.getSearchSummary()));
+      }
+
+      long endTime = System.currentTimeMillis();
+      int resultSetCount = resultList.getNumberOfItemsFound();
+      String result = (resultSetCount > 1) ? MessageManager
+              .getString("label.results") : MessageManager
+              .getString("label.result");
+      mainFrame.setTitle(frameTitle + " - " + resultSetCount + " " + result
+              + " (" + (endTime - startTime) + " milli secs)");
+    }
+  }
+
+  @Override
+  public void btn_ok_ActionPerformed()
+  {
+    loadSelectedPDBSequencesToAlignment();
+  }
+
+  @Override
+  public void btn_back_ActionPerformed()
+  {
+    mainFrame.dispose();
+    new SequenceFetcher(progressIdicator);
+  }
+
+  @Override
+  public void btn_cancel_ActionPerformed()
+  {
+    mainFrame.dispose();
+  }
+
+  /**
+   * Add the discovered/selected sequences to a target alignment window
+   */
+  public void loadSelectedPDBSequencesToAlignment()
+  {
+    mainFrame.dispose();
+    StringBuilder selectedIds = new StringBuilder();
+    int pdbIdCol = PDBRestClient.getPDBIdColumIndex(wantedFields, false);
+    int[] selectedRows = tbl_summary.getSelectedRows();
+    for (int summaryRow : selectedRows)
+    {
+      String pdbIdStr = tbl_summary.getValueAt(summaryRow, pdbIdCol)
+              .toString();
+      selectedIds.append(";").append(pdbIdStr);
+    }
+
+    String ids = selectedIds.deleteCharAt(0).toString();
+    seqFetcher.textArea.setText(ids);
+    Thread worker = new Thread(seqFetcher);
+    worker.start();
+  }
+
+  /**
+   * Populates search target combo-box options
+   */
+  public void populateCmbSearchTargetOptions()
+  {
+    List<PDBDocField> searchableTargets = new ArrayList<PDBDocField>();
+    searchableTargets.add(PDBDocField.PDB_ID);
+    searchableTargets.add(PDBDocField.PFAM_ACCESSION);
+    searchableTargets.add(PDBDocField.MOLECULE_TYPE);
+    searchableTargets.add(PDBDocField.MOLECULE_NAME);
+    searchableTargets.add(PDBDocField.UNIPROT_ACCESSION);
+    searchableTargets.add(PDBDocField.GENE_NAME);
+    searchableTargets.add(PDBDocField.GENUS);
+    searchableTargets.add(PDBDocField.ALL);
+
+    Collections.sort(searchableTargets, new Comparator<PDBDocField>()
+    {
+      @Override
+      public int compare(PDBDocField o1, PDBDocField o2)
+      {
+        return o1.getName().compareTo(o2.getName());
+      }
+    });
+
+    for (PDBDocField searchTarget : searchableTargets)
+    {
+      cmb_searchTarget.addItem(searchTarget);
+    }
+  }
+
+}
index b6d5a41..b129971 100755 (executable)
  */
 package jalview.gui;
 
-import java.util.*;
-import java.util.List;
-
-import java.awt.*;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
 
-import jalview.datamodel.*;
+import java.awt.Component;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 /**
  * Route datamodel/view update events for a sequence set to any display
@@ -36,57 +39,61 @@ import jalview.datamodel.*;
  */
 public class PaintRefresher
 {
-  static Hashtable components;
+  static Map<String, List<Component>> components = new HashMap<String, List<Component>>();
 
   /**
-   * DOCUMENT ME!
+   * Add the given component to those registered under the given sequence set
+   * id. Does nothing if already added.
    * 
    * @param comp
-   *          DOCUMENT ME!
    * @param al
-   *          DOCUMENT ME!
    */
   public static void Register(Component comp, String seqSetId)
   {
-    if (components == null)
-    {
-      components = new Hashtable();
-    }
-
     if (components.containsKey(seqSetId))
     {
-      Vector comps = (Vector) components.get(seqSetId);
+      List<Component> comps = components.get(seqSetId);
       if (!comps.contains(comp))
       {
-        comps.addElement(comp);
+        comps.add(comp);
       }
     }
     else
     {
-      Vector vcoms = new Vector();
-      vcoms.addElement(comp);
+      List<Component> vcoms = new ArrayList<Component>();
+      vcoms.add(comp);
       components.put(seqSetId, vcoms);
     }
   }
 
+  /**
+   * Remove this component from all registrations. Also removes a registered
+   * sequence set id if there are no remaining components registered against it.
+   * 
+   * @param comp
+   */
   public static void RemoveComponent(Component comp)
   {
-    if (components == null)
-    {
-      return;
-    }
-
-    Enumeration en = components.keys();
-    while (en.hasMoreElements())
+    List<String> emptied = new ArrayList<String>();
+    for (Entry<String, List<Component>> registered : components.entrySet())
     {
-      String id = en.nextElement().toString();
-      Vector comps = (Vector) components.get(id);
+      String id = registered.getKey();
+      List<Component> comps = components.get(id);
       comps.remove(comp);
-      if (comps.size() == 0)
+      if (comps.isEmpty())
       {
-        components.remove(id);
+        emptied.add(id);
       }
     }
+
+    /*
+     * Remove now empty ids after the above (to avoid
+     * ConcurrentModificationException).
+     */
+    for (String id : emptied)
+    {
+      components.remove(id);
+    }
   }
 
   public static void Refresh(Component source, String id)
@@ -97,24 +104,15 @@ public class PaintRefresher
   public static void Refresh(Component source, String id,
           boolean alignmentChanged, boolean validateSequences)
   {
-    if (components == null)
-    {
-      return;
-    }
-
-    Component comp;
-    Vector comps = (Vector) components.get(id);
+    List<Component> comps = components.get(id);
 
     if (comps == null)
     {
       return;
     }
 
-    Enumeration e = comps.elements();
-    while (e.hasMoreElements())
+    for (Component comp : comps)
     {
-      comp = (Component) e.nextElement();
-
       if (comp == source)
       {
         continue;
@@ -242,30 +240,20 @@ public class PaintRefresher
 
   static AlignmentPanel[] getAssociatedPanels(String id)
   {
-    if (components == null)
-    {
-      return new AlignmentPanel[0];
-    }
-    ;
-    Vector comps = (Vector) components.get(id);
+    List<Component> comps = components.get(id);
     if (comps == null)
     {
       return new AlignmentPanel[0];
     }
-    ;
-    Vector tmp = new Vector();
-    int i, iSize = comps.size();
-    for (i = 0; i < iSize; i++)
+    List<AlignmentPanel> tmp = new ArrayList<AlignmentPanel>();
+    for (Component comp : comps)
     {
-      if (comps.elementAt(i) instanceof AlignmentPanel)
+      if (comp instanceof AlignmentPanel)
       {
-        tmp.addElement(comps.elementAt(i));
+        tmp.add((AlignmentPanel) comp);
       }
     }
-    AlignmentPanel[] result = new AlignmentPanel[tmp.size()];
-    tmp.toArray(result);
-
-    return result;
+    return tmp.toArray(new AlignmentPanel[tmp.size()]);
   }
 
 }
index 66ceffe..22f1368 100755 (executable)
@@ -26,6 +26,7 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.jbgui.GPairwiseAlignPanel;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.event.ActionEvent;
 import java.util.Vector;
@@ -39,7 +40,7 @@ import java.util.Vector;
 public class PairwiseAlignPanel extends GPairwiseAlignPanel
 {
 
-  AlignViewport av;
+  AlignmentViewport av;
 
   Vector sequences;
 
@@ -49,7 +50,7 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
    * @param av
    *          DOCUMENT ME!
    */
-  public PairwiseAlignPanel(AlignViewport av)
+  public PairwiseAlignPanel(AlignmentViewport av)
   {
     super();
     this.av = av;
index 7878f75..1263b71 100644 (file)
  */
 package jalview.gui;
 
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Vector;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JColorChooser;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPopupMenu;
+import javax.swing.JRadioButtonMenuItem;
+
 import jalview.analysis.AAFrequency;
 import jalview.analysis.AlignmentAnnotationUtils;
+import jalview.analysis.AlignmentUtils;
 import jalview.analysis.Conservation;
 import jalview.commands.ChangeCaseCommand;
 import jalview.commands.EditCommand;
@@ -58,29 +80,6 @@ import jalview.util.GroupUrlLink.UrlStringTooLongException;
 import jalview.util.MessageManager;
 import jalview.util.UrlLink;
 
-import java.awt.Color;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Hashtable;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.Vector;
-
-import javax.swing.ButtonGroup;
-import javax.swing.JCheckBoxMenuItem;
-import javax.swing.JColorChooser;
-import javax.swing.JMenu;
-import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
-import javax.swing.JPopupMenu;
-import javax.swing.JRadioButtonMenuItem;
-
 /**
  * DOCUMENT ME!
  * 
@@ -125,9 +124,6 @@ public class PopupMenu extends JPopupMenu
 
   protected JRadioButtonMenuItem RNAInteractionColour = new JRadioButtonMenuItem();
 
-  // protected JRadioButtonMenuItem covariationColour = new
-  // JRadioButtonMenuItem();
-
   JRadioButtonMenuItem noColourmenuItem = new JRadioButtonMenuItem();
 
   protected JCheckBoxMenuItem conservationMenuItem = new JCheckBoxMenuItem();
@@ -182,12 +178,6 @@ public class PopupMenu extends JPopupMenu
 
   JMenuItem pdbFromFile = new JMenuItem();
 
-  // JBPNote: Commented these out - Should add these services via the web
-  // services menu system.
-  // JMenuItem ContraFold = new JMenuItem();
-
-  // JMenuItem RNAFold = new JMenuItem();
-
   JMenuItem enterPDB = new JMenuItem();
 
   JMenuItem discoverPDB = new JMenuItem();
@@ -198,13 +188,15 @@ public class PopupMenu extends JPopupMenu
 
   JMenu seqHideAnnotationsMenu = new JMenu();
 
-  JMenuItem seqAddReferenceAnnotations = new JMenuItem();
+  JMenuItem seqAddReferenceAnnotations = new JMenuItem(
+          MessageManager.getString("label.add_reference_annotations"));
 
   JMenu groupShowAnnotationsMenu = new JMenu();
 
   JMenu groupHideAnnotationsMenu = new JMenu();
 
-  JMenuItem groupAddReferenceAnnotations = new JMenuItem();
+  JMenuItem groupAddReferenceAnnotations = new JMenuItem(
+          MessageManager.getString("label.add_reference_annotations"));
 
   JMenuItem sequenceFeature = new JMenuItem();
 
@@ -212,15 +204,12 @@ public class PopupMenu extends JPopupMenu
 
   JMenu jMenu1 = new JMenu();
 
-  JMenu structureMenu = new JMenu();
+  JMenuItem structureMenu = new JMenuItem();
 
   JMenu viewStructureMenu = new JMenu();
 
-  // JMenu colStructureMenu = new JMenu();
   JMenuItem editSequence = new JMenuItem();
 
-  // JMenuItem annotationMenuItem = new JMenuItem();
-
   JMenu groupLinksMenu;
 
   JMenuItem hideInsertions = new JMenuItem();
@@ -273,7 +262,6 @@ public class PopupMenu extends JPopupMenu
     colours.add(BLOSUM62Colour);
     colours.add(purinePyrimidineColour);
     colours.add(RNAInteractionColour);
-    // colours.add(covariationColour);
 
     for (int i = 0; i < jalview.io.FormatAdapter.WRITEABLE_FORMATS.length; i++)
     {
@@ -378,7 +366,6 @@ public class PopupMenu extends JPopupMenu
         {
           structureMenu.remove(viewStructureMenu);
         }
-        // structureMenu.remove(colStructureMenu);
       }
       if (ap.av.getAlignment().isNucleotide() == true)
       {
@@ -392,26 +379,15 @@ public class PopupMenu extends JPopupMenu
             final String structureLine = aa[i].label + " (alignment)";
             menuItem = new JMenuItem();
             menuItem.setText(MessageManager.formatMessage(
-                    "label.2d_rna_structure_line", new String[]
+                    "label.2d_rna_structure_line", new Object[]
                     { structureLine }));
             menuItem.addActionListener(new java.awt.event.ActionListener()
             {
               @Override
               public void actionPerformed(ActionEvent e)
               {
-                // // System.out.println("1:"+structureLine);
-                // System.out.println("1:sname" + seq.getName());
-                // System.out.println("2:seq" + seq);
-                //
-                // // System.out.println("3:"+seq.getSequenceAsString());
-                // System.out.println("3:strucseq" + rnastruc);
-                // // System.out.println("4:struc"+seq.getRNA());
-                // System.out.println("5:name" + seq.getName());
-                // System.out.println("6:ap" + ap);
                 new AppVarna(structureLine, seq, seq.getSequenceAsString(),
                         rnastruc, seq.getName(), ap);
-                // new AppVarna(seq.getName(),seq,rnastruc,seq.getRNA(),
-                // seq.getName(), ap);
                 System.out.println("end");
               }
             });
@@ -419,7 +395,6 @@ public class PopupMenu extends JPopupMenu
           }
         }
 
-        // SequenceFeatures[] test = seq.getSequenceFeatures();
 
         if (seq.getAnnotation() != null)
         {
@@ -433,7 +408,7 @@ public class PopupMenu extends JPopupMenu
               // TODO: make rnastrucF a bit more nice
               menuItem = new JMenuItem();
               menuItem.setText(MessageManager.formatMessage(
-                      "label.2d_rna_sequence_name", new String[]
+                      "label.2d_rna_sequence_name", new Object[]
                       { seq.getName() }));
               menuItem.addActionListener(new java.awt.event.ActionListener()
               {
@@ -469,7 +444,7 @@ public class PopupMenu extends JPopupMenu
               && ap.av.getSelectionGroup().getSize() > 1)
       {
         menuItem = new JMenuItem(MessageManager.formatMessage(
-                "label.represent_group_with", new String[]
+                "label.represent_group_with", new Object[]
                 { seq.getName() }));
         menuItem.addActionListener(new java.awt.event.ActionListener()
         {
@@ -538,7 +513,7 @@ public class PopupMenu extends JPopupMenu
     if (sg != null && sg.getSize() > 0)
     {
       groupName.setText(MessageManager.formatMessage("label.name_param",
-              new String[]
+              new Object[]
               { sg.getName() }));
       groupName.setText(MessageManager
               .getString("label.edit_name_and_description_current_group"));
@@ -639,50 +614,6 @@ public class PopupMenu extends JPopupMenu
                 new PDBEntry[pdbe.size()]), pr = reppdb.values().toArray(
                 new PDBEntry[reppdb.size()]);
         final JMenuItem gpdbview, rpdbview;
-        if (pdbe.size() == 1)
-        {
-          structureMenu.add(gpdbview = new JMenuItem(MessageManager
-                  .formatMessage("label.view_structure_for", new String[]
-                  { sqass.getDisplayId(false) })));
-        }
-        else
-        {
-          structureMenu.add(gpdbview = new JMenuItem(MessageManager
-                  .formatMessage("label.view_all_structures", new String[]
-                  { new Integer(pdbe.size()).toString() })));
-        }
-        gpdbview.setToolTipText(MessageManager
-                .getString("label.open_new_jmol_view_with_all_structures_associated_current_selection_superimpose_using_alignment"));
-        gpdbview.addActionListener(new ActionListener()
-        {
-
-          @Override
-          public void actionPerformed(ActionEvent e)
-          {
-            new StructureViewer(ap.getStructureSelectionManager())
-                    .viewStructures(ap, pe, ap.av.collateForPDB(pe));
-          }
-        });
-        if (reppdb.size() > 1 && reppdb.size() < pdbe.size())
-        {
-          structureMenu.add(rpdbview = new JMenuItem(MessageManager
-                  .formatMessage(
-                          "label.view_all_representative_structures",
-                          new String[]
-                          { new Integer(reppdb.size()).toString() })));
-          rpdbview.setToolTipText(MessageManager
-                  .getString("label.open_new_jmol_view_with_all_representative_structures_associated_current_selection_superimpose_using_alignment"));
-          rpdbview.addActionListener(new ActionListener()
-          {
-
-            @Override
-            public void actionPerformed(ActionEvent e)
-            {
-              new StructureViewer(ap.getStructureSelectionManager())
-                      .viewStructures(ap, pr, ap.av.collateForPDB(pr));
-            }
-          });
-        }
       }
     }
     else
@@ -946,7 +877,7 @@ public class PopupMenu extends JPopupMenu
           final boolean actionIsShow)
   {
     String label = types.toString(); // [a, b, c]
-    label = label.substring(1, label.length() - 1);
+    label = label.substring(1, label.length() - 1); // a, b, c
     final JMenuItem item = new JMenuItem(label);
     item.setToolTipText(calcId);
     item.addActionListener(new java.awt.event.ActionListener()
@@ -954,41 +885,14 @@ public class PopupMenu extends JPopupMenu
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        showHideAnnotation_actionPerformed(types, forSequences, allTypes,
-                actionIsShow);
+        AlignmentUtils.showOrHideSequenceAnnotations(ap.getAlignment(), types,
+                forSequences, allTypes, actionIsShow);
+        refresh();
       }
     });
     showOrHideMenu.add(item);
   }
 
-  /**
-   * Action on selecting a list of annotation type (or the 'all types' values)
-   * to show or hide for the specified sequences.
-   * 
-   * @param types
-   * @param forSequences
-   * @param anyType
-   * @param doShow
-   */
-  protected void showHideAnnotation_actionPerformed(
-          Collection<String> types, List<SequenceI> forSequences,
-          boolean anyType, boolean doShow)
-  {
-    for (AlignmentAnnotation aa : ap.getAlignment()
-            .getAlignmentAnnotation())
-    {
-      if (anyType || types.contains(aa.label))
-      {
-        if ((aa.sequenceRef != null)
-                && forSequences.contains(aa.sequenceRef))
-        {
-          aa.visible = doShow;
-        }
-      }
-    }
-    refresh();
-  }
-
   private void buildGroupURLMenu(SequenceGroup sg, Vector groupLinks)
   {
 
@@ -1116,8 +1020,6 @@ public class PopupMenu extends JPopupMenu
       if (urlset != null)
       {
         int type = urlLink.getGroupURLType() & 3;
-        // System.out.println(urlLink.getGroupURLType()
-        // +" "+((String[])urlset[3])[0]);
         // first two bits ofurlLink type bitfield are sequenceids and sequences
         // TODO: FUTURE: ensure the groupURL menu structure can be generalised
         addshowLink(linkMenus[type], label
@@ -1157,7 +1059,7 @@ public class PopupMenu extends JPopupMenu
   {
     JMenuItem item = new JMenuItem(label);
     item.setToolTipText(MessageManager.formatMessage(
-            "label.open_url_param", new String[]
+            "label.open_url_param", new Object[]
             { url }));
     item.addActionListener(new java.awt.event.ActionListener()
     {
@@ -1428,32 +1330,7 @@ public class PopupMenu extends JPopupMenu
         pdbFromFile_actionPerformed();
       }
     });
-    // RNAFold.setText("From RNA Fold with predict2D");
-    // RNAFold.addActionListener(new ActionListener()
-    // {
-    // public void actionPerformed(ActionEvent e)
-    // {
-    // try {
-    // RNAFold_actionPerformed();
-    // } catch (Exception e1) {
-    // // TODO Auto-generated catch block
-    // e1.printStackTrace();
-    // }
-    // }
-    // });
-    // ContraFold.setText("From Contra Fold with predict2D");
-    // ContraFold.addActionListener(new ActionListener()
-    // {
-    // public void actionPerformed(ActionEvent e)
-    // {
-    // try {
-    // ContraFold_actionPerformed();
-    // } catch (Exception e1) {
-    // // TODO Auto-generated catch block
-    // e1.printStackTrace();
-    // }
-    // }
-    // });
+
     enterPDB.setText(MessageManager.getString("label.enter_pdb_id"));
     enterPDB.addActionListener(new ActionListener()
     {
@@ -1502,7 +1379,22 @@ public class PopupMenu extends JPopupMenu
       }
     });
     jMenu1.setText(MessageManager.getString("label.group"));
-    structureMenu.setText(MessageManager.getString("label.structure"));
+    structureMenu.setText(MessageManager.getString("label.view_structure"));
+    structureMenu.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent actionEvent)
+      {
+        SequenceI[] selectedSeqs = new SequenceI[]
+        { sequence };
+        if (ap.av.getSelectionGroup() != null)
+        {
+          selectedSeqs = ap.av.getSequenceSelection();
+        }
+        new StructureChooser(selectedSeqs, sequence, ap);
+      }
+    });
+
     viewStructureMenu.setText(MessageManager
             .getString("label.view_structure"));
     // colStructureMenu.setText("Colour By Structure");
@@ -1590,10 +1482,8 @@ public class PopupMenu extends JPopupMenu
     if (ap.getAlignment().isNucleotide())
     {
       // JBPNote - commented since the colourscheme isn't functional
-      // colourMenu.add(RNAInteractionColour);
       colourMenu.add(purinePyrimidineColour);
     }
-    // colourMenu.add(covariationColour);
     colourMenu.add(userDefinedColour);
 
     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
@@ -1619,7 +1509,6 @@ public class PopupMenu extends JPopupMenu
     colourMenu.addSeparator();
     colourMenu.add(abovePIDColour);
     colourMenu.add(conservationMenuItem);
-    // colourMenu.add(annotationMenuItem);
     editMenu.add(copy);
     editMenu.add(cut);
     editMenu.add(editSequence);
@@ -1640,9 +1529,6 @@ public class PopupMenu extends JPopupMenu
     jMenu1.add(showColourText);
     jMenu1.add(outline);
     jMenu1.add(displayNonconserved);
-    structureMenu.add(pdbMenu);
-    structureMenu.add(viewStructureMenu);
-    // structureMenu.add(colStructureMenu);
     noColourmenuItem.setText(MessageManager.getString("label.none"));
     noColourmenuItem.addActionListener(new java.awt.event.ActionListener()
     {
@@ -1815,68 +1701,22 @@ public class PopupMenu extends JPopupMenu
   protected void configureReferenceAnnotationsMenu(
           JMenuItem menuItem, List<SequenceI> forSequences)
   {
-    menuItem.setText(MessageManager
-            .getString("label.add_reference_annotations"));
     menuItem.setEnabled(false);
-    if (forSequences == null)
-    {
-      return;
-    }
 
     /*
      * Temporary store to hold distinct calcId / type pairs for the tooltip.
      * Using TreeMap means calcIds are shown in alphabetical order.
      */
     Map<String, String> tipEntries = new TreeMap<String, String>();
-    StringBuilder tooltip = new StringBuilder(64);
-    tooltip.append(MessageManager.getString("label.add_annotations_for"));
-
-    /*
-     * For each sequence selected in the alignment, make a list of any
-     * annotations on the underlying dataset sequence which are not already on
-     * the alignment.
-     * 
-     * Build a map of { alignmentSequence, <List of annotations to add> }
-     */
-    AlignmentI al = this.ap.av.getAlignment();
     final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<SequenceI, List<AlignmentAnnotation>>();
-    for (SequenceI seq : forSequences)
-    {
-      SequenceI dataset = seq.getDatasetSequence();
-      if (dataset == null)
-      {
-        continue;
-      }
-      AlignmentAnnotation[] datasetAnnotations = dataset.getAnnotation();
-      if (datasetAnnotations == null)
-      {
-        continue;
-      }
-      final List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
-      for (AlignmentAnnotation dsann : datasetAnnotations)
-      {
-        /*
-         * Find matching annotations on the alignment.
-         */
-        final Iterable<AlignmentAnnotation> matchedAlignmentAnnotations = al
-                .findAnnotations(seq, dsann.getCalcId(),
-                        dsann.label);
-        if (!matchedAlignmentAnnotations.iterator().hasNext())
-        {
-          result.add(dsann);
-          tipEntries.put(dsann.getCalcId(), dsann.label);
-        }
-      }
-      /*
-       * Save any addable annotations for this sequence
-       */
-      if (!result.isEmpty())
-      {
-        candidates.put(seq, result);
-      }
-    }
+    AlignmentI al = this.ap.av.getAlignment();
+    AlignmentUtils.findAddableReferenceAnnotations(forSequences,
+            tipEntries, candidates, al);
     if (!candidates.isEmpty())
     {
+      StringBuilder tooltip = new StringBuilder(64);
+      tooltip.append(MessageManager.getString("label.add_annotations_for"));
+
       /*
        * Found annotations that could be added. Enable the menu item, and
        * configure its tooltip and action.
@@ -1911,40 +1751,10 @@ public class PopupMenu extends JPopupMenu
   protected void addReferenceAnnotations_actionPerformed(
           Map<SequenceI, List<AlignmentAnnotation>> candidates)
   {
-    /*
-     * Add annotations at the top of the annotation, in the same order as their
-     * related sequences.
-     */
-    for (SequenceI seq : candidates.keySet())
-    {
-      for (AlignmentAnnotation ann : candidates.get(seq))
-      {
-        AlignmentAnnotation copyAnn = new AlignmentAnnotation(ann);
-        int startRes = 0;
-        int endRes = ann.annotations.length;
-        final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
-        if (selectionGroup != null)
-        {
-          startRes = selectionGroup.getStartRes();
-          endRes = selectionGroup.getEndRes();
-        }
-        copyAnn.restrict(startRes, endRes);
-
-        /*
-         * Add to the sequence (sets copyAnn.datasetSequence), unless the
-         * original annotation is already on the sequence.
-         */
-        if (!seq.hasAnnotation(ann))
-        {
-          seq.addAlignmentAnnotation(copyAnn);
-        }
-        // adjust for gaps
-        copyAnn.adjustForAlignment();
-        // add to the alignment and set visible
-        this.ap.getAlignment().addAnnotation(copyAnn);
-        copyAnn.visible = true;
-      }
-    }
+    final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
+    final AlignmentI alignment = this.ap.getAlignment();
+    AlignmentUtils.addReferenceAnnotations(candidates, alignment,
+            selectionGroup);
     refresh();
   }
 
@@ -2006,7 +1816,7 @@ public class PopupMenu extends JPopupMenu
               + MessageManager
                       .formatMessage(
                               "label.create_sequence_details_report_annotation_for",
-                              new String[]
+                              new Object[]
                               { seq.getDisplayId(true) }) + "</h2></p><p>");
       new SequenceAnnotationReport(null)
               .createSequenceAnnotationReport(
@@ -2023,10 +1833,10 @@ public class PopupMenu extends JPopupMenu
     }
     cap.setText("<html>" + contents.toString() + "</html>");
 
-    Desktop.instance.addInternalFrame(cap, MessageManager.formatMessage(
-            "label.sequece_details_for",
-            (sequences.length == 1 ? new String[]
-            { sequences[0].getDisplayId(true) } : new String[]
+    Desktop.addInternalFrame(cap, MessageManager.formatMessage(
+            "label.sequence_details_for",
+            (sequences.length == 1 ? new Object[]
+            { sequences[0].getDisplayId(true) } : new Object[]
             { MessageManager.getString("label.selection") })), 500, 400);
 
   }
@@ -2190,14 +2000,14 @@ public class PopupMenu extends JPopupMenu
       int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup()
               .getName());
 
-      sg.cs.setThreshold(threshold, ap.av.getIgnoreGapsConsensus());
+      sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus());
 
       SliderPanel.showPIDSlider();
     }
     else
     // remove PIDColouring
     {
-      sg.cs.setThreshold(0, ap.av.getIgnoreGapsConsensus());
+      sg.cs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
     }
 
     refresh();
@@ -2548,15 +2358,7 @@ public class PopupMenu extends JPopupMenu
     }
 
     int gsize = sg.getSize();
-    SequenceI[] hseqs;
-
-    hseqs = new SequenceI[gsize];
-
-    int index = 0;
-    for (int i = 0; i < gsize; i++)
-    {
-      hseqs[index++] = sg.getSequenceAt(i);
-    }
+    SequenceI[] hseqs = sg.getSequences().toArray(new SequenceI[gsize]);
 
     ap.av.hideSequence(hseqs);
     // refresh(); TODO: ? needed ?
@@ -2580,7 +2382,8 @@ public class PopupMenu extends JPopupMenu
 
     if (sg != null)
     {
-      int[][] startEnd = ap.av.getVisibleRegionBoundaries(sg.getStartRes(),
+      List<int[]> startEnd = ap.av.getVisibleRegionBoundaries(
+              sg.getStartRes(),
               sg.getEndRes() + 1);
 
       String description;
@@ -2619,7 +2422,7 @@ public class PopupMenu extends JPopupMenu
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
     cap.setForInput(null);
     Desktop.addInternalFrame(cap, MessageManager.formatMessage(
-            "label.alignment_output_command", new String[]
+            "label.alignment_output_command", new Object[]
             { e.getActionCommand() }), 600, 500);
 
     String[] omitHidden = null;
@@ -2638,10 +2441,10 @@ public class PopupMenu extends JPopupMenu
             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
     chooser.setFileView(new jalview.io.JalviewFileView());
     chooser.setDialogTitle(MessageManager.formatMessage(
-            "label.select_pdb_file_for", new String[]
+            "label.select_pdb_file_for", new Object[]
             { sequence.getDisplayId(false) }));
     chooser.setToolTipText(MessageManager.formatMessage(
-            "label.load_pdb_file_associate_with_sequence", new String[]
+            "label.load_pdb_file_associate_with_sequence", new Object[]
             { sequence.getDisplayId(false) }));
 
     int value = chooser.showOpenDialog(null);
@@ -2657,18 +2460,7 @@ public class PopupMenu extends JPopupMenu
 
   }
 
-  // JBNote: commented out - these won't be instantiated here...!
-  // public void RNAFold_actionPerformed() throws Exception
-  // {
-  // Predict2D P2D = new Predict2D();
-  // P2D.getStructure2DFromRNAFold("toto");
-  // }
-  //
-  // public void ContraFold_actionPerformed() throws Exception
-  // {
-  // Predict2D P2D = new Predict2D();
-  // P2D.getStructure2DFromContraFold("toto");
-  // }
+
   public void enterPDB_actionPerformed()
   {
     String id = JOptionPane.showInternalInputDialog(Desktop.desktop,
index ef7ca27..2a24491 100755 (executable)
@@ -62,6 +62,12 @@ import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 public class Preferences extends GPreferences
 {
 
+  public static final String DEFAULT_COLOUR = "DEFAULT_COLOUR";
+
+  public static final String DEFAULT_COLOUR_PROT = "DEFAULT_COLOUR_PROT";
+
+  public static final String DEFAULT_COLOUR_NUC = "DEFAULT_COLOUR_NUC";
+
   public static final String ADD_TEMPFACT_ANN = "ADD_TEMPFACT_ANN";
 
   public static final String ADD_SS_ANN = "ADD_SS_ANN";
@@ -267,10 +273,14 @@ public class Preferences extends GPreferences
      */
     for (int i = ColourSchemeProperty.FIRST_COLOUR; i <= ColourSchemeProperty.LAST_COLOUR; i++)
     {
-      colour.addItem(ColourSchemeProperty.getColourName(i));
+      protColour.addItem(ColourSchemeProperty.getColourName(i));
+      nucColour.addItem(ColourSchemeProperty.getColourName(i));
     }
-    String string = Cache.getDefault("DEFAULT_COLOUR", "None");
-    colour.setSelectedItem(string);
+    String oldProp = Cache.getDefault(DEFAULT_COLOUR, "None");
+    String newProp = Cache.getDefault(DEFAULT_COLOUR_PROT, null);
+    protColour.setSelectedItem(newProp != null ? newProp : oldProp);
+    newProp = Cache.getDefault(DEFAULT_COLOUR_NUC, null);
+    nucColour.setSelectedItem(newProp != null ? newProp : oldProp);
     minColour.setBackground(Cache.getDefaultColour("ANNOTATIONCOLOUR_MIN",
             Color.orange));
     maxColour.setBackground(Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX",
@@ -452,7 +462,9 @@ public class Preferences extends GPreferences
     /*
      * Save Colours settings
      */
-    Cache.applicationProperties.setProperty("DEFAULT_COLOUR", colour
+    Cache.applicationProperties.setProperty(DEFAULT_COLOUR_PROT, protColour
+            .getSelectedItem().toString());
+    Cache.applicationProperties.setProperty(DEFAULT_COLOUR_NUC, nucColour
             .getSelectedItem().toString());
     Cache.setColourProperty("ANNOTATIONCOLOUR_MIN",
             minColour.getBackground());
index eac0d46..e541540 100755 (executable)
@@ -276,10 +276,10 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
     }
     
     CommandI command = historyList.pop();
-    if (ap.av.historyList.contains(command))
+    if (ap.av.getHistoryList().contains(command))
     {
       command.undoCommand(af.getViewAlignments());
-      ap.av.historyList.remove(command);
+      ap.av.getHistoryList().remove(command);
       ap.av.firePropertyChange("alignment", null, ap.av.getAlignment().getSequences());
       af.updateEditMenuBar();
     }
index 7a79636..4241c5f 100755 (executable)
@@ -30,6 +30,7 @@ import jalview.api.RotatableCanvasI;
 import jalview.datamodel.*;
 import jalview.math.*;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 /**
  * DOCUMENT ME!
@@ -101,7 +102,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
 
   float scalefactor = 1;
 
-  AlignViewport av;
+  AlignmentViewport av;
 
   AlignmentPanel ap;
 
index 6ef5222..519f2e2 100755 (executable)
@@ -423,6 +423,8 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
 
     // Fill the selected columns
     ColumnSelection cs = av.getColumnSelection();
+    int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
+
     int s;
     if (cs != null)
     {
@@ -445,7 +447,7 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
 
         if ((sel >= startx) && (sel <= endx))
         {
-          gg.fillRect((sel - startx) * av.charWidth, 0, av.charWidth,
+          gg.fillRect((sel - startx) * avCharWidth, 0, avCharWidth,
                   getHeight());
         }
       }
@@ -456,7 +458,7 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
     int scalestartx = (startx / 10) * 10;
 
     FontMetrics fm = gg.getFontMetrics(av.getFont());
-    int y = av.charHeight - fm.getDescent();
+    int y = avCharHeight - fm.getDescent();
 
     if ((scalestartx % 10) == 0)
     {
@@ -472,26 +474,22 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
       {
         string = String.valueOf(av.getColumnSelection()
                 .adjustForHiddenColumns(i));
-        if ((i - startx - 1) * av.charWidth > maxX)
+        if ((i - startx - 1) * avCharWidth > maxX)
         {
-          gg.drawString(string, (i - startx - 1) * av.charWidth, y);
-          maxX = (i - startx + 1) * av.charWidth + fm.stringWidth(string);
+          gg.drawString(string, (i - startx - 1) * avCharWidth, y);
+          maxX = (i - startx + 1) * avCharWidth + fm.stringWidth(string);
         }
 
-        gg.drawLine(
-                ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
+        gg.drawLine(((i - startx - 1) * avCharWidth) + (avCharWidth / 2),
                 y + 2,
-                ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
+                ((i - startx - 1) * avCharWidth) + (avCharWidth / 2),
                 y + (fm.getDescent() * 2));
-
       }
       else
       {
-        gg.drawLine(
-                ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
-                y + fm.getDescent(),
-                ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
-                y + (fm.getDescent() * 2));
+        gg.drawLine(((i - startx - 1) * avCharWidth) + (avCharWidth / 2),
+                y + fm.getDescent(), ((i - startx - 1) * avCharWidth)
+                + (avCharWidth / 2), y + (fm.getDescent() * 2));
       }
     }
 
@@ -515,10 +513,10 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
           }
 
           gg.fillPolygon(new int[]
-          { res * av.charWidth - av.charHeight / 4,
-              res * av.charWidth + av.charHeight / 4, res * av.charWidth },
+          { res * avCharWidth - avCharHeight / 4,
+              res * avCharWidth + avCharHeight / 4, res * avCharWidth },
                   new int[]
-                  { y - av.charHeight / 2, y - av.charHeight / 2, y + 8 },
+                  { y - avCharHeight / 2, y - avCharHeight / 2, y + 8 },
                   3);
 
         }
@@ -527,7 +525,7 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
       if (reveal != null && reveal[0] > startx && reveal[0] < endx)
       {
         gg.drawString(MessageManager.getString("label.reveal_columns"),
-                reveal[0] * av.charWidth, 0);
+                reveal[0] * avCharWidth, 0);
       }
     }
 
index ff576ec..4c04ad3 100755 (executable)
@@ -81,6 +81,7 @@ public class SeqCanvas extends JComponent
   public SeqCanvas(AlignmentPanel ap)
   {
     this.av = ap.av;
+    updateViewport();
     fr = new FeatureRenderer(ap);
     sr = new SequenceRenderer(av);
     setLayout(new BorderLayout());
@@ -98,6 +99,13 @@ public class SeqCanvas extends JComponent
     return fr;
   }
 
+  int charHeight = 0, charWidth = 0;
+
+  private void updateViewport()
+  {
+    charHeight = av.getCharHeight();
+    charWidth = av.getCharWidth();
+  }
   /**
    * DOCUMENT ME!
    * 
@@ -110,12 +118,12 @@ public class SeqCanvas extends JComponent
    * @param ypos
    *          DOCUMENT ME!
    */
-  void drawNorthScale(Graphics g, int startx, int endx, int ypos)
+  private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
   {
+    updateViewport();
     int scalestartx = startx - (startx % 10) + 10;
 
     g.setColor(Color.black);
-
     // NORTH SCALE
     for (int i = scalestartx; i < endx; i += 10)
     {
@@ -125,12 +133,12 @@ public class SeqCanvas extends JComponent
         value = av.getColumnSelection().adjustForHiddenColumns(value);
       }
 
-      g.drawString(String.valueOf(value), (i - startx - 1) * av.charWidth,
-              ypos - (av.charHeight / 2));
+      g.drawString(String.valueOf(value), (i - startx - 1) * charWidth,
+              ypos - (charHeight / 2));
 
-      g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
-              (ypos + 2) - (av.charHeight / 2),
-              ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
+      g.drawLine(((i - startx - 1) * charWidth) + (charWidth / 2),
+              (ypos + 2) - (charHeight / 2), ((i - startx - 1) * charWidth)
+                      + (charWidth / 2),
               ypos - 2);
     }
   }
@@ -150,7 +158,7 @@ public class SeqCanvas extends JComponent
   void drawWestScale(Graphics g, int startx, int endx, int ypos)
   {
     FontMetrics fm = getFontMetrics(av.getFont());
-    ypos += av.charHeight;
+    ypos += charHeight;
 
     if (av.hasHiddenColumns())
     {
@@ -188,9 +196,9 @@ public class SeqCanvas extends JComponent
       if (value != -1)
       {
         int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))
-                - av.charWidth / 2;
-        g.drawString(value + "", x, (ypos + (i * av.charHeight))
-                - (av.charHeight / 5));
+                - charWidth / 2;
+        g.drawString(value + "", x, (ypos + (i * charHeight))
+                - (charHeight / 5));
       }
     }
   }
@@ -209,7 +217,7 @@ public class SeqCanvas extends JComponent
    */
   void drawEastScale(Graphics g, int startx, int endx, int ypos)
   {
-    ypos += av.charHeight;
+    ypos += charHeight;
 
     if (av.hasHiddenColumns())
     {
@@ -240,8 +248,8 @@ public class SeqCanvas extends JComponent
 
       if (value != -1)
       {
-        g.drawString(String.valueOf(value), 0, (ypos + (i * av.charHeight))
-                - (av.charHeight / 5));
+        g.drawString(String.valueOf(value), 0, (ypos + (i * charHeight))
+                - (charHeight / 5));
       }
     }
   }
@@ -265,10 +273,9 @@ public class SeqCanvas extends JComponent
     }
     fastpainting = true;
     fastPaint = true;
-
-    gg.copyArea(horizontal * av.charWidth, vertical * av.charHeight,
-            imgWidth, imgHeight, -horizontal * av.charWidth, -vertical
-                    * av.charHeight);
+    updateViewport();
+    gg.copyArea(horizontal * charWidth, vertical * charHeight, imgWidth,
+            imgHeight, -horizontal * charWidth, -vertical * charHeight);
 
     int sr = av.startRes;
     int er = av.endRes;
@@ -280,7 +287,7 @@ public class SeqCanvas extends JComponent
     if (horizontal > 0) // scrollbar pulled right, image to the left
     {
       er++;
-      transX = (er - sr - horizontal) * av.charWidth;
+      transX = (er - sr - horizontal) * charWidth;
       sr = er - horizontal;
     }
     else if (horizontal < 0)
@@ -297,7 +304,7 @@ public class SeqCanvas extends JComponent
       }
       else
       {
-        transY = imgHeight - (vertical * av.charHeight);
+        transY = imgHeight - (vertical * charHeight);
       }
     }
     else if (vertical < 0)
@@ -330,6 +337,7 @@ public class SeqCanvas extends JComponent
   // Set this to false to force a full panel paint
   public void paintComponent(Graphics g)
   {
+    updateViewport();
     BufferedImage lcimg = img; // take reference since other threads may null
     // img and call later.
     super.paintComponent(g);
@@ -348,8 +356,8 @@ public class SeqCanvas extends JComponent
     imgWidth = getWidth();
     imgHeight = getHeight();
 
-    imgWidth -= (imgWidth % av.charWidth);
-    imgHeight -= (imgHeight % av.charHeight);
+    imgWidth -= (imgWidth % charWidth);
+    imgHeight -= (imgHeight % charHeight);
 
     if ((imgWidth < 1) || (imgHeight < 1))
     {
@@ -412,17 +420,17 @@ public class SeqCanvas extends JComponent
     LABEL_EAST = 0;
     LABEL_WEST = 0;
 
-    if (av.scaleRightWrapped)
+    if (av.getScaleRightWrapped())
     {
       LABEL_EAST = fm.stringWidth(getMask());
     }
 
-    if (av.scaleLeftWrapped)
+    if (av.getScaleLeftWrapped())
     {
       LABEL_WEST = fm.stringWidth(getMask());
     }
 
-    return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
+    return (cwidth - LABEL_EAST - LABEL_WEST) / charWidth;
   }
 
   /**
@@ -466,28 +474,29 @@ public class SeqCanvas extends JComponent
   public void drawWrappedPanel(Graphics g, int canvasWidth,
           int canvasHeight, int startRes)
   {
+    updateViewport();
     AlignmentI al = av.getAlignment();
 
     FontMetrics fm = getFontMetrics(av.getFont());
 
-    if (av.scaleRightWrapped)
+    if (av.getScaleRightWrapped())
     {
       LABEL_EAST = fm.stringWidth(getMask());
     }
 
-    if (av.scaleLeftWrapped)
+    if (av.getScaleLeftWrapped())
     {
       LABEL_WEST = fm.stringWidth(getMask());
     }
 
-    int hgap = av.charHeight;
-    if (av.scaleAboveWrapped)
+    int hgap = charHeight;
+    if (av.getScaleAboveWrapped())
     {
-      hgap += av.charHeight;
+      hgap += charHeight;
     }
 
-    int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
-    int cHeight = av.getAlignment().getHeight() * av.charHeight;
+    int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
+    int cHeight = av.getAlignment().getHeight() * charHeight;
 
     av.setWrappedWidth(cWidth);
 
@@ -514,12 +523,12 @@ public class SeqCanvas extends JComponent
       g.setFont(av.getFont());
       g.setColor(Color.black);
 
-      if (av.scaleLeftWrapped)
+      if (av.getScaleLeftWrapped())
       {
         drawWestScale(g, startRes, endx, ypos);
       }
 
-      if (av.scaleRightWrapped)
+      if (av.getScaleRightWrapped())
       {
         g.translate(canvasWidth - LABEL_EAST, 0);
         drawEastScale(g, startRes, endx, ypos);
@@ -528,12 +537,12 @@ public class SeqCanvas extends JComponent
 
       g.translate(LABEL_WEST, 0);
 
-      if (av.scaleAboveWrapped)
+      if (av.getScaleAboveWrapped())
       {
         drawNorthScale(g, startRes, endx, ypos);
       }
 
-      if (av.hasHiddenColumns() && av.showHiddenMarkers)
+      if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
       {
         g.setColor(Color.blue);
         int res;
@@ -549,11 +558,11 @@ public class SeqCanvas extends JComponent
           }
 
           gg.fillPolygon(new int[]
-          { res * av.charWidth - av.charHeight / 4,
-              res * av.charWidth + av.charHeight / 4, res * av.charWidth },
+          { res * charWidth - charHeight / 4,
+              res * charWidth + charHeight / 4, res * charWidth },
                   new int[]
-                  { ypos - (av.charHeight / 2), ypos - (av.charHeight / 2),
-                      ypos - (av.charHeight / 2) + 8 }, 3);
+                  { ypos - (charHeight / 2), ypos - (charHeight / 2),
+                      ypos - (charHeight / 2) + 8 }, 3);
 
         }
       }
@@ -564,11 +573,11 @@ public class SeqCanvas extends JComponent
 
       if (clip == null)
       {
-        g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
+        g.setClip(0, 0, cWidth * charWidth, canvasHeight);
       }
       else
       {
-        g.setClip(0, (int) clip.getBounds().getY(), cWidth * av.charWidth,
+        g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
                 (int) clip.getBounds().getHeight());
       }
 
@@ -632,6 +641,7 @@ public class SeqCanvas extends JComponent
           int startSeq,
           int endSeq, int offset)
   {
+    updateViewport();
     if (!av.hasHiddenColumns())
     {
       draw(g1, startRes, endRes, startSeq, endSeq, offset);
@@ -644,9 +654,8 @@ public class SeqCanvas extends JComponent
       int blockStart = startRes;
       int blockEnd = endRes;
 
-      for (int i = 0; regions != null && i < regions.size(); i++)
+      for (int[] region : regions)
       {
-        int[] region = regions.get(i);
         int hideStart = region[0];
         int hideEnd = region[1];
 
@@ -658,7 +667,7 @@ public class SeqCanvas extends JComponent
 
         blockEnd = hideStart - 1;
 
-        g1.translate(screenY * av.charWidth, 0);
+        g1.translate(screenY * charWidth, 0);
 
         draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
 
@@ -666,12 +675,12 @@ public class SeqCanvas extends JComponent
         {
           g1.setColor(Color.blue);
 
-          g1.drawLine((blockEnd - blockStart + 1) * av.charWidth - 1,
-                  0 + offset, (blockEnd - blockStart + 1) * av.charWidth
-                          - 1, (endSeq - startSeq) * av.charHeight + offset);
+          g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
+                  0 + offset, (blockEnd - blockStart + 1) * charWidth - 1,
+                  (endSeq - startSeq) * charHeight + offset);
         }
 
-        g1.translate(-screenY * av.charWidth, 0);
+        g1.translate(-screenY * charWidth, 0);
         screenY += blockEnd - blockStart + 1;
         blockStart = hideEnd + 1;
       }
@@ -679,10 +688,10 @@ public class SeqCanvas extends JComponent
       if (screenY <= (endRes - startRes))
       {
         blockEnd = blockStart + (endRes - startRes) - screenY;
-        g1.translate(screenY * av.charWidth, 0);
+        g1.translate(screenY * charWidth, 0);
         draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
 
-        g1.translate(-screenY * av.charWidth, 0);
+        g1.translate(-screenY * charWidth, 0);
       }
     }
 
@@ -690,11 +699,12 @@ public class SeqCanvas extends JComponent
 
   // int startRes, int endRes, int startSeq, int endSeq, int x, int y,
   // int x1, int x2, int y1, int y2, int startx, int starty,
-  void draw(Graphics g, int startRes, int endRes, int startSeq, int endSeq,
+  private void draw(Graphics g, int startRes, int endRes, int startSeq,
+          int endSeq,
           int offset)
   {
     g.setFont(av.getFont());
-    sr.prepare(g, av.renderGaps);
+    sr.prepare(g, av.isRenderGaps());
 
     SequenceI nextSeq;
 
@@ -710,12 +720,12 @@ public class SeqCanvas extends JComponent
         continue;
       }
       sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
-              startRes, endRes, offset + ((i - startSeq) * av.charHeight));
+              startRes, endRes, offset + ((i - startSeq) * charHeight));
 
       if (av.isShowSequenceFeatures())
       {
         fr.drawSequence(g, nextSeq, startRes, endRes, offset
-                + ((i - startSeq) * av.charHeight));
+                + ((i - startSeq) * charHeight));
       }
 
       // / Highlight search Results once all sequences have been drawn
@@ -730,8 +740,8 @@ public class SeqCanvas extends JComponent
           {
             sr.drawHighlightedText(nextSeq, visibleResults[r],
                     visibleResults[r + 1], (visibleResults[r] - startRes)
-                            * av.charWidth, offset
-                            + ((i - startSeq) * av.charHeight));
+                            * charWidth, offset
+                            + ((i - startSeq) * charHeight));
           }
         }
       }
@@ -739,9 +749,8 @@ public class SeqCanvas extends JComponent
       if (av.cursorMode && cursorY == i && cursorX >= startRes
               && cursorX <= endRes)
       {
-        sr.drawCursor(nextSeq, cursorX,
-                (cursorX - startRes) * av.charWidth, offset
-                        + ((i - startSeq) * av.charHeight));
+        sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
+                offset + ((i - startSeq) * charHeight));
       }
     }
 
@@ -767,7 +776,7 @@ public class SeqCanvas extends JComponent
     int sy = -1;
     int ex = -1;
     int groupIndex = -1;
-    int visWidth = (endRes - startRes + 1) * av.charWidth;
+    int visWidth = (endRes - startRes + 1) * charWidth;
 
     if ((group == null) && (av.getAlignment().getGroups().size() > 0))
     {
@@ -787,16 +796,16 @@ public class SeqCanvas extends JComponent
 
         for (i = startSeq; i < endSeq; i++)
         {
-          sx = (group.getStartRes() - startRes) * av.charWidth;
-          sy = offset + ((i - startSeq) * av.charHeight);
-          ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) - 1;
+          sx = (group.getStartRes() - startRes) * charWidth;
+          sy = offset + ((i - startSeq) * charHeight);
+          ex = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth) - 1;
 
           if (sx + ex < 0 || sx > visWidth)
           {
             continue;
           }
 
-          if ((sx <= (endRes - startRes) * av.charWidth)
+          if ((sx <= (endRes - startRes) * charWidth)
                   && group.getSequences(null).contains(
                           av.getAlignment().getSequenceAt(i)))
           {
@@ -804,7 +813,7 @@ public class SeqCanvas extends JComponent
                     && !group.getSequences(null).contains(
                             av.getAlignment().getSequenceAt(i + 1)))
             {
-              bottom = sy + av.charHeight;
+              bottom = sy + charHeight;
             }
 
             if (!inGroup)
@@ -858,9 +867,9 @@ public class SeqCanvas extends JComponent
                 ex = visWidth;
               }
 
-              else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
+              else if (sx + ex >= (endRes - startRes + 1) * charWidth)
               {
-                ex = (endRes - startRes + 1) * av.charWidth;
+                ex = (endRes - startRes + 1) * charWidth;
               }
 
               if (top != -1)
@@ -882,7 +891,7 @@ public class SeqCanvas extends JComponent
 
         if (inGroup)
         {
-          sy = offset + ((i - startSeq) * av.charHeight);
+          sy = offset + ((i - startSeq) * charHeight);
           if (sx >= 0 && sx < visWidth)
           {
             g.drawLine(sx, oldY, sx, sy);
@@ -903,9 +912,9 @@ public class SeqCanvas extends JComponent
           {
             ex = visWidth;
           }
-          else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
+          else if (sx + ex >= (endRes - startRes + 1) * charWidth)
           {
-            ex = (endRes - startRes + 1) * av.charWidth;
+            ex = (endRes - startRes + 1) * charWidth;
           }
 
           if (top != -1)
index 1cc8715..afd3242 100644 (file)
  */
 package jalview.gui;
 
+import jalview.api.AlignViewportI;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
+import jalview.commands.EditCommand.Edit;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResults.Match;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
@@ -34,7 +37,11 @@ import jalview.structure.SelectionListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.SequenceListener;
 import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasSource;
+import jalview.util.Comparison;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -165,16 +172,17 @@ public class SeqPanel extends JPanel implements MouseListener,
     int res = 0;
     int x = evt.getX();
 
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
 
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      int hgap = av.getCharHeight();
+      if (av.getScaleAboveWrapped())
       {
-        hgap += av.charHeight;
+        hgap += av.getCharHeight();
       }
 
-      int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
+      int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
+              + hgap
               + seqCanvas.getAnnotationHeight();
 
       int y = evt.getY();
@@ -218,15 +226,16 @@ public class SeqPanel extends JPanel implements MouseListener,
     int seq = 0;
     int y = evt.getY();
 
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      int hgap = av.getCharHeight();
+      if (av.getScaleAboveWrapped())
       {
-        hgap += av.charHeight;
+        hgap += av.getCharHeight();
       }
 
-      int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
+      int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
+              + hgap
               + seqCanvas.getAnnotationHeight();
 
       y -= hgap;
@@ -243,22 +252,33 @@ public class SeqPanel extends JPanel implements MouseListener,
     return seq;
   }
 
+  /**
+   * When all of a sequence of edits are complete, put the resulting edit list
+   * on the history stack (undo list), and reset flags for editing in progress.
+   */
   void endEditing()
   {
-    if (editCommand != null && editCommand.getSize() > 0)
+    try
+    {
+      if (editCommand != null && editCommand.getSize() > 0)
+      {
+        ap.alignFrame.addHistoryItem(editCommand);
+        av.firePropertyChange("alignment", null, av.getAlignment()
+                .getSequences());
+      }
+    } finally
     {
-      ap.alignFrame.addHistoryItem(editCommand);
-      av.firePropertyChange("alignment", null, av.getAlignment()
-              .getSequences());
+      /*
+       * Tidy up come what may...
+       */
+      startseq = -1;
+      lastres = -1;
+      editingSeqs = false;
+      groupEditing = false;
+      keyboardNo1 = null;
+      keyboardNo2 = null;
+      editCommand = null;
     }
-
-    startseq = -1;
-    lastres = -1;
-    editingSeqs = false;
-    groupEditing = false;
-    keyboardNo1 = null;
-    keyboardNo2 = null;
-    editCommand = null;
   }
 
   void setCursorRow()
@@ -342,7 +362,7 @@ public class SeqPanel extends JPanel implements MouseListener,
     }
 
     endEditing();
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
       ap.scrollToWrappedVisible(seqCanvas.cursorX);
     }
@@ -356,7 +376,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       {
         ap.scrollUp(false);
       }
-      if (!av.wrapAlignment)
+      if (!av.getWrapAlignment())
       {
         while (seqCanvas.cursorX < av.getColumnSelection()
                 .adjustForHiddenColumns(av.startRes))
@@ -634,10 +654,16 @@ public class SeqPanel extends JPanel implements MouseListener,
         seqCanvas.revalidate();
       }
     }
+    setStatusMessage(results);
     seqCanvas.highlightSearchResults(results);
   }
 
   @Override
+  public VamsasSource getVamsasSource()
+  {
+    return this.ap == null ? null : this.ap.av;
+  }
+  @Override
   public void updateColours(SequenceI seq, int index)
   {
     System.out.println("update the seqPanel colours");
@@ -774,42 +800,72 @@ public class SeqPanel extends JPanel implements MouseListener,
    */
   int setStatusMessage(SequenceI sequence, int res, int seq)
   {
-    int pos = -1;
-    StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
-            + sequence.getName());
+    StringBuilder text = new StringBuilder(32);
 
-    Object obj = null;
+    /*
+     * Sequence number (if known), and sequence name.
+     */
+    String seqno = seq == -1 ? "" : " " + (seq + 1);
+    text.append("Sequence" + seqno + " ID: " + sequence.getName());
+
+    String residue = null;
+    /*
+     * Try to translate the display character to residue name (null for gap).
+     */
+    final String displayChar = String.valueOf(sequence.getCharAt(res));
     if (av.getAlignment().isNucleotide())
     {
-      obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
-              + "");
-      if (obj != null)
+      residue = ResidueProperties.nucleotideName.get(displayChar);
+      if (residue != null)
       {
-        text.append(" Nucleotide: ");
+        text.append(" Nucleotide: ").append(residue);
       }
     }
     else
     {
-      obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
-      if (obj != null)
+      residue = "X".equalsIgnoreCase(displayChar) ? "X"
+              : ResidueProperties.aa2Triplet.get(displayChar);
+      if (residue != null)
       {
-        text.append("  Residue: ");
+        text.append(" Residue: ").append(residue);
       }
     }
 
-    if (obj != null)
+    int pos = -1;
+    if (residue != null)
     {
       pos = sequence.findPosition(res);
-      if (obj != "")
-      {
-        text.append(obj + " (" + pos + ")");
-      }
+      text.append(" (").append(Integer.toString(pos)).append(")");
     }
     ap.alignFrame.statusBar.setText(text.toString());
     return pos;
   }
 
   /**
+   * Set the status bar message to highlight the first matched position in
+   * search results.
+   * 
+   * @param results
+   */
+  private void setStatusMessage(SearchResults results)
+  {
+    List<Match> matches = results.getResults();
+    if (!matches.isEmpty())
+    {
+      Match m = matches.get(0);
+      SequenceI seq = m.getSequence();
+      int sequenceIndex = this.av.getAlignment().findIndex(seq);
+
+      /*
+       * Convert position in sequence (base 1) to sequence character array index
+       * (base 0)
+       */
+      int start = m.getStart() - 1;
+      setStatusMessage(seq, start, sequenceIndex);
+    }
+  }
+
+  /**
    * DOCUMENT ME!
    * 
    * @param evt
@@ -820,7 +876,7 @@ public class SeqPanel extends JPanel implements MouseListener,
   {
     if (mouseWheelPressed)
     {
-      int oldWidth = av.charWidth;
+      int oldWidth = av.getCharWidth();
 
       // Which is bigger, left-right or up-down?
       if (Math.abs(evt.getY() - lastMousePress.getY()) > Math.abs(evt
@@ -842,26 +898,28 @@ public class SeqPanel extends JPanel implements MouseListener,
           fontSize = 1;
         }
 
-        av.setFont(new Font(av.font.getName(), av.font.getStyle(), fontSize));
-        av.charWidth = oldWidth;
+        av.setFont(
+                new Font(av.font.getName(), av.font.getStyle(), fontSize),
+                true);
+        av.setCharWidth(oldWidth);
         ap.fontChanged();
       }
       else
       {
-        if (evt.getX() < lastMousePress.getX() && av.charWidth > 1)
+        if (evt.getX() < lastMousePress.getX() && av.getCharWidth() > 1)
         {
-          av.charWidth--;
+          av.setCharWidth(av.getCharWidth() - 1);
         }
         else if (evt.getX() > lastMousePress.getX())
         {
-          av.charWidth++;
+          av.setCharWidth(av.getCharWidth() + 1);
         }
 
         ap.paintAlignment(false);
       }
 
       FontMetrics fm = getFontMetrics(av.getFont());
-      av.validCharWidth = fm.charWidth('M') <= av.charWidth;
+      av.validCharWidth = fm.charWidth('M') <= av.getCharWidth();
 
       lastMousePress = evt.getPoint();
 
@@ -924,7 +982,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
     }
 
-    StringBuffer message = new StringBuffer();
+    StringBuilder message = new StringBuilder(64);
     if (groupEditing)
     {
       message.append("Edit group:");
@@ -1149,8 +1207,8 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
         else
         {
-          editCommand.appendEdit(Action.INSERT_GAP, groupSeqs,
-                  startres, startres - lastres, av.getAlignment(), true);
+          appendEdit(Action.INSERT_GAP, groupSeqs, startres, startres
+                  - lastres);
         }
       }
       else
@@ -1165,8 +1223,8 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
         else
         {
-          editCommand.appendEdit(Action.DELETE_GAP, groupSeqs,
-                  startres, lastres - startres, av.getAlignment(), true);
+          appendEdit(Action.DELETE_GAP, groupSeqs, startres, lastres
+                  - startres);
         }
 
       }
@@ -1187,8 +1245,8 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
         else
         {
-          editCommand.appendEdit(Action.INSERT_GAP, new SequenceI[]
-          { seq }, lastres, startres - lastres, av.getAlignment(), true);
+          appendEdit(Action.INSERT_GAP, new SequenceI[]
+          { seq }, lastres, startres - lastres);
         }
       }
       else
@@ -1200,7 +1258,7 @@ public class SeqPanel extends JPanel implements MouseListener,
           {
             for (int j = lastres; j > startres; j--)
             {
-              if (!jalview.util.Comparison.isGap(seq.getCharAt(startres)))
+              if (!Comparison.isGap(seq.getCharAt(startres)))
               {
                 endEditing();
                 break;
@@ -1215,7 +1273,7 @@ public class SeqPanel extends JPanel implements MouseListener,
             int max = 0;
             for (int m = startres; m < lastres; m++)
             {
-              if (!jalview.util.Comparison.isGap(seq.getCharAt(m)))
+              if (!Comparison.isGap(seq.getCharAt(m)))
               {
                 break;
               }
@@ -1224,9 +1282,8 @@ public class SeqPanel extends JPanel implements MouseListener,
 
             if (max > 0)
             {
-              editCommand.appendEdit(Action.DELETE_GAP,
-                      new SequenceI[]
-                      { seq }, startres, max, av.getAlignment(), true);
+              appendEdit(Action.DELETE_GAP, new SequenceI[]
+              { seq }, startres, max);
             }
           }
         }
@@ -1242,8 +1299,8 @@ public class SeqPanel extends JPanel implements MouseListener,
           }
           else
           {
-            editCommand.appendEdit(Action.INSERT_NUC, new SequenceI[]
-            { seq }, lastres, startres - lastres, av.getAlignment(), true);
+            appendEdit(Action.INSERT_NUC, new SequenceI[]
+            { seq }, lastres, startres - lastres);
           }
         }
       }
@@ -1278,22 +1335,37 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
     }
 
-    editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1,
-            av.getAlignment(), true);
+    appendEdit(Action.DELETE_GAP, seq, blankColumn, 1);
 
-    editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1,
-            av.getAlignment(), true);
+    appendEdit(Action.INSERT_GAP, seq, j, 1);
 
   }
 
+  /**
+   * Helper method to add and perform one edit action.
+   * 
+   * @param action
+   * @param seq
+   * @param pos
+   * @param count
+   */
+  protected void appendEdit(Action action, SequenceI[] seq, int pos,
+          int count)
+  {
+
+    final Edit edit = new EditCommand().new Edit(action, seq, pos, count,
+            av.getAlignment().getGapCharacter());
+
+    editCommand.appendEdit(edit, av.getAlignment(),
+            true, null);
+  }
+
   void deleteChar(int j, SequenceI[] seq, int fixedColumn)
   {
 
-    editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1,
-            av.getAlignment(), true);
+    appendEdit(Action.DELETE_GAP, seq, j, 1);
 
-    editCommand.appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1,
-            av.getAlignment(), true);
+    appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1);
   }
 
   /**
@@ -1416,7 +1488,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     startWrapBlock = wrappedBlock;
 
-    if (av.wrapAlignment && seq > av.getAlignment().getHeight())
+    if (av.getWrapAlignment() && seq > av.getAlignment().getHeight())
     {
       JOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
               .getString("label.cannot_edit_annotations_in_wrapped_view"),
@@ -1799,52 +1871,68 @@ public class SeqPanel extends JPanel implements MouseListener,
     // handles selection messages...
     // TODO: extend config options to allow user to control if selections may be
     // shared between viewports.
-    if (av == source
-            || !av.followSelection
-            || (av.isSelectionGroupChanged(false) || av
-                    .isColSelChanged(false))
-            || (source instanceof AlignViewport && ((AlignViewport) source)
-                    .getSequenceSetId().equals(av.getSequenceSetId())))
+    boolean iSentTheSelection = (av == source
+            || (source instanceof AlignViewport && ((AlignmentViewport) source)
+            .getSequenceSetId().equals(av.getSequenceSetId())));
+    if (iSentTheSelection || !av.followSelection)
+    {
+      return;
+    }
+
+    /*
+     * Ignore the selection if there is one of our own pending.
+     */
+    if (av.isSelectionGroupChanged(false) || av.isColSelChanged(false))
+    {
+      return;
+    }
+
+    /*
+     * Check for selection in a view of which this one is a dna/protein
+     * complement.
+     */
+    if (selectionFromTranslation(seqsel, colsel, source))
     {
       return;
     }
+
     // do we want to thread this ? (contention with seqsel and colsel locks, I
     // suspect)
     // rules are: colsel is copied if there is a real intersection between
     // sequence selection
-    boolean repaint = false, copycolsel = true;
-    // if (!av.isSelectionGroupChanged(false))
+    boolean repaint = false;
+    boolean copycolsel = true;
+
+    SequenceGroup sgroup = null;
+    if (seqsel != null && seqsel.getSize() > 0)
     {
-      SequenceGroup sgroup = null;
-      if (seqsel != null && seqsel.getSize() > 0)
-      {
-        if (av.getAlignment() == null)
-        {
-          jalview.bin.Cache.log.warn("alignviewport av SeqSetId="
-                  + av.getSequenceSetId() + " ViewId=" + av.getViewId()
-                  + " 's alignment is NULL! returning immediatly.");
-          return;
-        }
-        sgroup = seqsel.intersect(av.getAlignment(),
-                (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
-        if ((sgroup == null || sgroup.getSize() == 0)
-                || (colsel == null || colsel.size() == 0))
-        {
-          // don't copy columns if the region didn't intersect.
-          copycolsel = false;
-        }
-      }
-      if (sgroup != null && sgroup.getSize() > 0)
+      if (av.getAlignment() == null)
       {
-        av.setSelectionGroup(sgroup);
+        jalview.bin.Cache.log.warn("alignviewport av SeqSetId="
+                + av.getSequenceSetId() + " ViewId=" + av.getViewId()
+                + " 's alignment is NULL! returning immediately.");
+        return;
       }
-      else
+      sgroup = seqsel.intersect(av.getAlignment(),
+              (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
+      if ((sgroup == null || sgroup.getSize() == 0)
+              || (colsel == null || colsel.size() == 0))
       {
-        av.setSelectionGroup(null);
+        // don't copy columns if the region didn't intersect.
+        copycolsel = false;
       }
-      av.isSelectionGroupChanged(true);
-      repaint = true;
     }
+    if (sgroup != null && sgroup.getSize() > 0)
+    {
+      av.setSelectionGroup(sgroup);
+    }
+    else
+    {
+      av.setSelectionGroup(null);
+    }
+    av.isSelectionGroupChanged(true);
+    repaint = true;
+
     if (copycolsel)
     {
       // the current selection is unset or from a previous message
@@ -1872,6 +1960,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       av.isColSelChanged(true);
       repaint = true;
     }
+
     if (copycolsel
             && av.hasHiddenColumns()
             && (av.getColumnSelection() == null || av.getColumnSelection()
@@ -1879,11 +1968,52 @@ public class SeqPanel extends JPanel implements MouseListener,
     {
       System.err.println("Bad things");
     }
-    if (repaint)
+    if (repaint) // always true!
     {
       // probably finessing with multiple redraws here
       PaintRefresher.Refresh(this, av.getSequenceSetId());
       // ap.paintAlignment(false);
     }
   }
+
+  /**
+   * If this panel is a cdna/protein translation view of the selection source,
+   * tries to map the source selection to a local one, and returns true. Else
+   * returns false.
+   * 
+   * @param seqsel
+   * @param colsel
+   * @param source
+   */
+  protected boolean selectionFromTranslation(SequenceGroup seqsel,
+          ColumnSelection colsel, SelectionSource source)
+  {
+    if (!(source instanceof AlignViewportI)) {
+      return false;
+    }
+    final AlignViewportI sourceAv = (AlignViewportI) source;
+    if (sourceAv.getCodingComplement() != av && av.getCodingComplement() != sourceAv)
+    {
+      return false;
+    }
+
+    /*
+     * Map sequence selection
+     */
+    SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av);
+    av.setSelectionGroup(sg);
+    av.isSelectionGroupChanged(true);
+
+    /*
+     * Map column selection
+     */
+    ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
+            av);
+    av.setColumnSelection(cs);
+    av.isColSelChanged(true);
+
+    PaintRefresher.Refresh(this, av.getSequenceSetId());
+
+    return true;
+  }
 }
index af4aa67..6a0c712 100755 (executable)
  */
 package jalview.gui;
 
-import java.util.*;
-import java.util.List;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import javax.swing.*;
-import javax.swing.tree.DefaultMutableTreeNode;
-
-import com.stevesoft.pat.Regex;
-
-import jalview.datamodel.*;
-import jalview.io.*;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.io.FormatAdapter;
+import jalview.io.IdentifyFile;
 import jalview.util.DBRefUtils;
 import jalview.util.MessageManager;
 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
 import jalview.ws.seqfetcher.DbSourceProxy;
+
 import java.awt.BorderLayout;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.SwingConstants;
+import javax.swing.tree.DefaultMutableTreeNode;
+
+import com.stevesoft.pat.Regex;
 
 public class SequenceFetcher extends JPanel implements Runnable
 {
-  // ASequenceFetcher sfetch;
+  JLabel dbeg = new JLabel();
+
+  JDatabaseTree database;
+
+  JButton databaseButt;
+
+  JLabel jLabel1 = new JLabel();
+
+  JCheckBox replacePunctuation = new JCheckBox();
+
+  JButton ok = new JButton();
+
+  JButton clear = new JButton();
+
+  JButton example = new JButton();
+
+  JButton close = new JButton();
+
+  JPanel jPanel1 = new JPanel();
+
+  JTextArea textArea = new JTextArea();
+
+  JScrollPane jScrollPane1 = new JScrollPane();
+
+  JPanel jPanel2 = new JPanel();
+
+  JPanel jPanel3 = new JPanel();
+
+  JPanel jPanel4 = new JPanel();
+
+  BorderLayout borderLayout1 = new BorderLayout();
+
+  BorderLayout borderLayout2 = new BorderLayout();
+
+  BorderLayout borderLayout3 = new BorderLayout();
+
   JInternalFrame frame;
 
   IProgressIndicator guiWindow;
@@ -62,6 +115,7 @@ public class SequenceFetcher extends JPanel implements Runnable
 
   private static Thread initingThread = null;
 
+  int debounceTrap = 0;
   /**
    * Blocking method that initialises and returns the shared instance of the
    * SequenceFetcher client
@@ -136,9 +190,10 @@ public class SequenceFetcher extends JPanel implements Runnable
     return sfetch;
   }
 
+  private IProgressIndicator progressIndicator;
   public SequenceFetcher(IProgressIndicator guiIndic)
   {
-    final IProgressIndicator guiWindow = guiIndic;
+    this.progressIndicator = guiIndic;
     final SequenceFetcher us = this;
     // launch initialiser thread
     Thread sf = new Thread(new Runnable()
@@ -147,9 +202,9 @@ public class SequenceFetcher extends JPanel implements Runnable
       @Override
       public void run()
       {
-        if (getSequenceFetcherSingleton(guiWindow) != null)
+        if (getSequenceFetcherSingleton(progressIndicator) != null)
         {
-          us.initGui(guiWindow);
+          us.initGui(progressIndicator);
         }
         else
         {
@@ -283,7 +338,9 @@ public class SequenceFetcher extends JPanel implements Runnable
       public void keyPressed(KeyEvent e)
       {
         if (e.getKeyCode() == KeyEvent.VK_ENTER)
+        {
           ok_actionPerformed();
+        }
       }
     });
     jPanel3.setLayout(borderLayout1);
@@ -298,37 +355,21 @@ public class SequenceFetcher extends JPanel implements Runnable
     databaseButt.setFont(JvSwingUtils.getLabelFont());
     database.addActionListener(new ActionListener()
     {
-
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        try
-        {
-          databaseButt.setText(database.getSelectedItem()
-                  + (database.getSelectedSources().size() > 1 ? " (and "
-                          + database.getSelectedSources().size()
-                          + " others)" : ""));
-          String eq = database.getExampleQueries();
-          dbeg.setText(MessageManager.formatMessage(
-                  "label.example_query_param", new String[]
-                  { eq }));
-          boolean enablePunct = !(eq != null && eq.indexOf(",") > -1);
-          for (DbSourceProxy dbs : database.getSelectedSources())
-          {
-            if (dbs instanceof jalview.ws.dbsources.das.datamodel.DasSequenceSource)
-            {
-              enablePunct = false;
-              break;
-            }
-          }
-          replacePunctuation.setEnabled(enablePunct);
+        debounceTrap++;
+        String currentSelection = database.getSelectedItem();
 
-        } catch (Exception ex)
+        if (!currentSelection.equalsIgnoreCase("pdb"))
         {
-          dbeg.setText("");
-          replacePunctuation.setEnabled(true);
+            otherSourceAction();
         }
-        jPanel2.repaint();
+        if (currentSelection.equalsIgnoreCase("pdb") && ((debounceTrap % 2) == 0))
+        {
+          pdbSourceAction();
+        }
+
       }
     });
     dbeg.setText("");
@@ -347,6 +388,44 @@ public class SequenceFetcher extends JPanel implements Runnable
 
   }
 
+  private void pdbSourceAction()
+  {
+    databaseButt.setText(database.getSelectedItem());
+    new PDBSearchPanel(this);
+    frame.dispose();
+  }
+
+  private void otherSourceAction()
+  {
+    try
+    {
+      databaseButt.setText(database.getSelectedItem()
+              + (database.getSelectedSources().size() > 1 ? " (and "
+                      + database.getSelectedSources().size() + " others)"
+                      : ""));
+      String eq = database.getExampleQueries();
+      dbeg.setText(MessageManager.formatMessage(
+              "label.example_query_param", new String[]
+              { eq }));
+      boolean enablePunct = !(eq != null && eq.indexOf(",") > -1);
+      for (DbSourceProxy dbs : database.getSelectedSources())
+      {
+        if (dbs instanceof jalview.ws.dbsources.das.datamodel.DasSequenceSource)
+        {
+          enablePunct = false;
+          break;
+        }
+      }
+      replacePunctuation.setEnabled(enablePunct);
+
+    } catch (Exception ex)
+    {
+      dbeg.setText("");
+      replacePunctuation.setEnabled(true);
+    }
+    jPanel2.repaint();
+  }
+
   protected void example_actionPerformed()
   {
     DbSourceProxy db = null;
@@ -365,41 +444,7 @@ public class SequenceFetcher extends JPanel implements Runnable
     jPanel3.repaint();
   }
 
-  JLabel dbeg = new JLabel();
-
-  JDatabaseTree database;
-
-  JButton databaseButt;
-
-  JLabel jLabel1 = new JLabel();
-
-  JCheckBox replacePunctuation = new JCheckBox();
-
-  JButton ok = new JButton();
-
-  JButton clear = new JButton();
-
-  JButton example = new JButton();
-
-  JButton close = new JButton();
-
-  JPanel jPanel1 = new JPanel();
-
-  JTextArea textArea = new JTextArea();
-
-  JScrollPane jScrollPane1 = new JScrollPane();
-
-  JPanel jPanel2 = new JPanel();
-
-  JPanel jPanel3 = new JPanel();
-
-  JPanel jPanel4 = new JPanel();
 
-  BorderLayout borderLayout1 = new BorderLayout();
-
-  BorderLayout borderLayout2 = new BorderLayout();
-
-  BorderLayout borderLayout3 = new BorderLayout();
 
   public void close_actionPerformed(ActionEvent e)
   {
@@ -772,6 +817,10 @@ public class SequenceFetcher extends JPanel implements Runnable
 
     if (al != null && al.getHeight() > 0)
     {
+      if (title == null)
+      {
+        title = getDefaultRetrievalTitle();
+      }
       if (alignFrame == null)
       {
         AlignFrame af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
@@ -784,17 +833,13 @@ public class SequenceFetcher extends JPanel implements Runnable
           // Alignments?
         }
 
-        if (title == null)
-        {
-          title = getDefaultRetrievalTitle();
-        }
         SequenceFeature[] sfs = null;
         List<SequenceI> alsqs;
         synchronized (alsqs = al.getSequences())
         {
           for (SequenceI sq : alsqs)
           {
-            if ((sfs = (sq).getDatasetSequence().getSequenceFeatures()) != null)
+            if ((sfs = sq.getSequenceFeatures()) != null)
             {
               if (sfs.length > 0)
               {
@@ -821,21 +866,7 @@ public class SequenceFetcher extends JPanel implements Runnable
       }
       else
       {
-        for (int i = 0; i < al.getHeight(); i++)
-        {
-          alignFrame.viewport.getAlignment().addSequence(
-                  al.getSequenceAt(i)); // this
-          // also
-          // creates
-          // dataset
-          // sequence
-          // entries
-        }
-        alignFrame.viewport.setEndSeq(alignFrame.viewport.getAlignment()
-                .getHeight());
-        alignFrame.viewport.getAlignment().getWidth();
-        alignFrame.viewport.firePropertyChange("alignment", null,
-                alignFrame.viewport.getAlignment().getSequences());
+        alignFrame.viewport.addAlignment(al, title);
       }
     }
     return al;
@@ -855,4 +886,14 @@ public class SequenceFetcher extends JPanel implements Runnable
       }
     });
   }
+
+  public IProgressIndicator getProgressIndicator()
+  {
+    return progressIndicator;
+  }
+
+  public void setProgressIndicator(IProgressIndicator progressIndicator)
+  {
+    this.progressIndicator = progressIndicator;
+  }
 }
index c6a3df2..70acb7b 100755 (executable)
@@ -21,7 +21,6 @@
 package jalview.gui;
 
 import jalview.api.FeatureRenderer;
-import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
@@ -38,6 +37,8 @@ import java.awt.Graphics;
  */
 public class SequenceRenderer implements jalview.api.SequenceRenderer
 {
+  final static int CHAR_TO_UPPER = 'A' - 'a';
+
   AlignViewport av;
 
   FontMetrics fm;
@@ -81,7 +82,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     // If EPS graphics, stringWidth will be a double, not an int
     double dwidth = fm.getStringBounds("M", g).getWidth();
 
-    monospacedFont = (dwidth == fm.getStringBounds("|", g).getWidth() && av.charWidth == dwidth);
+    monospacedFont = (dwidth == fm.getStringBounds("|", g).getWidth() && av
+            .getCharWidth() == dwidth);
 
     this.renderGaps = renderGaps;
   }
@@ -222,7 +224,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     int length = seq.getLength();
 
     int curStart = -1;
-    int curWidth = av.charWidth;
+    int curWidth = av.getCharWidth(), avWidth = av.getCharWidth(), avHeight = av
+            .getCharHeight();
 
     Color tempColour = null;
 
@@ -250,26 +253,25 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
       {
         if (tempColour != null)
         {
-          graphics.fillRect(av.charWidth * (curStart - start), y1,
-                  curWidth, av.charHeight);
+          graphics.fillRect(avWidth * (curStart - start), y1, curWidth,
+                  avHeight);
         }
 
         graphics.setColor(resBoxColour);
 
         curStart = i;
-        curWidth = av.charWidth;
+        curWidth = avWidth;
         tempColour = resBoxColour;
       }
       else
       {
-        curWidth += av.charWidth;
+        curWidth += avWidth;
       }
 
       i++;
     }
 
-    graphics.fillRect(av.charWidth * (curStart - start), y1, curWidth,
-            av.charHeight);
+    graphics.fillRect(avWidth * (curStart - start), y1, curWidth, avHeight);
 
   }
 
@@ -293,7 +295,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
    */
   public void drawText(SequenceI seq, int start, int end, int y1)
   {
-    y1 += av.charHeight - av.charHeight / 5; // height/5 replaces pady
+    y1 += av.getCharHeight() - av.getCharHeight() / 5; // height/5 replaces pady
     int charOffset = 0;
     char s;
 
@@ -301,12 +303,12 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     {
       end = seq.getLength() - 1;
     }
-    graphics.setColor(av.textColour);
+    graphics.setColor(av.getTextColour());
 
-    if (monospacedFont && av.showText && allGroups.length == 0
-            && !av.getColourText() && av.thresholdTextColour == 0)
+    if (monospacedFont && av.getShowText() && allGroups.length == 0
+            && !av.getColourText() && av.getThresholdTextColour() == 0)
     {
-      if (av.renderGaps)
+      if (av.isRenderGaps())
       {
         graphics.drawString(seq.getSequenceAsString(start, end + 1), 0, y1);
       }
@@ -323,7 +325,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
       boolean getboxColour = false;
       for (int i = start; i <= end; i++)
       {
-        graphics.setColor(av.textColour);
+        graphics.setColor(av.getTextColour());
         getboxColour = false;
         s = seq.getCharAt(i);
         if (!renderGaps && jalview.util.Comparison.isGap(s))
@@ -393,7 +395,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
             }
           }
 
-          if (av.thresholdTextColour > 0)
+          if (av.getThresholdTextColour() > 0)
           {
             if (!getboxColour)
             {
@@ -401,9 +403,9 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
             }
 
             if (resBoxColour.getRed() + resBoxColour.getBlue()
-                    + resBoxColour.getGreen() < av.thresholdTextColour)
+                    + resBoxColour.getGreen() < av.getThresholdTextColour())
             {
-              graphics.setColor(av.textColour2);
+              graphics.setColor(av.getTextColour2());
             }
           }
           if (av.getShowUnconserved())
@@ -415,26 +417,39 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
         }
 
-        charOffset = (av.charWidth - fm.charWidth(s)) / 2;
-        graphics.drawString(String.valueOf(s), charOffset + av.charWidth
+        charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
+        graphics.drawString(String.valueOf(s),
+                charOffset + av.getCharWidth()
                 * (i - start), y1);
 
       }
     }
   }
 
+  /**
+   * Returns 'conservedChar' to represent the given position if the sequence
+   * character at that position is equal to the consensus (ignoring case), else
+   * returns the sequence character
+   * 
+   * @param usesrep
+   * @param position
+   * @param sequenceChar
+   * @param conservedChar
+   * @return
+   */
   private char getDisplayChar(final boolean usesrep, int position,
-          char s, char c)
+          char sequenceChar, char conservedChar)
   {
-    // TODO - use currentSequenceGroup rather than alignemnt 
+    // TODO - use currentSequenceGroup rather than alignment
     // currentSequenceGroup.getConsensus()
     char conschar = (usesrep) ? av.getAlignment().getSeqrep().getCharAt(position) : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
             .charAt(0);
-    if (conschar != '-' && s == conschar)
+    if (conschar != '-'
+            && (sequenceChar == conschar || sequenceChar + CHAR_TO_UPPER == conschar))
     {
-      s = c;
+      sequenceChar = conservedChar;
     }
-    return s;
+    return sequenceChar;
   }
 
   /**
@@ -487,17 +502,17 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   public void drawHighlightedText(SequenceI seq, int start, int end,
           int x1, int y1)
   {
-    int pady = av.charHeight / 5;
+    int pady = av.getCharHeight() / 5;
     int charOffset = 0;
     graphics.setColor(Color.BLACK);
-    graphics.fillRect(x1, y1, av.charWidth * (end - start + 1),
-            av.charHeight);
+    graphics.fillRect(x1, y1, av.getCharWidth() * (end - start + 1),
+            av.getCharHeight());
     graphics.setColor(Color.white);
 
     char s = '~';
 
     // Need to find the sequence position here.
-    if (av.validCharWidth)
+    if (av.isValidCharWidth())
     {
       for (int i = start; i <= end; i++)
       {
@@ -506,29 +521,30 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           s = seq.getCharAt(i);
         }
 
-        charOffset = (av.charWidth - fm.charWidth(s)) / 2;
-        graphics.drawString(String.valueOf(s), charOffset + x1
-                + (av.charWidth * (i - start)), (y1 + av.charHeight) - pady);
+        charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
+        graphics.drawString(String.valueOf(s),
+                charOffset + x1 + (av.getCharWidth() * (i - start)),
+                (y1 + av.getCharHeight()) - pady);
       }
     }
   }
 
   public void drawCursor(SequenceI seq, int res, int x1, int y1)
   {
-    int pady = av.charHeight / 5;
+    int pady = av.getCharHeight() / 5;
     int charOffset = 0;
     graphics.setColor(Color.black);
-    graphics.fillRect(x1, y1, av.charWidth, av.charHeight);
+    graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
 
-    if (av.validCharWidth)
+    if (av.isValidCharWidth())
     {
       graphics.setColor(Color.white);
 
       char s = seq.getCharAt(res);
 
-      charOffset = (av.charWidth - fm.charWidth(s)) / 2;
+      charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
       graphics.drawString(String.valueOf(s), charOffset + x1,
-              (y1 + av.charHeight) - pady);
+              (y1 + av.getCharHeight()) - pady);
     }
 
   }
index c842826..af6bfff 100755 (executable)
  */
 package jalview.gui;
 
-import java.util.*;
+import jalview.datamodel.SequenceGroup;
+import jalview.jbgui.GSliderPanel;
+import jalview.schemes.ColourSchemeI;
+import jalview.util.MessageManager;
 
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.event.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Iterator;
 
-import jalview.datamodel.*;
-import jalview.jbgui.*;
-import jalview.schemes.*;
-import jalview.util.MessageManager;
+import javax.swing.JInternalFrame;
+import javax.swing.JLayeredPane;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
 
 /**
  * DOCUMENT ME!
@@ -286,13 +290,15 @@ public class SliderPanel extends GSliderPanel
       }
       else
       {
-        toChange.setThreshold(i, ap.av.getIgnoreGapsConsensus());
+        toChange.setThreshold(i, ap.av.isIgnoreGapsConsensus());
       }
       if (allGroups != null && allGroups.hasNext())
       {
         while ((toChange = allGroups.next().cs) == null
                 && allGroups.hasNext())
+        {
           ;
+        }
       }
       else
       {
diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java
new file mode 100644 (file)
index 0000000..5c4e4d2
--- /dev/null
@@ -0,0 +1,639 @@
+package jalview.gui;
+
+import java.awt.Component;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.beans.PropertyVetoException;
+import java.util.Map.Entry;
+
+import javax.swing.AbstractAction;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JMenuItem;
+import javax.swing.KeyStroke;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
+
+import jalview.api.SplitContainerI;
+import jalview.api.ViewStyleI;
+import jalview.datamodel.AlignmentI;
+import jalview.jbgui.GAlignFrame;
+import jalview.jbgui.GSplitFrame;
+import jalview.structure.StructureSelectionManager;
+import jalview.viewmodel.AlignmentViewport;
+
+/**
+ * An internal frame on the desktop that hosts a horizontally split view of
+ * linked DNA and Protein alignments. Additional views can be created in linked
+ * pairs, expanded to separate split frames, or regathered into a single frame.
+ * <p>
+ * (Some) operations on each alignment are automatically mirrored on the other.
+ * These include mouseover (highlighting), sequence and column selection,
+ * sequence ordering and sorting, and grouping, colouring and sorting by tree.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class SplitFrame extends GSplitFrame implements SplitContainerI
+{
+  private static final long serialVersionUID = 1L;
+
+  public SplitFrame(GAlignFrame top, GAlignFrame bottom)
+  {
+    super(top, bottom);
+    init();
+  }
+
+  /**
+   * Initialise this frame.
+   */
+  protected void init()
+  {
+    getTopFrame().setSplitFrame(this);
+    getBottomFrame().setSplitFrame(this);
+    getTopFrame().setVisible(true);
+    getBottomFrame().setVisible(true);
+
+    ((AlignFrame) getTopFrame()).getViewport().setCodingComplement(
+            ((AlignFrame) getBottomFrame()).getViewport());
+
+    int width = ((AlignFrame) getTopFrame()).getWidth();
+    // about 50 pixels for the SplitFrame's title bar etc
+    int height = ((AlignFrame) getTopFrame()).getHeight()
+            + ((AlignFrame) getBottomFrame()).getHeight() + 50;
+    height = Math.min(height, Desktop.instance.getHeight() - 20);
+    // setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 20);
+    setSize(width, height);
+
+    adjustLayout();
+
+    addCloseFrameListener();
+    
+    addKeyListener();
+
+    addKeyBindings();
+
+    addCommandListeners();
+  }
+
+  /**
+   * Set the top and bottom frames to listen to each others Commands (e.g. Edit,
+   * Order).
+   */
+  protected void addCommandListeners()
+  {
+    // TODO if CommandListener is only ever 1:1 for complementary views,
+    // may change broadcast pattern to direct messaging (more efficient)
+    final StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
+    ssm.addCommandListener(((AlignFrame) getTopFrame()).getViewport());
+    ssm.addCommandListener(((AlignFrame) getBottomFrame()).getViewport());
+  }
+
+  /**
+   * Do any tweaking and twerking of the layout wanted.
+   */
+  public void adjustLayout()
+  {
+    /*
+     * Ensure sequence ids are the same width for good alignment.
+     */
+    int w1 = ((AlignFrame) getTopFrame()).getViewport().getIdWidth();
+    int w2 = ((AlignFrame) getBottomFrame()).getViewport().getIdWidth();
+    int w3 = Math.max(w1, w2);
+    if (w1 != w3)
+    {
+      ((AlignFrame) getTopFrame()).getViewport().setIdWidth(w3);
+    }
+    if (w2 != w3)
+    {
+      ((AlignFrame) getBottomFrame()).getViewport().setIdWidth(w3);
+    }
+
+    /*
+     * Set the character width for protein to 3 times that for dna.
+     */
+    boolean scaleThreeToOne = true; // TODO a new Preference option?
+    if (scaleThreeToOne)
+    {
+      final AlignViewport topViewport = ((AlignFrame) getTopFrame()).viewport;
+      final AlignViewport bottomViewport = ((AlignFrame) getBottomFrame()).viewport;
+      final AlignmentI topAlignment = topViewport.getAlignment();
+      final AlignmentI bottomAlignment = bottomViewport.getAlignment();
+      AlignmentViewport cdna = topAlignment.isNucleotide() ? topViewport
+              : (bottomAlignment.isNucleotide() ? bottomViewport : null);
+      AlignmentViewport protein = !topAlignment.isNucleotide() ? topViewport
+              : (!bottomAlignment.isNucleotide() ? bottomViewport : null);
+      if (protein != null && cdna != null)
+      {
+        ViewStyleI vs = cdna.getViewStyle();
+        ViewStyleI vs2 = protein.getViewStyle();
+        vs2.setCharWidth(3 * vs.getCharWidth());
+        protein.setViewStyle(vs2);
+      }
+    }
+  }
+
+  /**
+   * Add a listener to tidy up when the frame is closed.
+   */
+  protected void addCloseFrameListener()
+  {
+    addInternalFrameListener(new InternalFrameAdapter()
+    {
+      @Override
+      public void internalFrameClosed(InternalFrameEvent evt)
+      {
+        if (getTopFrame() instanceof AlignFrame)
+        {
+          ((AlignFrame) getTopFrame())
+                  .closeMenuItem_actionPerformed(true);
+        }
+        if (getBottomFrame() instanceof AlignFrame)
+        {
+          ((AlignFrame) getBottomFrame())
+                  .closeMenuItem_actionPerformed(true);
+        }
+      };
+    });
+  }
+
+  /**
+   * Add a key listener that delegates to whichever split component the mouse is
+   * in (or does nothing if neither).
+   */
+  protected void addKeyListener()
+  {
+    addKeyListener(new KeyAdapter() {
+
+      @Override
+      public void keyPressed(KeyEvent e)
+      {
+        AlignFrame af = (AlignFrame) getFrameAtMouse();
+
+        /*
+         * Intercept and override any keys here if wanted.
+         */
+        if (!overrideKey(e, af))
+        {
+          if (af != null)
+          {
+            for (KeyListener kl : af.getKeyListeners())
+            {
+              kl.keyPressed(e);
+            }
+          }
+        }
+      }
+
+      @Override
+      public void keyReleased(KeyEvent e)
+      {
+        Component c = getFrameAtMouse();
+        if (c != null)
+        {
+          for (KeyListener kl : c.getKeyListeners())
+          {
+            kl.keyReleased(e);
+          }
+        }
+      }
+      
+    });
+  }
+
+  /**
+   * Returns true if the key event is overriden and actioned (or ignored) here,
+   * else returns false, indicating it should be delegated to the AlignFrame's
+   * usual handler.
+   * <p>
+   * We can't handle Cmd-Key combinations here, instead this is done by
+   * overriding key bindings.
+   * 
+   * @see addKeyOverrides
+   * @param e
+   * @param af
+   * @return
+   */
+  protected boolean overrideKey(KeyEvent e, AlignFrame af)
+  {
+    boolean actioned = false;
+    int keyCode = e.getKeyCode();
+    switch (keyCode)
+    {
+    case KeyEvent.VK_DOWN:
+      if (e.isAltDown() || !af.viewport.cursorMode)
+      {
+        /*
+         * Key down (or Alt-key-down in cursor mode) - move selected sequences
+         */
+        ((AlignFrame) getTopFrame()).moveSelectedSequences(false);
+        ((AlignFrame) getBottomFrame()).moveSelectedSequences(false);
+        actioned = true;
+        e.consume();
+      }
+      break;
+    case KeyEvent.VK_UP:
+      if (e.isAltDown() || !af.viewport.cursorMode)
+      {
+        /*
+         * Key up (or Alt-key-up in cursor mode) - move selected sequences
+         */
+        ((AlignFrame) getTopFrame()).moveSelectedSequences(true);
+        ((AlignFrame) getBottomFrame()).moveSelectedSequences(true);
+        actioned = true;
+        e.consume();
+      }
+    default:
+    }
+    return actioned;
+  }
+
+  /**
+   * Set key bindings (recommended for Swing over key accelerators).
+   */
+  private void addKeyBindings()
+  {
+    overrideDelegatedKeyBindings();
+
+    overrideImplementedKeyBindings();
+  }
+
+  /**
+   * Override key bindings with alternative action methods implemented in this
+   * class.
+   */
+  protected void overrideImplementedKeyBindings()
+  {
+    overrideFind();
+    overrideNewView();
+    overrideCloseView();
+    overrideExpandViews();
+    overrideGatherViews();
+  }
+
+  /**
+   * Replace Cmd-W close view action with our version.
+   */
+  protected void overrideCloseView()
+  {
+    AbstractAction action;
+    /*
+     * Ctrl-W / Cmd-W - close view or window
+     */
+    KeyStroke key_cmdW = KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    action = new AbstractAction()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        closeView_actionPerformed();
+      }
+    };
+    overrideKeyBinding(key_cmdW, action);
+  }
+
+  /**
+   * Replace Cmd-T new view action with our version.
+   */
+  protected void overrideNewView()
+  {
+    /*
+     * Ctrl-T / Cmd-T open new view
+     */
+    KeyStroke key_cmdT = KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    AbstractAction action = new AbstractAction()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        newView_actionPerformed();
+      }
+    };
+    overrideKeyBinding(key_cmdT, action);
+  }
+
+  /**
+   * For now, delegates key events to the corresponding key accelerator for the
+   * AlignFrame that the mouse is in. Hopefully can be simplified in future if
+   * AlignFrame is changed to use key bindings rather than accelerators.
+   */
+  protected void overrideDelegatedKeyBindings()
+  {
+    if (getTopFrame() instanceof AlignFrame)
+    {
+      /*
+       * Get all accelerator keys in the top frame (the bottom should be
+       * identical) and override each one.
+       */
+      for (Entry<KeyStroke, JMenuItem> acc : ((AlignFrame) getTopFrame())
+              .getAccelerators().entrySet())
+      {
+        overrideKeyBinding(acc);
+      }
+    }
+  }
+
+  /**
+   * Overrides an AlignFrame key accelerator with our version which delegates to
+   * the action listener in whichever frame has the mouse (and does nothing if
+   * neither has).
+   * 
+   * @param acc
+   */
+  private void overrideKeyBinding(Entry<KeyStroke, JMenuItem> acc)
+  {
+    final KeyStroke ks = acc.getKey();
+    InputMap inputMap = this.getInputMap(JComponent.WHEN_FOCUSED);
+    inputMap.put(ks, ks);
+    this.getActionMap().put(ks, new AbstractAction()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        Component c = getFrameAtMouse();
+        if (c != null && c instanceof AlignFrame)
+        {
+          for (ActionListener a : ((AlignFrame) c).getAccelerators()
+                  .get(ks).getActionListeners())
+          {
+            a.actionPerformed(null);
+          }
+        }
+      }
+    });
+  }
+
+  /**
+   * Replace an accelerator key's action with the specified action.
+   * 
+   * @param ks
+   */
+  protected void overrideKeyBinding(KeyStroke ks, AbstractAction action)
+  {
+    this.getActionMap().put(ks, action);
+    overrideMenuItem(ks, action);
+  }
+
+  /**
+   * Create and link new views (with matching names) in both panes.
+   * <p>
+   * Note this is _not_ multiple tabs, each hosting a split pane view, rather it
+   * is a single split pane with each split holding multiple tabs which are
+   * linked in pairs.
+   * <p>
+   * TODO implement instead with a tabbed holder in the SplitView, each tab
+   * holding a single JSplitPane. Would avoid a duplicated tab, at the cost of
+   * some additional coding.
+   */
+  protected void newView_actionPerformed()
+  {
+    AlignFrame topFrame = (AlignFrame) getTopFrame();
+    AlignFrame bottomFrame = (AlignFrame) getBottomFrame();
+
+    AlignmentPanel newTopPanel = topFrame.newView(null, true);
+    AlignmentPanel newBottomPanel = bottomFrame.newView(null, true);
+
+    /*
+     * This currently (for the first new view only) leaves the top pane on tab 0
+     * but the bottom on tab 1. This results from 'setInitialTabVisible' echoing
+     * from the bottom back to the first frame. Next line is a fudge to work
+     * around this. TODO find a better way.
+     */
+    if (topFrame.getTabIndex() != bottomFrame.getTabIndex())
+    {
+      topFrame.setDisplayedView(newTopPanel);
+    }
+
+    newBottomPanel.av.viewName = newTopPanel.av.viewName;
+    newTopPanel.av.setCodingComplement(newBottomPanel.av);
+
+    final StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
+    ssm.addCommandListener(newTopPanel.av);
+    ssm.addCommandListener(newBottomPanel.av);
+  }
+
+  /**
+   * Close the currently selected view in both panes. If there is only one view,
+   * close this split frame.
+   */
+  protected void closeView_actionPerformed()
+  {
+    int viewCount = ((AlignFrame) getTopFrame()).getAlignPanels().size();
+    if (viewCount < 2)
+    {
+      close();
+      return;
+    }
+
+    AlignmentPanel topPanel = ((AlignFrame) getTopFrame()).alignPanel;
+    AlignmentPanel bottomPanel = ((AlignFrame) getBottomFrame()).alignPanel;
+
+    ((AlignFrame) getTopFrame()).closeView(topPanel);
+    ((AlignFrame) getBottomFrame()).closeView(bottomPanel);
+
+  }
+
+  /**
+   * Close child frames and this split frame.
+   */
+  public void close()
+  {
+    ((AlignFrame) getTopFrame()).closeMenuItem_actionPerformed(true);
+    ((AlignFrame) getBottomFrame()).closeMenuItem_actionPerformed(true);
+    try
+    {
+      this.setClosed(true);
+    } catch (PropertyVetoException e)
+    {
+      // ignore
+    }
+  }
+
+  /**
+   * Replace AlignFrame 'expand views' action with SplitFrame version.
+   */
+  protected void overrideExpandViews()
+  {
+    KeyStroke key_X = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false);
+    AbstractAction action = new AbstractAction()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        expandViews_actionPerformed();
+      }
+    };
+    overrideMenuItem(key_X, action);
+  }
+
+  /**
+   * Replace AlignFrame 'gather views' action with SplitFrame version.
+   */
+  protected void overrideGatherViews()
+  {
+    KeyStroke key_G = KeyStroke.getKeyStroke(KeyEvent.VK_G, 0, false);
+    AbstractAction action = new AbstractAction()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        gatherViews_actionPerformed();
+      }
+    };
+    overrideMenuItem(key_G, action);
+  }
+
+  /**
+   * Override the menu action associated with the keystroke in the child frames,
+   * replacing it with the given action.
+   * 
+   * @param ks
+   * @param action
+   */
+  private void overrideMenuItem(KeyStroke ks, AbstractAction action)
+  {
+    overrideMenuItem(ks, action, getTopFrame());
+    overrideMenuItem(ks, action, getBottomFrame());
+  }
+
+  /**
+   * Override the menu action associated with the keystroke in one child frame,
+   * replacing it with the given action. Mwahahahaha.
+   * 
+   * @param key
+   * @param action
+   * @param comp
+   */
+  private void overrideMenuItem(KeyStroke key, final AbstractAction action,
+          JComponent comp)
+  {
+    if (comp instanceof AlignFrame)
+    {
+      JMenuItem mi = ((AlignFrame) comp).getAccelerators().get(key);
+      if (mi != null)
+      {
+        for (ActionListener al : mi.getActionListeners())
+        {
+          mi.removeActionListener(al);
+        }
+        mi.addActionListener(new ActionListener()
+        {
+          @Override
+          public void actionPerformed(ActionEvent e)
+          {
+            action.actionPerformed(e);
+          }
+        });
+      }
+    }
+  }
+
+  /**
+   * Expand any multiple views (which are always in pairs) into separate split
+   * frames.
+   */
+  protected void expandViews_actionPerformed()
+  {
+    Desktop.instance.explodeViews(this);
+  }
+
+  /**
+   * Gather any other SplitFrame views of this alignment back in as multiple
+   * (pairs of) views in this SplitFrame.
+   */
+  protected void gatherViews_actionPerformed()
+  {
+    Desktop.instance.gatherViews(this);
+  }
+
+  /**
+   * Returns the alignment in the complementary frame to the one given.
+   */
+  @Override
+  public AlignmentI getComplement(Object alignFrame)
+  {
+    if (alignFrame == this.getTopFrame())
+    {
+      return ((AlignFrame) getBottomFrame()).viewport.getAlignment();
+    }
+    else if (alignFrame == this.getBottomFrame())
+    {
+      return ((AlignFrame) getTopFrame()).viewport.getAlignment();
+    }
+    return null;
+  }
+
+  /**
+   * Returns the title of the complementary frame to the one given.
+   */
+  @Override
+  public String getComplementTitle(Object alignFrame)
+  {
+    if (alignFrame == this.getTopFrame())
+    {
+      return ((AlignFrame) getBottomFrame()).getTitle();
+    }
+    else if (alignFrame == this.getBottomFrame())
+    {
+      return ((AlignFrame) getTopFrame()).getTitle();
+    }
+    return null;
+  }
+
+  /**
+   * Set the 'other half' to hidden / revealed.
+   */
+  @Override
+  public void setComplementVisible(Object alignFrame, boolean show)
+  {
+    /*
+     * Hiding the AlignPanel suppresses unnecessary repaints
+     */
+    if (alignFrame == getTopFrame())
+    {
+      ((AlignFrame) getBottomFrame()).alignPanel.setVisible(show);
+    }
+    else if (alignFrame == getBottomFrame())
+    {
+      ((AlignFrame) getTopFrame()).alignPanel.setVisible(show);
+    }
+    super.setComplementVisible(alignFrame, show);
+  }
+
+  /**
+   * Replace Cmd-F Find action with our version. This is necessary because the
+   * 'default' Finder searches in the first AlignFrame it finds. We need it to
+   * search in the half of the SplitFrame that has the mouse.
+   */
+  protected void overrideFind()
+  {
+    /*
+     * Ctrl-F / Cmd-F open Finder dialog, 'focused' on the right alignment
+     */
+    KeyStroke key_cmdF = KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    AbstractAction action = new AbstractAction()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        Component c = getFrameAtMouse();
+        if (c != null && c instanceof AlignFrame)
+        {
+          AlignFrame af = (AlignFrame) c;
+          new Finder(af.viewport, af.alignPanel);
+        }
+      }
+    };
+    overrideKeyBinding(key_cmdF, action);
+  }
+}
+
diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java
new file mode 100644 (file)
index 0000000..3a54cc1
--- /dev/null
@@ -0,0 +1,634 @@
+/*
+
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 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 jalview.datamodel.DBRefEntry;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.jbgui.GStructureChooser;
+import jalview.jbgui.PDBDocFieldPreferences;
+import jalview.util.MessageManager;
+import jalview.ws.dbsources.PDBRestClient;
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+import jalview.ws.uimodel.PDBRestRequest;
+import jalview.ws.uimodel.PDBRestResponse;
+import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
+
+import java.awt.event.ItemEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+
+/**
+ * Provides the behaviors for the Structure chooser Panel
+ * 
+ * @author tcnofoegbu
+ *
+ */
+@SuppressWarnings("serial")
+public class StructureChooser extends GStructureChooser
+{
+  private boolean structuresDiscovered = false;
+
+  private SequenceI selectedSequence;
+
+  private SequenceI[] selectedSequences;
+
+  private IProgressIndicator progressIndicator;
+
+  private Collection<PDBResponseSummary> discoveredStructuresSet;
+
+  private PDBRestRequest lastPdbRequest;
+
+  private PDBRestClient pdbRestCleint;
+
+  private String selectedPdbFileName;
+
+  private boolean isValidPBDEntry;
+
+  public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
+          AlignmentPanel ap)
+  {
+    this.ap = ap;
+    this.selectedSequence = selectedSeq;
+    this.selectedSequences = selectedSeqs;
+    this.progressIndicator = (ap == null) ? null : ap.alignFrame;
+    init();
+  }
+
+  /**
+   * Initializes parameters used by the Structure Chooser Panel
+   */
+  public void init()
+  {
+    Thread discoverPDBStructuresThread = new Thread(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        long startTime = System.currentTimeMillis();
+        String msg = MessageManager.getString("status.fetching_db_refs");
+        updateProgressIndicator(msg, startTime);
+        fetchStructuresMetaData();
+        populateFilterComboBox();
+        updateProgressIndicator(null, startTime);
+        mainFrame.setVisible(true);
+        updateCurrentView();
+      }
+    });
+    discoverPDBStructuresThread.start();
+  }
+
+  /**
+   * Updates the progress indicator with the specified message
+   * 
+   * @param message
+   *          displayed message for the operation
+   * @param id
+   *          unique handle for this indicator
+   */
+  public void updateProgressIndicator(String message, long id)
+  {
+    if (progressIndicator != null)
+    {
+      progressIndicator.setProgressBar(message, id);
+    }
+  }
+
+  /**
+   * Retrieve meta-data for all the structure(s) for a given sequence(s) in a
+   * selection group
+   */
+  public void fetchStructuresMetaData()
+  {
+    long startTime = System.currentTimeMillis();
+    Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
+            .getStructureSummaryFields();
+
+    discoveredStructuresSet = new LinkedHashSet<PDBResponseSummary>();
+    for (SequenceI seq : selectedSequences)
+    {
+      PDBRestRequest pdbRequest = new PDBRestRequest();
+      pdbRequest.setAllowEmptySeq(false);
+      pdbRequest.setResponseSize(500);
+      pdbRequest.setFieldToSearchBy("(text:");
+      pdbRequest.setWantedFields(wantedFields);
+      pdbRequest.setSearchTerm(buildQuery(seq) + ")");
+      pdbRequest.setAssociatedSequence(seq.getName());
+      pdbRestCleint = new PDBRestClient();
+      PDBRestResponse resultList = pdbRestCleint.executeRequest(pdbRequest);
+      lastPdbRequest = pdbRequest;
+      if (resultList.getSearchSummary() != null
+              && !resultList.getSearchSummary().isEmpty())
+      {
+        discoveredStructuresSet.addAll(resultList.getSearchSummary());
+        updateSequenceDbRef(seq, resultList.getSearchSummary());
+      }
+    }
+
+    int noOfStructuresFound = 0;
+    if (discoveredStructuresSet != null
+            && !discoveredStructuresSet.isEmpty())
+    {
+      tbl_summary.setModel(PDBRestResponse.getTableModel(lastPdbRequest,
+              discoveredStructuresSet));
+      structuresDiscovered = true;
+      noOfStructuresFound = discoveredStructuresSet.size();
+    }
+    String totalTime = (System.currentTimeMillis() - startTime)
+            + " milli secs";
+    mainFrame.setTitle("Structure Chooser - " + noOfStructuresFound
+            + " Found (" + totalTime + ")");
+  }
+
+  /**
+   * Update the DBRef entry for a given sequence with values retrieved from
+   * PDBResponseSummary
+   * 
+   * @param seq
+   *          the Sequence to update its DBRef entry
+   * @param responseSummaries
+   *          a collection of PDBResponseSummary
+   */
+  public void updateSequenceDbRef(SequenceI seq,
+          Collection<PDBResponseSummary> responseSummaries)
+  {
+    for (PDBResponseSummary response : responseSummaries)
+    {
+      PDBEntry newEntry = new PDBEntry();
+      newEntry.setId(response.getPdbId());
+      newEntry.setType("PDB");
+      seq.getDatasetSequence().addPDBId(newEntry);
+    }
+  }
+
+  /**
+   * Builds a query string for a given sequences using its DBRef entries
+   * 
+   * @param seq
+   *          the sequences to build a query for
+   * @return the built query string
+   */
+  @SuppressWarnings("unchecked")
+  public static String buildQuery(SequenceI seq)
+  {
+    String query = seq.getName();
+    StringBuilder queryBuilder = new StringBuilder();
+    int count = 0;
+
+    if (seq.getPDBId() != null)
+    {
+      for (PDBEntry entry : (Vector<PDBEntry>) seq.getPDBId())
+      {
+        queryBuilder.append("text:").append(entry.getId()).append(" OR ");
+      }
+    }
+
+    if (seq.getDBRef() != null && seq.getDBRef().length != 0)
+    {
+      for (DBRefEntry dbRef : seq.getDBRef())
+      {
+        queryBuilder.append("text:")
+                .append(dbRef.getAccessionId().replaceAll("GO:", ""))
+                .append(" OR ");
+        ++count;
+        if (count > 10)
+        {
+          break;
+        }
+      }
+      int endIndex = queryBuilder.lastIndexOf(" OR ");
+      query = queryBuilder.toString().substring(5, endIndex);
+    }
+    return query;
+  }
+
+  /**
+   * Filters a given list of discovered structures based on supplied argument
+   * 
+   * @param fieldToFilterBy
+   *          the field to filter by
+   */
+  public void filterResultSet(final String fieldToFilterBy)
+  {
+    Thread filterThread = new Thread(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        long startTime = System.currentTimeMillis();
+        try
+        {
+          lbl_loading.setVisible(true);
+
+          Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
+                  .getStructureSummaryFields();
+          Collection<PDBResponseSummary> filteredResponse = new HashSet<PDBResponseSummary>();
+          for (SequenceI seq : selectedSequences)
+          {
+            PDBRestRequest pdbRequest = new PDBRestRequest();
+            pdbRequest.setAllowEmptySeq(false);
+            pdbRequest.setResponseSize(1);
+            pdbRequest.setFieldToSearchBy("(text:");
+            pdbRequest.setFieldToSortBy(fieldToFilterBy,
+                    !chk_invertFilter.isSelected());
+            pdbRequest.setSearchTerm(buildQuery(seq) + ")");
+            pdbRequest.setWantedFields(wantedFields);
+            pdbRequest.setAssociatedSequence(seq.getName());
+            pdbRestCleint = new PDBRestClient();
+            PDBRestResponse resultList = pdbRestCleint
+                    .executeRequest(pdbRequest);
+            lastPdbRequest = pdbRequest;
+            if (resultList.getSearchSummary() != null
+                    && !resultList.getSearchSummary().isEmpty())
+            {
+              filteredResponse.addAll(resultList.getSearchSummary());
+            }
+          }
+
+          if (!filteredResponse.isEmpty())
+          {
+            final int filterResponseCount = filteredResponse.size();
+            Collection<PDBResponseSummary> reorderedStructuresSet = new LinkedHashSet<PDBResponseSummary>();
+            reorderedStructuresSet.addAll(filteredResponse);
+            reorderedStructuresSet.addAll(discoveredStructuresSet);
+            tbl_summary.setModel(PDBRestResponse.getTableModel(
+                    lastPdbRequest, reorderedStructuresSet));
+
+            // Update table selection model here
+            tbl_summary.addRowSelectionInterval(0, filterResponseCount - 1);
+
+          }
+
+          lbl_loading.setVisible(false);
+          String totalTime = (System.currentTimeMillis() - startTime)
+                  + " milli secs";
+          mainFrame.setTitle("Structure Chooser - Filter time ("
+                  + totalTime + ")");
+
+          validateSelections();
+        } catch (Exception e)
+        {
+          e.printStackTrace();
+        }
+      }
+    });
+    filterThread.start();
+  }
+
+
+  /**
+   * Handles action event for btn_pdbFromFile
+   */
+  public void pdbFromFile_actionPerformed()
+  {
+    jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
+            jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
+    chooser.setFileView(new jalview.io.JalviewFileView());
+    chooser.setDialogTitle(MessageManager.formatMessage(
+            "label.select_pdb_file_for", new String[]
+            { selectedSequence.getDisplayId(false) }));
+    chooser.setToolTipText(MessageManager.formatMessage(
+            "label.load_pdb_file_associate_with_sequence", new String[]
+            { selectedSequence.getDisplayId(false) }));
+
+    int value = chooser.showOpenDialog(null);
+    if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
+    {
+      selectedPdbFileName = chooser.getSelectedFile().getPath();
+      jalview.bin.Cache.setProperty("LAST_DIRECTORY", selectedPdbFileName);
+      validateSelections();
+    }
+  }
+
+  /**
+   * Populates the filter combo-box options dynamically depending on discovered
+   * structures
+   */
+  protected void populateFilterComboBox()
+  {
+    if (isStructuresDiscovered())
+    {
+      cmb_filterOption.addItem(new FilterOption("Best Quality",
+              PDBDocField.OVERALL_QUALITY.getCode(), VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Best UniProt Coverage",
+              PDBDocField.UNIPROT_COVERAGE.getCode(), VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Highest Resolution",
+              PDBDocField.RESOLUTION.getCode(), VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Highest Protein Chain",
+              PDBDocField.PROTEIN_CHAIN_COUNT.getCode(), VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Highest Bound Molecules",
+              PDBDocField.BOUND_MOLECULE_COUNT.getCode(), VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Highest Polymer Residues",
+              PDBDocField.POLYMER_RESIDUE_COUNT.getCode(), VIEWS_FILTER));
+    }
+    cmb_filterOption.addItem(new FilterOption("Enter PDB Id", "-",
+            VIEWS_ENTER_ID));
+    cmb_filterOption.addItem(new FilterOption("From File", "-",
+            VIEWS_FROM_FILE));
+  }
+
+  /**
+   * Updates the displayed view based on the selected filter option
+   */
+  protected void updateCurrentView()
+  {
+    FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+            .getSelectedItem());
+    layout_switchableViews.show(pnl_switchableViews,
+            selectedFilterOpt.getView());
+    String filterTitle = mainFrame.getTitle();
+    mainFrame.setTitle(frameTitle);
+    chk_invertFilter.setVisible(false);
+    if (selectedFilterOpt.getView() == VIEWS_FILTER)
+    {
+      mainFrame.setTitle(filterTitle);
+      chk_invertFilter.setVisible(true);
+      filterResultSet(selectedFilterOpt.getValue());
+    }
+    else
+    {
+      idInputAssSeqPanel.loadCmbAssSeq();
+      fileChooserAssSeqPanel.loadCmbAssSeq();
+    }
+    validateSelections();
+  }
+
+  /**
+   * Validates user selection and activates the view button if all parameters
+   * are correct
+   */
+  public void validateSelections()
+  {
+    FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+            .getSelectedItem());
+    btn_view.setEnabled(false);
+    String currentView = selectedFilterOpt.getView();
+    if (currentView == VIEWS_FILTER)
+    {
+      if (tbl_summary.getSelectedRows().length > 0)
+      {
+        btn_view.setEnabled(true);
+      }
+    }
+    else if (currentView == VIEWS_ENTER_ID)
+    {
+      validateAssociationEnterPdb();
+    }
+    else if (currentView == VIEWS_FROM_FILE)
+    {
+      validateAssociationFromFile();
+    }
+  }
+
+  /**
+   * Validates inputs from the Manual PDB entry panel
+   */
+  public void validateAssociationEnterPdb()
+  {
+    AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) idInputAssSeqPanel
+            .getCmb_assSeq().getSelectedItem();
+    lbl_pdbManualFetchStatus.setIcon(errorImage);
+    if (selectedSequences.length == 1
+            || !assSeqOpt.getName().equalsIgnoreCase(
+                    "-Select Associated Seq-"))
+    {
+      txt_search.setEnabled(true);
+      if (isValidPBDEntry)
+      {
+        btn_view.setEnabled(true);
+        lbl_pdbManualFetchStatus.setIcon(goodImage);
+      }
+    }
+    else
+    {
+      txt_search.setEnabled(false);
+      lbl_pdbManualFetchStatus.setIcon(errorImage);
+    }
+  }
+
+  /**
+   * Validates inputs for the manual PDB file selection options
+   */
+  public void validateAssociationFromFile()
+  {
+    AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
+            .getCmb_assSeq().getSelectedItem();
+    lbl_fromFileStatus.setIcon(errorImage);
+    if (selectedSequences.length == 1
+            || (assSeqOpt != null
+            && !assSeqOpt.getName().equalsIgnoreCase(
+                    "-Select Associated Seq-")))
+    {
+      btn_pdbFromFile.setEnabled(true);
+      if (selectedPdbFileName != null && selectedPdbFileName.length() > 0)
+      {
+        btn_view.setEnabled(true);
+        lbl_fromFileStatus.setIcon(goodImage);
+      }
+    }
+    else
+    {
+      btn_pdbFromFile.setEnabled(false);
+      lbl_fromFileStatus.setIcon(errorImage);
+    }
+  }
+
+  @Override
+  public void cmbAssSeqStateChanged()
+  {
+    validateSelections();
+  }
+
+  /**
+   * Handles the state change event for the 'filter' combo-box and 'invert'
+   * check-box
+   */
+  @Override
+  protected void stateChanged(ItemEvent e)
+  {
+    if (e.getSource() instanceof JCheckBox)
+    {
+      updateCurrentView();
+    }
+    else
+    {
+      if (e.getStateChange() == ItemEvent.SELECTED)
+      {
+        updateCurrentView();
+      }
+    }
+
+  }
+
+  /**
+   * Handles action event for btn_ok
+   */
+  @Override
+  public void ok_ActionPerformed()
+  {
+    FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+            .getSelectedItem());
+    String currentView = selectedFilterOpt.getView();
+    if (currentView == VIEWS_FILTER)
+    {
+      int pdbIdCol = PDBRestClient.getPDBIdColumIndex(
+              lastPdbRequest.getWantedFields(), true);
+      int[] selectedRows = tbl_summary.getSelectedRows();
+      PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
+      int count = 0;
+      for (int summaryRow : selectedRows)
+      {
+        String pdbIdStr = tbl_summary.getValueAt(summaryRow, pdbIdCol)
+                .toString();
+        PDBEntry pdbEntry = new PDBEntry();
+        pdbEntry.setId(pdbIdStr);
+        pdbEntry.setType("PDB");
+        pdbEntriesToView[count++] = pdbEntry;
+      }
+      new StructureViewer(ap.getStructureSelectionManager())
+              .viewStructures(ap, pdbEntriesToView,
+                      ap.av.collateForPDB(pdbEntriesToView));
+    }
+    else if (currentView == VIEWS_ENTER_ID)
+    {
+      selectedSequence = ((AssociateSeqOptions) idInputAssSeqPanel
+              .getCmb_assSeq().getSelectedItem()).getSequence();
+      PDBEntry pdbEntry = new PDBEntry();
+      pdbEntry.setId(txt_search.getText());
+      pdbEntry.setType("PDB");
+      selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
+      PDBEntry[] pdbEntriesToView = new PDBEntry[]
+      { pdbEntry };
+      new StructureViewer(ap.getStructureSelectionManager())
+              .viewStructures(ap, pdbEntriesToView,
+                      ap.av.collateForPDB(pdbEntriesToView));
+    }
+    else if (currentView == VIEWS_FROM_FILE)
+    {
+      selectedSequence = ((AssociateSeqOptions) fileChooserAssSeqPanel
+              .getCmb_assSeq().getSelectedItem()).getSequence();
+      new AssociatePdbFileWithSeq().associatePdbWithSeq(
+              selectedPdbFileName, jalview.io.AppletFormatAdapter.FILE,
+              selectedSequence, true, Desktop.instance);
+    }
+    mainFrame.dispose();
+  }
+
+  /**
+   * Populates the combo-box used in associating manually fetched structures to
+   * a unique sequence when more than one sequence selection is made.
+   */
+  public void populateCmbAssociateSeqOptions(
+          JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq)
+  {
+    cmb_assSeq.removeAllItems();
+    cmb_assSeq.addItem(new AssociateSeqOptions("-Select Associated Seq-",
+            null));
+    // cmb_assSeq.addItem(new AssociateSeqOptions("Auto Detect", null));
+    lbl_associateSeq.setVisible(false);
+    if (selectedSequences.length > 1)
+    {
+      for (SequenceI seq : selectedSequences)
+      {
+        cmb_assSeq.addItem(new AssociateSeqOptions(seq));
+      }
+    }
+    else
+    {
+      String seqName = selectedSequence.getDisplayId(false);
+      seqName = seqName.length() <= 40 ? seqName : seqName.substring(0, 39);
+      lbl_associateSeq.setText(seqName);
+      lbl_associateSeq.setVisible(true);
+      cmb_assSeq.setVisible(false);
+    }
+  }
+
+  public boolean isStructuresDiscovered()
+  {
+    return structuresDiscovered;
+  }
+
+  public void setStructuresDiscovered(boolean structuresDiscovered)
+  {
+    this.structuresDiscovered = structuresDiscovered;
+  }
+
+  public Collection<PDBResponseSummary> getDiscoveredStructuresSet()
+  {
+    return discoveredStructuresSet;
+  }
+
+  @Override
+  protected void txt_search_ActionPerformed()
+  {
+    isValidPBDEntry = false;
+    if (txt_search.getText().length() > 0)
+    {
+      List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+      wantedFields.add(PDBDocField.PDB_ID);
+      PDBRestRequest pdbRequest = new PDBRestRequest();
+      pdbRequest.setAllowEmptySeq(false);
+      pdbRequest.setResponseSize(1);
+      pdbRequest.setFieldToSearchBy("(pdb_id:");
+      pdbRequest.setWantedFields(wantedFields);
+      pdbRequest.setSearchTerm(txt_search.getText() + ")");
+      pdbRequest.setAssociatedSequence(selectedSequence.getName());
+      pdbRestCleint = new PDBRestClient();
+      PDBRestResponse resultList = pdbRestCleint.executeRequest(pdbRequest);
+      if (resultList.getSearchSummary() != null
+              && resultList.getSearchSummary().size() > 0)
+      {
+        isValidPBDEntry = true;
+      }
+    }
+    validateSelections();
+  }
+
+  @Override
+  public void tabRefresh()
+  {
+    if (selectedSequences != null)
+    {
+      Thread refreshThread = new Thread(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          fetchStructuresMetaData();
+          filterResultSet(((FilterOption) cmb_filterOption
+                  .getSelectedItem()).getValue());
+        }
+      });
+      refreshThread.start();
+    }
+  }
+
+}
index 0b5f34d..c8a4a08 100644 (file)
  */
 package jalview.gui;
 
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.event.*;
-
-import jalview.datamodel.*;
+import jalview.datamodel.SequenceGroup;
 import jalview.util.MessageManager;
 
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.JColorChooser;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
 public class TextColourChooser
 {
   AlignmentPanel ap;
@@ -42,9 +52,9 @@ public class TextColourChooser
     int original1, original2, originalThreshold;
     if (sg == null)
     {
-      original1 = ap.av.textColour.getRGB();
-      original2 = ap.av.textColour2.getRGB();
-      originalThreshold = ap.av.thresholdTextColour;
+      original1 = ap.av.getTextColour().getRGB();
+      original2 = ap.av.getTextColour2().getRGB();
+      originalThreshold = ap.av.getThresholdTextColour();
     }
     else
     {
@@ -119,9 +129,9 @@ public class TextColourChooser
     {
       if (sg == null)
       {
-        ap.av.textColour = new Color(original1);
-        ap.av.textColour2 = new Color(original2);
-        ap.av.thresholdTextColour = originalThreshold;
+        ap.av.setTextColour(new Color(original1));
+        ap.av.setTextColour2(new Color(original2));
+        ap.av.setThresholdTextColour(originalThreshold);
       }
       else
       {
@@ -136,7 +146,7 @@ public class TextColourChooser
   {
     if (sg == null)
     {
-      ap.av.textColour = col;
+      ap.av.setTextColour(col);
       if (ap.av.getColourAppliesToAllGroups())
       {
         setGroupTextColour();
@@ -154,7 +164,7 @@ public class TextColourChooser
   {
     if (sg == null)
     {
-      ap.av.textColour2 = col;
+      ap.av.setTextColour2(col);
       if (ap.av.getColourAppliesToAllGroups())
       {
         setGroupTextColour();
@@ -172,7 +182,7 @@ public class TextColourChooser
   {
     if (sg == null)
     {
-      ap.av.thresholdTextColour = value;
+      ap.av.setThresholdTextColour(value);
       if (ap.av.getColourAppliesToAllGroups())
       {
         setGroupTextColour();
@@ -195,9 +205,9 @@ public class TextColourChooser
 
     for (SequenceGroup sg : ap.av.getAlignment().getGroups())
     {
-      sg.textColour = ap.av.textColour;
-      sg.textColour2 = ap.av.textColour2;
-      sg.thresholdTextColour = ap.av.thresholdTextColour;
+      sg.textColour = ap.av.getTextColour();
+      sg.textColour2 = ap.av.getTextColour2();
+      sg.thresholdTextColour = ap.av.getThresholdTextColour();
     }
   }
 
index b3a0a75..1bce84b 100755 (executable)
  */
 package jalview.gui;
 
-import java.util.*;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.print.*;
-import javax.swing.*;
-
-import jalview.analysis.*;
-import jalview.datamodel.*;
-import jalview.schemes.*;
-import jalview.util.*;
+import jalview.analysis.Conservation;
+import jalview.analysis.NJTree;
+import jalview.api.AlignViewportI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequenceNode;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.ResidueProperties;
+import jalview.schemes.UserColourScheme;
+import jalview.structure.SelectionSource;
+import jalview.util.Format;
+import jalview.util.MappingUtils;
+import jalview.util.MessageManager;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.print.PageFormat;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+import java.awt.print.PrinterJob;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.swing.JColorChooser;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.ToolTipManager;
 
 /**
  * DOCUMENT ME!
@@ -39,7 +69,7 @@ import jalview.util.*;
  * @version $Revision$
  */
 public class TreeCanvas extends JPanel implements MouseListener, Runnable,
-        Printable, MouseMotionListener
+        Printable, MouseMotionListener, SelectionSource
 {
   /** DOCUMENT ME!! */
   public static final String PLACEHOLDER = " * ";
@@ -208,7 +238,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
 
       if (node.element() instanceof SequenceI)
       {
-        SequenceI seq = (SequenceI) ((SequenceNode) node).element();
+        SequenceI seq = (SequenceI) node.element();
 
         if (av.getSequenceColour(seq) == Color.white)
         {
@@ -258,14 +288,14 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
       Rectangle rect = new Rectangle(xend + 10, ypos - charHeight / 2,
               charWidth, charHeight);
 
-      nameHash.put((SequenceI) node.element(), rect);
+      nameHash.put(node.element(), rect);
 
       // Colour selected leaves differently
       SequenceGroup selected = av.getSelectionGroup();
 
       if ((selected != null)
               && selected.getSequences(null).contains(
-                      (SequenceI) node.element()))
+                      node.element()))
       {
         g.setColor(Color.gray);
 
@@ -290,7 +320,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
       int xend = (int) (height * scale) + offx;
       int ypos = (int) (node.ycount * chunk) + offy;
 
-      g.setColor(((SequenceNode) node).color.darker());
+      g.setColor(node.color.darker());
 
       // Draw horizontal line
       g.drawLine(xstart, ypos, xend, ypos);
@@ -493,7 +523,8 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
         {
           for (int a = 0; a < aps.length; a++)
           {
-            aps[a].av.setSequenceColour((SequenceI) node.element(), c);
+            final SequenceI seq = (SequenceI) node.element();
+            aps[a].av.setSequenceColour(seq, c);
           }
         }
       }
@@ -682,7 +713,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
 
     labelLength = fm.stringWidth(longestName) + 20; // 20 allows for scrollbar
 
-    float wscale = (float) (width - labelLength - (offx * 2))
+    float wscale = (width - labelLength - (offx * 2))
             / tree.getMaxHeight();
 
     SequenceNode top = tree.getTopNode();
@@ -708,7 +739,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
         g2.setColor(Color.gray);
       }
 
-      int x = (int) ((threshold * (float) (getWidth() - labelLength - (2 * offx))) + offx);
+      int x = (int) ((threshold * (getWidth() - labelLength - (2 * offx))) + offx);
 
       g2.drawLine(x, 0, x, getHeight());
     }
@@ -854,12 +885,20 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
 
         AlignmentPanel[] aps = getAssociatedPanels();
 
+        // TODO push calls below into a single AlignViewportI method?
+        // see also AlignViewController.deleteGroups
         for (int a = 0; a < aps.length; a++)
         {
           aps[a].av.setSelectionGroup(null);
           aps[a].av.getAlignment().deleteAllGroups();
           aps[a].av.clearSequenceColours();
         }
+        if (av.getCodingComplement() != null)
+        {
+          av.getCodingComplement().setSelectionGroup(null);
+          av.getCodingComplement().getAlignment().deleteAllGroups();
+          av.getCodingComplement().clearSequenceColours();
+        }
         colourGroups();
       }
 
@@ -916,13 +955,14 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
         if (cs != null)
         {
           cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
-                  av.getIgnoreGapsConsensus());
+                  av.isIgnoreGapsConsensus());
         }
       }
       sg.cs = cs;
       // sg.recalcConservation();
       sg.setName("JTreeGroup:" + sg.hashCode());
       sg.setIdColour(col);
+
       for (int a = 0; a < aps.length; a++)
       {
         if (aps[a].av.getGlobalColourScheme() != null
@@ -939,7 +979,25 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
 
         aps[a].av.getAlignment().addGroup(new SequenceGroup(sg));
       }
+
+      // TODO can we push all of the below into AlignViewportI?
+      av.getAlignment().addGroup(sg);
+      final AlignViewportI codingComplement = av.getCodingComplement();
+      if (codingComplement != null)
+      {
+        SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av,
+                codingComplement);
+        if (mappedGroup.getSequences().size() > 0)
+        {
+          codingComplement.getAlignment().addGroup(mappedGroup);
+          for (SequenceI seq : mappedGroup.getSequences())
+          {
+            codingComplement.setSequenceColour(seq, col.brighter());
+          }
+        }
+      }
     }
+
     // notify the panel to redo any group specific stuff.
     for (int a = 0; a < aps.length; a++)
     {
@@ -948,6 +1006,13 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
       // to any Jmols listening in
     }
 
+    if (av.getCodingComplement() != null)
+    {
+      ((AlignViewport) av.getCodingComplement()).getAlignPanel().updateAnnotation();
+      /*
+       * idPanel. repaint ()
+       */
+    }
   }
 
   /**
index b8eba8f..7177e43 100755 (executable)
@@ -43,6 +43,7 @@ import jalview.io.NewickFile;
 import jalview.jbgui.GTreePanel;
 import jalview.schemes.ResidueProperties;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Font;
 import java.awt.Graphics;
@@ -138,7 +139,7 @@ public class TreePanel extends GTreePanel
     return treeCanvas.av.getAlignment();
   }
 
-  public AlignViewport getViewPort()
+  public AlignmentViewport getViewPort()
   {
     return treeCanvas.av;
   }
@@ -242,7 +243,8 @@ public class TreePanel extends GTreePanel
       associateLeavesMenu.add(item);
     }
 
-    final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem("All Views");
+    final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem(
+            "label.all_views");
     buttonGroup.add(itemf);
     itemf.setSelected(treeCanvas.applyToAllViews);
     itemf.addActionListener(new ActionListener()
@@ -343,7 +345,7 @@ public class TreePanel extends GTreePanel
       av.setCurrentTree(tree);
       if (av.getSortByTree())
       {
-        sortByTree_actionPerformed(null);
+        sortByTree_actionPerformed();
       }
     }
   }
@@ -531,7 +533,7 @@ public class TreePanel extends GTreePanel
         // msaorder);
 
         Desktop.addInternalFrame(af, MessageManager.formatMessage(
-                "label.original_data_for_params", new String[]
+                "label.original_data_for_params", new Object[]
                 { this.title }), AlignFrame.DEFAULT_WIDTH,
                 AlignFrame.DEFAULT_HEIGHT);
       }
@@ -555,7 +557,8 @@ public class TreePanel extends GTreePanel
    * 
    * @param e
    */
-  public void sortByTree_actionPerformed(ActionEvent e)
+  @Override
+  public void sortByTree_actionPerformed()
   {
 
     if (treeCanvas.applyToAllViews)
@@ -617,7 +620,7 @@ public class TreePanel extends GTreePanel
 
   public CommandI sortAlignmentIn(AlignmentPanel ap)
   {
-    AlignViewport av = ap.av;
+    AlignmentViewport av = ap.av;
     SequenceI[] oldOrder = av.getAlignment().getSequencesArray();
     AlignmentSorter.sortByTree(av.getAlignment(), tree);
     CommandI undo;
index d7a521c..c5562c6 100755 (executable)
@@ -528,7 +528,7 @@ public class UserDefinedColours extends GUserDefinedColours implements
        }
     }else{
        for (int i = 0; i < 24; i++){
-               JButton button = (JButton) upperCaseButtons.get(i);
+               JButton button = upperCaseButtons.get(i);
                newColours[i] = button.getBackground();
        }
     }
@@ -547,7 +547,7 @@ public class UserDefinedColours extends GUserDefinedColours implements
          }
       }else{
          for (int i = 0; i < 23; i++){
-                 JButton button = (JButton) lowerCaseButtons.get(i);
+                 JButton button = lowerCaseButtons.get(i);
                  newColours[i] = button.getBackground();
          }
       }
@@ -556,7 +556,7 @@ public class UserDefinedColours extends GUserDefinedColours implements
 
     if (ap != null)
     {
-      ucs.setThreshold(0, ap.av.getIgnoreGapsConsensus());
+      ucs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
     }
 
     return ucs;
index 3671b39..6444667 100644 (file)
@@ -32,12 +32,12 @@ import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasListener;
 import jalview.structure.VamsasSource;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.io.File;
 import java.io.IOException;
-import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
@@ -347,7 +347,9 @@ public class VamsasApplication implements SelectionSource, VamsasSource
   public void end_session(boolean promptUser)
   {
     if (!inSession())
+    {
       throw new Error(MessageManager.getString("error.jalview_no_connected_vamsas_session"));
+    }
     Cache.log.info("Jalview disconnecting from the Vamsas Session.");
     try
     {
@@ -958,11 +960,14 @@ public class VamsasApplication implements SelectionSource, VamsasSource
 
           int i = -1;
 
-          public void mouseOver(SequenceI seq, int index,
+          @Override
+          public void mouseOverSequence(SequenceI seq, int index,
                   VamsasSource source)
           {
             if (jv2vobj == null)
+            {
               return;
+            }
             if (seq != last || i != index)
             {
               VorbaId v = (VorbaId) jv2vobj.get(seq);
@@ -998,7 +1003,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
               AlignmentI visal = null;
               if (source instanceof AlignViewport)
               {
-                visal = ((AlignViewport) source).getAlignment();
+                visal = ((AlignmentViewport) source).getAlignment();
               }
               SelectionMessage sm = null;
               if ((seqsel == null || seqsel.getSize() == 0)
@@ -1009,7 +1014,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
                 {
                   // the empty selection.
                   sm = new SelectionMessage("jalview", new String[]
-                  { ((AlignViewport) source).getSequenceSetId() }, null,
+                  { ((AlignmentViewport) source).getSequenceSetId() }, null,
                           true);
                 }
                 else
@@ -1050,10 +1055,9 @@ public class VamsasApplication implements SelectionSource, VamsasSource
                   {
                     // gather selected columns outwith the sequence positions
                     // too
-                    Enumeration cols = colsel.getSelected().elements();
-                    while (cols.hasMoreElements())
+                    for (Object obj : colsel.getSelected())
                     {
-                      int ival = ((Integer) cols.nextElement()).intValue();
+                      int ival = ((Integer) obj).intValue();
                       Pos p = new Pos();
                       p.setI(ival + 1);
                       range.addPos(p);
index 07e3a9b..ff03fef 100644 (file)
@@ -1306,7 +1306,7 @@ public class WsJobParameters extends JPanel implements ItemListener,
    */
   protected void updateWebServiceMenus()
   {
-    for (AlignFrame alignFrame : Desktop.getAlignframes())
+    for (AlignFrame alignFrame : Desktop.getAlignFrames())
     {
       alignFrame.BuildWebServiceMenu();
     }
index d076b7a..c69a0c7 100755 (executable)
@@ -46,10 +46,24 @@ public class AppletFormatAdapter
    * List of valid format strings used in the isValidFormat method
    */
   public static final String[] READABLE_FORMATS = new String[]
-          { "BLC", "CLUSTAL", "FASTA", "MSF", "PileUp", "PIR", "PFAM", "STH",
-      "PDB", "JnetFile", "RNAML", PhylipFile.FILE_DESC, "HTML" }; // ,
-                                                                              // "SimpleBLAST"
-                                                                              // };
+  { "BLC", "CLUSTAL", "FASTA", "MSF", "PileUp", "PIR", "PFAM", "STH",
+      "PDB", "JnetFile", "RNAML", PhylipFile.FILE_DESC, "HTML" };
+
+  /**
+   * List of readable format file extensions by application in order
+   * corresponding to READABLE_FNAMES
+   */
+  public static final String[] READABLE_EXTENSIONS = new String[]
+  { "fa, fasta, mfa, fastq", "aln", "pfam", "msf", "pir", "blc", "amsa",
+      "sto,stk", "xml,rnaml", PhylipFile.FILE_EXT, "jar,jvp", "html" };
+
+  /**
+   * List of readable formats by application in order corresponding to
+   * READABLE_EXTENSIONS
+   */
+  public static final String[] READABLE_FNAMES = new String[]
+  { "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "Stockholm",
+      "RNAML", PhylipFile.FILE_DESC, "Jalview", "HTML" };
 
   /**
    * List of valid format strings for use by callers of the formatSequences
@@ -75,26 +89,6 @@ public class AppletFormatAdapter
   { "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "STH",
       PhylipFile.FILE_DESC, "Jalview" };
 
-  /**
-   * List of readable format file extensions by application in order
-   * corresponding to READABLE_FNAMES
-   */
-  public static final String[] READABLE_EXTENSIONS = new String[]
-          { "fa, fasta, mfa, fastq", "aln", "pfam", "msf", "pir", "blc", "amsa",
-      "jar,jvp", "sto,stk", "xml,rnaml", PhylipFile.FILE_EXT,
- "html" }; // ".blast"
-
-  /**
-   * List of readable formats by application in order corresponding to
-   * READABLE_EXTENSIONS
-   */
-  public static final String[] READABLE_FNAMES = new String[]
-          { "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "Jalview",
-      "Stockholm", "RNAML", PhylipFile.FILE_DESC, "HTML" };// ,
-
-  // "SimpleBLAST"
-  // };
-
   public static String INVALID_CHARACTERS = "Contains invalid characters";
 
   // TODO: make these messages dynamic
index db43a3f..d2c4a7f 100644 (file)
@@ -5,7 +5,6 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.exceptions.NoFileSelectedException;
-import jalview.gui.AlignViewport;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.FeatureRenderer;
 import jalview.json.binding.v1.BioJsAlignmentPojo;
@@ -13,6 +12,7 @@ import jalview.json.binding.v1.BioJsFeaturePojo;
 import jalview.json.binding.v1.BioJsSeqPojo;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Color;
 import java.io.BufferedReader;
@@ -26,7 +26,7 @@ import com.json.JSONException;
 
 public class BioJsHTMLOutput
 {
-  private AlignViewport av;
+  private AlignmentViewport av;
 
   private jalview.api.FeatureRenderer fr;
 
@@ -137,8 +137,7 @@ public class BioJsHTMLOutput
       seqPojo.setName(name.toString());
       seqPojo.setSeq(seq.getSequenceAsString());
 
-      SequenceFeature[] seqFeatures = seq.getDatasetSequence()
-              .getSequenceFeatures();
+      SequenceFeature[] seqFeatures = seq.getSequenceFeatures();
       if (seqFeatures != null)
       {
         ArrayList<BioJsFeaturePojo> bjsSeqFeatures = new ArrayList<BioJsFeaturePojo>();
index 38033ff..3322689 100755 (executable)
@@ -332,13 +332,7 @@ public class FileLoader implements Runnable
           }
           if (viewport != null)
           {
-            // TODO: create undo object for this JAL-1101
-            for (int i = 0; i < al.getHeight(); i++)
-            {
-              viewport.getAlignment().addSequence(al.getSequenceAt(i));
-            }
-            viewport.firePropertyChange("alignment", null, viewport
-                    .getAlignment().getSequences());
+            viewport.addAlignment(al, title);
           }
           else
           {
index c81be4b..3de5b30 100755 (executable)
  */
 package jalview.io;
 
-import java.io.*;
-import java.util.*;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.util.Format;
 
-import jalview.datamodel.*;
-import jalview.util.*;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.util.Vector;
 
 /**
  * DOCUMENT ME!
@@ -63,10 +66,6 @@ public class MSFfile extends AlignFile
     super(source);
   }
 
-  {
-    // TODO Auto-generated constructor stub
-  }
-
   /**
    * DOCUMENT ME!
    */
index 610c03f..07cfa8b 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.io;
 
+
 import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
@@ -364,7 +365,7 @@ public class SequenceAnnotationReport
     }
 
     // ADD NON POSITIONAL SEQUENCE INFO
-    SequenceFeature[] features = ds.getSequenceFeatures();
+    SequenceFeature[] features = sequence.getSequenceFeatures();
     if (showNpFeats && features != null)
     {
       for (int i = 0; i < features.length; i++)
index e5e14ad..6490d28 100644 (file)
@@ -978,6 +978,7 @@ public class StockholmFile extends AlignFile
             {
               feature = ds.getSequenceFeatures()[0].type;
             }
+            // ?bug - feature may still have previous loop value
             String key = type2id(feature);
 
             if (key == null)
index 58adfb2..7df7cb2 100644 (file)
@@ -34,6 +34,7 @@ import jalview.io.vamsas.DatastoreItem;
 import jalview.io.vamsas.DatastoreRegistry;
 import jalview.io.vamsas.Rangetype;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.io.IOException;
 import java.util.Enumeration;
@@ -42,12 +43,35 @@ import java.util.Hashtable;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.Vector;
 import java.util.jar.JarInputStream;
 import java.util.jar.JarOutputStream;
 
-import uk.ac.vamsas.client.*;
-import uk.ac.vamsas.objects.core.*;
+import uk.ac.vamsas.client.IClientAppdata;
+import uk.ac.vamsas.client.IClientDocument;
+import uk.ac.vamsas.client.Vobject;
+import uk.ac.vamsas.client.VorbaId;
+import uk.ac.vamsas.objects.core.Alignment;
+import uk.ac.vamsas.objects.core.AlignmentSequence;
+import uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation;
+import uk.ac.vamsas.objects.core.AnnotationElement;
+import uk.ac.vamsas.objects.core.DataSet;
+import uk.ac.vamsas.objects.core.DataSetAnnotations;
+import uk.ac.vamsas.objects.core.DbRef;
+import uk.ac.vamsas.objects.core.Entry;
+import uk.ac.vamsas.objects.core.Glyph;
+import uk.ac.vamsas.objects.core.Local;
+import uk.ac.vamsas.objects.core.MapType;
+import uk.ac.vamsas.objects.core.Mapped;
+import uk.ac.vamsas.objects.core.Property;
+import uk.ac.vamsas.objects.core.Provenance;
+import uk.ac.vamsas.objects.core.RangeAnnotation;
+import uk.ac.vamsas.objects.core.RangeType;
+import uk.ac.vamsas.objects.core.Seg;
+import uk.ac.vamsas.objects.core.Sequence;
+import uk.ac.vamsas.objects.core.SequenceType;
+import uk.ac.vamsas.objects.core.VAMSAS;
 import uk.ac.vamsas.objects.utils.Properties;
 
 /*
@@ -127,7 +151,7 @@ public class VamsasAppDatastore
   private void buildSkipList()
   {
     skipList = new Hashtable();
-    AlignFrame[] al = Desktop.getAlignframes();
+    AlignFrame[] al = Desktop.getAlignFrames();
     for (int f = 0; al != null && f < al.length; f++)
     {
       skipList.put(al[f].getViewport().getSequenceSetId(), al[f]);
@@ -728,12 +752,12 @@ public class VamsasAppDatastore
    * @return true if alignment associated with this view will be stored in
    *         document.
    */
-  public boolean alignmentWillBeSkipped(AlignViewport av)
+  public boolean alignmentWillBeSkipped(AlignmentViewport av)
   {
     return (!av.getAlignment().isAligned());
   }
 
-  private void addToSkipList(AlignViewport av)
+  private void addToSkipList(AlignmentViewport av)
   {
     if (skipList == null)
     {
@@ -1068,8 +1092,10 @@ public class VamsasAppDatastore
         an.addProperty(Properties.newProperty(THRESHOLD,
                 Properties.FLOATTYPE, "" + alan.getThreshold().value));
         if (alan.getThreshold().label != null)
+        {
           an.addProperty(Properties.newProperty(THRESHOLD + "Name",
                   Properties.STRINGTYPE, "" + alan.getThreshold().label));
+        }
       }
       ((DataSet) sref.getV_parent()).addDataSetAnnotations(an);
       bindjvvobj(alan, an);
@@ -1381,12 +1407,12 @@ public class VamsasAppDatastore
     // sync,
     // and if any contain more than one view, then remove the one generated by
     // document update.
-    AlignViewport views[], av = null;
+    AlignmentViewport views[], av = null;
     AlignFrame af = null;
     Iterator newviews = newAlignmentViews.iterator();
     while (newviews.hasNext())
     {
-      av = (AlignViewport) newviews.next();
+      av = (AlignmentViewport) newviews.next();
       af = Desktop.getAlignFrameFor(av);
       // TODO implement this : af.getNumberOfViews
       String seqsetidobj = av.getSequenceSetId();
@@ -1403,7 +1429,8 @@ public class VamsasAppDatastore
         // to the align frames.
         boolean gathered = false;
         String newviewid = null;
-        AlignedCodonFrame[] mappings = av.getAlignment().getCodonFrames();
+        Set<AlignedCodonFrame> mappings = av.getAlignment()
+                .getCodonFrames();
         for (int i = 0; i < views.length; i++)
         {
           if (views[i] != av)
@@ -1438,7 +1465,7 @@ public class VamsasAppDatastore
         {
           // ensure sequence mappings from vamsas document view still
           // active
-          if (mappings != null && mappings.length > 0)
+          if (mappings != null)
           {
             jalview.structure.StructureSelectionManager
                     .getStructureSelectionManager(Desktop.instance)
@@ -1682,7 +1709,7 @@ public class VamsasAppDatastore
             uk.ac.vamsas.objects.core.Alignment alignment = dataset
                     .getAlignment(al);
             // TODO check this handles multiple views properly
-            AlignViewport av = findViewport(alignment);
+            AlignmentViewport av = findViewport(alignment);
 
             jalview.datamodel.AlignmentI jal = null;
             if (av != null)
@@ -1956,10 +1983,10 @@ public class VamsasAppDatastore
     return newAlignmentViews.size();
   }
 
-  public AlignViewport findViewport(Alignment alignment)
+  public AlignmentViewport findViewport(Alignment alignment)
   {
-    AlignViewport av = null;
-    AlignViewport[] avs = Desktop
+    AlignmentViewport av = null;
+    AlignmentViewport[] avs = Desktop
             .getViewports((String) getvObj2jv(alignment));
     if (avs != null)
     {
@@ -2207,6 +2234,7 @@ public class VamsasAppDatastore
             Cache.log.warn("Failed to parse threshold property");
           }
           if (val != null)
+          {
             if (gl == null)
             {
               gl = new GraphLine(val.floatValue(), "", java.awt.Color.black);
@@ -2215,11 +2243,14 @@ public class VamsasAppDatastore
             {
               gl.value = val.floatValue();
             }
+          }
         }
         else if (props[p].getName().equalsIgnoreCase(THRESHOLD + "Name"))
         {
           if (gl == null)
+          {
             gl = new GraphLine(0, "", java.awt.Color.black);
+          }
           gl.label = props[p].getContent();
         }
       }
@@ -2539,15 +2570,15 @@ public class VamsasAppDatastore
    * initialise a range type object from a set of start/end inclusive intervals
    * 
    * @param mrt
-   * @param range
+   * @param ranges
    */
-  private void initRangeType(RangeType mrt, int[] range)
+  private void initRangeType(RangeType mrt, List<int[]> ranges)
   {
-    for (int i = 0; i < range.length; i += 2)
+    for (int[] range : ranges)
     {
       Seg vSeg = new Seg();
-      vSeg.setStart(range[i]);
-      vSeg.setEnd(range[i + 1]);
+      vSeg.setStart(range[0]);
+      vSeg.setEnd(range[1]);
       mrt.addSeg(vSeg);
     }
   }
@@ -2670,10 +2701,10 @@ public class VamsasAppDatastore
     return vobj2jv;
   }
 
-  public void storeSequenceMappings(AlignViewport viewport, String title)
+  public void storeSequenceMappings(AlignmentViewport viewport, String title)
           throws Exception
   {
-    AlignViewport av = viewport;
+    AlignmentViewport av = viewport;
     try
     {
       jalview.datamodel.AlignmentI jal = av.getAlignment();
@@ -2695,18 +2726,15 @@ public class VamsasAppDatastore
 
       }
       // Store any sequence mappings.
-      if (av.getAlignment().getCodonFrames() != null
-              && av.getAlignment().getCodonFrames().length > 0)
+      Set<AlignedCodonFrame> cframes = av.getAlignment().getCodonFrames();
+      if (cframes != null)
       {
-        jalview.datamodel.AlignedCodonFrame[] cframes = av.getAlignment()
-                .getCodonFrames();
-        for (int cf = 0; cf < cframes.length; cf++)
+        for (AlignedCodonFrame acf : cframes)
         {
-          if (cframes[cf].getdnaSeqs() != null
-                  && cframes[cf].getdnaSeqs().length > 0)
+          if (acf.getdnaSeqs() != null && acf.getdnaSeqs().length > 0)
           {
-            jalview.datamodel.SequenceI[] dmps = cframes[cf].getdnaSeqs();
-            jalview.datamodel.Mapping[] mps = cframes[cf].getProtMappings();
+            jalview.datamodel.SequenceI[] dmps = acf.getdnaSeqs();
+            jalview.datamodel.Mapping[] mps = acf.getProtMappings();
             for (int smp = 0; smp < mps.length; smp++)
             {
               uk.ac.vamsas.objects.core.SequenceType mfrom = (SequenceType) getjv2vObj(dmps[smp]);
index e7f8cf2..08b1b52 100644 (file)
  */
 package jalview.io.vamsas;
 
+import jalview.io.VamsasAppDatastore;
+import jalview.util.MessageManager;
+
+import java.util.List;
 import java.util.Vector;
 
 import uk.ac.vamsas.client.Vobject;
@@ -28,8 +32,6 @@ import uk.ac.vamsas.objects.core.MapType;
 import uk.ac.vamsas.objects.core.Mapped;
 import uk.ac.vamsas.objects.core.RangeType;
 import uk.ac.vamsas.objects.core.Seg;
-import jalview.io.VamsasAppDatastore;
-import jalview.util.MessageManager;
 
 /**
  * Enhances DatastoreItem objects with additional functions to do with RangeType
@@ -221,15 +223,15 @@ public abstract class Rangetype extends DatastoreItem
    * initialise a range type object from a set of start/end inclusive intervals
    * 
    * @param mrt
-   * @param range
+   * @param ranges
    */
-  protected void initRangeType(RangeType mrt, int[] range)
+  protected void initRangeType(RangeType mrt, List<int[]> ranges)
   {
-    for (int i = 0; i < range.length; i += 2)
+    for (int[] range : ranges)
     {
       Seg vSeg = new Seg();
-      vSeg.setStart(range[i]);
-      vSeg.setEnd(range[i + 1]);
+      vSeg.setStart(range[0]);
+      vSeg.setEnd(range[1]);
       vSeg.setInclusive(true);
       mrt.addSeg(vSeg);
     }
index 97137f8..4929a06 100644 (file)
  */
 package jalview.io.vamsas;
 
-import java.util.Vector;
-
 import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Mapping;
 import jalview.datamodel.SequenceI;
 import jalview.gui.Desktop;
 import jalview.io.VamsasAppDatastore;
-import uk.ac.vamsas.client.Vobject;
+
+import java.util.Vector;
+
 import uk.ac.vamsas.objects.core.AlignmentSequence;
 import uk.ac.vamsas.objects.core.DataSet;
 import uk.ac.vamsas.objects.core.Sequence;
@@ -283,12 +284,12 @@ public class Sequencemapping extends Rangetype
       jalview.bin.Cache.log.info("Ignoring non sequence-sequence mapping");
       return;
     }
-    mobj = this.getvObj2jv((Vobject) sdloc);
+    mobj = this.getvObj2jv(sdloc);
     if (mobj instanceof SequenceI)
     {
       from = (SequenceI) mobj;
     }
-    mobj = this.getvObj2jv((Vobject) sdmap);
+    mobj = this.getvObj2jv(sdmap);
     if (mobj instanceof SequenceI)
     {
       to = (SequenceI) mobj;
@@ -325,19 +326,17 @@ public class Sequencemapping extends Rangetype
     }
     // create mapping storage object and make each dataset alignment reference
     // it.
-    jalview.datamodel.AlignmentI dsLoc = (jalview.datamodel.AlignmentI) getvObj2jv(sdloc
-            .getV_parent());
-    jalview.datamodel.AlignmentI dsMap = (jalview.datamodel.AlignmentI) getvObj2jv(sdmap
-            .getV_parent());
-    AlignedCodonFrame afc = new AlignedCodonFrame(0);
+    AlignmentI dsLoc = (AlignmentI) getvObj2jv(sdloc.getV_parent());
+    AlignmentI dsMap = (AlignmentI) getvObj2jv(sdmap.getV_parent());
+    AlignedCodonFrame acf = new AlignedCodonFrame();
 
     if (dsLoc != null && dsLoc != dsMap)
     {
-      dsLoc.addCodonFrame(afc);
+      dsLoc.addCodonFrame(acf);
     }
     if (dsMap != null)
     {
-      dsMap.addCodonFrame(afc);
+      dsMap.addCodonFrame(acf);
     }
     // create and add the new mapping to (each) dataset's codonFrame
 
@@ -350,24 +349,22 @@ public class Sequencemapping extends Rangetype
         mapping = new jalview.util.MapList(mapping.getToRanges(),
                 mapping.getFromRanges(), mapping.getToRatio(),
                 mapping.getFromRatio());
-        afc.addMap(to, from, mapping);
+        acf.addMap(to, from, mapping);
       }
       else
       {
         mapping = this.parsemapType(sequenceMapping, 3, 1); // correct sense
-        afc.addMap(from, to, mapping);
+        acf.addMap(from, to, mapping);
       }
     }
     else
     {
       mapping = this.parsemapType(sequenceMapping, 1, 1); // correct sense
-      afc.addMap(from, to, mapping);
+      acf.addMap(from, to, mapping);
     }
     bindjvvobj(mapping, sequenceMapping);
     jalview.structure.StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance).addMappings(
-                    new AlignedCodonFrame[]
-                    { afc });
+            .getStructureSelectionManager(Desktop.instance).addMapping(acf);
     // Try to link up any conjugate database references in the two sequences
     // matchConjugateDBRefs(from, to, mapping);
     // Try to propagate any dbrefs across this mapping.
index 1cbc848..b5ada26 100644 (file)
@@ -35,10 +35,10 @@ import jalview.datamodel.SeqCigar;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.SequenceNode;
-import jalview.gui.AlignViewport;
 import jalview.gui.TreePanel;
 import jalview.io.NewickFile;
 import jalview.io.VamsasAppDatastore;
+import jalview.viewmodel.AlignmentViewport;
 import uk.ac.vamsas.client.Vobject;
 import uk.ac.vamsas.objects.core.AlignmentSequence;
 import uk.ac.vamsas.objects.core.Entry;
@@ -510,7 +510,7 @@ public class Tree extends DatastoreItem
    */
   public Object[] recoverInputData(Provenance tp)
   {
-    AlignViewport javport = null;
+    AlignmentViewport javport = null;
     jalview.datamodel.AlignmentI jal = null;
     jalview.datamodel.CigarArray view = null;
     for (int pe = 0; pe < tp.getEntryCount(); pe++)
@@ -604,7 +604,7 @@ public class Tree extends DatastoreItem
     return null;
   }
 
-  private AlignViewport getViewport(Vobject v_parent)
+  private AlignmentViewport getViewport(Vobject v_parent)
   {
     if (v_parent instanceof uk.ac.vamsas.objects.core.Alignment)
     {
index d3d3d29..9d0279b 100644 (file)
@@ -37,7 +37,9 @@ public class MouseOverListener extends JSFunctionExec implements
 
   int i = -1;
 
-  public void mouseOver(SequenceI seq, int index, VamsasSource source)
+  @Override
+  public void mouseOverSequence(SequenceI seq, int index,
+          VamsasSource source)
   {
     if (seq != last || i != index)
     {
index 4db44e1..f46cd90 100644 (file)
@@ -20,9 +20,6 @@
  */
 package jalview.javascript;
 
-import java.awt.Color;
-import java.util.ArrayList;
-
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
@@ -30,11 +27,15 @@ import jalview.appletgui.AlignFrame;
 import jalview.bin.JalviewLite;
 import jalview.datamodel.SequenceI;
 import jalview.ext.jmol.JmolCommands;
+import jalview.structure.AtomSpec;
 import jalview.structure.StructureListener;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureMappingcommandSet;
 import jalview.structure.StructureSelectionManager;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Propagate events involving PDB structures associated with sequences to a
  * javascript function. Generally, the javascript handler is called with a
@@ -133,7 +134,6 @@ public class MouseOverStructureListener extends JSFunctionExec implements
     return modelSet;
   }
 
-  @Override
   public void mouseOverStructure(int atomIndex, String strInfo)
   {
 
@@ -144,24 +144,27 @@ public class MouseOverStructureListener extends JSFunctionExec implements
   }
 
   @Override
-  public void highlightAtom(int atomIndex, int pdbResNum, String chain,
-          String pdbId)
+  public void highlightAtoms(List<AtomSpec> atoms)
   {
-    String[] st = new String[0];
-    try
-    {
-      executeJavascriptFunction(_listenerfn, st = new String[]
-      { "mouseover", "" + pdbId, "" + chain, "" + (pdbResNum),
-          "" + atomIndex });
-    } catch (Exception ex)
+    for (AtomSpec atom : atoms)
     {
-      System.err.println("Couldn't execute callback with " + _listenerfn
-              + " using args { " + st[0] + ", " + st[1] + ", " + st[2]
-              + "," + st[3] + "\n");
-      ex.printStackTrace();
-
+      try
+      {
+        // TODO is this right? StructureSelectionManager passes pdbFile as the
+        // field that is interpreted (in 2.8.2) as pdbId?
+        // JBPComment: yep - this is right! the Javascript harness uses the
+        // absolute pdbFile URI to locate the PDB file in the external viewer
+        executeJavascriptFunction(_listenerfn, new String[]
+        { "mouseover", "" + atom.getPdbFile(),
+                    "" + atom.getChain(),
+            "" + (atom.getPdbResNum()), "" + atom.getAtomIndex() });
+      } catch (Exception ex)
+      {
+        System.err.println("Couldn't execute callback with " + _listenerfn
+                + " for atomSpec: " + atom);
+        ex.printStackTrace();
+      }
     }
-
   }
 
   @Override
@@ -283,13 +286,6 @@ public class MouseOverStructureListener extends JSFunctionExec implements
   }
 
   @Override
-  public Color getColour(int atomIndex, int pdbResNum, String chain,
-          String pdbId)
-  {
-    return null;
-  }
-
-  @Override
   public AlignFrame getAlignFrame()
   {
     // associated with all alignframes, always.
index 78aaca6..befa3b1 100755 (executable)
  */
 package jalview.jbgui;
 
-import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
-import jalview.bin.Cache;
-import jalview.gui.JvSwingUtils;
-import jalview.gui.Preferences;
-import jalview.schemes.ColourSchemeProperty;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.GridLayout;
@@ -35,8 +28,11 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.FocusAdapter;
 import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.swing.BorderFactory;
 import javax.swing.ButtonGroup;
@@ -50,77 +46,40 @@ import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JRadioButtonMenuItem;
 import javax.swing.JTabbedPane;
+import javax.swing.KeyStroke;
 import javax.swing.SwingUtilities;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
+import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
+import jalview.api.SplitContainerI;
+import jalview.bin.Cache;
+import jalview.gui.JvSwingUtils;
+import jalview.gui.Preferences;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.util.MessageManager;
+
 public class GAlignFrame extends JInternalFrame
 {
   protected JMenuBar alignFrameMenuBar = new JMenuBar();
 
-  protected JMenu fileMenu = new JMenu();
-
   protected JMenuItem closeMenuItem = new JMenuItem();
 
-  protected JMenu editMenu = new JMenu();
-
-  protected JMenu viewMenu = new JMenu();
-
-  protected JMenu annotationsMenu = new JMenu();
-
   protected JMenu colourMenu = new JMenu();
 
-  protected JMenu calculateMenu = new JMenu();
-
   protected JMenu webService = new JMenu();
 
   protected JMenuItem webServiceNoServices;
 
-  protected JMenuItem selectAllSequenceMenuItem = new JMenuItem();
-
-  protected JMenuItem deselectAllSequenceMenuItem = new JMenuItem();
-
-  protected JMenuItem invertSequenceMenuItem = new JMenuItem();
-
-  protected JMenuItem remove2LeftMenuItem = new JMenuItem();
-
-  protected JMenuItem remove2RightMenuItem = new JMenuItem();
-
-  protected JMenuItem removeGappedColumnMenuItem = new JMenuItem();
-
-  protected JMenuItem removeAllGapsMenuItem = new JMenuItem();
-
   public JCheckBoxMenuItem viewBoxesMenuItem = new JCheckBoxMenuItem();
 
   public JCheckBoxMenuItem viewTextMenuItem = new JCheckBoxMenuItem();
 
-  protected JMenuItem sortPairwiseMenuItem = new JMenuItem();
-
-  protected JMenuItem sortIDMenuItem = new JMenuItem();
-
-  protected JMenuItem sortLengthMenuItem = new JMenuItem();
-
-  protected JMenuItem sortGroupMenuItem = new JMenuItem();
-
   protected JMenu sortByAnnotScore = new JMenu();
 
-  protected JMenuItem removeRedundancyMenuItem = new JMenuItem();
-
-  protected JMenuItem pairwiseAlignmentMenuItem = new JMenuItem();
-
-  protected JMenuItem PCAMenuItem = new JMenuItem();
-
-  protected JMenuItem averageDistanceTreeMenuItem = new JMenuItem();
-
-  protected JMenuItem neighbourTreeMenuItem = new JMenuItem();
-
-  BorderLayout borderLayout1 = new BorderLayout();
-
   public JLabel statusBar = new JLabel();
 
-  protected JMenuItem saveAs = new JMenuItem();
-
   protected JMenu outputTextboxMenu = new JMenu();
 
   protected JRadioButtonMenuItem clustalColour = new JRadioButtonMenuItem();
@@ -156,20 +115,12 @@ public class GAlignFrame extends JInternalFrame
 
   protected JRadioButtonMenuItem tcoffeeColour = new JRadioButtonMenuItem();
 
-  JMenuItem njTreeBlosumMenuItem = new JMenuItem();
-
-  JMenuItem avDistanceTreeBlosumMenuItem = new JMenuItem();
-
   public JCheckBoxMenuItem annotationPanelMenuItem = new JCheckBoxMenuItem();
 
   public JCheckBoxMenuItem colourTextMenuItem = new JCheckBoxMenuItem();
 
   public JCheckBoxMenuItem showNonconservedMenuItem = new JCheckBoxMenuItem();
 
-  JMenuItem htmlMenuItem = new JMenuItem();
-
-  JMenuItem overviewMenuItem = new JMenuItem();
-
   protected JMenuItem undoMenuItem = new JMenuItem();
 
   protected JMenuItem redoMenuItem = new JMenuItem();
@@ -180,60 +131,30 @@ public class GAlignFrame extends JInternalFrame
 
   public JCheckBoxMenuItem wrapMenuItem = new JCheckBoxMenuItem();
 
-  JMenuItem printMenuItem = new JMenuItem();
-
   public JCheckBoxMenuItem renderGapsMenuItem = new JCheckBoxMenuItem();
 
-  JMenuItem findMenuItem = new JMenuItem();
-
   public JCheckBoxMenuItem abovePIDThreshold = new JCheckBoxMenuItem();
 
   public JCheckBoxMenuItem showSeqFeatures = new JCheckBoxMenuItem();
 
   public JCheckBoxMenuItem showSeqFeaturesHeight = new JCheckBoxMenuItem();
 
-  JMenuItem deleteGroups = new JMenuItem();
-
-  JMenuItem createGroup = new JMenuItem();
-
-  JMenuItem unGroup = new JMenuItem();
-
-  JMenuItem delete = new JMenuItem();
-
   JMenuItem copy = new JMenuItem();
 
   JMenuItem cut = new JMenuItem();
 
   JMenu pasteMenu = new JMenu();
 
-  JMenuItem pasteNew = new JMenuItem();
-
-  JMenuItem pasteThis = new JMenuItem();
-
   public JCheckBoxMenuItem applyToAllGroups = new JCheckBoxMenuItem();
 
-  JMenuItem createPNG = new JMenuItem();
-
-  JMenuItem createBioJS = new JMenuItem();
-
-  JMenuItem createSVG = new JMenuItem();
-
-  protected JMenuItem font = new JMenuItem();
-
   public JCheckBoxMenuItem seqLimits = new JCheckBoxMenuItem();
 
-  JMenuItem epsFile = new JMenuItem();
-
-  JMenuItem LoadtreeMenuItem = new JMenuItem();
-
   public JCheckBoxMenuItem scaleAbove = new JCheckBoxMenuItem();
 
   public JCheckBoxMenuItem scaleLeft = new JCheckBoxMenuItem();
 
   public JCheckBoxMenuItem scaleRight = new JCheckBoxMenuItem();
 
-  protected JMenuItem modifyPID = new JMenuItem();
-
   protected JMenuItem modifyConservation = new JMenuItem();
 
   protected JMenu sortByTreeMenu = new JMenu();
@@ -242,8 +163,6 @@ public class GAlignFrame extends JInternalFrame
 
   protected JMenu calculateTree = new JMenu();
 
-  JMenu jMenu2 = new JMenu();
-
   protected JCheckBoxMenuItem padGapsMenuitem = new JCheckBoxMenuItem();
 
   protected JCheckBoxMenuItem showNpFeatsMenuitem = new JCheckBoxMenuItem();
@@ -252,68 +171,20 @@ public class GAlignFrame extends JInternalFrame
 
   protected ButtonGroup colours = new ButtonGroup();
 
-  JMenuItem vamsasStore = new JMenuItem();
-
   protected JMenuItem showTranslation = new JMenuItem();
 
-  protected JMenuItem extractScores = new JMenuItem();
-
-  protected JMenuItem expandAlignment = new JMenuItem();
-
   protected JMenu showProducts = new JMenu();
 
-  public JMenuItem openFeatureSettings = new JMenuItem();
-
-  JMenuItem fetchSequence = new JMenuItem();
-
-  JMenuItem annotationColour = new JMenuItem();
-
-  JMenuItem annotationColumn = new JMenuItem();
-
   protected JMenuItem rnahelicesColour = new JMenuItem();
 
-  JMenuItem associatedData = new JMenuItem();
-
   protected JCheckBoxMenuItem autoCalculate = new JCheckBoxMenuItem();
 
   protected JCheckBoxMenuItem sortByTree = new JCheckBoxMenuItem();
 
   protected JCheckBoxMenuItem listenToViewSelections = new JCheckBoxMenuItem();
 
-  JMenu addSequenceMenu = new JMenu();
-
-  JMenuItem addFromFile = new JMenuItem();
-
-  JMenuItem addFromText = new JMenuItem();
-
-  JMenuItem addFromURL = new JMenuItem();
-
-  JMenuItem exportAnnotations = new JMenuItem();
-
-  JMenuItem exportFeatures = new JMenuItem();
-
   protected JPanel statusPanel = new JPanel();
 
-  GridLayout gridLayout1 = new GridLayout();
-
-  JMenu jMenu3 = new JMenu();
-
-  JMenuItem showAllSeqs = new JMenuItem();
-
-  JMenuItem showAllColumns = new JMenuItem();
-
-  JMenu hideMenu = new JMenu();
-
-  JMenuItem hideSelSequences = new JMenuItem();
-
-  JMenuItem hideSelColumns = new JMenuItem();
-
-  JMenuItem hideAllButSelection = new JMenuItem();
-
-  JMenuItem hideAllSelection = new JMenuItem();
-
-  JMenuItem showAllhidden = new JMenuItem();
-
   protected JMenuItem showAllSeqAnnotations = new JMenuItem();
 
   protected JMenuItem hideAllSeqAnnotations = new JMenuItem();
@@ -322,27 +193,15 @@ public class GAlignFrame extends JInternalFrame
 
   protected JMenuItem hideAllAlAnnotations = new JMenuItem();
 
-  protected JCheckBoxMenuItem sortAnnBySequence = new JCheckBoxMenuItem();
-
-  protected JCheckBoxMenuItem sortAnnByLabel = new JCheckBoxMenuItem();
+  protected JCheckBoxMenuItem showComplementMenuItem = new JCheckBoxMenuItem();
 
   protected JCheckBoxMenuItem hiddenMarkers = new JCheckBoxMenuItem();
 
-  JMenuItem invertColSel = new JMenuItem();
-
   protected JTabbedPane tabbedPane = new JTabbedPane();
 
-  JMenuItem save = new JMenuItem();
-
   protected JMenuItem reload = new JMenuItem();
 
-  JMenuItem newView = new JMenuItem();
-
-  JMenuItem textColour = new JMenuItem();
-
-  JMenu formatMenu = new JMenu();
-
-  JMenu selectMenu = new JMenu();
+  protected JMenu formatMenu = new JMenu();
 
   protected JCheckBoxMenuItem idRightAlign = new JCheckBoxMenuItem();
 
@@ -354,18 +213,6 @@ public class GAlignFrame extends JInternalFrame
 
   protected JMenuItem expandViews = new JMenuItem();
 
-  JMenuItem pageSetup = new JMenuItem();
-
-  JMenuItem alignmentProperties = new JMenuItem();
-
-  JMenu tooltipSettingsMenu = new JMenu();
-
-  private JMenuItem justifyLeftMenuItem = new JMenuItem();
-
-  private JMenuItem justifyRightMenuItem = new JMenuItem();
-
-  JMenu autoAnnMenu = new JMenu();
-
   protected JCheckBoxMenuItem showGroupConsensus = new JCheckBoxMenuItem();
 
   protected JCheckBoxMenuItem showGroupConservation = new JCheckBoxMenuItem();
@@ -378,16 +225,14 @@ public class GAlignFrame extends JInternalFrame
 
   protected JCheckBoxMenuItem applyAutoAnnotationSettings = new JCheckBoxMenuItem();
 
-  protected JRadioButtonMenuItem showAutoFirst = new JRadioButtonMenuItem();
-
-  protected JRadioButtonMenuItem showAutoLast = new JRadioButtonMenuItem();
-
-  private JMenuItem grpsFromSelection = new JMenuItem();
-
   private SequenceAnnotationOrder annotationSortOrder;
 
   private boolean showAutoCalculatedAbove = false;
 
+  private Map<KeyStroke, JMenuItem> accelerators = new HashMap<KeyStroke, JMenuItem>();
+
+  private SplitContainerI splitFrame;
+
   public GAlignFrame()
   {
     try
@@ -401,7 +246,7 @@ public class GAlignFrame extends JInternalFrame
         JMenuItem item = new JMenuItem(
                 jalview.io.FormatAdapter.WRITEABLE_FORMATS[i]);
 
-        item.addActionListener(new java.awt.event.ActionListener()
+        item.addActionListener(new ActionListener()
         {
           @Override
           public void actionPerformed(ActionEvent e)
@@ -505,9 +350,8 @@ public class GAlignFrame extends JInternalFrame
     // colours.add(covariationColour);
     colours.add(tcoffeeColour);
     colours.add(RNAInteractionColour);
-    setColourSelected(jalview.bin.Cache
-            .getDefault("DEFAULT_COLOUR", "None"));
-
+    setColourSelected(jalview.bin.Cache.getDefault(
+            Preferences.DEFAULT_COLOUR, "None"));
   }
 
   public void setColourSelected(String defaultColour)
@@ -609,82 +453,88 @@ public class GAlignFrame extends JInternalFrame
 
   private void jbInit() throws Exception
   {
-    fileMenu.setText(MessageManager.getString("action.file"));
-    saveAs.setText(MessageManager.getString("action.save_as") + "...");
-    saveAs.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_S, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask()
-                    | java.awt.event.KeyEvent.SHIFT_MASK, false));
-    saveAs.addActionListener(new ActionListener()
+    JMenuItem saveAs = new JMenuItem(
+            MessageManager.getString("action.save_as") + "...");
+    ActionListener al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         saveAs_actionPerformed(e);
       }
-    });
+    };
+    KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask()
+            | KeyEvent.SHIFT_MASK, false);
+    addMenuActionAndAccelerator(keyStroke, saveAs, al);
+
     closeMenuItem.setText(MessageManager.getString("action.close"));
-    closeMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_W, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    closeMenuItem.addActionListener(new java.awt.event.ActionListener()
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         closeMenuItem_actionPerformed(false);
       }
-    });
-    editMenu.setText(MessageManager.getString("action.edit"));
-    viewMenu.setText(MessageManager.getString("action.view"));
-    annotationsMenu.setText(MessageManager.getString("action.annotations"));
+    };
+    addMenuActionAndAccelerator(keyStroke, closeMenuItem, al);
+
+    JMenu editMenu = new JMenu(MessageManager.getString("action.edit"));
+    JMenu viewMenu = new JMenu(MessageManager.getString("action.view"));
+    JMenu annotationsMenu = new JMenu(
+            MessageManager.getString("action.annotations"));
+    JMenu showMenu = new JMenu(MessageManager.getString("action.show"));
     colourMenu.setText(MessageManager.getString("action.colour"));
-    calculateMenu.setText(MessageManager.getString("action.calculate"));
+    JMenu calculateMenu = new JMenu(
+            MessageManager.getString("action.calculate"));
     webService.setText(MessageManager.getString("action.web_service"));
-    selectAllSequenceMenuItem.setText(MessageManager
+    JMenuItem selectAllSequenceMenuItem = new JMenuItem(
+            MessageManager
             .getString("action.select_all"));
-    selectAllSequenceMenuItem.setAccelerator(javax.swing.KeyStroke
-            .getKeyStroke(java.awt.event.KeyEvent.VK_A, Toolkit
-                    .getDefaultToolkit().getMenuShortcutKeyMask(), false));
-    selectAllSequenceMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                selectAllSequenceMenuItem_actionPerformed(e);
-              }
-            });
-    deselectAllSequenceMenuItem.setText(MessageManager
-            .getString("action.deselect_all"));
-    deselectAllSequenceMenuItem.setAccelerator(javax.swing.KeyStroke
-            .getKeyStroke(java.awt.event.KeyEvent.VK_ESCAPE, 0, false));
-    deselectAllSequenceMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                deselectAllSequenceMenuItem_actionPerformed(e);
-              }
-            });
-    invertSequenceMenuItem.setText(MessageManager
-            .getString("action.invert_sequence_selection"));
-    invertSequenceMenuItem.setAccelerator(javax.swing.KeyStroke
-            .getKeyStroke(java.awt.event.KeyEvent.VK_I, Toolkit
-                    .getDefaultToolkit().getMenuShortcutKeyMask(), false));
-    invertSequenceMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                invertSequenceMenuItem_actionPerformed(e);
-              }
-            });
-    grpsFromSelection.setText(MessageManager
-            .getString("action.make_groups_selection"));
-    grpsFromSelection.addActionListener(new java.awt.event.ActionListener()
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        selectAllSequenceMenuItem_actionPerformed(e);
+      }
+    };
+    addMenuActionAndAccelerator(keyStroke, selectAllSequenceMenuItem, al);
+
+    JMenuItem deselectAllSequenceMenuItem = new JMenuItem(
+            MessageManager.getString("action.deselect_all"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        deselectAllSequenceMenuItem_actionPerformed(e);
+      }
+    };
+    addMenuActionAndAccelerator(keyStroke, deselectAllSequenceMenuItem, al);
+
+    JMenuItem invertSequenceMenuItem = new JMenuItem(
+            MessageManager.getString("action.invert_sequence_selection"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        invertSequenceMenuItem_actionPerformed(e);
+      }
+    };
+    addMenuActionAndAccelerator(keyStroke, invertSequenceMenuItem, al);
+
+    JMenuItem grpsFromSelection = new JMenuItem(
+            MessageManager.getString("action.make_groups_selection"));
+    grpsFromSelection.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -692,11 +542,11 @@ public class GAlignFrame extends JInternalFrame
         makeGrpsFromSelection_actionPerformed(e);
       }
     });
-    expandAlignment.setText(MessageManager
-            .getString("action.view_flanking_regions"));
+    JMenuItem expandAlignment = new JMenuItem(
+            MessageManager.getString("action.view_flanking_regions"));
     expandAlignment.setToolTipText(MessageManager
             .getString("label.view_flanking_regions"));
-    expandAlignment.addActionListener(new java.awt.event.ActionListener()
+    expandAlignment.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -704,88 +554,86 @@ public class GAlignFrame extends JInternalFrame
         expand_newalign(e);
       }
     });
-    remove2LeftMenuItem.setText(MessageManager
-            .getString("action.remove_left"));
-    remove2LeftMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_L, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    remove2LeftMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                remove2LeftMenuItem_actionPerformed(e);
-              }
-            });
-    remove2RightMenuItem.setText(MessageManager
-            .getString("action.remove_right"));
-    remove2RightMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_R, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    remove2RightMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                remove2RightMenuItem_actionPerformed(e);
-              }
-            });
-    removeGappedColumnMenuItem.setText(MessageManager
-            .getString("action.remove_empty_columns"));
-    removeGappedColumnMenuItem.setAccelerator(javax.swing.KeyStroke
-            .getKeyStroke(java.awt.event.KeyEvent.VK_E, Toolkit
-                    .getDefaultToolkit().getMenuShortcutKeyMask(), false));
-    removeGappedColumnMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                removeGappedColumnMenuItem_actionPerformed(e);
-              }
-            });
-    removeAllGapsMenuItem.setText(MessageManager
-            .getString("action.remove_all_gaps"));
-    removeAllGapsMenuItem.setAccelerator(javax.swing.KeyStroke
-            .getKeyStroke(java.awt.event.KeyEvent.VK_E, Toolkit
-                    .getDefaultToolkit().getMenuShortcutKeyMask()
-                    | java.awt.event.KeyEvent.SHIFT_MASK, false));
-    removeAllGapsMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                removeAllGapsMenuItem_actionPerformed(e);
-              }
-            });
-    justifyLeftMenuItem.setText(MessageManager
-            .getString("action.left_justify_alignment"));
-    justifyLeftMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                justifyLeftMenuItem_actionPerformed(e);
-              }
-            });
-    justifyRightMenuItem.setText(MessageManager
-            .getString("action.right_justify_alignment"));
-    justifyRightMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                justifyRightMenuItem_actionPerformed(e);
-              }
-            });
+    JMenuItem remove2LeftMenuItem = new JMenuItem(
+            MessageManager.getString("action.remove_left"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_L, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        remove2LeftMenuItem_actionPerformed(e);
+      }
+    };
+    addMenuActionAndAccelerator(keyStroke, remove2LeftMenuItem, al);
+
+    JMenuItem remove2RightMenuItem = new JMenuItem(
+            MessageManager.getString("action.remove_right"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        remove2RightMenuItem_actionPerformed(e);
+      }
+    };
+    addMenuActionAndAccelerator(keyStroke, remove2RightMenuItem, al);
+
+    JMenuItem removeGappedColumnMenuItem = new JMenuItem(
+            MessageManager.getString("action.remove_empty_columns"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        removeGappedColumnMenuItem_actionPerformed(e);
+      }
+    };
+    addMenuActionAndAccelerator(keyStroke, removeGappedColumnMenuItem, al);
+
+    JMenuItem removeAllGapsMenuItem = new JMenuItem(
+            MessageManager.getString("action.remove_all_gaps"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask()
+            | KeyEvent.SHIFT_MASK, false);
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        removeAllGapsMenuItem_actionPerformed(e);
+      }
+    };
+    addMenuActionAndAccelerator(keyStroke, removeAllGapsMenuItem, al);
+
+    JMenuItem justifyLeftMenuItem = new JMenuItem(
+            MessageManager.getString("action.left_justify_alignment"));
+    justifyLeftMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        justifyLeftMenuItem_actionPerformed(e);
+      }
+    });
+    JMenuItem justifyRightMenuItem = new JMenuItem(
+            MessageManager.getString("action.right_justify_alignment"));
+    justifyRightMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        justifyRightMenuItem_actionPerformed(e);
+      }
+    });
     viewBoxesMenuItem.setText(MessageManager.getString("action.boxes"));
     viewBoxesMenuItem.setState(true);
-    viewBoxesMenuItem.addActionListener(new java.awt.event.ActionListener()
+    viewBoxesMenuItem.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -795,7 +643,7 @@ public class GAlignFrame extends JInternalFrame
     });
     viewTextMenuItem.setText(MessageManager.getString("action.text"));
     viewTextMenuItem.setState(true);
-    viewTextMenuItem.addActionListener(new java.awt.event.ActionListener()
+    viewTextMenuItem.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -806,28 +654,27 @@ public class GAlignFrame extends JInternalFrame
     showNonconservedMenuItem.setText(MessageManager
             .getString("label.show_non_conversed"));
     showNonconservedMenuItem.setState(false);
-    showNonconservedMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                showUnconservedMenuItem_actionPerformed(e);
-              }
-            });
-    sortPairwiseMenuItem.setText(MessageManager
-            .getString("action.by_pairwise_id"));
-    sortPairwiseMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                sortPairwiseMenuItem_actionPerformed(e);
-              }
-            });
-    sortIDMenuItem.setText(MessageManager.getString("action.by_id"));
-    sortIDMenuItem.addActionListener(new java.awt.event.ActionListener()
+    showNonconservedMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        showUnconservedMenuItem_actionPerformed(e);
+      }
+    });
+    JMenuItem sortPairwiseMenuItem = new JMenuItem(
+            MessageManager.getString("action.by_pairwise_id"));
+    sortPairwiseMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        sortPairwiseMenuItem_actionPerformed(e);
+      }
+    });
+    JMenuItem sortIDMenuItem = new JMenuItem(
+            MessageManager.getString("action.by_id"));
+    sortIDMenuItem.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -835,19 +682,19 @@ public class GAlignFrame extends JInternalFrame
         sortIDMenuItem_actionPerformed(e);
       }
     });
-    sortLengthMenuItem
-            .setText(MessageManager.getString("action.by_length"));
-    sortLengthMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                sortLengthMenuItem_actionPerformed(e);
-              }
-            });
-    sortGroupMenuItem.setText(MessageManager.getString("action.by_group"));
-    sortGroupMenuItem.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem sortLengthMenuItem = new JMenuItem(
+            MessageManager.getString("action.by_length"));
+    sortLengthMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        sortLengthMenuItem_actionPerformed(e);
+      }
+    });
+    JMenuItem sortGroupMenuItem = new JMenuItem(
+            MessageManager.getString("action.by_group"));
+    sortGroupMenuItem.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -855,34 +702,34 @@ public class GAlignFrame extends JInternalFrame
         sortGroupMenuItem_actionPerformed(e);
       }
     });
-    removeRedundancyMenuItem.setText(MessageManager
+
+    JMenuItem removeRedundancyMenuItem = new JMenuItem(MessageManager
             .getString("action.remove_redundancy").concat("..."));
-    removeRedundancyMenuItem.setAccelerator(javax.swing.KeyStroke
-            .getKeyStroke(java.awt.event.KeyEvent.VK_D, Toolkit
-                    .getDefaultToolkit().getMenuShortcutKeyMask(), false));
-    removeRedundancyMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                removeRedundancyMenuItem_actionPerformed(e);
-              }
-            });
-    pairwiseAlignmentMenuItem.setText(MessageManager
-            .getString("action.pairwise_alignment"));
-    pairwiseAlignmentMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                pairwiseAlignmentMenuItem_actionPerformed(e);
-              }
-            });
-    PCAMenuItem.setText(MessageManager
-            .getString("label.principal_component_analysis"));
-    PCAMenuItem.addActionListener(new java.awt.event.ActionListener()
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        removeRedundancyMenuItem_actionPerformed(e);
+      }
+    };
+    addMenuActionAndAccelerator(keyStroke, removeRedundancyMenuItem, al);
+
+    JMenuItem pairwiseAlignmentMenuItem = new JMenuItem(
+            MessageManager.getString("action.pairwise_alignment"));
+    pairwiseAlignmentMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        pairwiseAlignmentMenuItem_actionPerformed(e);
+      }
+    });
+    JMenuItem PCAMenuItem = new JMenuItem(
+            MessageManager.getString("label.principal_component_analysis"));
+    PCAMenuItem.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -890,29 +737,28 @@ public class GAlignFrame extends JInternalFrame
         PCAMenuItem_actionPerformed(e);
       }
     });
-    averageDistanceTreeMenuItem.setText(MessageManager
-            .getString("label.average_distance_identity"));
-    averageDistanceTreeMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                averageDistanceTreeMenuItem_actionPerformed(e);
-              }
-            });
-    neighbourTreeMenuItem.setText(MessageManager
-            .getString("label.neighbour_joining_identity"));
-    neighbourTreeMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                neighbourTreeMenuItem_actionPerformed(e);
-              }
-            });
-    this.getContentPane().setLayout(borderLayout1);
+    JMenuItem averageDistanceTreeMenuItem = new JMenuItem(
+            MessageManager.getString("label.average_distance_identity"));
+    averageDistanceTreeMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        averageDistanceTreeMenuItem_actionPerformed(e);
+      }
+    });
+    JMenuItem neighbourTreeMenuItem = new JMenuItem(
+            MessageManager.getString("label.neighbour_joining_identity"));
+    neighbourTreeMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        neighbourTreeMenuItem_actionPerformed(e);
+      }
+    });
+
+    this.getContentPane().setLayout(new BorderLayout());
     alignFrameMenuBar.setFont(new java.awt.Font("Verdana", 0, 11));
     statusBar.setBackground(Color.white);
     statusBar.setFont(new java.awt.Font("Verdana", 0, 11));
@@ -921,8 +767,7 @@ public class GAlignFrame extends JInternalFrame
     outputTextboxMenu.setText(MessageManager
             .getString("label.out_to_textbox"));
     clustalColour.setText(MessageManager.getString("label.clustalx"));
-
-    clustalColour.addActionListener(new java.awt.event.ActionListener()
+    clustalColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -931,7 +776,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
     zappoColour.setText(MessageManager.getString("label.zappo"));
-    zappoColour.addActionListener(new java.awt.event.ActionListener()
+    zappoColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -940,7 +785,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
     taylorColour.setText(MessageManager.getString("label.taylor"));
-    taylorColour.addActionListener(new java.awt.event.ActionListener()
+    taylorColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -950,17 +795,16 @@ public class GAlignFrame extends JInternalFrame
     });
     hydrophobicityColour.setText(MessageManager
             .getString("label.hydrophobicity"));
-    hydrophobicityColour
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                hydrophobicityColour_actionPerformed(e);
-              }
-            });
+    hydrophobicityColour.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hydrophobicityColour_actionPerformed(e);
+      }
+    });
     helixColour.setText(MessageManager.getString("label.helix_propensity"));
-    helixColour.addActionListener(new java.awt.event.ActionListener()
+    helixColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -970,7 +814,7 @@ public class GAlignFrame extends JInternalFrame
     });
     strandColour.setText(MessageManager
             .getString("label.strand_propensity"));
-    strandColour.addActionListener(new java.awt.event.ActionListener()
+    strandColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -979,7 +823,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
     turnColour.setText(MessageManager.getString("label.turn_propensity"));
-    turnColour.addActionListener(new java.awt.event.ActionListener()
+    turnColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -988,7 +832,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
     buriedColour.setText(MessageManager.getString("label.buried_index"));
-    buriedColour.addActionListener(new java.awt.event.ActionListener()
+    buriedColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -998,7 +842,7 @@ public class GAlignFrame extends JInternalFrame
     });
     userDefinedColour.setText(MessageManager
             .getString("action.user_defined"));
-    userDefinedColour.addActionListener(new java.awt.event.ActionListener()
+    userDefinedColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1008,7 +852,7 @@ public class GAlignFrame extends JInternalFrame
     });
     PIDColour
             .setText(MessageManager.getString("label.percentage_identity"));
-    PIDColour.addActionListener(new java.awt.event.ActionListener()
+    PIDColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1018,7 +862,7 @@ public class GAlignFrame extends JInternalFrame
     });
     BLOSUM62Colour
             .setText(MessageManager.getString("label.blosum62_score"));
-    BLOSUM62Colour.addActionListener(new java.awt.event.ActionListener()
+    BLOSUM62Colour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1027,7 +871,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
     nucleotideColour.setText(MessageManager.getString("label.nucleotide"));
-    nucleotideColour.addActionListener(new java.awt.event.ActionListener()
+    nucleotideColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1038,55 +882,51 @@ public class GAlignFrame extends JInternalFrame
 
     purinePyrimidineColour.setText(MessageManager
             .getString("label.purine_pyrimidine"));
-    purinePyrimidineColour
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                purinePyrimidineColour_actionPerformed(e);
-              }
-            });
+    purinePyrimidineColour.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        purinePyrimidineColour_actionPerformed(e);
+      }
+    });
 
     RNAInteractionColour.setText("RNA Interaction type");
-    RNAInteractionColour
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                RNAInteractionColour_actionPerformed(e);
-              }
-            });
+    RNAInteractionColour.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        RNAInteractionColour_actionPerformed(e);
+      }
+    });
     /*
      * covariationColour.setText("Covariation");
-     * covariationColour.addActionListener(new java.awt.event.ActionListener() {
-     * public void actionPerformed(ActionEvent e) {
-     * covariationColour_actionPerformed(e); } });
+     * covariationColour.addActionListener(new ActionListener() { public void
+     * actionPerformed(ActionEvent e) { covariationColour_actionPerformed(e); }
+     * });
      */
 
-    avDistanceTreeBlosumMenuItem.setText(MessageManager
-            .getString("label.average_distance_bloslum62"));
-    avDistanceTreeBlosumMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                avTreeBlosumMenuItem_actionPerformed(e);
-              }
-            });
-    njTreeBlosumMenuItem.setText(MessageManager
-            .getString("label.neighbour_blosum62"));
-    njTreeBlosumMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                njTreeBlosumMenuItem_actionPerformed(e);
-              }
-            });
+    JMenuItem avDistanceTreeBlosumMenuItem = new JMenuItem(
+            MessageManager.getString("label.average_distance_bloslum62"));
+    avDistanceTreeBlosumMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        avTreeBlosumMenuItem_actionPerformed(e);
+      }
+    });
+    JMenuItem njTreeBlosumMenuItem = new JMenuItem(
+            MessageManager.getString("label.neighbour_blosum62"));
+    njTreeBlosumMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        njTreeBlosumMenuItem_actionPerformed(e);
+      }
+    });
     annotationPanelMenuItem.setActionCommand("");
     annotationPanelMenuItem.setText(MessageManager
             .getString("label.show_annotations"));
@@ -1149,8 +989,11 @@ public class GAlignFrame extends JInternalFrame
     SequenceAnnotationOrder sortAnnotationsBy = SequenceAnnotationOrder
             .valueOf(Cache.getDefault(Preferences.SORT_ANNOTATIONS,
                     SequenceAnnotationOrder.NONE.name()));
-    sortAnnBySequence.setText(MessageManager
-            .getString("label.sort_annotations_by_sequence"));
+    final JCheckBoxMenuItem sortAnnBySequence = new JCheckBoxMenuItem(
+            MessageManager.getString("label.sort_annotations_by_sequence"));
+    final JCheckBoxMenuItem sortAnnByLabel = new JCheckBoxMenuItem(
+            MessageManager.getString("label.sort_annotations_by_label"));
+
     sortAnnBySequence
             .setSelected(sortAnnotationsBy == SequenceAnnotationOrder.SEQUENCE_AND_LABEL);
     sortAnnBySequence.addActionListener(new ActionListener()
@@ -1165,8 +1008,6 @@ public class GAlignFrame extends JInternalFrame
         sortAnnotations_actionPerformed();
       }
     });
-    sortAnnByLabel.setText(MessageManager
-            .getString("label.sort_annotations_by_label"));
     sortAnnByLabel
             .setSelected(sortAnnotationsBy == SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
     sortAnnByLabel.addActionListener(new ActionListener()
@@ -1183,17 +1024,18 @@ public class GAlignFrame extends JInternalFrame
     });
     colourTextMenuItem.setText(MessageManager
             .getString("label.colour_text"));
-    colourTextMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                colourTextMenuItem_actionPerformed(e);
-              }
-            });
-    htmlMenuItem.setText(MessageManager.getString("label.html"));
-    htmlMenuItem.addActionListener(new java.awt.event.ActionListener()
+    colourTextMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        colourTextMenuItem_actionPerformed(e);
+      }
+    });
+
+    JMenuItem htmlMenuItem = new JMenuItem(
+            MessageManager.getString("label.html"));
+    htmlMenuItem.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1202,9 +1044,8 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
-    // TODO uncomment when supported by MassageManager
-    // createBioJS.setText(MessageManager.getString("label.biojs_html_export"));
-    createBioJS.setText("BioJS");
+    JMenuItem createBioJS = new JMenuItem(
+            MessageManager.getString("label.biojs_html_export"));
     createBioJS.addActionListener(new java.awt.event.ActionListener()
     {
       @Override
@@ -1214,9 +1055,9 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
-    overviewMenuItem.setText(MessageManager
-            .getString("label.overview_window"));
-    overviewMenuItem.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem overviewMenuItem = new JMenuItem(
+            MessageManager.getString("label.overview_window"));
+    overviewMenuItem.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1224,45 +1065,47 @@ public class GAlignFrame extends JInternalFrame
         overviewMenuItem_actionPerformed(e);
       }
     });
+
     undoMenuItem.setEnabled(false);
     undoMenuItem.setText(MessageManager.getString("action.undo"));
-    undoMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_Z, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    undoMenuItem.addActionListener(new java.awt.event.ActionListener()
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         undoMenuItem_actionPerformed(e);
       }
-    });
+    };
+    addMenuActionAndAccelerator(keyStroke, undoMenuItem, al);
+
     redoMenuItem.setEnabled(false);
     redoMenuItem.setText(MessageManager.getString("action.redo"));
-    redoMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_Y, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    redoMenuItem.addActionListener(new java.awt.event.ActionListener()
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         redoMenuItem_actionPerformed(e);
       }
-    });
+    };
+    addMenuActionAndAccelerator(keyStroke, redoMenuItem, al);
+
     conservationMenuItem.setText(MessageManager
             .getString("action.by_conservation"));
-    conservationMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                conservationMenuItem_actionPerformed(e);
-              }
-            });
+    conservationMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        conservationMenuItem_actionPerformed(e);
+      }
+    });
     noColourmenuItem.setText(MessageManager.getString("label.none"));
-    noColourmenuItem.addActionListener(new java.awt.event.ActionListener()
+    noColourmenuItem.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1271,7 +1114,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
     wrapMenuItem.setText(MessageManager.getString("label.wrap"));
-    wrapMenuItem.addActionListener(new java.awt.event.ActionListener()
+    wrapMenuItem.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1279,47 +1122,52 @@ public class GAlignFrame extends JInternalFrame
         wrapMenuItem_actionPerformed(e);
       }
     });
-    printMenuItem.setText(MessageManager.getString("action.print") + "...");
-    printMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_P, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    printMenuItem.addActionListener(new java.awt.event.ActionListener()
+
+    JMenuItem printMenuItem = new JMenuItem(
+            MessageManager.getString("action.print") + "...");
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_P, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         printMenuItem_actionPerformed(e);
       }
-    });
+    };
+    addMenuActionAndAccelerator(keyStroke, printMenuItem, al);
+
     renderGapsMenuItem
             .setText(MessageManager.getString("action.show_gaps"));
     renderGapsMenuItem.setState(true);
-    renderGapsMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                renderGapsMenuItem_actionPerformed(e);
-              }
-            });
-    findMenuItem.setText(MessageManager.getString("action.find"));
-    findMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_F, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
+    renderGapsMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        renderGapsMenuItem_actionPerformed(e);
+      }
+    });
+
+    JMenuItem findMenuItem = new JMenuItem(
+            MessageManager.getString("action.find"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
     findMenuItem.setToolTipText(JvSwingUtils.wrapTooltip(true,
             MessageManager.getString("label.find_tip")));
-    findMenuItem.addActionListener(new java.awt.event.ActionListener()
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         findMenuItem_actionPerformed(e);
       }
-    });
+    };
+    addMenuActionAndAccelerator(keyStroke, findMenuItem, al);
+
     abovePIDThreshold.setText(MessageManager
             .getString("label.above_identity_threshold"));
-    abovePIDThreshold.addActionListener(new java.awt.event.ActionListener()
+    abovePIDThreshold.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1442,12 +1290,14 @@ public class GAlignFrame extends JInternalFrame
     });
 
     ButtonGroup buttonGroup = new ButtonGroup();
+    final JRadioButtonMenuItem showAutoFirst = new JRadioButtonMenuItem(
+            MessageManager.getString("label.show_first"));
+    final JRadioButtonMenuItem showAutoLast = new JRadioButtonMenuItem(
+            MessageManager.getString("label.show_last"));
     buttonGroup.add(showAutoFirst);
     buttonGroup.add(showAutoLast);
-    showAutoFirst.setText(MessageManager.getString("label.show_first"));
     showAutoFirst.setSelected(Cache.getDefault(
-            Preferences.SHOW_AUTOCALC_ABOVE,
-            false));
+            Preferences.SHOW_AUTOCALC_ABOVE, false));
     showAutoFirst.addActionListener(new ActionListener()
     {
       @Override
@@ -1457,7 +1307,6 @@ public class GAlignFrame extends JInternalFrame
         sortAnnotations_actionPerformed();
       }
     });
-    showAutoLast.setText(MessageManager.getString("label.show_last"));
     showAutoLast.setSelected(!showAutoFirst.isSelected());
     showAutoLast.addActionListener(new ActionListener()
     {
@@ -1470,7 +1319,7 @@ public class GAlignFrame extends JInternalFrame
     });
 
     nucleotideColour.setText(MessageManager.getString("label.nucleotide"));
-    nucleotideColour.addActionListener(new java.awt.event.ActionListener()
+    nucleotideColour.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1491,71 +1340,79 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
-    deleteGroups
-            .setText(MessageManager.getString("action.undefine_groups"));
-    deleteGroups.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_U, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    deleteGroups.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem deleteGroups = new JMenuItem(
+            MessageManager.getString("action.undefine_groups"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_U, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         deleteGroups_actionPerformed(e);
       }
-    });
-    createGroup.setText(MessageManager.getString("action.create_groups"));
-    createGroup.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_G, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    createGroup.addActionListener(new java.awt.event.ActionListener()
+    };
+    addMenuActionAndAccelerator(keyStroke, deleteGroups, al);
+
+    JMenuItem createGroup = new JMenuItem(
+            MessageManager.getString("action.create_groups"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         createGroup_actionPerformed(e);
       }
-    });
-    unGroup.setText(MessageManager.getString("action.remove_group"));
-    unGroup.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_G, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask()
-                    | java.awt.event.KeyEvent.SHIFT_MASK, false));
-    unGroup.addActionListener(new java.awt.event.ActionListener()
+    };
+    addMenuActionAndAccelerator(keyStroke, createGroup, al);
+
+    JMenuItem unGroup = new JMenuItem(
+            MessageManager.getString("action.remove_group"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask()
+            | KeyEvent.SHIFT_MASK, false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         unGroup_actionPerformed(e);
       }
-    });
+    };
+    addMenuActionAndAccelerator(keyStroke, unGroup, al);
+
     copy.setText(MessageManager.getString("action.copy"));
-    copy.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_C, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
 
-    copy.addActionListener(new java.awt.event.ActionListener()
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         copy_actionPerformed(e);
       }
-    });
+    };
+    addMenuActionAndAccelerator(keyStroke, copy, al);
+
     cut.setText(MessageManager.getString("action.cut"));
-    cut.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_X, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    cut.addActionListener(new java.awt.event.ActionListener()
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         cut_actionPerformed(e);
       }
-    });
-    delete.setText(MessageManager.getString("action.delete"));
-    delete.addActionListener(new java.awt.event.ActionListener()
+    };
+    addMenuActionAndAccelerator(keyStroke, cut, al);
+
+    JMenuItem delete = new JMenuItem(
+            MessageManager.getString("action.delete"));
+    delete.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1563,35 +1420,40 @@ public class GAlignFrame extends JInternalFrame
         delete_actionPerformed(e);
       }
     });
+
     pasteMenu.setText(MessageManager.getString("action.paste"));
-    pasteNew.setText(MessageManager.getString("label.to_new_alignment"));
-    pasteNew.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_V, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask()
-                    | java.awt.event.KeyEvent.SHIFT_MASK, false));
-    pasteNew.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem pasteNew = new JMenuItem(
+            MessageManager.getString("label.to_new_alignment"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask()
+            | KeyEvent.SHIFT_MASK, false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         pasteNew_actionPerformed(e);
       }
-    });
-    pasteThis.setText(MessageManager.getString("label.to_this_alignment"));
-    pasteThis.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_V, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    pasteThis.addActionListener(new java.awt.event.ActionListener()
+    };
+    addMenuActionAndAccelerator(keyStroke, pasteNew, al);
+
+    JMenuItem pasteThis = new JMenuItem(
+            MessageManager.getString("label.to_this_alignment"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         pasteThis_actionPerformed(e);
       }
-    });
+    };
+    addMenuActionAndAccelerator(keyStroke, pasteThis, al);
+
     applyToAllGroups.setText(MessageManager
             .getString("label.apply_colour_to_all_groups"));
-    applyToAllGroups.addActionListener(new java.awt.event.ActionListener()
+    applyToAllGroups.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1599,7 +1461,8 @@ public class GAlignFrame extends JInternalFrame
         applyToAllGroups_actionPerformed(e);
       }
     });
-    createPNG.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem createPNG = new JMenuItem("PNG");
+    createPNG.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1609,10 +1472,9 @@ public class GAlignFrame extends JInternalFrame
     });
     createPNG.setActionCommand(MessageManager
             .getString("label.save_png_image"));
-    createPNG.setText("PNG");
 
-    font.setText(MessageManager.getString("action.font"));
-    font.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem font = new JMenuItem(MessageManager.getString("action.font"));
+    font.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1623,7 +1485,7 @@ public class GAlignFrame extends JInternalFrame
     seqLimits.setText(MessageManager
             .getString("label.show_sequence_limits"));
     seqLimits.setState(jalview.bin.Cache.getDefault("SHOW_JVSUFFIX", true));
-    seqLimits.addActionListener(new java.awt.event.ActionListener()
+    seqLimits.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1631,8 +1493,8 @@ public class GAlignFrame extends JInternalFrame
         seqLimit_actionPerformed(e);
       }
     });
-    epsFile.setText("EPS");
-    epsFile.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem epsFile = new JMenuItem("EPS");
+    epsFile.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1641,8 +1503,8 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
-    createSVG.setText("SVG");
-    createSVG.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem createSVG = new JMenuItem("SVG");
+    createSVG.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1651,22 +1513,22 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
-    LoadtreeMenuItem.setActionCommand(MessageManager
+    JMenuItem loadTreeMenuItem = new JMenuItem(
+            MessageManager.getString("label.load_associated_tree"));
+    loadTreeMenuItem.setActionCommand(MessageManager
             .getString("label.load_tree_for_sequence_set"));
-    LoadtreeMenuItem.setText(MessageManager
-            .getString("label.load_associated_tree"));
-    LoadtreeMenuItem.addActionListener(new java.awt.event.ActionListener()
+    loadTreeMenuItem.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        LoadtreeMenuItem_actionPerformed(e);
+        loadTreeMenuItem_actionPerformed(e);
       }
     });
 
     scaleAbove.setVisible(false);
     scaleAbove.setText(MessageManager.getString("action.scale_above"));
-    scaleAbove.addActionListener(new java.awt.event.ActionListener()
+    scaleAbove.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1677,7 +1539,7 @@ public class GAlignFrame extends JInternalFrame
     scaleLeft.setVisible(false);
     scaleLeft.setSelected(true);
     scaleLeft.setText(MessageManager.getString("action.scale_left"));
-    scaleLeft.addActionListener(new java.awt.event.ActionListener()
+    scaleLeft.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1688,7 +1550,7 @@ public class GAlignFrame extends JInternalFrame
     scaleRight.setVisible(false);
     scaleRight.setSelected(true);
     scaleRight.setText(MessageManager.getString("action.scale_right"));
-    scaleRight.addActionListener(new java.awt.event.ActionListener()
+    scaleRight.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1700,15 +1562,14 @@ public class GAlignFrame extends JInternalFrame
     centreColumnLabelsMenuItem.setState(false);
     centreColumnLabelsMenuItem.setText(MessageManager
             .getString("label.centre_column_labels"));
-    centreColumnLabelsMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                centreColumnLabels_actionPerformed(e);
-              }
-            });
+    centreColumnLabelsMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        centreColumnLabels_actionPerformed(e);
+      }
+    });
     followHighlightMenuItem.setVisible(true);
     followHighlightMenuItem.setState(true);
     followHighlightMenuItem.setText(MessageManager
@@ -1724,9 +1585,9 @@ public class GAlignFrame extends JInternalFrame
 
     });
 
-    modifyPID.setText(MessageManager
-            .getString("label.modify_identity_thereshold"));
-    modifyPID.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem modifyPID = new JMenuItem(
+            MessageManager.getString("label.modify_identity_thereshold"));
+    modifyPID.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1736,15 +1597,14 @@ public class GAlignFrame extends JInternalFrame
     });
     modifyConservation.setText(MessageManager
             .getString("label.modify_conservation_thereshold"));
-    modifyConservation
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                modifyConservation_actionPerformed(e);
-              }
-            });
+    modifyConservation.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        modifyConservation_actionPerformed(e);
+      }
+    });
     sortByTreeMenu
             .setText(MessageManager.getString("action.by_tree_order"));
     sort.setText(MessageManager.getString("action.sort"));
@@ -1793,7 +1653,6 @@ public class GAlignFrame extends JInternalFrame
     calculateTree
             .setText(MessageManager.getString("action.calculate_tree"));
 
-    jMenu2.setText(MessageManager.getString("label.export_image"));
     padGapsMenuitem.setText(MessageManager.getString("label.pad_gaps"));
     padGapsMenuitem.setState(jalview.bin.Cache
             .getDefault("PAD_GAPS", false));
@@ -1805,8 +1664,9 @@ public class GAlignFrame extends JInternalFrame
         padGapsMenuitem_actionPerformed(e);
       }
     });
+    JMenuItem vamsasStore = new JMenuItem(
+            MessageManager.getString("label.vamsas_store"));
     vamsasStore.setVisible(false);
-    vamsasStore.setText(MessageManager.getString("label.vamsas_store"));
     vamsasStore.addActionListener(new ActionListener()
     {
       @Override
@@ -1825,8 +1685,9 @@ public class GAlignFrame extends JInternalFrame
         showTranslation_actionPerformed(e);
       }
     });
-    extractScores.setText(MessageManager.getString("label.extract_scores")
-            + "...");
+
+    JMenuItem extractScores = new JMenuItem(
+            MessageManager.getString("label.extract_scores") + "...");
     extractScores.addActionListener(new ActionListener()
     {
       @Override
@@ -1835,17 +1696,14 @@ public class GAlignFrame extends JInternalFrame
         extractScores_actionPerformed(e);
       }
     });
-    extractScores.setVisible(true); // JBPNote: TODO: make gui for regex based
-    // score extraction
+    extractScores.setVisible(true);
+    // JBPNote: TODO: make gui for regex based score extraction
+
+    // for show products actions see AlignFrame.canShowProducts
     showProducts.setText(MessageManager.getString("label.get_cross_refs"));
-    /*
-     * showProducts.addActionListener(new ActionListener() {
-     * 
-     * public void actionPerformed(ActionEvent e) {
-     * showProducts_actionPerformed(e); } });
-     */
-    openFeatureSettings.setText(MessageManager
-            .getString("label.feature_settings"));
+
+    JMenuItem openFeatureSettings = new JMenuItem(
+            MessageManager.getString("label.feature_settings"));
     openFeatureSettings.addActionListener(new ActionListener()
     {
       @Override
@@ -1854,8 +1712,8 @@ public class GAlignFrame extends JInternalFrame
         featureSettings_actionPerformed(e);
       }
     });
-    fetchSequence
-            .setText(MessageManager.getString("label.fetch_sequences"));
+    JMenuItem fetchSequence = new JMenuItem(
+            MessageManager.getString("label.fetch_sequences"));
     fetchSequence.addActionListener(new ActionListener()
     {
       @Override
@@ -1865,8 +1723,8 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
-    annotationColour.setText(MessageManager
-            .getString("action.by_annotation"));
+    JMenuItem annotationColour = new JMenuItem(
+            MessageManager.getString("action.by_annotation"));
     annotationColour.addActionListener(new ActionListener()
     {
       @Override
@@ -1876,8 +1734,8 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
-    annotationColumn.setText(MessageManager
-            .getString("action.select_by_annotation"));
+    JMenuItem annotationColumn = new JMenuItem(
+            MessageManager.getString("action.select_by_annotation"));
     annotationColumn.addActionListener(new ActionListener()
     {
       @Override
@@ -1898,8 +1756,8 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
-    associatedData.setText(MessageManager
-            .getString("label.load_features_annotations"));
+    JMenuItem associatedData = new JMenuItem(
+            MessageManager.getString("label.load_features_annotations"));
     associatedData.addActionListener(new ActionListener()
     {
       @Override
@@ -1953,9 +1811,10 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
-    addSequenceMenu
-            .setText(MessageManager.getString("label.add_sequences"));
-    addFromFile.setText(MessageManager.getString("label.from_file"));
+    JMenu addSequenceMenu = new JMenu(
+            MessageManager.getString("label.add_sequences"));
+    JMenuItem addFromFile = new JMenuItem(
+            MessageManager.getString("label.from_file"));
     addFromFile.addActionListener(new ActionListener()
     {
       @Override
@@ -1964,7 +1823,8 @@ public class GAlignFrame extends JInternalFrame
         addFromFile_actionPerformed(e);
       }
     });
-    addFromText.setText(MessageManager.getString("label.from_textbox"));
+    JMenuItem addFromText = new JMenuItem(
+            MessageManager.getString("label.from_textbox"));
     addFromText.addActionListener(new ActionListener()
     {
       @Override
@@ -1973,7 +1833,8 @@ public class GAlignFrame extends JInternalFrame
         addFromText_actionPerformed(e);
       }
     });
-    addFromURL.setText(MessageManager.getString("label.from_url"));
+    JMenuItem addFromURL = new JMenuItem(
+            MessageManager.getString("label.from_url"));
     addFromURL.addActionListener(new ActionListener()
     {
       @Override
@@ -1982,8 +1843,8 @@ public class GAlignFrame extends JInternalFrame
         addFromURL_actionPerformed(e);
       }
     });
-    exportFeatures.setText(MessageManager
-            .getString("label.export_features"));
+    JMenuItem exportFeatures = new JMenuItem(
+            MessageManager.getString("label.export_features"));
     exportFeatures.addActionListener(new ActionListener()
     {
       @Override
@@ -1992,8 +1853,8 @@ public class GAlignFrame extends JInternalFrame
         exportFeatures_actionPerformed(e);
       }
     });
-    exportAnnotations.setText(MessageManager
-            .getString("label.export_annotations"));
+    JMenuItem exportAnnotations = new JMenuItem(
+            MessageManager.getString("label.export_annotations"));
     exportAnnotations.addActionListener(new ActionListener()
     {
       @Override
@@ -2002,9 +1863,9 @@ public class GAlignFrame extends JInternalFrame
         exportAnnotations_actionPerformed(e);
       }
     });
-    statusPanel.setLayout(gridLayout1);
-    jMenu3.setText(MessageManager.getString("action.show"));
-    showAllSeqs.setText(MessageManager.getString("label.all_sequences"));
+    statusPanel.setLayout(new GridLayout());
+    JMenuItem showAllSeqs = new JMenuItem(
+            MessageManager.getString("label.all_sequences"));
     showAllSeqs.setToolTipText(MessageManager
             .getString("label.toggle_sequence_visibility"));
     showAllSeqs.addActionListener(new ActionListener()
@@ -2015,7 +1876,8 @@ public class GAlignFrame extends JInternalFrame
         showAllSeqs_actionPerformed(e);
       }
     });
-    showAllColumns.setText(MessageManager.getString("label.all_columns"));
+    JMenuItem showAllColumns = new JMenuItem(
+            MessageManager.getString("label.all_columns"));
     showAllColumns.setToolTipText(MessageManager
             .getString("label.toggle_columns_visibility"));
     showAllColumns.addActionListener(new ActionListener()
@@ -2026,9 +1888,9 @@ public class GAlignFrame extends JInternalFrame
         showAllColumns_actionPerformed(e);
       }
     });
-    hideMenu.setText(MessageManager.getString("action.hide"));
-    hideSelSequences.setText(MessageManager
-            .getString("label.selected_sequences"));
+    JMenu hideMenu = new JMenu(MessageManager.getString("action.hide"));
+    JMenuItem hideSelSequences = new JMenuItem(
+            MessageManager.getString("label.selected_sequences"));
     hideSelSequences.setToolTipText(MessageManager
             .getString("label.toggle_sequence_visibility"));
     hideSelSequences.addActionListener(new ActionListener()
@@ -2039,8 +1901,8 @@ public class GAlignFrame extends JInternalFrame
         hideSelSequences_actionPerformed(e);
       }
     });
-    hideSelColumns.setText(MessageManager
-            .getString("label.selected_columns"));
+    JMenuItem hideSelColumns = new JMenuItem(
+            MessageManager.getString("label.selected_columns"));
     hideSelColumns.setToolTipText(MessageManager
             .getString("label.toggle_columns_visibility"));
     hideSelColumns.addActionListener(new ActionListener()
@@ -2051,8 +1913,8 @@ public class GAlignFrame extends JInternalFrame
         hideSelColumns_actionPerformed(e);
       }
     });
-    hideAllSelection.setText(MessageManager
-            .getString("label.selected_region"));
+    JMenuItem hideAllSelection = new JMenuItem(
+            MessageManager.getString("label.selected_region"));
     hideAllSelection.addActionListener(new ActionListener()
     {
       @Override
@@ -2062,8 +1924,8 @@ public class GAlignFrame extends JInternalFrame
       }
     });
     // TODO: should be hidden if no selection exists.
-    hideAllButSelection.setText(MessageManager
-            .getString("label.all_but_selected_region"));
+    JMenuItem hideAllButSelection = new JMenuItem(
+            MessageManager.getString("label.all_but_selected_region"));
     hideAllButSelection.addActionListener(new ActionListener()
     {
       @Override
@@ -2072,8 +1934,8 @@ public class GAlignFrame extends JInternalFrame
         hideAllButSelection_actionPerformed(e);
       }
     });
-    showAllhidden.setText(MessageManager
-            .getString("label.all_sequences_columns"));
+    JMenuItem showAllhidden = new JMenuItem(
+            MessageManager.getString("label.all_sequences_columns"));
     showAllhidden.setToolTipText(MessageManager
             .getString("label.toggles_visibility_hidden_selected_regions"));
     showAllhidden.addActionListener(new ActionListener()
@@ -2094,20 +1956,32 @@ public class GAlignFrame extends JInternalFrame
         hiddenMarkers_actionPerformed(e);
       }
     });
-    invertColSel.setText(MessageManager
-            .getString("action.invert_column_selection"));
-    invertColSel.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_I, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask()
-                    | java.awt.event.KeyEvent.ALT_MASK, false));
-    invertColSel.addActionListener(new ActionListener()
+
+    JMenuItem invertColSel = new JMenuItem(
+            MessageManager.getString("action.invert_column_selection"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I,
+            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
+            | KeyEvent.ALT_MASK, false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         invertColSel_actionPerformed(e);
       }
+    };
+    addMenuActionAndAccelerator(keyStroke, invertColSel, al);
+
+    showComplementMenuItem.setVisible(false);
+    showComplementMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        showComplement_actionPerformed(showComplementMenuItem.getState());
+      }
     });
+
     tabbedPane.addChangeListener(new javax.swing.event.ChangeListener()
     {
       @Override
@@ -2134,18 +2008,20 @@ public class GAlignFrame extends JInternalFrame
         tabbedPane_focusGained(e);
       }
     });
-    save.setText(MessageManager.getString("action.save"));
-    save.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_S, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    save.addActionListener(new ActionListener()
+
+    JMenuItem save = new JMenuItem(MessageManager.getString("action.save"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         save_actionPerformed(e);
       }
-    });
+    };
+    addMenuActionAndAccelerator(keyStroke, save, al);
+
     reload.setEnabled(false);
     reload.setText(MessageManager.getString("action.reload"));
     reload.addActionListener(new ActionListener()
@@ -2156,23 +2032,26 @@ public class GAlignFrame extends JInternalFrame
         reload_actionPerformed(e);
       }
     });
-    newView.setText(MessageManager.getString("action.new_view"));
-    newView.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_T, Toolkit.getDefaultToolkit()
-                    .getMenuShortcutKeyMask(), false));
-    newView.addActionListener(new ActionListener()
+
+    JMenuItem newView = new JMenuItem(
+            MessageManager.getString("action.new_view"));
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit
+            .getDefaultToolkit().getMenuShortcutKeyMask(), false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         newView_actionPerformed(e);
       }
-    });
+    };
+    addMenuActionAndAccelerator(keyStroke, newView, al);
+
     tabbedPane.setToolTipText("<html><i>"
             + MessageManager.getString("label.rename_tab_eXpand_reGroup")
             + "</i></html>");
-    textColour.setText(MessageManager.getString("label.colour_text")
-            + "...");
+    JMenuItem textColour = new JMenuItem(
+            MessageManager.getString("label.colour_text") + "...");
     textColour.addActionListener(new ActionListener()
     {
       @Override
@@ -2182,7 +2061,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
     formatMenu.setText(MessageManager.getString("action.format"));
-    selectMenu.setText(MessageManager.getString("action.select"));
+    JMenu selectMenu = new JMenu(MessageManager.getString("action.select"));
     idRightAlign.setText(MessageManager
             .getString("label.right_align_sequence_id"));
     idRightAlign.addActionListener(new ActionListener()
@@ -2193,32 +2072,35 @@ public class GAlignFrame extends JInternalFrame
         idRightAlign_actionPerformed(e);
       }
     });
+
     gatherViews.setEnabled(false);
     gatherViews.setText(MessageManager.getString("action.gather_views"));
-    gatherViews.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_G, 0, false));
-    gatherViews.addActionListener(new ActionListener()
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G, 0, false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         gatherViews_actionPerformed(e);
       }
-    });
+    };
+    addMenuActionAndAccelerator(keyStroke, gatherViews, al);
+
     expandViews.setEnabled(false);
     expandViews.setText(MessageManager.getString("action.expand_views"));
-    expandViews.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
-            java.awt.event.KeyEvent.VK_X, 0, false));
-    expandViews.addActionListener(new ActionListener()
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false);
+    al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         expandViews_actionPerformed(e);
       }
-    });
-    pageSetup
-            .setText(MessageManager.getString("action.page_setup") + "...");
+    };
+    addMenuActionAndAccelerator(keyStroke, expandViews, al);
+
+    JMenuItem pageSetup = new JMenuItem(
+            MessageManager.getString("action.page_setup") + "...");
     pageSetup.addActionListener(new ActionListener()
     {
       @Override
@@ -2227,8 +2109,8 @@ public class GAlignFrame extends JInternalFrame
         pageSetup_actionPerformed(e);
       }
     });
-    alignmentProperties.setText(MessageManager
-            .getString("label.alignment_props") + "...");
+    JMenuItem alignmentProperties = new JMenuItem(
+            MessageManager.getString("label.alignment_props") + "...");
     alignmentProperties.addActionListener(new ActionListener()
     {
       @Override
@@ -2237,10 +2119,14 @@ public class GAlignFrame extends JInternalFrame
         alignmentProperties();
       }
     });
-    tooltipSettingsMenu.setText(MessageManager
-            .getString("label.sequence_id_tooltip"));
-    autoAnnMenu.setText(MessageManager
-            .getString("label.autocalculated_annotation"));
+    JMenu tooltipSettingsMenu = new JMenu(
+            MessageManager.getString("label.sequence_id_tooltip"));
+    JMenu autoAnnMenu = new JMenu(
+            MessageManager.getString("label.autocalculated_annotation"));
+
+    JMenu exportImageMenu = new JMenu(
+            MessageManager.getString("label.export_image"));
+    JMenu fileMenu = new JMenu(MessageManager.getString("action.file"));
     alignFrameMenuBar.add(fileMenu);
     alignFrameMenuBar.add(editMenu);
     alignFrameMenuBar.add(selectMenu);
@@ -2261,10 +2147,10 @@ public class GAlignFrame extends JInternalFrame
     fileMenu.add(pageSetup);
     fileMenu.add(printMenuItem);
     fileMenu.addSeparator();
-    fileMenu.add(jMenu2);
+    fileMenu.add(exportImageMenu);
     fileMenu.add(exportFeatures);
     fileMenu.add(exportAnnotations);
-    fileMenu.add(LoadtreeMenuItem);
+    fileMenu.add(loadTreeMenuItem);
     fileMenu.add(associatedData);
     fileMenu.addSeparator();
     fileMenu.add(closeMenuItem);
@@ -2287,14 +2173,28 @@ public class GAlignFrame extends JInternalFrame
     // editMenu.add(justifyRightMenuItem);
     // editMenu.addSeparator();
     editMenu.add(padGapsMenuitem);
+
     viewMenu.add(newView);
     viewMenu.add(expandViews);
     viewMenu.add(gatherViews);
     viewMenu.addSeparator();
-    viewMenu.add(jMenu3);
+    viewMenu.add(showMenu);
     viewMenu.add(hideMenu);
+    viewMenu.add(showComplementMenuItem);
     viewMenu.addSeparator();
     viewMenu.add(followHighlightMenuItem);
+    viewMenu.addSeparator();
+    viewMenu.add(showSeqFeatures);
+    // viewMenu.add(showSeqFeaturesHeight);
+    viewMenu.add(openFeatureSettings);
+    tooltipSettingsMenu.add(showDbRefsMenuitem);
+    tooltipSettingsMenu.add(showNpFeatsMenuitem);
+    viewMenu.add(tooltipSettingsMenu);
+    viewMenu.addSeparator();
+    viewMenu.add(alignmentProperties);
+    viewMenu.addSeparator();
+    viewMenu.add(overviewMenuItem);
+
     annotationsMenu.add(annotationPanelMenuItem);
     annotationsMenu.addSeparator();
     annotationsMenu.add(showAllAlAnnotations);
@@ -2316,18 +2216,7 @@ public class GAlignFrame extends JInternalFrame
     autoAnnMenu.add(showGroupConservation);
     autoAnnMenu.add(showGroupConsensus);
     annotationsMenu.add(autoAnnMenu);
-    viewMenu.addSeparator();
-    viewMenu.add(showSeqFeatures);
-    // viewMenu.add(showSeqFeaturesHeight);
 
-    viewMenu.add(openFeatureSettings);
-    tooltipSettingsMenu.add(showDbRefsMenuitem);
-    tooltipSettingsMenu.add(showNpFeatsMenuitem);
-    viewMenu.add(tooltipSettingsMenu);
-    viewMenu.addSeparator();
-    viewMenu.add(alignmentProperties);
-    viewMenu.addSeparator();
-    viewMenu.add(overviewMenuItem);
     colourMenu.add(applyToAllGroups);
     colourMenu.add(textColour);
     colourMenu.addSeparator();
@@ -2377,20 +2266,20 @@ public class GAlignFrame extends JInternalFrame
     sort.add(sortGroupMenuItem);
     sort.add(sortPairwiseMenuItem);
     sort.add(sortByTreeMenu);
-    jMenu2.add(htmlMenuItem);
-    jMenu2.add(epsFile);
-    jMenu2.add(createPNG);
-    jMenu2.add(createBioJS);
-    jMenu2.add(createSVG);
+    exportImageMenu.add(htmlMenuItem);
+    exportImageMenu.add(epsFile);
+    exportImageMenu.add(createPNG);
+    exportImageMenu.add(createBioJS);
+    exportImageMenu.add(createSVG);
     addSequenceMenu.add(addFromFile);
     addSequenceMenu.add(addFromText);
     addSequenceMenu.add(addFromURL);
     this.getContentPane().add(statusPanel, java.awt.BorderLayout.SOUTH);
     statusPanel.add(statusBar, null);
     this.getContentPane().add(tabbedPane, java.awt.BorderLayout.CENTER);
-    jMenu3.add(showAllColumns);
-    jMenu3.add(showAllSeqs);
-    jMenu3.add(showAllhidden);
+    showMenu.add(showAllColumns);
+    showMenu.add(showAllSeqs);
+    showMenu.add(showAllhidden);
     hideMenu.add(hideSelColumns);
     hideMenu.add(hideSelSequences);
     hideMenu.add(hideAllSelection);
@@ -2430,6 +2319,22 @@ public class GAlignFrame extends JInternalFrame
   }
 
   /**
+   * Adds the given action listener and key accelerator to the given menu item.
+   * Also saves in a lookup table to support lookup of action by key stroke.
+   * 
+   * @param keyStroke
+   * @param menuItem
+   * @param actionListener
+   */
+  protected void addMenuActionAndAccelerator(KeyStroke keyStroke,
+          JMenuItem menuItem, ActionListener actionListener)
+  {
+    menuItem.setAccelerator(keyStroke);
+    accelerators.put(keyStroke, menuItem);
+    menuItem.addActionListener(actionListener);
+  }
+
+  /**
    * Action on clicking sort annotations by type.
    * 
    * @param sortOrder
@@ -2595,10 +2500,6 @@ public class GAlignFrame extends JInternalFrame
   {
   }
 
-  protected void showProducts_actionPerformed(ActionEvent e)
-  {
-  }
-
   protected void buildSortByAnnotationScoresMenu()
   {
   }
@@ -2902,7 +2803,7 @@ public class GAlignFrame extends JInternalFrame
   {
 
   }
-  protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
+  protected void loadTreeMenuItem_actionPerformed(ActionEvent e)
   {
 
   }
@@ -3160,4 +3061,50 @@ public class GAlignFrame extends JInternalFrame
   {
     this.annotationSortOrder = annotationSortOrder;
   }
+
+  public Map<KeyStroke, JMenuItem> getAccelerators()
+  {
+    return this.accelerators;
+  }
+
+  /**
+   * Returns the selected index of the tabbed pane, or -1 if none selected
+   * (including the case where the tabbed pane has not been made visible).
+   * 
+   * @return
+   */
+  public int getTabIndex()
+  {
+    return tabbedPane.getSelectedIndex();
+  }
+
+  public JPanel getStatusPanel()
+  {
+    return statusPanel;
+  }
+
+  /**
+   * Sets a reference to the containing split frame. Also makes the 'toggle
+   * split view' menu item visible and checked.
+   * 
+   * @param sf
+   */
+  public void setSplitFrame(SplitContainerI sf)
+  {
+    this.splitFrame = sf;
+    if (sf != null)
+    {
+      this.showComplementMenuItem.setVisible(true);
+      this.showComplementMenuItem.setState(true);
+    }
+  }
+
+  public SplitContainerI getSplitViewContainer()
+  {
+    return this.splitFrame;
+  }
+
+  protected void showComplement_actionPerformed(boolean state)
+  {
+  }
 }
diff --git a/src/jalview/jbgui/GPDBSearchPanel.java b/src/jalview/jbgui/GPDBSearchPanel.java
new file mode 100644 (file)
index 0000000..29a0014
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 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.jbgui;
+
+import jalview.gui.Desktop;
+import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
+import jalview.util.MessageManager;
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+/**
+ * GUI layout for PDB Fetch Panel
+ * 
+ * @author tcnofoegbu
+ *
+ */
+@SuppressWarnings("serial")
+public abstract class GPDBSearchPanel extends JPanel
+{
+  protected String frameTitle = MessageManager
+          .getString("label.pdb_sequence_getcher");
+
+  protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
+
+  protected JComboBox<PDBDocField> cmb_searchTarget = new JComboBox<PDBDocField>();
+
+  protected JButton btn_ok = new JButton();
+
+  protected JButton btn_back = new JButton();
+  
+  protected JButton btn_cancel = new JButton();
+  
+  protected JTextField txt_search = new JTextField(20);
+  
+  protected JTable tbl_summary = new JTable();
+
+  protected JScrollPane scrl_searchResult = new JScrollPane(
+tbl_summary);
+
+  private JTabbedPane tabbedPane = new JTabbedPane();
+
+  private PDBDocFieldPreferences pdbDocFieldPrefs = new PDBDocFieldPreferences(
+          PreferenceSource.SEARCH_SUMMARY);
+
+  private JPanel pnl_actions = new JPanel();
+
+  private JPanel pnl_results = new JPanel();
+
+  private JPanel pnl_inputs = new JPanel();
+
+  private BorderLayout mainLayout = new BorderLayout();
+
+  public GPDBSearchPanel()
+  {
+    try
+    {
+      jbInit();
+      mainFrame.invalidate();
+      mainFrame.pack();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Initializes the GUI default properties
+   * 
+   * @throws Exception
+   */
+  private void jbInit() throws Exception
+  {
+    tbl_summary.setAutoCreateRowSorter(true);
+    btn_back.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_back.setText(MessageManager.getString("action.back"));
+    btn_back.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        btn_back_ActionPerformed();
+      }
+    });
+    btn_ok.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_ok.setText(MessageManager.getString("action.ok"));
+    btn_ok.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        btn_ok_ActionPerformed();
+      }
+    });
+    btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_cancel.setText(MessageManager.getString("action.cancel"));
+    btn_cancel.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        btn_cancel_ActionPerformed();
+      }
+    });
+
+    scrl_searchResult.setPreferredSize(new Dimension(500, 300));
+    scrl_searchResult
+            .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+
+    cmb_searchTarget.setFont(new java.awt.Font("Verdana", 0, 12));
+    cmb_searchTarget.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+    });
+
+    populateCmbSearchTargetOptions();
+
+    txt_search.setFont(new java.awt.Font("Verdana", 0, 12));
+    txt_search.getDocument().addDocumentListener(new DocumentListener()
+    {
+      @Override
+      public void insertUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+
+      @Override
+      public void removeUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+
+      @Override
+      public void changedUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+    });
+
+    final String searchTabTitle = MessageManager
+            .getString("label.search_result");
+    ChangeListener changeListener = new ChangeListener()
+    {
+      public void stateChanged(ChangeEvent changeEvent)
+      {
+        JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
+                .getSource();
+        int index = sourceTabbedPane.getSelectedIndex();
+        if (sourceTabbedPane.getTitleAt(index).equals(searchTabTitle))
+        {
+          txt_search_ActionPerformed();
+        }
+      }
+    };
+    tabbedPane.addChangeListener(changeListener);
+    tabbedPane.setPreferredSize(new Dimension(500, 300));
+    tabbedPane.add(searchTabTitle, scrl_searchResult);
+    tabbedPane.add(
+            MessageManager.getString("label.configure_displayed_columns"),
+            pdbDocFieldPrefs);
+
+    pnl_actions.add(btn_back);
+    pnl_actions.add(btn_ok);
+    pnl_actions.add(btn_cancel);
+
+    pnl_results.add(tabbedPane);
+    pnl_inputs.add(cmb_searchTarget);
+    pnl_inputs.add(txt_search);
+
+    this.setLayout(mainLayout);
+    this.add(pnl_inputs, java.awt.BorderLayout.NORTH);
+    this.add(pnl_results, java.awt.BorderLayout.CENTER);
+    this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
+    mainFrame.setVisible(true);
+    mainFrame.setContentPane(this);
+    mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+    Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400);
+  }
+
+  public JComboBox<PDBDocField> getCmbSearchTarget()
+  {
+    return cmb_searchTarget;
+  }
+
+  public JTextField getTxtSearch()
+  {
+    return txt_search;
+  }
+
+  public JInternalFrame getMainFrame()
+  {
+    return mainFrame;
+  }
+
+  public abstract void txt_search_ActionPerformed();
+
+  public abstract void btn_ok_ActionPerformed();
+
+  public abstract void btn_back_ActionPerformed();
+
+  public abstract void btn_cancel_ActionPerformed();
+
+  public abstract void populateCmbSearchTargetOptions();
+
+}
index 8f226ef..1d35477 100755 (executable)
@@ -22,6 +22,7 @@ package jalview.jbgui;
 
 import jalview.gui.JvSwingUtils;
 import jalview.gui.StructureViewer.ViewerType;
+import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -149,6 +150,7 @@ public class GPreferences extends JPanel
 
   protected JTextField chimeraPath = new JTextField();
 
+
   /*
    * Colours tab components
    */
@@ -156,7 +158,9 @@ public class GPreferences extends JPanel
 
   protected JPanel maxColour = new JPanel();
 
-  protected JComboBox<String> colour = new JComboBox<String>();
+  protected JComboBox<String> protColour = new JComboBox<String>();
+
+  protected JComboBox<String> nucColour = new JComboBox<String>();
 
   /*
    * Connections tab components
@@ -692,16 +696,30 @@ public class GPreferences extends JPanel
         maxColour_actionPerformed(maxColour);
       }
     });
-    colour.setFont(verdana11);
-    colour.setBounds(new Rectangle(172, 225, 155, 21));
-    JLabel colourLabel = new JLabel();
-    colourLabel.setFont(verdana11);
-    colourLabel.setHorizontalAlignment(SwingConstants.RIGHT);
-    colourLabel.setText(MessageManager.getString("label.alignment_colour")
+
+    protColour.setFont(verdana11);
+    protColour.setBounds(new Rectangle(172, 225, 155, 21));
+    JLabel protColourLabel = new JLabel();
+    protColourLabel.setFont(verdana11);
+    protColourLabel.setHorizontalAlignment(SwingConstants.LEFT);
+    protColourLabel.setText(MessageManager
+            .getString("label.prot_alignment_colour") + " ");
+    JvSwingUtils.addtoLayout(coloursTab, MessageManager
+            .getString("label.default_colour_scheme_for_alignment"),
+            protColourLabel, protColour);
+
+    nucColour.setFont(verdana11);
+    nucColour.setBounds(new Rectangle(172, 240, 155, 21));
+    JLabel nucColourLabel = new JLabel();
+    nucColourLabel.setFont(verdana11);
+    nucColourLabel.setHorizontalAlignment(SwingConstants.LEFT);
+    nucColourLabel.setText(MessageManager
+            .getString("label.nuc_alignment_colour")
             + " ");
     JvSwingUtils.addtoLayout(coloursTab, MessageManager
             .getString("label.default_colour_scheme_for_alignment"),
-            colourLabel, colour);
+            nucColourLabel, nucColour);
+
     JPanel annotationShding = new JPanel();
     annotationShding.setBorder(new TitledBorder(MessageManager
             .getString("label.annotation_shading_default")));
@@ -729,8 +747,8 @@ public class GPreferences extends JPanel
             .getString("label.structure_options")));
     structureTab.setLayout(null);
     final int width = 400;
-    final int height = 23;
-    final int lineSpacing = 30;
+    final int height = 22;
+    final int lineSpacing = 25;
     int ypos = 30;
 
     structFromPdb.setFont(verdana11);
@@ -824,9 +842,23 @@ public class GPreferences extends JPanel
     });
     structureTab.add(chimeraPath);
 
+    ypos += lineSpacing;
+    // scrl_pdbDocFieldConfig.setPreferredSize(new Dimension(450, 100));
+    // scrl_pdbDocFieldConfig
+    // .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+    // scrl_pdbDocFieldConfig.setBounds();
+    PDBDocFieldPreferences docFieldPref = new PDBDocFieldPreferences(
+            PreferenceSource.PREFERENCES);
+    docFieldPref.setBounds(new Rectangle(10, ypos + 5, 450, 120));
+    structureTab.add(docFieldPref);
+
+
+
+
     return structureTab;
   }
 
+
   /**
    * Action on choosing a structure viewer from combobox options.
    * 
diff --git a/src/jalview/jbgui/GSplitFrame.java b/src/jalview/jbgui/GSplitFrame.java
new file mode 100644 (file)
index 0000000..a377571
--- /dev/null
@@ -0,0 +1,142 @@
+package jalview.jbgui;
+
+import java.awt.Component;
+import java.awt.MouseInfo;
+import java.awt.Point;
+import java.awt.Rectangle;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JSplitPane;
+import javax.swing.plaf.basic.BasicInternalFrameUI;
+
+import jalview.util.Platform;
+
+public class GSplitFrame extends JInternalFrame
+{
+  private static final long serialVersionUID = 1L;
+
+  private GAlignFrame topFrame;
+
+  private GAlignFrame bottomFrame;
+
+  private JSplitPane splitPane;
+
+  /**
+   * Constructor
+   * 
+   * @param top
+   * @param bottom
+   */
+  public GSplitFrame(GAlignFrame top, GAlignFrame bottom)
+  {
+    this.topFrame = top;
+    this.bottomFrame = bottom;
+
+    hideTitleBars();
+
+    addSplitPane();
+  }
+
+  /**
+   * Create and add the split pane containing the top and bottom components.
+   */
+  protected void addSplitPane()
+  {
+    splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topFrame,
+            bottomFrame);
+    splitPane.setVisible(true);
+    final double ratio = topFrame.getHeight()
+            / (double) (topFrame.getHeight() + bottomFrame.getHeight());
+    splitPane.setDividerLocation(ratio);
+    splitPane.setResizeWeight(ratio);
+    splitPane.setDividerSize(5);
+    add(splitPane);
+  }
+
+  /**
+   * Try to hide the title bars as a waste of precious space.
+   * 
+   * @see http
+   *      ://stackoverflow.com/questions/7218971/java-method-works-on-windows
+   *      -but-not-macintosh -java
+   */
+  protected void hideTitleBars()
+  {
+    if (new Platform().isAMac())
+    {
+      // this saves some space - but doesn't hide the title bar
+      topFrame.putClientProperty("JInternalFrame.isPalette", true);
+      // topFrame.getRootPane().putClientProperty("Window.style", "small");
+      bottomFrame.putClientProperty("JInternalFrame.isPalette", true);
+    }
+    else
+    {
+      ((BasicInternalFrameUI) topFrame.getUI()).setNorthPane(null);
+      ((BasicInternalFrameUI) bottomFrame.getUI()).setNorthPane(null);
+    }
+  }
+
+  public GAlignFrame getTopFrame()
+  {
+    return topFrame;
+  }
+
+  public GAlignFrame getBottomFrame()
+  {
+    return bottomFrame;
+  }
+
+  /**
+   * Returns the split pane component the mouse is in, or null if neither.
+   * 
+   * @return
+   */
+  protected GAlignFrame getFrameAtMouse()
+  {
+    Point loc = MouseInfo.getPointerInfo().getLocation();
+    
+    if (isIn(loc, splitPane.getTopComponent()))
+    {
+      return getTopFrame();
+    }
+    else if (isIn(loc, splitPane.getBottomComponent()))
+    {
+      return getBottomFrame();
+    }
+    return null;
+  }
+
+  private boolean isIn(Point loc, Component comp)
+  {
+    if (!comp.isVisible())
+    {
+      return false;
+    }
+    Point p = comp.getLocationOnScreen();
+    Rectangle r = new Rectangle(p.x, p.y, comp.getWidth(), comp.getHeight());
+    return r.contains(loc);
+  }
+
+  /**
+   * Make the complement of the specified split component visible or hidden,
+   * adjusting the position of the split divide.
+   */
+  public void setComplementVisible(Object alignFrame, boolean show)
+  {
+    if (alignFrame == this.topFrame)
+    {
+      this.bottomFrame.setVisible(show);
+    }
+    else if (alignFrame == this.bottomFrame)
+    {
+      this.topFrame.setVisible(show);
+    }
+    if (show)
+    {
+      // SplitPane needs nudging to restore 50-50 split
+      // TODO save/restore other ratios
+      splitPane.setDividerLocation(0.5d);
+    }
+    validate();
+  }
+}
diff --git a/src/jalview/jbgui/GStructureChooser.java b/src/jalview/jbgui/GStructureChooser.java
new file mode 100644 (file)
index 0000000..b79a7f7
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 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.jbgui;
+
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.Desktop;
+import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
+import jalview.util.MessageManager;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+@SuppressWarnings("serial")
+/**
+ * GUI layout for structure chooser 
+ * @author tcnofoegbu
+ *
+ */
+public abstract class GStructureChooser extends JPanel implements
+        ItemListener
+{
+  protected String frameTitle = MessageManager
+          .getString("label.structure_chooser");
+
+  protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
+
+  protected JComboBox<FilterOption> cmb_filterOption = new JComboBox<FilterOption>();
+
+  protected AlignmentPanel ap;
+
+  protected JLabel lbl_result = new JLabel(
+          MessageManager.getString("label.select"));
+
+  protected JButton btn_view = new JButton();
+
+  protected JButton btn_cancel = new JButton();
+
+  protected JButton btn_pdbFromFile = new JButton();
+
+  protected JTextField txt_search = new JTextField(14);
+
+  private JPanel pnl_actions = new JPanel();
+
+  private JPanel pnl_filter = new JPanel();
+
+  private JPanel pnl_idInput = new JPanel(new FlowLayout());
+
+  private JPanel pnl_fileChooser = new JPanel(new FlowLayout());
+
+  private JPanel pnl_idInputBL = new JPanel(new BorderLayout());
+
+  private JPanel pnl_fileChooserBL = new JPanel(new BorderLayout());
+
+  protected JPanel pnl_switchableViews = new JPanel(new CardLayout());
+
+  protected CardLayout layout_switchableViews = (CardLayout) (pnl_switchableViews
+          .getLayout());
+
+  private BorderLayout mainLayout = new BorderLayout();
+
+  protected JCheckBox chk_rememberSettings = new JCheckBox(
+          MessageManager.getString("label.dont_ask_me_again"));
+
+  protected JCheckBox chk_invertFilter = new JCheckBox(
+          MessageManager.getString("label.invert"));
+
+  protected ImageIcon loadingImage = new ImageIcon(getClass().getResource(
+          "/images/loading.gif"));
+
+  protected ImageIcon goodImage = new ImageIcon(getClass().getResource(
+          "/images/good.png"));
+
+  protected ImageIcon errorImage = new ImageIcon(getClass().getResource(
+          "/images/error.png"));
+
+  protected JLabel lbl_loading = new JLabel(loadingImage);
+
+  protected JLabel lbl_pdbManualFetchStatus = new JLabel(errorImage);
+
+  protected JLabel lbl_fromFileStatus = new JLabel(errorImage);
+
+
+  protected AssciateSeqPanel idInputAssSeqPanel = new AssciateSeqPanel();
+
+  protected AssciateSeqPanel fileChooserAssSeqPanel = new AssciateSeqPanel();
+
+  protected static final String VIEWS_FILTER = "VIEWS_FILTER";
+
+  protected static final String VIEWS_FROM_FILE = "VIEWS_FROM_FILE";
+
+  protected static final String VIEWS_ENTER_ID = "VIEWS_ENTER_ID";
+
+  protected JTable tbl_summary = new JTable();
+
+  protected JScrollPane scrl_foundStructures = new JScrollPane(
+          tbl_summary);
+
+  private JTabbedPane tabbedPane = new JTabbedPane();
+
+  private PDBDocFieldPreferences pdbDocFieldPrefs = new PDBDocFieldPreferences(
+          PreferenceSource.STRUCTURE_CHOOSER);
+
+  public GStructureChooser()
+  {
+    try
+    {
+      jbInit();
+      mainFrame.setVisible(false);
+      mainFrame.invalidate();
+      mainFrame.pack();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Initializes the GUI default properties
+   * 
+   * @throws Exception
+   */
+  private void jbInit() throws Exception
+  {
+    tbl_summary.setAutoCreateRowSorter(true);
+    btn_view.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_view.setText(MessageManager.getString("action.view"));
+    btn_view.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        ok_ActionPerformed();
+      }
+    });
+    btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_cancel.setText(MessageManager.getString("action.cancel"));
+    btn_cancel.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        mainFrame.dispose();
+      }
+    });
+
+    btn_pdbFromFile.setFont(new java.awt.Font("Verdana", 0, 12));
+    String btn_title = MessageManager.getString("label.select_pdb_file");
+    btn_pdbFromFile.setText(btn_title + "              ");
+    btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        pdbFromFile_actionPerformed();
+      }
+    });
+
+    scrl_foundStructures.setPreferredSize(new Dimension(500, 300));
+    scrl_foundStructures
+            .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+
+    cmb_filterOption.setFont(new java.awt.Font("Verdana", 0, 12));
+    chk_invertFilter.setFont(new java.awt.Font("Verdana", 0, 12));
+    chk_rememberSettings.setFont(new java.awt.Font("Verdana", 0, 12));
+
+
+    txt_search.setToolTipText(MessageManager
+            .getString("label.enter_pdb_id"));
+    cmb_filterOption.setToolTipText(MessageManager
+            .getString("info.select_filter_option"));
+    txt_search.getDocument().addDocumentListener(new DocumentListener()
+    {
+      @Override
+      public void insertUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+
+      @Override
+      public void removeUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+
+      @Override
+      public void changedUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+    });
+
+    cmb_filterOption.addItemListener(this);
+    chk_invertFilter.addItemListener(this);
+
+    pnl_actions.add(chk_rememberSettings);
+    pnl_actions.add(btn_view);
+    pnl_actions.add(btn_cancel);
+
+    // pnl_filter.add(lbl_result);
+    pnl_filter.add(cmb_filterOption);
+    pnl_filter.add(lbl_loading);
+    pnl_filter.add(chk_invertFilter);
+    lbl_loading.setVisible(false);
+
+    pnl_idInput.add(txt_search);
+    pnl_idInput.add(lbl_pdbManualFetchStatus);
+
+    pnl_fileChooser.add(btn_pdbFromFile);
+    pnl_fileChooser.add(lbl_fromFileStatus);
+
+    pnl_fileChooserBL.add(fileChooserAssSeqPanel, BorderLayout.NORTH);
+    pnl_fileChooserBL.add(pnl_fileChooser, BorderLayout.CENTER);
+
+    pnl_idInputBL.add(idInputAssSeqPanel, BorderLayout.NORTH);
+    pnl_idInputBL.add(pnl_idInput, BorderLayout.CENTER);
+    
+    final String foundStructureSummary = MessageManager
+            .getString("label.found_structures_summary");
+
+    ChangeListener changeListener = new ChangeListener()
+    {
+      public void stateChanged(ChangeEvent changeEvent)
+      {
+        JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
+                .getSource();
+        int index = sourceTabbedPane.getSelectedIndex();
+        if (sourceTabbedPane.getTitleAt(index)
+                .equals(foundStructureSummary))
+        {
+          tabRefresh();
+        }
+      }
+    };
+    tabbedPane.addChangeListener(changeListener);
+    tabbedPane.setPreferredSize(new Dimension(500, 300));
+    tabbedPane.add(foundStructureSummary, scrl_foundStructures);
+    tabbedPane.add(
+            MessageManager.getString("label.configure_displayed_columns"),
+            pdbDocFieldPrefs);
+    
+
+    pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
+    pnl_switchableViews.add(pnl_idInputBL, VIEWS_ENTER_ID);
+    pnl_switchableViews.add(tabbedPane, VIEWS_FILTER);
+    
+    this.setLayout(mainLayout);
+    this.add(pnl_filter, java.awt.BorderLayout.NORTH);
+    this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
+    this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
+
+    mainFrame.setVisible(true);
+    mainFrame.setContentPane(this);
+    mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+    Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400);
+  }
+
+  
+  @Override
+  /**
+   * Event listener for the 'filter' combo-box and 'invert' check-box
+   */
+  public void itemStateChanged(ItemEvent e)
+  {
+    stateChanged(e);
+  }
+
+  /**
+   * This inner class provides the data model for the structure filter combo-box
+   * 
+   * @author tcnofoegbu
+   *
+   */
+  public class FilterOption
+  {
+    private String name;
+
+    private String value;
+
+    private String view;
+
+    public FilterOption(String name, String value, String view)
+    {
+      this.name = name;
+      this.value = value;
+      this.view = view;
+    }
+
+    public String getName()
+    {
+      return name;
+    }
+
+    public void setName(String name)
+    {
+      this.name = name;
+    }
+
+    public String getValue()
+    {
+      return value;
+    }
+
+    public void setValue(String value)
+    {
+      this.value = value;
+    }
+
+    public String getView()
+    {
+      return view;
+    }
+
+    public void setView(String view)
+    {
+      this.view = view;
+    }
+
+    public String toString()
+    {
+      return this.name;
+    }
+  }
+
+  /**
+   * This inner class provides the provides the data model for associate
+   * sequence combo-box - cmb_assSeq
+   * 
+   * @author tcnofoegbu
+   *
+   */
+  public class AssociateSeqOptions
+  {
+    private SequenceI sequence;
+    private String name;
+
+    public AssociateSeqOptions(SequenceI seq)
+    {
+      this.sequence = seq;
+      this.name = (seq.getName().length() >= 23) ? seq.getName().substring(
+              0, 23) : seq.getName();
+    }
+
+    public AssociateSeqOptions(String name, SequenceI seq)
+    {
+      this.name = name;
+      this.sequence = seq;
+    }
+
+    public String toString()
+    {
+      return name;
+    }
+
+    public String getName()
+    {
+      return name;
+    }
+
+    public void setName(String name)
+    {
+      this.name = name;
+    }
+
+    public SequenceI getSequence()
+    {
+      return sequence;
+    }
+
+    public void setSequence(SequenceI sequence)
+    {
+      this.sequence = sequence;
+    }
+
+  }
+
+  /**
+   * This inner class holds the Layout and configuration of the panel which
+   * handles association of manually fetched structures to a unique sequence
+   * when more than one sequence selection is made
+   * 
+   * @author tcnofoegbu
+   *
+   */
+  public class AssciateSeqPanel extends JPanel implements ItemListener
+  {
+    private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<AssociateSeqOptions>();
+
+    private JLabel lbl_associateSeq = new JLabel();
+
+    public AssciateSeqPanel()
+    {
+      this.setLayout(new FlowLayout());
+      this.add(cmb_assSeq);
+      this.add(lbl_associateSeq);
+      cmb_assSeq.setToolTipText(MessageManager
+              .getString("info.associate_wit_sequence"));
+      cmb_assSeq.addItemListener(this);
+    }
+
+    public void loadCmbAssSeq()
+    {
+      populateCmbAssociateSeqOptions(cmb_assSeq, lbl_associateSeq);
+    }
+
+    public JComboBox<AssociateSeqOptions> getCmb_assSeq()
+    {
+      return cmb_assSeq;
+    }
+
+    public void setCmb_assSeq(JComboBox<AssociateSeqOptions> cmb_assSeq)
+    {
+      this.cmb_assSeq = cmb_assSeq;
+    }
+
+    @Override
+    public void itemStateChanged(ItemEvent e)
+    {
+      if (e.getStateChange() == ItemEvent.SELECTED)
+      {
+        cmbAssSeqStateChanged();
+      }
+    }
+  }
+
+  public JComboBox<FilterOption> getCmbFilterOption()
+  {
+    return cmb_filterOption;
+  }
+
+  protected abstract void stateChanged(ItemEvent e);
+
+  protected abstract void updateCurrentView();
+
+  protected abstract void populateFilterComboBox();
+
+  protected abstract void ok_ActionPerformed();
+
+  protected abstract void pdbFromFile_actionPerformed();
+
+  protected abstract void txt_search_ActionPerformed();
+
+  public abstract void populateCmbAssociateSeqOptions(
+          JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq);
+
+  public abstract void cmbAssSeqStateChanged();
+
+  public abstract void tabRefresh();
+}
index 3417734..d067753 100755 (executable)
@@ -22,10 +22,19 @@ package jalview.jbgui;
 
 import jalview.util.MessageManager;
 
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.event.*;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JInternalFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JScrollPane;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuListener;
 
 public class GTreePanel extends JInternalFrame
 {
@@ -124,7 +133,7 @@ public class GTreePanel extends JInternalFrame
     {
       public void actionPerformed(ActionEvent e)
       {
-        sortByTree_actionPerformed(e);
+        sortByTree_actionPerformed();
       }
     });
     font.setText(MessageManager.getString("action.font"));
@@ -282,7 +291,7 @@ public class GTreePanel extends JInternalFrame
   {
   }
 
-  public void sortByTree_actionPerformed(ActionEvent e)
+  public void sortByTree_actionPerformed()
   {
 
   }
diff --git a/src/jalview/jbgui/PDBDocFieldPreferences.java b/src/jalview/jbgui/PDBDocFieldPreferences.java
new file mode 100644 (file)
index 0000000..2021d0b
--- /dev/null
@@ -0,0 +1,228 @@
+package jalview.jbgui;
+
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.AbstractTableModel;
+
+@SuppressWarnings("serial")
+public class PDBDocFieldPreferences extends JScrollPane
+{
+  protected JTable tbl_pdbDocFieldConfig = new JTable();
+
+  protected JScrollPane scrl_pdbDocFieldConfig = new JScrollPane(
+          tbl_pdbDocFieldConfig);
+
+  private HashMap<String, PDBDocField> map = new HashMap<String, PDBDocField>();
+
+  private static Collection<PDBDocField> searchSummaryFields = new HashSet<PDBDocField>();
+
+  private static Collection<PDBDocField> structureSummaryFields = new HashSet<PDBDocField>();
+
+  public enum PreferenceSource
+  {
+    SEARCH_SUMMARY, STRUCTURE_CHOOSER, PREFERENCES;
+  }
+
+  private PreferenceSource currentSource;
+
+  static
+  {
+    searchSummaryFields.add(PDBDocField.PDB_ID);
+    searchSummaryFields.add(PDBDocField.TITLE);
+
+    structureSummaryFields.add(PDBDocField.PDB_ID);
+    structureSummaryFields.add(PDBDocField.TITLE);
+  }
+
+  public PDBDocFieldPreferences(PreferenceSource source)
+  {
+    tbl_pdbDocFieldConfig.setAutoCreateRowSorter(true);
+    this.getViewport().add(tbl_pdbDocFieldConfig);
+    this.currentSource = source;
+
+    String[] columnNames = null;
+    switch (source)
+    {
+    case SEARCH_SUMMARY:
+      columnNames = new String[]
+      { "PDB Feild", "Show in search summary" };
+      break;
+    case STRUCTURE_CHOOSER:
+      columnNames = new String[]
+      { "PDB Feild", "Show in structure summary" };
+      break;
+    case PREFERENCES:
+      columnNames = new String[]
+      { "PDB Feild", "Show in search summary", "Show in structure summary" };
+      break;
+    default:
+      break;
+    }
+
+    Object[][] data = new Object[PDBDocField.values().length - 1][3];
+    int x = 0;
+    for (PDBDocField field : PDBDocField.values())
+    {
+      if (field.getName().equalsIgnoreCase("all"))
+      {
+        continue;
+      }
+
+      switch (source)
+      {
+      case SEARCH_SUMMARY:
+        data[x++] = new Object[]
+        { field.getName(), searchSummaryFields.contains(field) };
+        break;
+      case STRUCTURE_CHOOSER:
+        data[x++] = new Object[]
+        { field.getName(), structureSummaryFields.contains(field) };
+        break;
+      case PREFERENCES:
+        data[x++] = new Object[]
+        { field.getName(), searchSummaryFields.contains(field),
+            structureSummaryFields.contains(field) };
+        break;
+      default:
+        break;
+      }
+      map.put(field.getName(), field);
+    }
+
+    PDBFieldTableModel model = new PDBFieldTableModel(columnNames, data);
+    tbl_pdbDocFieldConfig.setModel(model);
+  }
+
+  public static Collection<PDBDocField> getSearchSummaryFields()
+  {
+    return searchSummaryFields;
+  }
+
+  public static void setSearchSummaryFields(
+          Collection<PDBDocField> searchSummaryFields)
+  {
+    PDBDocFieldPreferences.searchSummaryFields = searchSummaryFields;
+  }
+
+  public static Collection<PDBDocField> getStructureSummaryFields()
+  {
+    return structureSummaryFields;
+  }
+
+  public static void setStructureSummaryFields(
+          Collection<PDBDocField> structureSummaryFields)
+  {
+    PDBDocFieldPreferences.structureSummaryFields = structureSummaryFields;
+  }
+
+  class PDBFieldTableModel extends AbstractTableModel
+  {
+
+    public PDBFieldTableModel(String[] columnNames, Object[][] data)
+    {
+      this.data = data;
+      this.columnNames = columnNames;
+    }
+
+    private Object[][] data;
+
+    private String[] columnNames;
+
+    public int getColumnCount()
+    {
+      return columnNames.length;
+    }
+
+    public int getRowCount()
+    {
+      return data.length;
+    }
+
+    public String getColumnName(int col)
+    {
+      return columnNames[col];
+    }
+
+    public Object getValueAt(int row, int col)
+    {
+      return data[row][col];
+    }
+
+    /*
+     * JTable uses this method to determine the default renderer/ editor for
+     * each cell. If we didn't implement this method, then the last column would
+     * contain text ("true"/"false"), rather than a check box.
+     */
+    public Class getColumnClass(int c)
+    {
+      return getValueAt(0, c).getClass();
+    }
+
+    /*
+     * Don't need to implement this method unless your table's editable.
+     */
+    public boolean isCellEditable(int row, int col)
+    {
+      // Note that the data/cell address is constant,
+      // no matter where the cell appears onscreen.
+      return col == 1 || col == 2;
+
+    }
+
+    /*
+     * Don't need to implement this method unless your table's data can change.
+     */
+    public void setValueAt(Object value, int row, int col)
+    {
+      data[row][col] = value;
+      fireTableCellUpdated(row, col);
+
+      String name = getValueAt(row, 0).toString();
+      boolean selected = ((Boolean) value).booleanValue();
+
+      PDBDocField pdbField = map.get(name);
+
+      if (currentSource == PreferenceSource.SEARCH_SUMMARY)
+      {
+        updatePrefs(searchSummaryFields, pdbField, selected);
+      }
+      else if (currentSource == PreferenceSource.STRUCTURE_CHOOSER)
+      {
+        updatePrefs(structureSummaryFields, pdbField, selected);
+      }
+      else if (currentSource == PreferenceSource.PREFERENCES)
+      {
+        if (col == 1)
+        {
+          updatePrefs(searchSummaryFields, pdbField, selected);
+        }
+        else if (col == 2)
+        {
+          updatePrefs(structureSummaryFields, pdbField, selected);
+        }
+      }
+    }
+
+    private void updatePrefs(Collection<PDBDocField> prefConfig,
+            PDBDocField pdbField,
+            boolean selected)
+    {
+      if (prefConfig.contains(pdbField) && !selected)
+      {
+        prefConfig.remove(pdbField);
+      }
+
+      if (!prefConfig.contains(pdbField) && selected)
+      {
+        prefConfig.add(pdbField);
+      }
+    }
+
+  }
+}
index d4fda25..2ba7aff 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.renderer;
 
 import jalview.analysis.AAFrequency;
+import jalview.analysis.CodingUtils;
 import jalview.analysis.StructureFrequency;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
@@ -142,6 +143,8 @@ public class AnnotationRenderer
 
   private Hashtable[] hconsensus;
 
+  private Hashtable[] complementConsensus;
+
   private Hashtable[] hStrucConsensus;
 
   private boolean av_ignoreGapsConsensus;
@@ -298,24 +301,37 @@ public class AnnotationRenderer
       profcolour = av.getAlignment().isNucleotide() ? new jalview.schemes.NucleotideColourScheme()
               : new jalview.schemes.ZappoColourScheme();
     }
-    boolean rna = av.getAlignment().isNucleotide();
     columnSelection = av.getColumnSelection();
-    hconsensus = av.getSequenceConsensusHash();// hconsensus;
-    hStrucConsensus = av.getRnaStructureConsensusHash(); // hStrucConsensus;
-    av_ignoreGapsConsensus = av.getIgnoreGapsConsensus();
+    hconsensus = av.getSequenceConsensusHash();
+    complementConsensus = av.getComplementConsensusHash();
+    hStrucConsensus = av.getRnaStructureConsensusHash();
+    av_ignoreGapsConsensus = av.isIgnoreGapsConsensus();
   }
 
+  /**
+   * Returns profile data; the first element is the profile type, the second is
+   * the number of distinct values, the third the total count, and the remainder
+   * depend on the profile type.
+   * 
+   * @param aa
+   * @param column
+   * @return
+   */
   public int[] getProfileFor(AlignmentAnnotation aa, int column)
   {
     // TODO : consider refactoring the global alignment calculation
     // properties/rendering attributes as a global 'alignment group' which holds
     // all vis settings for the alignment as a whole rather than a subset
     //
-    if (aa.autoCalculated && aa.label.startsWith("Consensus"))
+    if (aa.autoCalculated
+            && (aa.label.startsWith("Consensus") || aa.label
+                    .startsWith("cDNA Consensus")))
     {
+      boolean forComplement = aa.label.startsWith("cDNA Consensus");
       if (aa.groupRef != null && aa.groupRef.consensusData != null
               && aa.groupRef.isShowSequenceLogo())
       {
+        // TODO? group consensus for cDNA complement
         return AAFrequency.extractProfile(
                 aa.groupRef.consensusData[column],
                 aa.groupRef.getIgnoreGapsConsensus());
@@ -324,8 +340,16 @@ public class AnnotationRenderer
       // be stored
       if (aa.groupRef == null && aa.sequenceRef == null)
       {
-        return AAFrequency.extractProfile(hconsensus[column],
-                av_ignoreGapsConsensus);
+        if (forComplement)
+        {
+          return AAFrequency.extractCdnaProfile(
+                  complementConsensus[column], av_ignoreGapsConsensus);
+        }
+        else
+        {
+          return AAFrequency.extractProfile(hconsensus[column],
+                  av_ignoreGapsConsensus);
+        }
       }
     }
     else
@@ -400,12 +424,15 @@ public class AnnotationRenderer
     boolean validRes = false;
     boolean validEnd = false;
     boolean labelAllCols = false;
-    boolean centreColLabels, centreColLabelsDef = av
-            .getCentreColumnLabels();
+    boolean centreColLabels;
+    boolean centreColLabelsDef = av.isCentreColumnLabels();
     boolean scaleColLabel = false;
-    AlignmentAnnotation consensusAnnot = av
-            .getAlignmentConsensusAnnotation(), structConsensusAnnot = av
+    final AlignmentAnnotation consensusAnnot = av
+            .getAlignmentConsensusAnnotation();
+    final AlignmentAnnotation structConsensusAnnot = av
             .getAlignmentStrucConsensusAnnotation();
+    final AlignmentAnnotation complementConsensusAnnot = av
+            .getComplementConsensusAnnotation();
     boolean renderHistogram = true, renderProfile = true, normaliseProfile = false, isRNA = rna;
 
     BitSet graphGroupDrawn = new BitSet();
@@ -432,7 +459,8 @@ public class AnnotationRenderer
           renderProfile = row.groupRef.isShowSequenceLogo();
           normaliseProfile = row.groupRef.isNormaliseSequenceLogo();
         }
-        else if (row == consensusAnnot || row == structConsensusAnnot)
+        else if (row == consensusAnnot || row == structConsensusAnnot
+                || row == complementConsensusAnnot)
         {
           renderHistogram = av_renderHistogram;
           renderProfile = av_renderProfile;
@@ -556,6 +584,8 @@ public class AnnotationRenderer
           {
             validRes = true;
           }
+          final String displayChar = validRes ? row_annotations[column].displayCharacter
+                  : null;
           if (x > -1)
           {
             if (activeRow == i)
@@ -585,21 +615,19 @@ public class AnnotationRenderer
               g.setColor(Color.orange.darker());
               g.fillRect(x * charWidth, y, charWidth, charHeight);
             }
-            if (validCharWidth
-                    && validRes
-                    && row_annotations[column].displayCharacter != null
-                    && (row_annotations[column].displayCharacter.length() > 0))
+            if (validCharWidth && validRes && displayChar != null
+                    && (displayChar.length() > 0))
             {
 
-              if (centreColLabels || scaleColLabel)
+              fmWidth = fm.charsWidth(displayChar.toCharArray(), 0,
+                      displayChar.length());
+              if (/* centreColLabels || */scaleColLabel)
               {
-                fmWidth = fm.charsWidth(
-                        row_annotations[column].displayCharacter
-                                .toCharArray(), 0,
-                        row_annotations[column].displayCharacter.length());
-
-                if (scaleColLabel)
-                {
+                // fmWidth = fm.charsWidth(displayChar.toCharArray(), 0,
+                // displayChar.length());
+                //
+                // if (scaleColLabel)
+                // {
                   // justify the label and scale to fit in column
                   if (fmWidth > charWidth)
                   {
@@ -611,14 +639,13 @@ public class AnnotationRenderer
                     // and update the label's width to reflect the scaling.
                     fmWidth = charWidth;
                   }
-                }
-              }
-              else
-              {
-                fmWidth = fm
-                        .charWidth(row_annotations[column].displayCharacter
-                                .charAt(0));
+                // }
               }
+              // TODO is it ok to use width of / show all characters here?
+              // else
+              // {
+              // fmWidth = fm.charWidth(displayChar.charAt(0));
+              // }
               charOffset = (int) ((charWidth - fmWidth) / 2f);
 
               if (row_annotations[column].colour == null)
@@ -632,17 +659,17 @@ public class AnnotationRenderer
 
               if (column == 0 || row.graph > 0)
               {
-                g.drawString(row_annotations[column].displayCharacter,
-                        (x * charWidth) + charOffset, y + iconOffset);
+                g.drawString(displayChar, (x * charWidth) + charOffset, y
+                        + iconOffset);
               }
               else if (row_annotations[column - 1] == null
                       || (labelAllCols
-                              || !row_annotations[column].displayCharacter
-                                      .equals(row_annotations[column - 1].displayCharacter) || (row_annotations[column].displayCharacter
+                              || !displayChar
+                                      .equals(row_annotations[column - 1].displayCharacter) || (displayChar
                               .length() < 2 && row_annotations[column].secondaryStructure == ' ')))
               {
-                g.drawString(row_annotations[column].displayCharacter, x
-                        * charWidth + charOffset, y + iconOffset);
+                g.drawString(displayChar, x * charWidth + charOffset, y
+                        + iconOffset);
               }
               g.setFont(ofont);
             }
@@ -655,7 +682,7 @@ public class AnnotationRenderer
             if (ss == '(')
             {
               // distinguish between forward/backward base-pairing
-              if (row_annotations[column].displayCharacter.indexOf(')') > -1)
+              if (displayChar.indexOf(')') > -1)
               {
 
                 ss = ')';
@@ -664,7 +691,7 @@ public class AnnotationRenderer
             }
             if (ss == '[')
             {
-              if ((row_annotations[column].displayCharacter.indexOf(']') > -1))
+              if ((displayChar.indexOf(']') > -1))
               {
                 ss = ']';
 
@@ -673,7 +700,7 @@ public class AnnotationRenderer
             if (ss == '{')
             {
               // distinguish between forward/backward base-pairing
-              if (row_annotations[column].displayCharacter.indexOf('}') > -1)
+              if (displayChar.indexOf('}') > -1)
               {
                 ss = '}';
 
@@ -682,7 +709,7 @@ public class AnnotationRenderer
             if (ss == '<')
             {
               // distinguish between forward/backward base-pairing
-              if (row_annotations[column].displayCharacter.indexOf('<') > -1)
+              if (displayChar.indexOf('<') > -1)
               {
                 ss = '>';
 
@@ -691,7 +718,7 @@ public class AnnotationRenderer
             if (ss >= 65)
             {
               // distinguish between forward/backward base-pairing
-              if (row_annotations[column].displayCharacter.indexOf(ss + 32) > -1)
+              if (displayChar.indexOf(ss + 32) > -1)
               {
 
                 ss = (char) (ss + 32);
@@ -1303,10 +1330,16 @@ public class AnnotationRenderer
       if (renderProfile)
       {
 
+        /*
+         * {profile type, #values, total count, char1, pct1, char2, pct2...}
+         */
         int profl[] = getProfileFor(_aa, column);
+
         // just try to draw the logo if profl is not null
-        if (profl != null && profl[1] != 0)
+        if (profl != null && profl[2] != 0)
         {
+          boolean isStructureProfile = profl[0] == AlignmentAnnotation.STRUCTURE_PROFILE;
+          boolean isCdnaProfile = profl[0] == AlignmentAnnotation.CDNA_PROFILE;
           float ht = normaliseProfile ? y - _aa.graphHeight : y1;
           double htn = normaliseProfile ? _aa.graphHeight : (y2 - y1);// aa.graphHeight;
           double hght;
@@ -1315,55 +1348,80 @@ public class AnnotationRenderer
           char[] dc;
 
           /**
-           * profl.length == 74 indicates that the profile of a secondary
-           * structure conservation row was accesed. Therefore dc gets length 2,
-           * to have space for a basepair instead of just a single nucleotide
+           * Render a single base for a sequence profile, a base pair for
+           * structure profile, and a triplet for a cdna profile
            */
-          if (profl.length == 74)
-          {
-            dc = new char[2];
-          }
-          else
-          {
-            dc = new char[1];
-          }
+          dc = new char[isStructureProfile ? 2 : (isCdnaProfile ? 3 : 1)];
+
           LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
-          double scale = 1f / (normaliseProfile ? profl[1] : 100f);
+          double scale = 1f / (normaliseProfile ? profl[2] : 100f);
           float ofontHeight = 1f / lm.getAscent();// magnify to fill box
           double scl = 0.0;
-          for (int c = 2; c < profl[0];)
-          {
-            dc[0] = (char) profl[c++];
 
-            if (_aa.label.startsWith("StrucConsensus"))
+          /*
+           * Traverse the character(s)/percentage data in the array
+           */
+          int c = 3;
+          int valuesProcessed = 0;
+          // profl[1] is the number of values in the profile
+          while (valuesProcessed < profl[1])
+          {
+            if (isStructureProfile)
             {
+              // todo can we encode a structure pair as an int, like codons?
+              dc[0] = (char) profl[c++];
               dc[1] = (char) profl[c++];
             }
+            else if (isCdnaProfile)
+            {
+              dc = CodingUtils.decodeCodon(profl[c++]);
+            }
+            else
+            {
+              dc[0] = (char) profl[c++];
+            }
 
             wdth = charWidth;
             wdth /= fm.charsWidth(dc, 0, dc.length);
 
             ht += scl;
+            // next profl[] position is profile % for the character(s)
+            scl = htn * scale * profl[c++];
+            lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics()
+                    .getFontRenderContext());
+            g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
+                    wdth, scl / lm.getAscent())));
+            lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
+
+            // Debug - render boxes around characters
+            // g.setColor(Color.red);
+            // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
+            // (int)(scl));
+            // g.setColor(profcolour.findColour(dc[0]).darker());
+
+            /*
+             * Set character colour as per alignment colour scheme; use the
+             * codon translation if a cDNA profile
+             */
+            Color colour = null;
+            if (isCdnaProfile)
             {
-              scl = htn * scale * profl[c++];
-              lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics()
-                      .getFontRenderContext());
-              g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
-                      wdth, scl / lm.getAscent())));
-              lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
-
-              // Debug - render boxes around characters
-              // g.setColor(Color.red);
-              // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
-              // (int)(scl));
-              // g.setColor(profcolour.findColour(dc[0]).darker());
-              g.setColor(profcolour.findColour(dc[0], column, null));
-
-              hght = (ht + (scl - lm.getDescent() - lm.getBaselineOffsets()[lm
-                      .getBaselineIndex()]));
-
-              g.drawChars(dc, 0, dc.length, x * charWidth, (int) hght);
+              final String codonTranslation = ResidueProperties
+                      .codonTranslate(new String(dc));
+              colour = profcolour.findColour(codonTranslation.charAt(0),
+                      column, null);
             }
+            else
+            {
+              colour = profcolour.findColour(dc[0], column, null);
+            }
+            g.setColor(colour == Color.white ? Color.lightGray : colour);
+
+            hght = (ht + (scl - lm.getDescent() - lm.getBaselineOffsets()[lm
+                    .getBaselineIndex()]));
+
+            g.drawChars(dc, 0, dc.length, x * charWidth, (int) hght);
+            valuesProcessed++;
           }
           g.setFont(ofont);
         }
index af83df1..b86afbc 100644 (file)
@@ -24,6 +24,14 @@ import java.awt.FontMetrics;
 import java.awt.Image;
 import java.awt.image.ImageObserver;
 
+/**
+ * semi-insulated interface for rendering a faded image whilst a calculation is
+ * in progress on annotation panel. Will need to remove java.awt dependencies
+ * for Android/etc
+ * 
+ * @author jprocter
+ *
+ */
 public interface AwtRenderPanelI extends ImageObserver
 {
   /**
index 5e6ac29..648f776 100644 (file)
@@ -20,28 +20,6 @@ public class FeatureRenderer extends
 
   boolean offscreenRender = false;
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param g
-   *          DOCUMENT ME!
-   * @param seq
-   *          DOCUMENT ME!
-   * @param sg
-   *          DOCUMENT ME!
-   * @param start
-   *          DOCUMENT ME!
-   * @param end
-   *          DOCUMENT ME!
-   * @param x1
-   *          DOCUMENT ME!
-   * @param y1
-   *          DOCUMENT ME!
-   * @param width
-   *          DOCUMENT ME!
-   * @param height
-   *          DOCUMENT ME!
-   */
   protected SequenceI lastSeq;
 
   char s;
@@ -99,7 +77,6 @@ public class FeatureRenderer extends
         charOffset = (av_charWidth - fm.charWidth(s)) / 2;
         g.drawString(String.valueOf(s), charOffset
                 + (av_charWidth * (i - start)), pady);
-
       }
     }
   }
@@ -164,7 +141,6 @@ public class FeatureRenderer extends
         charOffset = (av_charWidth - fm.charWidth(s)) / 2;
         g.drawString(String.valueOf(s), charOffset
                 + (av_charWidth * (i - start)), pady);
-
       }
     }
   }
@@ -188,30 +164,29 @@ public class FeatureRenderer extends
       return initialCol;
     }
 
-    final SequenceI aseq = (seq.getDatasetSequence() != null) ? seq
-            .getDatasetSequence() : seq;
+    SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
     if (seq != lastSeq)
     {
       lastSeq = seq;
-      sequenceFeatures = aseq.getSequenceFeatures();
-      if (sequenceFeatures != null)
+      lastSequenceFeatures = sequenceFeatures;
+      if (lastSequenceFeatures != null)
       {
-        sfSize = sequenceFeatures.length;
+        sfSize = lastSequenceFeatures.length;
       }
     }
     else
     {
-      if (sequenceFeatures != aseq.getSequenceFeatures())
+      if (lastSequenceFeatures != sequenceFeatures)
       {
-        sequenceFeatures = aseq.getSequenceFeatures();
-        if (sequenceFeatures != null)
+        lastSequenceFeatures = sequenceFeatures;
+        if (lastSequenceFeatures != null)
         {
-          sfSize = sequenceFeatures.length;
+          sfSize = lastSequenceFeatures.length;
         }
       }
     }
 
-    if (sequenceFeatures == null || sfSize == 0)
+    if (lastSequenceFeatures == null || sfSize == 0)
     {
       return initialCol;
     }
@@ -255,7 +230,7 @@ public class FeatureRenderer extends
 
   }
 
-  private volatile SequenceFeature[] sequenceFeatures;
+  private volatile SequenceFeature[] lastSequenceFeatures;
 
   int sfSize;
 
@@ -268,10 +243,8 @@ public class FeatureRenderer extends
   public synchronized void drawSequence(Graphics g, final SequenceI seq,
           int start, int end, int y1)
   {
-    final SequenceI aseq = (seq.getDatasetSequence() != null) ? seq
-            .getDatasetSequence() : seq;
-    if (aseq.getSequenceFeatures() == null
-            || aseq.getSequenceFeatures().length == 0)
+    SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
+    if (sequenceFeatures == null || sequenceFeatures.length == 0)
     {
       return;
     }
@@ -284,10 +257,10 @@ public class FeatureRenderer extends
     updateFeatures();
 
     if (lastSeq == null || seq != lastSeq
-            || aseq.getSequenceFeatures() != sequenceFeatures)
+            || sequenceFeatures != lastSequenceFeatures)
     {
       lastSeq = seq;
-      sequenceFeatures = aseq.getSequenceFeatures();
+      lastSequenceFeatures = sequenceFeatures;
     }
 
     if (transparency != 1 && g != null)
@@ -303,7 +276,7 @@ public class FeatureRenderer extends
       epos = lastSeq.findPosition(end);
     }
 
-    sfSize = sequenceFeatures.length;
+    sfSize = lastSequenceFeatures.length;
     String type;
     for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
     {
@@ -318,25 +291,24 @@ public class FeatureRenderer extends
       // current feature to render
       for (sfindex = 0; sfindex < sfSize; sfindex++)
       {
-        if (!sequenceFeatures[sfindex].type.equals(type))
+        final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
+        if (!sequenceFeature.type.equals(type))
         {
           continue;
         }
 
         if (featureGroups != null
-                && sequenceFeatures[sfindex].featureGroup != null
-                && sequenceFeatures[sfindex].featureGroup.length() != 0
-                && featureGroups
-                        .containsKey(sequenceFeatures[sfindex].featureGroup)
-                && !featureGroups
-                        .get(sequenceFeatures[sfindex].featureGroup)
+                && sequenceFeature.featureGroup != null
+                && sequenceFeature.featureGroup.length() != 0
+                && featureGroups.containsKey(sequenceFeature.featureGroup)
+                && !featureGroups.get(sequenceFeature.featureGroup)
                         .booleanValue())
         {
           continue;
         }
 
         if (!offscreenRender
-                && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
+                && (sequenceFeature.getBegin() > epos || sequenceFeature
                         .getEnd() < spos))
         {
           continue;
@@ -344,56 +316,51 @@ public class FeatureRenderer extends
 
         if (offscreenRender && offscreenImage == null)
         {
-          if (sequenceFeatures[sfindex].begin <= start
-                  && sequenceFeatures[sfindex].end >= start)
+          if (sequenceFeature.begin <= start
+                  && sequenceFeature.end >= start)
           {
             // 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(
-                    getColour(sequenceFeatures[sfindex]).getRGB());
+            currentColour = new Integer(getColour(sequenceFeature).getRGB());
             // used to be retreived from av.featuresDisplayed
             // currentColour = av.featuresDisplayed
             // .get(sequenceFeatures[sfindex].type);
 
           }
         }
-        else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
+        else if (sequenceFeature.type.equals("disulfide bond"))
         {
-
-          renderFeature(g, seq,
-                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                  getColour(sequenceFeatures[sfindex])
+          renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
+                  seq.findIndex(sequenceFeature.begin) - 1,
+                  getColour(sequenceFeature)
                   // new Color(((Integer) av.featuresDisplayed
                   // .get(sequenceFeatures[sfindex].type)).intValue())
                   , start, end, y1);
-          renderFeature(g, seq,
-                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                  getColour(sequenceFeatures[sfindex])
+          renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
+                  seq.findIndex(sequenceFeature.end) - 1,
+                  getColour(sequenceFeature)
                   // new Color(((Integer) av.featuresDisplayed
                   // .get(sequenceFeatures[sfindex].type)).intValue())
                   , start, end, y1);
 
         }
-        else if (showFeature(sequenceFeatures[sfindex]))
+        else if (showFeature(sequenceFeature))
         {
           if (av_isShowSeqFeatureHeight
-                  && sequenceFeatures[sfindex].score != Float.NaN)
+                  && sequenceFeature.score != Float.NaN)
           {
             renderScoreFeature(g, seq,
-                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                    getColour(sequenceFeatures[sfindex]), start, end, y1,
-                    normaliseScore(sequenceFeatures[sfindex]));
+                    seq.findIndex(sequenceFeature.begin) - 1,
+                    seq.findIndex(sequenceFeature.end) - 1,
+                    getColour(sequenceFeature), start, end, y1,
+                    normaliseScore(sequenceFeature));
           }
           else
           {
-            renderFeature(g, seq,
-                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                    getColour(sequenceFeatures[sfindex]), start, end, y1);
+            renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
+                    seq.findIndex(sequenceFeature.end) - 1,
+                    getColour(sequenceFeature), start, end, y1);
           }
         }
 
index 022dca7..7730e5f 100644 (file)
  */
 package jalview.schemabinding.version2;
 
+  //---------------------------------/
+ //- Imported classes and packages -/
 //---------------------------------/
-//- Imported classes and packages -/
-//---------------------------------/
-
-import jalview.util.MessageManager;
 
 import org.exolab.castor.xml.Marshaller;
 import org.exolab.castor.xml.Unmarshaller;
@@ -34,2701 +32,2670 @@ import org.exolab.castor.xml.Unmarshaller;
  * 
  * @version $Revision$ $Date$
  */
-public class Viewport implements java.io.Serializable
-{
-
-  // --------------------------/
-  // - Class/Member Variables -/
-  // --------------------------/
-
-  /**
-   * Field _conservationSelected.
-   */
-  private boolean _conservationSelected;
-
-  /**
-   * keeps track of state for field: _conservationSelected
-   */
-  private boolean _has_conservationSelected;
-
-  /**
-   * Field _pidSelected.
-   */
-  private boolean _pidSelected;
-
-  /**
-   * keeps track of state for field: _pidSelected
-   */
-  private boolean _has_pidSelected;
-
-  /**
-   * Field _bgColour.
-   */
-  private java.lang.String _bgColour;
-
-  /**
-   * Field _consThreshold.
-   */
-  private int _consThreshold;
-
-  /**
-   * keeps track of state for field: _consThreshold
-   */
-  private boolean _has_consThreshold;
-
-  /**
-   * Field _pidThreshold.
-   */
-  private int _pidThreshold;
-
-  /**
-   * keeps track of state for field: _pidThreshold
-   */
-  private boolean _has_pidThreshold;
-
-  /**
-   * Field _title.
-   */
-  private java.lang.String _title;
-
-  /**
-   * Field _showFullId.
-   */
-  private boolean _showFullId;
-
-  /**
-   * keeps track of state for field: _showFullId
-   */
-  private boolean _has_showFullId;
-
-  /**
-   * Field _rightAlignIds.
-   */
-  private boolean _rightAlignIds;
-
-  /**
-   * keeps track of state for field: _rightAlignIds
-   */
-  private boolean _has_rightAlignIds;
-
-  /**
-   * Field _showText.
-   */
-  private boolean _showText;
-
-  /**
-   * keeps track of state for field: _showText
-   */
-  private boolean _has_showText;
-
-  /**
-   * Field _showColourText.
-   */
-  private boolean _showColourText;
-
-  /**
-   * keeps track of state for field: _showColourText
-   */
-  private boolean _has_showColourText;
-
-  /**
-   * Field _showUnconserved.
-   */
-  private boolean _showUnconserved = false;
-
-  /**
-   * keeps track of state for field: _showUnconserved
-   */
-  private boolean _has_showUnconserved;
-
-  /**
-   * Field _showBoxes.
-   */
-  private boolean _showBoxes;
-
-  /**
-   * keeps track of state for field: _showBoxes
-   */
-  private boolean _has_showBoxes;
-
-  /**
-   * Field _wrapAlignment.
-   */
-  private boolean _wrapAlignment;
-
-  /**
-   * keeps track of state for field: _wrapAlignment
-   */
-  private boolean _has_wrapAlignment;
-
-  /**
-   * Field _renderGaps.
-   */
-  private boolean _renderGaps;
-
-  /**
-   * keeps track of state for field: _renderGaps
-   */
-  private boolean _has_renderGaps;
-
-  /**
-   * Field _showSequenceFeatures.
-   */
-  private boolean _showSequenceFeatures;
-
-  /**
-   * keeps track of state for field: _showSequenceFeatures
-   */
-  private boolean _has_showSequenceFeatures;
-
-  /**
-   * Field _showNPfeatureTooltip.
-   */
-  private boolean _showNPfeatureTooltip;
-
-  /**
-   * keeps track of state for field: _showNPfeatureTooltip
-   */
-  private boolean _has_showNPfeatureTooltip;
-
-  /**
-   * Field _showDbRefTooltip.
-   */
-  private boolean _showDbRefTooltip;
-
-  /**
-   * keeps track of state for field: _showDbRefTooltip
-   */
-  private boolean _has_showDbRefTooltip;
-
-  /**
-   * Field _followHighlight.
-   */
-  private boolean _followHighlight = true;
-
-  /**
-   * keeps track of state for field: _followHighlight
-   */
-  private boolean _has_followHighlight;
-
-  /**
-   * Field _followSelection.
-   */
-  private boolean _followSelection = true;
-
-  /**
-   * keeps track of state for field: _followSelection
-   */
-  private boolean _has_followSelection;
-
-  /**
-   * Field _showAnnotation.
-   */
-  private boolean _showAnnotation;
-
-  /**
-   * keeps track of state for field: _showAnnotation
-   */
-  private boolean _has_showAnnotation;
-
-  /**
-   * Field _centreColumnLabels.
-   */
-  private boolean _centreColumnLabels = false;
-
-  /**
-   * keeps track of state for field: _centreColumnLabels
-   */
-  private boolean _has_centreColumnLabels;
-
-  /**
-   * Field _showGroupConservation.
-   */
-  private boolean _showGroupConservation = false;
-
-  /**
-   * keeps track of state for field: _showGroupConservation
-   */
-  private boolean _has_showGroupConservation;
-
-  /**
-   * Field _showGroupConsensus.
-   */
-  private boolean _showGroupConsensus = false;
-
-  /**
-   * keeps track of state for field: _showGroupConsensus
-   */
-  private boolean _has_showGroupConsensus;
-
-  /**
-   * Field _showConsensusHistogram.
-   */
-  private boolean _showConsensusHistogram = true;
-
-  /**
-   * keeps track of state for field: _showConsensusHistogram
-   */
-  private boolean _has_showConsensusHistogram;
-
-  /**
-   * Field _showSequenceLogo.
-   */
-  private boolean _showSequenceLogo = false;
-
-  /**
-   * keeps track of state for field: _showSequenceLogo
-   */
-  private boolean _has_showSequenceLogo;
-
-  /**
-   * Field _normaliseSequenceLogo.
-   */
-  private boolean _normaliseSequenceLogo = false;
-
-  /**
-   * keeps track of state for field: _normaliseSequenceLogo
-   */
-  private boolean _has_normaliseSequenceLogo;
-
-  /**
-   * Field _ignoreGapsinConsensus.
-   */
-  private boolean _ignoreGapsinConsensus = true;
-
-  /**
-   * keeps track of state for field: _ignoreGapsinConsensus
-   */
-  private boolean _has_ignoreGapsinConsensus;
-
-  /**
-   * Field _startRes.
-   */
-  private int _startRes;
-
-  /**
-   * keeps track of state for field: _startRes
-   */
-  private boolean _has_startRes;
-
-  /**
-   * Field _startSeq.
-   */
-  private int _startSeq;
-
-  /**
-   * keeps track of state for field: _startSeq
-   */
-  private boolean _has_startSeq;
-
-  /**
-   * Field _fontName.
-   */
-  private java.lang.String _fontName;
-
-  /**
-   * Field _fontSize.
-   */
-  private int _fontSize;
-
-  /**
-   * keeps track of state for field: _fontSize
-   */
-  private boolean _has_fontSize;
-
-  /**
-   * Field _fontStyle.
-   */
-  private int _fontStyle;
-
-  /**
-   * keeps track of state for field: _fontStyle
-   */
-  private boolean _has_fontStyle;
-
-  /**
-   * Field _viewName.
-   */
-  private java.lang.String _viewName;
-
-  /**
-   * Field _sequenceSetId.
-   */
-  private java.lang.String _sequenceSetId;
-
-  /**
-   * Field _gatheredViews.
-   */
-  private boolean _gatheredViews;
-
-  /**
-   * keeps track of state for field: _gatheredViews
-   */
-  private boolean _has_gatheredViews;
-
-  /**
-   * Field _textCol1.
-   */
-  private int _textCol1;
-
-  /**
-   * keeps track of state for field: _textCol1
-   */
-  private boolean _has_textCol1;
-
-  /**
-   * Field _textCol2.
-   */
-  private int _textCol2;
-
-  /**
-   * keeps track of state for field: _textCol2
-   */
-  private boolean _has_textCol2;
-
-  /**
-   * Field _textColThreshold.
-   */
-  private int _textColThreshold;
-
-  /**
-   * keeps track of state for field: _textColThreshold
-   */
-  private boolean _has_textColThreshold;
-
-  /**
-   * unique id used by jalview to synchronize between stored and instantiated
-   * views
-   * 
-   */
-  private java.lang.String _id;
-
-  /**
-   * Field _width.
-   */
-  private int _width;
-
-  /**
-   * keeps track of state for field: _width
-   */
-  private boolean _has_width;
-
-  /**
-   * Field _height.
-   */
-  private int _height;
-
-  /**
-   * keeps track of state for field: _height
-   */
-  private boolean _has_height;
-
-  /**
-   * Field _xpos.
-   */
-  private int _xpos;
-
-  /**
-   * keeps track of state for field: _xpos
-   */
-  private boolean _has_xpos;
-
-  /**
-   * Field _ypos.
-   */
-  private int _ypos;
-
-  /**
-   * keeps track of state for field: _ypos
-   */
-  private boolean _has_ypos;
-
-  /**
-   * Field _annotationColours.
-   */
-  private jalview.schemabinding.version2.AnnotationColours _annotationColours;
-
-  /**
-   * Field _hiddenColumnsList.
-   */
-  private java.util.Vector _hiddenColumnsList;
-
-  /**
-   * Field _calcIdParamList.
-   */
-  private java.util.Vector _calcIdParamList;
-
-  // ----------------/
-  // - Constructors -/
-  // ----------------/
-
-  public Viewport()
-  {
-    super();
-    this._hiddenColumnsList = new java.util.Vector();
-    this._calcIdParamList = new java.util.Vector();
-  }
-
-  // -----------/
-  // - Methods -/
-  // -----------/
-
-  /**
-   * 
-   * 
-   * @param vCalcIdParam
-   * @throws java.lang.IndexOutOfBoundsException
-   *           if the index given is outside the bounds of the collection
-   */
-  public void addCalcIdParam(
-          final jalview.schemabinding.version2.CalcIdParam vCalcIdParam)
-          throws java.lang.IndexOutOfBoundsException
-  {
-    this._calcIdParamList.addElement(vCalcIdParam);
-  }
-
-  /**
-   * 
-   * 
-   * @param index
-   * @param vCalcIdParam
-   * @throws java.lang.IndexOutOfBoundsException
-   *           if the index given is outside the bounds of the collection
-   */
-  public void addCalcIdParam(final int index,
-          final jalview.schemabinding.version2.CalcIdParam vCalcIdParam)
-          throws java.lang.IndexOutOfBoundsException
-  {
-    this._calcIdParamList.add(index, vCalcIdParam);
-  }
-
-  /**
-   * 
-   * 
-   * @param vHiddenColumns
-   * @throws java.lang.IndexOutOfBoundsException
-   *           if the index given is outside the bounds of the collection
-   */
-  public void addHiddenColumns(
-          final jalview.schemabinding.version2.HiddenColumns vHiddenColumns)
-          throws java.lang.IndexOutOfBoundsException
-  {
-    this._hiddenColumnsList.addElement(vHiddenColumns);
-  }
-
-  /**
-   * 
-   * 
-   * @param index
-   * @param vHiddenColumns
-   * @throws java.lang.IndexOutOfBoundsException
-   *           if the index given is outside the bounds of the collection
-   */
-  public void addHiddenColumns(final int index,
-          final jalview.schemabinding.version2.HiddenColumns vHiddenColumns)
-          throws java.lang.IndexOutOfBoundsException
-  {
-    this._hiddenColumnsList.add(index, vHiddenColumns);
-  }
-
-  /**
-     */
-  public void deleteCentreColumnLabels()
-  {
-    this._has_centreColumnLabels = false;
-  }
-
-  /**
-     */
-  public void deleteConsThreshold()
-  {
-    this._has_consThreshold = false;
-  }
-
-  /**
-     */
-  public void deleteConservationSelected()
-  {
-    this._has_conservationSelected = false;
-  }
-
-  /**
-     */
-  public void deleteFollowHighlight()
-  {
-    this._has_followHighlight = false;
-  }
-
-  /**
-     */
-  public void deleteFollowSelection()
-  {
-    this._has_followSelection = false;
-  }
-
-  /**
-     */
-  public void deleteFontSize()
-  {
-    this._has_fontSize = false;
-  }
-
-  /**
-     */
-  public void deleteFontStyle()
-  {
-    this._has_fontStyle = false;
-  }
-
-  /**
-     */
-  public void deleteGatheredViews()
-  {
-    this._has_gatheredViews = false;
-  }
-
-  /**
-     */
-  public void deleteHeight()
-  {
-    this._has_height = false;
-  }
+public class Viewport implements java.io.Serializable {
+
+
+      //--------------------------/
+     //- Class/Member Variables -/
+    //--------------------------/
+
+    /**
+     * Field _conservationSelected.
+     */
+    private boolean _conservationSelected;
+
+    /**
+     * keeps track of state for field: _conservationSelected
+     */
+    private boolean _has_conservationSelected;
+
+    /**
+     * Field _pidSelected.
+     */
+    private boolean _pidSelected;
+
+    /**
+     * keeps track of state for field: _pidSelected
+     */
+    private boolean _has_pidSelected;
+
+    /**
+     * Field _bgColour.
+     */
+    private java.lang.String _bgColour;
+
+    /**
+     * Field _consThreshold.
+     */
+    private int _consThreshold;
+
+    /**
+     * keeps track of state for field: _consThreshold
+     */
+    private boolean _has_consThreshold;
+
+    /**
+     * Field _pidThreshold.
+     */
+    private int _pidThreshold;
+
+    /**
+     * keeps track of state for field: _pidThreshold
+     */
+    private boolean _has_pidThreshold;
+
+    /**
+     * Field _title.
+     */
+    private java.lang.String _title;
+
+    /**
+     * Field _showFullId.
+     */
+    private boolean _showFullId;
+
+    /**
+     * keeps track of state for field: _showFullId
+     */
+    private boolean _has_showFullId;
+
+    /**
+     * Field _rightAlignIds.
+     */
+    private boolean _rightAlignIds;
+
+    /**
+     * keeps track of state for field: _rightAlignIds
+     */
+    private boolean _has_rightAlignIds;
+
+    /**
+     * Field _showText.
+     */
+    private boolean _showText;
+
+    /**
+     * keeps track of state for field: _showText
+     */
+    private boolean _has_showText;
+
+    /**
+     * Field _showColourText.
+     */
+    private boolean _showColourText;
+
+    /**
+     * keeps track of state for field: _showColourText
+     */
+    private boolean _has_showColourText;
+
+    /**
+     * Field _showUnconserved.
+     */
+    private boolean _showUnconserved = false;
+
+    /**
+     * keeps track of state for field: _showUnconserved
+     */
+    private boolean _has_showUnconserved;
+
+    /**
+     * Field _showBoxes.
+     */
+    private boolean _showBoxes;
+
+    /**
+     * keeps track of state for field: _showBoxes
+     */
+    private boolean _has_showBoxes;
+
+    /**
+     * Field _wrapAlignment.
+     */
+    private boolean _wrapAlignment;
+
+    /**
+     * keeps track of state for field: _wrapAlignment
+     */
+    private boolean _has_wrapAlignment;
+
+    /**
+     * Field _renderGaps.
+     */
+    private boolean _renderGaps;
+
+    /**
+     * keeps track of state for field: _renderGaps
+     */
+    private boolean _has_renderGaps;
+
+    /**
+     * Field _showSequenceFeatures.
+     */
+    private boolean _showSequenceFeatures;
+
+    /**
+     * keeps track of state for field: _showSequenceFeatures
+     */
+    private boolean _has_showSequenceFeatures;
+
+    /**
+     * Field _showNPfeatureTooltip.
+     */
+    private boolean _showNPfeatureTooltip;
+
+    /**
+     * keeps track of state for field: _showNPfeatureTooltip
+     */
+    private boolean _has_showNPfeatureTooltip;
+
+    /**
+     * Field _showDbRefTooltip.
+     */
+    private boolean _showDbRefTooltip;
+
+    /**
+     * keeps track of state for field: _showDbRefTooltip
+     */
+    private boolean _has_showDbRefTooltip;
+
+    /**
+     * Field _followHighlight.
+     */
+    private boolean _followHighlight = true;
+
+    /**
+     * keeps track of state for field: _followHighlight
+     */
+    private boolean _has_followHighlight;
+
+    /**
+     * Field _followSelection.
+     */
+    private boolean _followSelection = true;
+
+    /**
+     * keeps track of state for field: _followSelection
+     */
+    private boolean _has_followSelection;
+
+    /**
+     * Field _showAnnotation.
+     */
+    private boolean _showAnnotation;
+
+    /**
+     * keeps track of state for field: _showAnnotation
+     */
+    private boolean _has_showAnnotation;
+
+    /**
+     * Field _centreColumnLabels.
+     */
+    private boolean _centreColumnLabels = false;
+
+    /**
+     * keeps track of state for field: _centreColumnLabels
+     */
+    private boolean _has_centreColumnLabels;
+
+    /**
+     * Field _showGroupConservation.
+     */
+    private boolean _showGroupConservation = false;
+
+    /**
+     * keeps track of state for field: _showGroupConservation
+     */
+    private boolean _has_showGroupConservation;
+
+    /**
+     * Field _showGroupConsensus.
+     */
+    private boolean _showGroupConsensus = false;
+
+    /**
+     * keeps track of state for field: _showGroupConsensus
+     */
+    private boolean _has_showGroupConsensus;
+
+    /**
+     * Field _showConsensusHistogram.
+     */
+    private boolean _showConsensusHistogram = true;
+
+    /**
+     * keeps track of state for field: _showConsensusHistogram
+     */
+    private boolean _has_showConsensusHistogram;
+
+    /**
+     * Field _showSequenceLogo.
+     */
+    private boolean _showSequenceLogo = false;
+
+    /**
+     * keeps track of state for field: _showSequenceLogo
+     */
+    private boolean _has_showSequenceLogo;
+
+    /**
+     * Field _normaliseSequenceLogo.
+     */
+    private boolean _normaliseSequenceLogo = false;
+
+    /**
+     * keeps track of state for field: _normaliseSequenceLogo
+     */
+    private boolean _has_normaliseSequenceLogo;
+
+    /**
+     * Field _ignoreGapsinConsensus.
+     */
+    private boolean _ignoreGapsinConsensus = true;
+
+    /**
+     * keeps track of state for field: _ignoreGapsinConsensus
+     */
+    private boolean _has_ignoreGapsinConsensus;
+
+    /**
+     * Field _startRes.
+     */
+    private int _startRes;
+
+    /**
+     * keeps track of state for field: _startRes
+     */
+    private boolean _has_startRes;
+
+    /**
+     * Field _startSeq.
+     */
+    private int _startSeq;
+
+    /**
+     * keeps track of state for field: _startSeq
+     */
+    private boolean _has_startSeq;
+
+    /**
+     * Field _fontName.
+     */
+    private java.lang.String _fontName;
+
+    /**
+     * Field _fontSize.
+     */
+    private int _fontSize;
+
+    /**
+     * keeps track of state for field: _fontSize
+     */
+    private boolean _has_fontSize;
+
+    /**
+     * Field _fontStyle.
+     */
+    private int _fontStyle;
+
+    /**
+     * keeps track of state for field: _fontStyle
+     */
+    private boolean _has_fontStyle;
+
+    /**
+     * Field _viewName.
+     */
+    private java.lang.String _viewName;
 
-  /**
+    /**
+     * Field _sequenceSetId.
      */
-  public void deleteIgnoreGapsinConsensus()
-  {
-    this._has_ignoreGapsinConsensus = false;
-  }
+    private java.lang.String _sequenceSetId;
 
-  /**
+    /**
+     * Field _gatheredViews.
      */
-  public void deleteNormaliseSequenceLogo()
-  {
-    this._has_normaliseSequenceLogo = false;
-  }
+    private boolean _gatheredViews;
 
-  /**
+    /**
+     * keeps track of state for field: _gatheredViews
      */
-  public void deletePidSelected()
-  {
-    this._has_pidSelected = false;
-  }
+    private boolean _has_gatheredViews;
 
-  /**
+    /**
+     * Field _textCol1.
      */
-  public void deletePidThreshold()
-  {
-    this._has_pidThreshold = false;
-  }
+    private int _textCol1;
 
-  /**
+    /**
+     * keeps track of state for field: _textCol1
      */
-  public void deleteRenderGaps()
-  {
-    this._has_renderGaps = false;
-  }
+    private boolean _has_textCol1;
 
-  /**
+    /**
+     * Field _textCol2.
      */
-  public void deleteRightAlignIds()
-  {
-    this._has_rightAlignIds = false;
-  }
+    private int _textCol2;
 
-  /**
+    /**
+     * keeps track of state for field: _textCol2
      */
-  public void deleteShowAnnotation()
-  {
-    this._has_showAnnotation = false;
-  }
+    private boolean _has_textCol2;
 
-  /**
+    /**
+     * Field _textColThreshold.
      */
-  public void deleteShowBoxes()
-  {
-    this._has_showBoxes = false;
-  }
+    private int _textColThreshold;
 
-  /**
+    /**
+     * keeps track of state for field: _textColThreshold
      */
-  public void deleteShowColourText()
-  {
-    this._has_showColourText = false;
-  }
+    private boolean _has_textColThreshold;
 
-  /**
+    /**
+     * unique id used by jalview to
+     *  synchronize between stored and
+     *  instantiated views
+     *  
      */
-  public void deleteShowConsensusHistogram()
-  {
-    this._has_showConsensusHistogram = false;
-  }
+    private java.lang.String _id;
 
-  /**
+    /**
+     * The viewport id of this viewport's (cdna/protein) coding
+     * complement, if any
+     *  
      */
-  public void deleteShowDbRefTooltip()
-  {
-    this._has_showDbRefTooltip = false;
-  }
+    private java.lang.String _complementId;
 
-  /**
+    /**
+     * Field _width.
      */
-  public void deleteShowFullId()
-  {
-    this._has_showFullId = false;
-  }
+    private int _width;
 
-  /**
+    /**
+     * keeps track of state for field: _width
      */
-  public void deleteShowGroupConsensus()
-  {
-    this._has_showGroupConsensus = false;
-  }
+    private boolean _has_width;
 
-  /**
+    /**
+     * Field _height.
      */
-  public void deleteShowGroupConservation()
-  {
-    this._has_showGroupConservation = false;
-  }
+    private int _height;
 
-  /**
+    /**
+     * keeps track of state for field: _height
      */
-  public void deleteShowNPfeatureTooltip()
-  {
-    this._has_showNPfeatureTooltip = false;
-  }
+    private boolean _has_height;
 
-  /**
+    /**
+     * Field _xpos.
      */
-  public void deleteShowSequenceFeatures()
-  {
-    this._has_showSequenceFeatures = false;
-  }
+    private int _xpos;
 
-  /**
+    /**
+     * keeps track of state for field: _xpos
      */
-  public void deleteShowSequenceLogo()
-  {
-    this._has_showSequenceLogo = false;
-  }
+    private boolean _has_xpos;
 
-  /**
+    /**
+     * Field _ypos.
      */
-  public void deleteShowText()
-  {
-    this._has_showText = false;
-  }
+    private int _ypos;
 
-  /**
+    /**
+     * keeps track of state for field: _ypos
      */
-  public void deleteShowUnconserved()
-  {
-    this._has_showUnconserved = false;
-  }
+    private boolean _has_ypos;
 
-  /**
+    /**
+     * Field _annotationColours.
      */
-  public void deleteStartRes()
-  {
-    this._has_startRes = false;
-  }
-
-  /**
-     */
-  public void deleteStartSeq()
-  {
-    this._has_startSeq = false;
-  }
-
-  /**
-     */
-  public void deleteTextCol1()
-  {
-    this._has_textCol1 = false;
-  }
-
-  /**
-     */
-  public void deleteTextCol2()
-  {
-    this._has_textCol2 = false;
-  }
-
-  /**
-     */
-  public void deleteTextColThreshold()
-  {
-    this._has_textColThreshold = false;
-  }
-
-  /**
-     */
-  public void deleteWidth()
-  {
-    this._has_width = false;
-  }
-
-  /**
-     */
-  public void deleteWrapAlignment()
-  {
-    this._has_wrapAlignment = false;
-  }
-
-  /**
-     */
-  public void deleteXpos()
-  {
-    this._has_xpos = false;
-  }
-
-  /**
-     */
-  public void deleteYpos()
-  {
-    this._has_ypos = false;
-  }
-
-  /**
-   * Method enumerateCalcIdParam.
-   * 
-   * @return an Enumeration over all jalview.schemabinding.version2.CalcIdParam
-   *         elements
-   */
-  public java.util.Enumeration enumerateCalcIdParam()
-  {
-    return this._calcIdParamList.elements();
-  }
-
-  /**
-   * Method enumerateHiddenColumns.
-   * 
-   * @return an Enumeration over all
-   *         jalview.schemabinding.version2.HiddenColumns elements
-   */
-  public java.util.Enumeration enumerateHiddenColumns()
-  {
-    return this._hiddenColumnsList.elements();
-  }
-
-  /**
-   * Returns the value of field 'annotationColours'.
-   * 
-   * @return the value of field 'AnnotationColours'.
-   */
-  public jalview.schemabinding.version2.AnnotationColours getAnnotationColours()
-  {
-    return this._annotationColours;
-  }
-
-  /**
-   * Returns the value of field 'bgColour'.
-   * 
-   * @return the value of field 'BgColour'.
-   */
-  public java.lang.String getBgColour()
-  {
-    return this._bgColour;
-  }
-
-  /**
-   * Method getCalcIdParam.
-   * 
-   * @param index
-   * @throws java.lang.IndexOutOfBoundsException
-   *           if the index given is outside the bounds of the collection
-   * @return the value of the jalview.schemabinding.version2.CalcIdParam at the
-   *         given index
-   */
-  public jalview.schemabinding.version2.CalcIdParam getCalcIdParam(
-          final int index) throws java.lang.IndexOutOfBoundsException
-  {
-    // check bounds for index
-    if (index < 0 || index >= this._calcIdParamList.size())
-    {
-        throw new IndexOutOfBoundsException(MessageManager.formatMessage("exception.index_value_not_in_range", new String[]{
-                         "getCalcIdParam",
-                         Integer.valueOf(index).toString(),
-                         Integer.valueOf((this._calcIdParamList.size() - 1)).toString()
-          })); 
-    }
-
-    return (jalview.schemabinding.version2.CalcIdParam) _calcIdParamList
-            .get(index);
-  }
-
-  /**
-   * Method getCalcIdParam.Returns the contents of the collection in an Array.
-   * <p>
-   * Note: Just in case the collection contents are changing in another thread,
-   * we pass a 0-length Array of the correct type into the API call. This way we
-   * <i>know</i> that the Array returned is of exactly the correct length.
-   * 
-   * @return this collection as an Array
-   */
-  public jalview.schemabinding.version2.CalcIdParam[] getCalcIdParam()
-  {
-    jalview.schemabinding.version2.CalcIdParam[] array = new jalview.schemabinding.version2.CalcIdParam[0];
-    return (jalview.schemabinding.version2.CalcIdParam[]) this._calcIdParamList
-            .toArray(array);
-  }
-
-  /**
-   * Method getCalcIdParamCount.
-   * 
-   * @return the size of this collection
-   */
-  public int getCalcIdParamCount()
-  {
-    return this._calcIdParamList.size();
-  }
-
-  /**
-   * Returns the value of field 'centreColumnLabels'.
-   * 
-   * @return the value of field 'CentreColumnLabels'.
-   */
-  public boolean getCentreColumnLabels()
-  {
-    return this._centreColumnLabels;
-  }
-
-  /**
-   * Returns the value of field 'consThreshold'.
-   * 
-   * @return the value of field 'ConsThreshold'.
-   */
-  public int getConsThreshold()
-  {
-    return this._consThreshold;
-  }
-
-  /**
-   * Returns the value of field 'conservationSelected'.
-   * 
-   * @return the value of field 'ConservationSelected'.
-   */
-  public boolean getConservationSelected()
-  {
-    return this._conservationSelected;
-  }
-
-  /**
-   * Returns the value of field 'followHighlight'.
-   * 
-   * @return the value of field 'FollowHighlight'.
-   */
-  public boolean getFollowHighlight()
-  {
-    return this._followHighlight;
-  }
-
-  /**
-   * Returns the value of field 'followSelection'.
-   * 
-   * @return the value of field 'FollowSelection'.
-   */
-  public boolean getFollowSelection()
-  {
-    return this._followSelection;
-  }
-
-  /**
-   * Returns the value of field 'fontName'.
-   * 
-   * @return the value of field 'FontName'.
-   */
-  public java.lang.String getFontName()
-  {
-    return this._fontName;
-  }
-
-  /**
-   * Returns the value of field 'fontSize'.
-   * 
-   * @return the value of field 'FontSize'.
-   */
-  public int getFontSize()
-  {
-    return this._fontSize;
-  }
-
-  /**
-   * Returns the value of field 'fontStyle'.
-   * 
-   * @return the value of field 'FontStyle'.
-   */
-  public int getFontStyle()
-  {
-    return this._fontStyle;
-  }
-
-  /**
-   * Returns the value of field 'gatheredViews'.
-   * 
-   * @return the value of field 'GatheredViews'.
-   */
-  public boolean getGatheredViews()
-  {
-    return this._gatheredViews;
-  }
-
-  /**
-   * Returns the value of field 'height'.
-   * 
-   * @return the value of field 'Height'.
-   */
-  public int getHeight()
-  {
-    return this._height;
-  }
-
-  /**
-   * Method getHiddenColumns.
-   * 
-   * @param index
-   * @throws java.lang.IndexOutOfBoundsException
-   *           if the index given is outside the bounds of the collection
-   * @return the value of the jalview.schemabinding.version2.HiddenColumns at
-   *         the given index
-   */
-  public jalview.schemabinding.version2.HiddenColumns getHiddenColumns(
-          final int index) throws java.lang.IndexOutOfBoundsException
-  {
-    // check bounds for index
-    if (index < 0 || index >= this._hiddenColumnsList.size())
-    {
-        throw new IndexOutOfBoundsException(MessageManager.formatMessage("exception.index_value_not_in_range", new String[]{
-                 "getHiddenColumns",
-                 Integer.valueOf(index).toString(),
-                 Integer.valueOf((this._hiddenColumnsList.size() - 1)).toString()
-        })); 
-    }
-
-    return (jalview.schemabinding.version2.HiddenColumns) _hiddenColumnsList
-            .get(index);
-  }
-
-  /**
-   * Method getHiddenColumns.Returns the contents of the collection in an Array.
-   * <p>
-   * Note: Just in case the collection contents are changing in another thread,
-   * we pass a 0-length Array of the correct type into the API call. This way we
-   * <i>know</i> that the Array returned is of exactly the correct length.
-   * 
-   * @return this collection as an Array
-   */
-  public jalview.schemabinding.version2.HiddenColumns[] getHiddenColumns()
-  {
-    jalview.schemabinding.version2.HiddenColumns[] array = new jalview.schemabinding.version2.HiddenColumns[0];
-    return (jalview.schemabinding.version2.HiddenColumns[]) this._hiddenColumnsList
-            .toArray(array);
-  }
-
-  /**
-   * Method getHiddenColumnsCount.
-   * 
-   * @return the size of this collection
-   */
-  public int getHiddenColumnsCount()
-  {
-    return this._hiddenColumnsList.size();
-  }
-
-  /**
-   * Returns the value of field 'id'. The field 'id' has the following
-   * description: unique id used by jalview to synchronize between stored and
-   * instantiated views
-   * 
-   * 
-   * @return the value of field 'Id'.
-   */
-  public java.lang.String getId()
-  {
-    return this._id;
-  }
-
-  /**
-   * Returns the value of field 'ignoreGapsinConsensus'.
-   * 
-   * @return the value of field 'IgnoreGapsinConsensus'.
-   */
-  public boolean getIgnoreGapsinConsensus()
-  {
-    return this._ignoreGapsinConsensus;
-  }
-
-  /**
-   * Returns the value of field 'normaliseSequenceLogo'.
-   * 
-   * @return the value of field 'NormaliseSequenceLogo'.
-   */
-  public boolean getNormaliseSequenceLogo()
-  {
-    return this._normaliseSequenceLogo;
-  }
-
-  /**
-   * Returns the value of field 'pidSelected'.
-   * 
-   * @return the value of field 'PidSelected'.
-   */
-  public boolean getPidSelected()
-  {
-    return this._pidSelected;
-  }
-
-  /**
-   * Returns the value of field 'pidThreshold'.
-   * 
-   * @return the value of field 'PidThreshold'.
-   */
-  public int getPidThreshold()
-  {
-    return this._pidThreshold;
-  }
-
-  /**
-   * Returns the value of field 'renderGaps'.
-   * 
-   * @return the value of field 'RenderGaps'.
-   */
-  public boolean getRenderGaps()
-  {
-    return this._renderGaps;
-  }
-
-  /**
-   * Returns the value of field 'rightAlignIds'.
-   * 
-   * @return the value of field 'RightAlignIds'.
-   */
-  public boolean getRightAlignIds()
-  {
-    return this._rightAlignIds;
-  }
-
-  /**
-   * Returns the value of field 'sequenceSetId'.
-   * 
-   * @return the value of field 'SequenceSetId'.
-   */
-  public java.lang.String getSequenceSetId()
-  {
-    return this._sequenceSetId;
-  }
-
-  /**
-   * Returns the value of field 'showAnnotation'.
-   * 
-   * @return the value of field 'ShowAnnotation'.
-   */
-  public boolean getShowAnnotation()
-  {
-    return this._showAnnotation;
-  }
-
-  /**
-   * Returns the value of field 'showBoxes'.
-   * 
-   * @return the value of field 'ShowBoxes'.
-   */
-  public boolean getShowBoxes()
-  {
-    return this._showBoxes;
-  }
-
-  /**
-   * Returns the value of field 'showColourText'.
-   * 
-   * @return the value of field 'ShowColourText'.
-   */
-  public boolean getShowColourText()
-  {
-    return this._showColourText;
-  }
-
-  /**
-   * Returns the value of field 'showConsensusHistogram'.
-   * 
-   * @return the value of field 'ShowConsensusHistogram'.
-   */
-  public boolean getShowConsensusHistogram()
-  {
-    return this._showConsensusHistogram;
-  }
-
-  /**
-   * Returns the value of field 'showDbRefTooltip'.
-   * 
-   * @return the value of field 'ShowDbRefTooltip'.
-   */
-  public boolean getShowDbRefTooltip()
-  {
-    return this._showDbRefTooltip;
-  }
-
-  /**
-   * Returns the value of field 'showFullId'.
-   * 
-   * @return the value of field 'ShowFullId'.
-   */
-  public boolean getShowFullId()
-  {
-    return this._showFullId;
-  }
-
-  /**
-   * Returns the value of field 'showGroupConsensus'.
-   * 
-   * @return the value of field 'ShowGroupConsensus'.
-   */
-  public boolean getShowGroupConsensus()
-  {
-    return this._showGroupConsensus;
-  }
-
-  /**
-   * Returns the value of field 'showGroupConservation'.
-   * 
-   * @return the value of field 'ShowGroupConservation'.
-   */
-  public boolean getShowGroupConservation()
-  {
-    return this._showGroupConservation;
-  }
-
-  /**
-   * Returns the value of field 'showNPfeatureTooltip'.
-   * 
-   * @return the value of field 'ShowNPfeatureTooltip'.
-   */
-  public boolean getShowNPfeatureTooltip()
-  {
-    return this._showNPfeatureTooltip;
-  }
-
-  /**
-   * Returns the value of field 'showSequenceFeatures'.
-   * 
-   * @return the value of field 'ShowSequenceFeatures'.
-   */
-  public boolean getShowSequenceFeatures()
-  {
-    return this._showSequenceFeatures;
-  }
-
-  /**
-   * Returns the value of field 'showSequenceLogo'.
-   * 
-   * @return the value of field 'ShowSequenceLogo'.
-   */
-  public boolean getShowSequenceLogo()
-  {
-    return this._showSequenceLogo;
-  }
-
-  /**
-   * Returns the value of field 'showText'.
-   * 
-   * @return the value of field 'ShowText'.
-   */
-  public boolean getShowText()
-  {
-    return this._showText;
-  }
-
-  /**
-   * Returns the value of field 'showUnconserved'.
-   * 
-   * @return the value of field 'ShowUnconserved'.
-   */
-  public boolean getShowUnconserved()
-  {
-    return this._showUnconserved;
-  }
-
-  /**
-   * Returns the value of field 'startRes'.
-   * 
-   * @return the value of field 'StartRes'.
-   */
-  public int getStartRes()
-  {
-    return this._startRes;
-  }
-
-  /**
-   * Returns the value of field 'startSeq'.
-   * 
-   * @return the value of field 'StartSeq'.
-   */
-  public int getStartSeq()
-  {
-    return this._startSeq;
-  }
-
-  /**
-   * Returns the value of field 'textCol1'.
-   * 
-   * @return the value of field 'TextCol1'.
-   */
-  public int getTextCol1()
-  {
-    return this._textCol1;
-  }
-
-  /**
-   * Returns the value of field 'textCol2'.
-   * 
-   * @return the value of field 'TextCol2'.
-   */
-  public int getTextCol2()
-  {
-    return this._textCol2;
-  }
-
-  /**
-   * Returns the value of field 'textColThreshold'.
-   * 
-   * @return the value of field 'TextColThreshold'.
-   */
-  public int getTextColThreshold()
-  {
-    return this._textColThreshold;
-  }
-
-  /**
-   * Returns the value of field 'title'.
-   * 
-   * @return the value of field 'Title'.
-   */
-  public java.lang.String getTitle()
-  {
-    return this._title;
-  }
-
-  /**
-   * Returns the value of field 'viewName'.
-   * 
-   * @return the value of field 'ViewName'.
-   */
-  public java.lang.String getViewName()
-  {
-    return this._viewName;
-  }
-
-  /**
-   * Returns the value of field 'width'.
-   * 
-   * @return the value of field 'Width'.
-   */
-  public int getWidth()
-  {
-    return this._width;
-  }
-
-  /**
-   * Returns the value of field 'wrapAlignment'.
-   * 
-   * @return the value of field 'WrapAlignment'.
-   */
-  public boolean getWrapAlignment()
-  {
-    return this._wrapAlignment;
-  }
-
-  /**
-   * Returns the value of field 'xpos'.
-   * 
-   * @return the value of field 'Xpos'.
-   */
-  public int getXpos()
-  {
-    return this._xpos;
-  }
-
-  /**
-   * Returns the value of field 'ypos'.
-   * 
-   * @return the value of field 'Ypos'.
-   */
-  public int getYpos()
-  {
-    return this._ypos;
-  }
-
-  /**
-   * Method hasCentreColumnLabels.
-   * 
-   * @return true if at least one CentreColumnLabels has been adde
-   */
-  public boolean hasCentreColumnLabels()
-  {
-    return this._has_centreColumnLabels;
-  }
-
-  /**
-   * Method hasConsThreshold.
-   * 
-   * @return true if at least one ConsThreshold has been added
-   */
-  public boolean hasConsThreshold()
-  {
-    return this._has_consThreshold;
-  }
-
-  /**
-   * Method hasConservationSelected.
-   * 
-   * @return true if at least one ConservationSelected has been added
-   */
-  public boolean hasConservationSelected()
-  {
-    return this._has_conservationSelected;
-  }
-
-  /**
-   * Method hasFollowHighlight.
-   * 
-   * @return true if at least one FollowHighlight has been added
-   */
-  public boolean hasFollowHighlight()
-  {
-    return this._has_followHighlight;
-  }
-
-  /**
-   * Method hasFollowSelection.
-   * 
-   * @return true if at least one FollowSelection has been added
-   */
-  public boolean hasFollowSelection()
-  {
-    return this._has_followSelection;
-  }
-
-  /**
-   * Method hasFontSize.
-   * 
-   * @return true if at least one FontSize has been added
-   */
-  public boolean hasFontSize()
-  {
-    return this._has_fontSize;
-  }
-
-  /**
-   * Method hasFontStyle.
-   * 
-   * @return true if at least one FontStyle has been added
-   */
-  public boolean hasFontStyle()
-  {
-    return this._has_fontStyle;
-  }
-
-  /**
-   * Method hasGatheredViews.
-   * 
-   * @return true if at least one GatheredViews has been added
-   */
-  public boolean hasGatheredViews()
-  {
-    return this._has_gatheredViews;
-  }
-
-  /**
-   * Method hasHeight.
-   * 
-   * @return true if at least one Height has been added
-   */
-  public boolean hasHeight()
-  {
-    return this._has_height;
-  }
-
-  /**
-   * Method hasIgnoreGapsinConsensus.
-   * 
-   * @return true if at least one IgnoreGapsinConsensus has been added
-   */
-  public boolean hasIgnoreGapsinConsensus()
-  {
-    return this._has_ignoreGapsinConsensus;
-  }
-
-  /**
-   * Method hasNormaliseSequenceLogo.
-   * 
-   * @return true if at least one NormaliseSequenceLogo has been added
-   */
-  public boolean hasNormaliseSequenceLogo()
-  {
-    return this._has_normaliseSequenceLogo;
-  }
-
-  /**
-   * Method hasPidSelected.
-   * 
-   * @return true if at least one PidSelected has been added
-   */
-  public boolean hasPidSelected()
-  {
-    return this._has_pidSelected;
-  }
-
-  /**
-   * Method hasPidThreshold.
-   * 
-   * @return true if at least one PidThreshold has been added
-   */
-  public boolean hasPidThreshold()
-  {
-    return this._has_pidThreshold;
-  }
-
-  /**
-   * Method hasRenderGaps.
-   * 
-   * @return true if at least one RenderGaps has been added
-   */
-  public boolean hasRenderGaps()
-  {
-    return this._has_renderGaps;
-  }
-
-  /**
-   * Method hasRightAlignIds.
-   * 
-   * @return true if at least one RightAlignIds has been added
-   */
-  public boolean hasRightAlignIds()
-  {
-    return this._has_rightAlignIds;
-  }
-
-  /**
-   * Method hasShowAnnotation.
-   * 
-   * @return true if at least one ShowAnnotation has been added
-   */
-  public boolean hasShowAnnotation()
-  {
-    return this._has_showAnnotation;
-  }
-
-  /**
-   * Method hasShowBoxes.
-   * 
-   * @return true if at least one ShowBoxes has been added
-   */
-  public boolean hasShowBoxes()
-  {
-    return this._has_showBoxes;
-  }
-
-  /**
-   * Method hasShowColourText.
-   * 
-   * @return true if at least one ShowColourText has been added
-   */
-  public boolean hasShowColourText()
-  {
-    return this._has_showColourText;
-  }
-
-  /**
-   * Method hasShowConsensusHistogram.
-   * 
-   * @return true if at least one ShowConsensusHistogram has been added
-   */
-  public boolean hasShowConsensusHistogram()
-  {
-    return this._has_showConsensusHistogram;
-  }
-
-  /**
-   * Method hasShowDbRefTooltip.
-   * 
-   * @return true if at least one ShowDbRefTooltip has been added
-   */
-  public boolean hasShowDbRefTooltip()
-  {
-    return this._has_showDbRefTooltip;
-  }
-
-  /**
-   * Method hasShowFullId.
-   * 
-   * @return true if at least one ShowFullId has been added
-   */
-  public boolean hasShowFullId()
-  {
-    return this._has_showFullId;
-  }
-
-  /**
-   * Method hasShowGroupConsensus.
-   * 
-   * @return true if at least one ShowGroupConsensus has been adde
-   */
-  public boolean hasShowGroupConsensus()
-  {
-    return this._has_showGroupConsensus;
-  }
-
-  /**
-   * Method hasShowGroupConservation.
-   * 
-   * @return true if at least one ShowGroupConservation has been added
-   */
-  public boolean hasShowGroupConservation()
-  {
-    return this._has_showGroupConservation;
-  }
-
-  /**
-   * Method hasShowNPfeatureTooltip.
-   * 
-   * @return true if at least one ShowNPfeatureTooltip has been added
-   */
-  public boolean hasShowNPfeatureTooltip()
-  {
-    return this._has_showNPfeatureTooltip;
-  }
-
-  /**
-   * Method hasShowSequenceFeatures.
-   * 
-   * @return true if at least one ShowSequenceFeatures has been added
-   */
-  public boolean hasShowSequenceFeatures()
-  {
-    return this._has_showSequenceFeatures;
-  }
-
-  /**
-   * Method hasShowSequenceLogo.
-   * 
-   * @return true if at least one ShowSequenceLogo has been added
-   */
-  public boolean hasShowSequenceLogo()
-  {
-    return this._has_showSequenceLogo;
-  }
-
-  /**
-   * Method hasShowText.
-   * 
-   * @return true if at least one ShowText has been added
-   */
-  public boolean hasShowText()
-  {
-    return this._has_showText;
-  }
-
-  /**
-   * Method hasShowUnconserved.
-   * 
-   * @return true if at least one ShowUnconserved has been added
-   */
-  public boolean hasShowUnconserved()
-  {
-    return this._has_showUnconserved;
-  }
-
-  /**
-   * Method hasStartRes.
-   * 
-   * @return true if at least one StartRes has been added
-   */
-  public boolean hasStartRes()
-  {
-    return this._has_startRes;
-  }
-
-  /**
-   * Method hasStartSeq.
-   * 
-   * @return true if at least one StartSeq has been added
-   */
-  public boolean hasStartSeq()
-  {
-    return this._has_startSeq;
-  }
-
-  /**
-   * Method hasTextCol1.
-   * 
-   * @return true if at least one TextCol1 has been added
-   */
-  public boolean hasTextCol1()
-  {
-    return this._has_textCol1;
-  }
-
-  /**
-   * Method hasTextCol2.
-   * 
-   * @return true if at least one TextCol2 has been added
-   */
-  public boolean hasTextCol2()
-  {
-    return this._has_textCol2;
-  }
-
-  /**
-   * Method hasTextColThreshold.
-   * 
-   * @return true if at least one TextColThreshold has been added
-   */
-  public boolean hasTextColThreshold()
-  {
-    return this._has_textColThreshold;
-  }
-
-  /**
-   * Method hasWidth.
-   * 
-   * @return true if at least one Width has been added
-   */
-  public boolean hasWidth()
-  {
-    return this._has_width;
-  }
-
-  /**
-   * Method hasWrapAlignment.
-   * 
-   * @return true if at least one WrapAlignment has been added
-   */
-  public boolean hasWrapAlignment()
-  {
-    return this._has_wrapAlignment;
-  }
-
-  /**
-   * Method hasXpos.
-   * 
-   * @return true if at least one Xpos has been added
-   */
-  public boolean hasXpos()
-  {
-    return this._has_xpos;
-  }
-
-  /**
-   * Method hasYpos.
-   * 
-   * @return true if at least one Ypos has been added
-   */
-  public boolean hasYpos()
-  {
-    return this._has_ypos;
-  }
-
-  /**
-   * Returns the value of field 'centreColumnLabels'.
-   * 
-   * @return the value of field 'CentreColumnLabels'.
-   */
-  public boolean isCentreColumnLabels()
-  {
-    return this._centreColumnLabels;
-  }
-
-  /**
-   * Returns the value of field 'conservationSelected'.
-   * 
-   * @return the value of field 'ConservationSelected'.
-   */
-  public boolean isConservationSelected()
-  {
-    return this._conservationSelected;
-  }
-
-  /**
-   * Returns the value of field 'followHighlight'.
-   * 
-   * @return the value of field 'FollowHighlight'.
-   */
-  public boolean isFollowHighlight()
-  {
-    return this._followHighlight;
-  }
-
-  /**
-   * Returns the value of field 'followSelection'.
-   * 
-   * @return the value of field 'FollowSelection'.
-   */
-  public boolean isFollowSelection()
-  {
-    return this._followSelection;
-  }
-
-  /**
-   * Returns the value of field 'gatheredViews'.
-   * 
-   * @return the value of field 'GatheredViews'.
-   */
-  public boolean isGatheredViews()
-  {
-    return this._gatheredViews;
-  }
-
-  /**
-   * Returns the value of field 'ignoreGapsinConsensus'.
-   * 
-   * @return the value of field 'IgnoreGapsinConsensus'.
-   */
-  public boolean isIgnoreGapsinConsensus()
-  {
-    return this._ignoreGapsinConsensus;
-  }
-
-  /**
-   * Returns the value of field 'normaliseSequenceLogo'.
-   * 
-   * @return the value of field 'NormaliseSequenceLogo'.
-   */
-  public boolean isNormaliseSequenceLogo()
-  {
-    return this._normaliseSequenceLogo;
-  }
-
-  /**
-   * Returns the value of field 'pidSelected'.
-   * 
-   * @return the value of field 'PidSelected'.
-   */
-  public boolean isPidSelected()
-  {
-    return this._pidSelected;
-  }
-
-  /**
-   * Returns the value of field 'renderGaps'.
-   * 
-   * @return the value of field 'RenderGaps'.
-   */
-  public boolean isRenderGaps()
-  {
-    return this._renderGaps;
-  }
-
-  /**
-   * Returns the value of field 'rightAlignIds'.
-   * 
-   * @return the value of field 'RightAlignIds'.
-   */
-  public boolean isRightAlignIds()
-  {
-    return this._rightAlignIds;
-  }
-
-  /**
-   * Returns the value of field 'showAnnotation'.
-   * 
-   * @return the value of field 'ShowAnnotation'.
-   */
-  public boolean isShowAnnotation()
-  {
-    return this._showAnnotation;
-  }
-
-  /**
-   * Returns the value of field 'showBoxes'.
-   * 
-   * @return the value of field 'ShowBoxes'.
-   */
-  public boolean isShowBoxes()
-  {
-    return this._showBoxes;
-  }
-
-  /**
-   * Returns the value of field 'showColourText'.
-   * 
-   * @return the value of field 'ShowColourText'.
-   */
-  public boolean isShowColourText()
-  {
-    return this._showColourText;
-  }
-
-  /**
-   * Returns the value of field 'showConsensusHistogram'.
-   * 
-   * @return the value of field 'ShowConsensusHistogram'.
-   */
-  public boolean isShowConsensusHistogram()
-  {
-    return this._showConsensusHistogram;
-  }
-
-  /**
-   * Returns the value of field 'showDbRefTooltip'.
-   * 
-   * @return the value of field 'ShowDbRefTooltip'.
-   */
-  public boolean isShowDbRefTooltip()
-  {
-    return this._showDbRefTooltip;
-  }
-
-  /**
-   * Returns the value of field 'showFullId'.
-   * 
-   * @return the value of field 'ShowFullId'.
-   */
-  public boolean isShowFullId()
-  {
-    return this._showFullId;
-  }
-
-  /**
-   * Returns the value of field 'showGroupConsensus'.
-   * 
-   * @return the value of field 'ShowGroupConsensus'.
-   */
-  public boolean isShowGroupConsensus()
-  {
-    return this._showGroupConsensus;
-  }
-
-  /**
-   * Returns the value of field 'showGroupConservation'.
-   * 
-   * @return the value of field 'ShowGroupConservation'.
-   */
-  public boolean isShowGroupConservation()
-  {
-    return this._showGroupConservation;
-  }
-
-  /**
-   * Returns the value of field 'showNPfeatureTooltip'.
-   * 
-   * @return the value of field 'ShowNPfeatureTooltip'.
-   */
-  public boolean isShowNPfeatureTooltip()
-  {
-    return this._showNPfeatureTooltip;
-  }
-
-  /**
-   * Returns the value of field 'showSequenceFeatures'.
-   * 
-   * @return the value of field 'ShowSequenceFeatures'.
-   */
-  public boolean isShowSequenceFeatures()
-  {
-    return this._showSequenceFeatures;
-  }
-
-  /**
-   * Returns the value of field 'showSequenceLogo'.
-   * 
-   * @return the value of field 'ShowSequenceLogo'.
-   */
-  public boolean isShowSequenceLogo()
-  {
-    return this._showSequenceLogo;
-  }
-
-  /**
-   * Returns the value of field 'showText'.
-   * 
-   * @return the value of field 'ShowText'.
-   */
-  public boolean isShowText()
-  {
-    return this._showText;
-  }
-
-  /**
-   * Returns the value of field 'showUnconserved'.
-   * 
-   * @return the value of field 'ShowUnconserved'.
-   */
-  public boolean isShowUnconserved()
-  {
-    return this._showUnconserved;
-  }
-
-  /**
-   * Method isValid.
-   * 
-   * @return true if this object is valid according to the schema
-   */
-  public boolean isValid()
-  {
-    try
-    {
-      validate();
-    } catch (org.exolab.castor.xml.ValidationException vex)
-    {
-      return false;
-    }
-    return true;
-  }
-
-  /**
-   * Returns the value of field 'wrapAlignment'.
-   * 
-   * @return the value of field 'WrapAlignment'.
-   */
-  public boolean isWrapAlignment()
-  {
-    return this._wrapAlignment;
-  }
-
-  /**
-   * 
-   * 
-   * @param out
-   * @throws org.exolab.castor.xml.MarshalException
-   *           if object is null or if any SAXException is thrown during
-   *           marshaling
-   * @throws org.exolab.castor.xml.ValidationException
-   *           if this object is an invalid instance according to the schema
-   */
-  public void marshal(final java.io.Writer out)
-          throws org.exolab.castor.xml.MarshalException,
-          org.exolab.castor.xml.ValidationException
-  {
-    Marshaller.marshal(this, out);
-  }
-
-  /**
-   * 
-   * 
-   * @param handler
-   * @throws java.io.IOException
-   *           if an IOException occurs during marshaling
-   * @throws org.exolab.castor.xml.ValidationException
-   *           if this object is an invalid instance according to the schema
-   * @throws org.exolab.castor.xml.MarshalException
-   *           if object is null or if any SAXException is thrown during
-   *           marshaling
-   */
-  public void marshal(final org.xml.sax.ContentHandler handler)
-          throws java.io.IOException,
-          org.exolab.castor.xml.MarshalException,
-          org.exolab.castor.xml.ValidationException
-  {
-    Marshaller.marshal(this, handler);
-  }
-
-  /**
-     */
-  public void removeAllCalcIdParam()
-  {
-    this._calcIdParamList.clear();
-  }
-
-  /**
-     */
-  public void removeAllHiddenColumns()
-  {
-    this._hiddenColumnsList.clear();
-  }
-
-  /**
-   * Method removeCalcIdParam.
-   * 
-   * @param vCalcIdParam
-   * @return true if the object was removed from the collection.
-   */
-  public boolean removeCalcIdParam(
-          final jalview.schemabinding.version2.CalcIdParam vCalcIdParam)
-  {
-    boolean removed = _calcIdParamList.remove(vCalcIdParam);
-    return removed;
-  }
-
-  /**
-   * Method removeCalcIdParamAt.
-   * 
-   * @param index
-   * @return the element removed from the collection
-   */
-  public jalview.schemabinding.version2.CalcIdParam removeCalcIdParamAt(
-          final int index)
-  {
-    java.lang.Object obj = this._calcIdParamList.remove(index);
-    return (jalview.schemabinding.version2.CalcIdParam) obj;
-  }
-
-  /**
-   * Method removeHiddenColumns.
-   * 
-   * @param vHiddenColumns
-   * @return true if the object was removed from the collection.
-   */
-  public boolean removeHiddenColumns(
-          final jalview.schemabinding.version2.HiddenColumns vHiddenColumns)
-  {
-    boolean removed = _hiddenColumnsList.remove(vHiddenColumns);
-    return removed;
-  }
-
-  /**
-   * Method removeHiddenColumnsAt.
-   * 
-   * @param index
-   * @return the element removed from the collection
-   */
-  public jalview.schemabinding.version2.HiddenColumns removeHiddenColumnsAt(
-          final int index)
-  {
-    java.lang.Object obj = this._hiddenColumnsList.remove(index);
-    return (jalview.schemabinding.version2.HiddenColumns) obj;
-  }
-
-  /**
-   * Sets the value of field 'annotationColours'.
-   * 
-   * @param annotationColours
-   *          the value of field 'annotationColours'.
-   */
-  public void setAnnotationColours(
-          final jalview.schemabinding.version2.AnnotationColours annotationColours)
-  {
-    this._annotationColours = annotationColours;
-  }
-
-  /**
-   * Sets the value of field 'bgColour'.
-   * 
-   * @param bgColour
-   *          the value of field 'bgColour'.
-   */
-  public void setBgColour(final java.lang.String bgColour)
-  {
-    this._bgColour = bgColour;
-  }
-
-  /**
-   * 
-   * 
-   * @param index
-   * @param vCalcIdParam
-   * @throws java.lang.IndexOutOfBoundsException
-   *           if the index given is outside the bounds of the collection
-   */
-  public void setCalcIdParam(final int index,
-          final jalview.schemabinding.version2.CalcIdParam vCalcIdParam)
-          throws java.lang.IndexOutOfBoundsException
-  {
-    // check bounds for index
-    if (index < 0 || index >= this._calcIdParamList.size())
-    {
-        throw new IndexOutOfBoundsException(MessageManager.formatMessage("exception.index_value_not_in_range", new String[]{
-                         "setCalcIdParam",
-                         Integer.valueOf(index).toString(),
-                         Integer.valueOf((this._calcIdParamList.size() - 1)).toString()
-          })); 
-    }
-
-    this._calcIdParamList.set(index, vCalcIdParam);
-  }
-
-  /**
-   * 
-   * 
-   * @param vCalcIdParamArray
-   */
-  public void setCalcIdParam(
-          final jalview.schemabinding.version2.CalcIdParam[] vCalcIdParamArray)
-  {
-    // -- copy array
-    _calcIdParamList.clear();
-
-    for (int i = 0; i < vCalcIdParamArray.length; i++)
-    {
-      this._calcIdParamList.add(vCalcIdParamArray[i]);
-    }
-  }
-
-  /**
-   * Sets the value of field 'centreColumnLabels'.
-   * 
-   * @param centreColumnLabels
-   *          the value of field 'centreColumnLabels'.
-   */
-  public void setCentreColumnLabels(final boolean centreColumnLabels)
-  {
-    this._centreColumnLabels = centreColumnLabels;
-    this._has_centreColumnLabels = true;
-  }
-
-  /**
-   * Sets the value of field 'consThreshold'.
-   * 
-   * @param consThreshold
-   *          the value of field 'consThreshold'.
-   */
-  public void setConsThreshold(final int consThreshold)
-  {
-    this._consThreshold = consThreshold;
-    this._has_consThreshold = true;
-  }
-
-  /**
-   * Sets the value of field 'conservationSelected'.
-   * 
-   * @param conservationSelected
-   *          the value of field 'conservationSelected'.
-   */
-  public void setConservationSelected(final boolean conservationSelected)
-  {
-    this._conservationSelected = conservationSelected;
-    this._has_conservationSelected = true;
-  }
-
-  /**
-   * Sets the value of field 'followHighlight'.
-   * 
-   * @param followHighlight
-   *          the value of field 'followHighlight'.
-   */
-  public void setFollowHighlight(final boolean followHighlight)
-  {
-    this._followHighlight = followHighlight;
-    this._has_followHighlight = true;
-  }
-
-  /**
-   * Sets the value of field 'followSelection'.
-   * 
-   * @param followSelection
-   *          the value of field 'followSelection'.
-   */
-  public void setFollowSelection(final boolean followSelection)
-  {
-    this._followSelection = followSelection;
-    this._has_followSelection = true;
-  }
-
-  /**
-   * Sets the value of field 'fontName'.
-   * 
-   * @param fontName
-   *          the value of field 'fontName'.
-   */
-  public void setFontName(final java.lang.String fontName)
-  {
-    this._fontName = fontName;
-  }
-
-  /**
-   * Sets the value of field 'fontSize'.
-   * 
-   * @param fontSize
-   *          the value of field 'fontSize'.
-   */
-  public void setFontSize(final int fontSize)
-  {
-    this._fontSize = fontSize;
-    this._has_fontSize = true;
-  }
-
-  /**
-   * Sets the value of field 'fontStyle'.
-   * 
-   * @param fontStyle
-   *          the value of field 'fontStyle'.
-   */
-  public void setFontStyle(final int fontStyle)
-  {
-    this._fontStyle = fontStyle;
-    this._has_fontStyle = true;
-  }
-
-  /**
-   * Sets the value of field 'gatheredViews'.
-   * 
-   * @param gatheredViews
-   *          the value of field 'gatheredViews'.
-   */
-  public void setGatheredViews(final boolean gatheredViews)
-  {
-    this._gatheredViews = gatheredViews;
-    this._has_gatheredViews = true;
-  }
-
-  /**
-   * Sets the value of field 'height'.
-   * 
-   * @param height
-   *          the value of field 'height'.
-   */
-  public void setHeight(final int height)
-  {
-    this._height = height;
-    this._has_height = true;
-  }
-
-  /**
-   * 
-   * 
-   * @param index
-   * @param vHiddenColumns
-   * @throws java.lang.IndexOutOfBoundsException
-   *           if the index given is outside the bounds of the collection
-   */
-  public void setHiddenColumns(final int index,
-          final jalview.schemabinding.version2.HiddenColumns vHiddenColumns)
-          throws java.lang.IndexOutOfBoundsException
-  {
-    // check bounds for index
-    if (index < 0 || index >= this._hiddenColumnsList.size())
-    {
-        throw new IndexOutOfBoundsException(MessageManager.formatMessage("exception.index_value_not_in_range", new String[]{
-                 "setHiddenColumns",
-                 Integer.valueOf(index).toString(),
-                 Integer.valueOf((this._hiddenColumnsList.size() - 1)).toString()
-        })); 
-    }
-
-    this._hiddenColumnsList.set(index, vHiddenColumns);
-  }
-
-  /**
-   * 
-   * 
-   * @param vHiddenColumnsArray
-   */
-  public void setHiddenColumns(
-          final jalview.schemabinding.version2.HiddenColumns[] vHiddenColumnsArray)
-  {
-    // -- copy array
-    _hiddenColumnsList.clear();
-
-    for (int i = 0; i < vHiddenColumnsArray.length; i++)
-    {
-      this._hiddenColumnsList.add(vHiddenColumnsArray[i]);
-    }
-  }
-
-  /**
-   * Sets the value of field 'id'. The field 'id' has the following description:
-   * unique id used by jalview to synchronize between stored and instantiated
-   * views
-   * 
-   * 
-   * @param id
-   *          the value of field 'id'.
-   */
-  public void setId(final java.lang.String id)
-  {
-    this._id = id;
-  }
-
-  /**
-   * Sets the value of field 'ignoreGapsinConsensus'.
-   * 
-   * @param ignoreGapsinConsensus
-   *          the value of field 'ignoreGapsinConsensus'.
-   */
-  public void setIgnoreGapsinConsensus(final boolean ignoreGapsinConsensus)
-  {
-    this._ignoreGapsinConsensus = ignoreGapsinConsensus;
-    this._has_ignoreGapsinConsensus = true;
-  }
-
-  /**
-   * Sets the value of field 'normaliseSequenceLogo'.
-   * 
-   * @param normaliseSequenceLogo
-   *          the value of field 'normaliseSequenceLogo'.
-   */
-  public void setNormaliseSequenceLogo(final boolean normaliseSequenceLogo)
-  {
-    this._normaliseSequenceLogo = normaliseSequenceLogo;
-    this._has_normaliseSequenceLogo = true;
-  }
-
-  /**
-   * Sets the value of field 'pidSelected'.
-   * 
-   * @param pidSelected
-   *          the value of field 'pidSelected'.
-   */
-  public void setPidSelected(final boolean pidSelected)
-  {
-    this._pidSelected = pidSelected;
-    this._has_pidSelected = true;
-  }
-
-  /**
-   * Sets the value of field 'pidThreshold'.
-   * 
-   * @param pidThreshold
-   *          the value of field 'pidThreshold'.
-   */
-  public void setPidThreshold(final int pidThreshold)
-  {
-    this._pidThreshold = pidThreshold;
-    this._has_pidThreshold = true;
-  }
-
-  /**
-   * Sets the value of field 'renderGaps'.
-   * 
-   * @param renderGaps
-   *          the value of field 'renderGaps'.
-   */
-  public void setRenderGaps(final boolean renderGaps)
-  {
-    this._renderGaps = renderGaps;
-    this._has_renderGaps = true;
-  }
-
-  /**
-   * Sets the value of field 'rightAlignIds'.
-   * 
-   * @param rightAlignIds
-   *          the value of field 'rightAlignIds'.
-   */
-  public void setRightAlignIds(final boolean rightAlignIds)
-  {
-    this._rightAlignIds = rightAlignIds;
-    this._has_rightAlignIds = true;
-  }
-
-  /**
-   * Sets the value of field 'sequenceSetId'.
-   * 
-   * @param sequenceSetId
-   *          the value of field 'sequenceSetId'.
-   */
-  public void setSequenceSetId(final java.lang.String sequenceSetId)
-  {
-    this._sequenceSetId = sequenceSetId;
-  }
-
-  /**
-   * Sets the value of field 'showAnnotation'.
-   * 
-   * @param showAnnotation
-   *          the value of field 'showAnnotation'.
-   */
-  public void setShowAnnotation(final boolean showAnnotation)
-  {
-    this._showAnnotation = showAnnotation;
-    this._has_showAnnotation = true;
-  }
-
-  /**
-   * Sets the value of field 'showBoxes'.
-   * 
-   * @param showBoxes
-   *          the value of field 'showBoxes'.
-   */
-  public void setShowBoxes(final boolean showBoxes)
-  {
-    this._showBoxes = showBoxes;
-    this._has_showBoxes = true;
-  }
-
-  /**
-   * Sets the value of field 'showColourText'.
-   * 
-   * @param showColourText
-   *          the value of field 'showColourText'.
-   */
-  public void setShowColourText(final boolean showColourText)
-  {
-    this._showColourText = showColourText;
-    this._has_showColourText = true;
-  }
-
-  /**
-   * Sets the value of field 'showConsensusHistogram'.
-   * 
-   * @param showConsensusHistogram
-   *          the value of field 'showConsensusHistogram'.
-   */
-  public void setShowConsensusHistogram(final boolean showConsensusHistogram)
-  {
-    this._showConsensusHistogram = showConsensusHistogram;
-    this._has_showConsensusHistogram = true;
-  }
-
-  /**
-   * Sets the value of field 'showDbRefTooltip'.
-   * 
-   * @param showDbRefTooltip
-   *          the value of field 'showDbRefTooltip'
-   */
-  public void setShowDbRefTooltip(final boolean showDbRefTooltip)
-  {
-    this._showDbRefTooltip = showDbRefTooltip;
-    this._has_showDbRefTooltip = true;
-  }
-
-  /**
-   * Sets the value of field 'showFullId'.
-   * 
-   * @param showFullId
-   *          the value of field 'showFullId'.
-   */
-  public void setShowFullId(final boolean showFullId)
-  {
-    this._showFullId = showFullId;
-    this._has_showFullId = true;
-  }
-
-  /**
-   * Sets the value of field 'showGroupConsensus'.
-   * 
-   * @param showGroupConsensus
-   *          the value of field 'showGroupConsensus'.
-   */
-  public void setShowGroupConsensus(final boolean showGroupConsensus)
-  {
-    this._showGroupConsensus = showGroupConsensus;
-    this._has_showGroupConsensus = true;
-  }
-
-  /**
-   * Sets the value of field 'showGroupConservation'.
-   * 
-   * @param showGroupConservation
-   *          the value of field 'showGroupConservation'.
-   */
-  public void setShowGroupConservation(final boolean showGroupConservation)
-  {
-    this._showGroupConservation = showGroupConservation;
-    this._has_showGroupConservation = true;
-  }
-
-  /**
-   * Sets the value of field 'showNPfeatureTooltip'.
-   * 
-   * @param showNPfeatureTooltip
-   *          the value of field 'showNPfeatureTooltip'.
-   */
-  public void setShowNPfeatureTooltip(final boolean showNPfeatureTooltip)
-  {
-    this._showNPfeatureTooltip = showNPfeatureTooltip;
-    this._has_showNPfeatureTooltip = true;
-  }
-
-  /**
-   * Sets the value of field 'showSequenceFeatures'.
-   * 
-   * @param showSequenceFeatures
-   *          the value of field 'showSequenceFeatures'.
-   */
-  public void setShowSequenceFeatures(final boolean showSequenceFeatures)
-  {
-    this._showSequenceFeatures = showSequenceFeatures;
-    this._has_showSequenceFeatures = true;
-  }
-
-  /**
-   * Sets the value of field 'showSequenceLogo'.
-   * 
-   * @param showSequenceLogo
-   *          the value of field 'showSequenceLogo'
-   */
-  public void setShowSequenceLogo(final boolean showSequenceLogo)
-  {
-    this._showSequenceLogo = showSequenceLogo;
-    this._has_showSequenceLogo = true;
-  }
-
-  /**
-   * Sets the value of field 'showText'.
-   * 
-   * @param showText
-   *          the value of field 'showText'.
-   */
-  public void setShowText(final boolean showText)
-  {
-    this._showText = showText;
-    this._has_showText = true;
-  }
-
-  /**
-   * Sets the value of field 'showUnconserved'.
-   * 
-   * @param showUnconserved
-   *          the value of field 'showUnconserved'.
-   */
-  public void setShowUnconserved(final boolean showUnconserved)
-  {
-    this._showUnconserved = showUnconserved;
-    this._has_showUnconserved = true;
-  }
-
-  /**
-   * Sets the value of field 'startRes'.
-   * 
-   * @param startRes
-   *          the value of field 'startRes'.
-   */
-  public void setStartRes(final int startRes)
-  {
-    this._startRes = startRes;
-    this._has_startRes = true;
-  }
-
-  /**
-   * Sets the value of field 'startSeq'.
-   * 
-   * @param startSeq
-   *          the value of field 'startSeq'.
-   */
-  public void setStartSeq(final int startSeq)
-  {
-    this._startSeq = startSeq;
-    this._has_startSeq = true;
-  }
-
-  /**
-   * Sets the value of field 'textCol1'.
-   * 
-   * @param textCol1
-   *          the value of field 'textCol1'.
-   */
-  public void setTextCol1(final int textCol1)
-  {
-    this._textCol1 = textCol1;
-    this._has_textCol1 = true;
-  }
-
-  /**
-   * Sets the value of field 'textCol2'.
-   * 
-   * @param textCol2
-   *          the value of field 'textCol2'.
-   */
-  public void setTextCol2(final int textCol2)
-  {
-    this._textCol2 = textCol2;
-    this._has_textCol2 = true;
-  }
-
-  /**
-   * Sets the value of field 'textColThreshold'.
-   * 
-   * @param textColThreshold
-   *          the value of field 'textColThreshold'
-   */
-  public void setTextColThreshold(final int textColThreshold)
-  {
-    this._textColThreshold = textColThreshold;
-    this._has_textColThreshold = true;
-  }
-
-  /**
-   * Sets the value of field 'title'.
-   * 
-   * @param title
-   *          the value of field 'title'.
-   */
-  public void setTitle(final java.lang.String title)
-  {
-    this._title = title;
-  }
-
-  /**
-   * Sets the value of field 'viewName'.
-   * 
-   * @param viewName
-   *          the value of field 'viewName'.
-   */
-  public void setViewName(final java.lang.String viewName)
-  {
-    this._viewName = viewName;
-  }
-
-  /**
-   * Sets the value of field 'width'.
-   * 
-   * @param width
-   *          the value of field 'width'.
-   */
-  public void setWidth(final int width)
-  {
-    this._width = width;
-    this._has_width = true;
-  }
-
-  /**
-   * Sets the value of field 'wrapAlignment'.
-   * 
-   * @param wrapAlignment
-   *          the value of field 'wrapAlignment'.
-   */
-  public void setWrapAlignment(final boolean wrapAlignment)
-  {
-    this._wrapAlignment = wrapAlignment;
-    this._has_wrapAlignment = true;
-  }
-
-  /**
-   * Sets the value of field 'xpos'.
-   * 
-   * @param xpos
-   *          the value of field 'xpos'.
-   */
-  public void setXpos(final int xpos)
-  {
-    this._xpos = xpos;
-    this._has_xpos = true;
-  }
-
-  /**
-   * Sets the value of field 'ypos'.
-   * 
-   * @param ypos
-   *          the value of field 'ypos'.
-   */
-  public void setYpos(final int ypos)
-  {
-    this._ypos = ypos;
-    this._has_ypos = true;
-  }
-
-  /**
-   * Method unmarshal.
-   * 
-   * @param reader
-   * @throws org.exolab.castor.xml.MarshalException
-   *           if object is null or if any SAXException is thrown during
-   *           marshaling
-   * @throws org.exolab.castor.xml.ValidationException
-   *           if this object is an invalid instance according to the schema
-   * @return the unmarshaled jalview.schemabinding.version2.Viewport
-   */
-  public static jalview.schemabinding.version2.Viewport unmarshal(
-          final java.io.Reader reader)
-          throws org.exolab.castor.xml.MarshalException,
-          org.exolab.castor.xml.ValidationException
-  {
-    return (jalview.schemabinding.version2.Viewport) Unmarshaller
-            .unmarshal(jalview.schemabinding.version2.Viewport.class,
-                    reader);
-  }
-
-  /**
-   * 
-   * 
-   * @throws org.exolab.castor.xml.ValidationException
-   *           if this object is an invalid instance according to the schema
-   */
-  public void validate() throws org.exolab.castor.xml.ValidationException
-  {
-    org.exolab.castor.xml.Validator validator = new org.exolab.castor.xml.Validator();
-    validator.validate(this);
-  }
+    private jalview.schemabinding.version2.AnnotationColours _annotationColours;
+
+    /**
+     * Field _hiddenColumnsList.
+     */
+    private java.util.Vector _hiddenColumnsList;
+
+    /**
+     * Field _calcIdParamList.
+     */
+    private java.util.Vector _calcIdParamList;
+
+
+      //----------------/
+     //- Constructors -/
+    //----------------/
+
+    public Viewport() {
+        super();
+        this._hiddenColumnsList = new java.util.Vector();
+        this._calcIdParamList = new java.util.Vector();
+    }
+
+
+      //-----------/
+     //- Methods -/
+    //-----------/
+
+    /**
+     * 
+     * 
+     * @param vCalcIdParam
+     * @throws java.lang.IndexOutOfBoundsException if the index
+     * given is outside the bounds of the collection
+     */
+    public void addCalcIdParam(
+            final jalview.schemabinding.version2.CalcIdParam vCalcIdParam)
+    throws java.lang.IndexOutOfBoundsException {
+        this._calcIdParamList.addElement(vCalcIdParam);
+    }
+
+    /**
+     * 
+     * 
+     * @param index
+     * @param vCalcIdParam
+     * @throws java.lang.IndexOutOfBoundsException if the index
+     * given is outside the bounds of the collection
+     */
+    public void addCalcIdParam(
+            final int index,
+            final jalview.schemabinding.version2.CalcIdParam vCalcIdParam)
+    throws java.lang.IndexOutOfBoundsException {
+        this._calcIdParamList.add(index, vCalcIdParam);
+    }
+
+    /**
+     * 
+     * 
+     * @param vHiddenColumns
+     * @throws java.lang.IndexOutOfBoundsException if the index
+     * given is outside the bounds of the collection
+     */
+    public void addHiddenColumns(
+            final jalview.schemabinding.version2.HiddenColumns vHiddenColumns)
+    throws java.lang.IndexOutOfBoundsException {
+        this._hiddenColumnsList.addElement(vHiddenColumns);
+    }
+
+    /**
+     * 
+     * 
+     * @param index
+     * @param vHiddenColumns
+     * @throws java.lang.IndexOutOfBoundsException if the index
+     * given is outside the bounds of the collection
+     */
+    public void addHiddenColumns(
+            final int index,
+            final jalview.schemabinding.version2.HiddenColumns vHiddenColumns)
+    throws java.lang.IndexOutOfBoundsException {
+        this._hiddenColumnsList.add(index, vHiddenColumns);
+    }
+
+    /**
+     */
+    public void deleteCentreColumnLabels(
+    ) {
+        this._has_centreColumnLabels= false;
+    }
+
+    /**
+     */
+    public void deleteConsThreshold(
+    ) {
+        this._has_consThreshold= false;
+    }
+
+    /**
+     */
+    public void deleteConservationSelected(
+    ) {
+        this._has_conservationSelected= false;
+    }
+
+    /**
+     */
+    public void deleteFollowHighlight(
+    ) {
+        this._has_followHighlight= false;
+    }
+
+    /**
+     */
+    public void deleteFollowSelection(
+    ) {
+        this._has_followSelection= false;
+    }
+
+    /**
+     */
+    public void deleteFontSize(
+    ) {
+        this._has_fontSize= false;
+    }
+
+    /**
+     */
+    public void deleteFontStyle(
+    ) {
+        this._has_fontStyle= false;
+    }
+
+    /**
+     */
+    public void deleteGatheredViews(
+    ) {
+        this._has_gatheredViews= false;
+    }
+
+    /**
+     */
+    public void deleteHeight(
+    ) {
+        this._has_height= false;
+    }
+
+    /**
+     */
+    public void deleteIgnoreGapsinConsensus(
+    ) {
+        this._has_ignoreGapsinConsensus= false;
+    }
+
+    /**
+     */
+    public void deleteNormaliseSequenceLogo(
+    ) {
+        this._has_normaliseSequenceLogo= false;
+    }
+
+    /**
+     */
+    public void deletePidSelected(
+    ) {
+        this._has_pidSelected= false;
+    }
+
+    /**
+     */
+    public void deletePidThreshold(
+    ) {
+        this._has_pidThreshold= false;
+    }
+
+    /**
+     */
+    public void deleteRenderGaps(
+    ) {
+        this._has_renderGaps= false;
+    }
+
+    /**
+     */
+    public void deleteRightAlignIds(
+    ) {
+        this._has_rightAlignIds= false;
+    }
+
+    /**
+     */
+    public void deleteShowAnnotation(
+    ) {
+        this._has_showAnnotation= false;
+    }
+
+    /**
+     */
+    public void deleteShowBoxes(
+    ) {
+        this._has_showBoxes= false;
+    }
+
+    /**
+     */
+    public void deleteShowColourText(
+    ) {
+        this._has_showColourText= false;
+    }
+
+    /**
+     */
+    public void deleteShowConsensusHistogram(
+    ) {
+        this._has_showConsensusHistogram= false;
+    }
+
+    /**
+     */
+    public void deleteShowDbRefTooltip(
+    ) {
+        this._has_showDbRefTooltip= false;
+    }
+
+    /**
+     */
+    public void deleteShowFullId(
+    ) {
+        this._has_showFullId= false;
+    }
+
+    /**
+     */
+    public void deleteShowGroupConsensus(
+    ) {
+        this._has_showGroupConsensus= false;
+    }
+
+    /**
+     */
+    public void deleteShowGroupConservation(
+    ) {
+        this._has_showGroupConservation= false;
+    }
+
+    /**
+     */
+    public void deleteShowNPfeatureTooltip(
+    ) {
+        this._has_showNPfeatureTooltip= false;
+    }
+
+    /**
+     */
+    public void deleteShowSequenceFeatures(
+    ) {
+        this._has_showSequenceFeatures= false;
+    }
+
+    /**
+     */
+    public void deleteShowSequenceLogo(
+    ) {
+        this._has_showSequenceLogo= false;
+    }
+
+    /**
+     */
+    public void deleteShowText(
+    ) {
+        this._has_showText= false;
+    }
+
+    /**
+     */
+    public void deleteShowUnconserved(
+    ) {
+        this._has_showUnconserved= false;
+    }
+
+    /**
+     */
+    public void deleteStartRes(
+    ) {
+        this._has_startRes= false;
+    }
+
+    /**
+     */
+    public void deleteStartSeq(
+    ) {
+        this._has_startSeq= false;
+    }
+
+    /**
+     */
+    public void deleteTextCol1(
+    ) {
+        this._has_textCol1= false;
+    }
+
+    /**
+     */
+    public void deleteTextCol2(
+    ) {
+        this._has_textCol2= false;
+    }
+
+    /**
+     */
+    public void deleteTextColThreshold(
+    ) {
+        this._has_textColThreshold= false;
+    }
+
+    /**
+     */
+    public void deleteWidth(
+    ) {
+        this._has_width= false;
+    }
+
+    /**
+     */
+    public void deleteWrapAlignment(
+    ) {
+        this._has_wrapAlignment= false;
+    }
+
+    /**
+     */
+    public void deleteXpos(
+    ) {
+        this._has_xpos= false;
+    }
+
+    /**
+     */
+    public void deleteYpos(
+    ) {
+        this._has_ypos= false;
+    }
+
+    /**
+     * Method enumerateCalcIdParam.
+     * 
+     * @return an Enumeration over all
+     * jalview.schemabinding.version2.CalcIdParam elements
+     */
+    public java.util.Enumeration enumerateCalcIdParam(
+    ) {
+        return this._calcIdParamList.elements();
+    }
+
+    /**
+     * Method enumerateHiddenColumns.
+     * 
+     * @return an Enumeration over all
+     * jalview.schemabinding.version2.HiddenColumns elements
+     */
+    public java.util.Enumeration enumerateHiddenColumns(
+    ) {
+        return this._hiddenColumnsList.elements();
+    }
+
+    /**
+     * Returns the value of field 'annotationColours'.
+     * 
+     * @return the value of field 'AnnotationColours'.
+     */
+    public jalview.schemabinding.version2.AnnotationColours getAnnotationColours(
+    ) {
+        return this._annotationColours;
+    }
+
+    /**
+     * Returns the value of field 'bgColour'.
+     * 
+     * @return the value of field 'BgColour'.
+     */
+    public java.lang.String getBgColour(
+    ) {
+        return this._bgColour;
+    }
+
+    /**
+     * Method getCalcIdParam.
+     * 
+     * @param index
+     * @throws java.lang.IndexOutOfBoundsException if the index
+     * given is outside the bounds of the collection
+     * @return the value of the
+     * jalview.schemabinding.version2.CalcIdParam at the given index
+     */
+    public jalview.schemabinding.version2.CalcIdParam getCalcIdParam(
+            final int index)
+    throws java.lang.IndexOutOfBoundsException {
+        // check bounds for index
+        if (index < 0 || index >= this._calcIdParamList.size()) {
+            throw new IndexOutOfBoundsException("getCalcIdParam: Index value '" + index + "' not in range [0.." + (this._calcIdParamList.size() - 1) + "]");
+        }
+        
+        return (jalview.schemabinding.version2.CalcIdParam) _calcIdParamList.get(index);
+    }
+
+    /**
+     * Method getCalcIdParam.Returns the contents of the collection
+     * in an Array.  <p>Note:  Just in case the collection contents
+     * are changing in another thread, we pass a 0-length Array of
+     * the correct type into the API call.  This way we <i>know</i>
+     * that the Array returned is of exactly the correct length.
+     * 
+     * @return this collection as an Array
+     */
+    public jalview.schemabinding.version2.CalcIdParam[] getCalcIdParam(
+    ) {
+        jalview.schemabinding.version2.CalcIdParam[] array = new jalview.schemabinding.version2.CalcIdParam[0];
+        return (jalview.schemabinding.version2.CalcIdParam[]) this._calcIdParamList.toArray(array);
+    }
+
+    /**
+     * Method getCalcIdParamCount.
+     * 
+     * @return the size of this collection
+     */
+    public int getCalcIdParamCount(
+    ) {
+        return this._calcIdParamList.size();
+    }
+
+    /**
+     * Returns the value of field 'centreColumnLabels'.
+     * 
+     * @return the value of field 'CentreColumnLabels'.
+     */
+    public boolean getCentreColumnLabels(
+    ) {
+        return this._centreColumnLabels;
+    }
+
+    /**
+     * Returns the value of field 'complementId'. The field
+     * 'complementId' has the following description: The viewport
+     * id of this viewport's (cdna/protein) coding complement, if
+     * any
+     *  
+     * 
+     * @return the value of field 'ComplementId'.
+     */
+    public java.lang.String getComplementId(
+    ) {
+        return this._complementId;
+    }
+
+    /**
+     * Returns the value of field 'consThreshold'.
+     * 
+     * @return the value of field 'ConsThreshold'.
+     */
+    public int getConsThreshold(
+    ) {
+        return this._consThreshold;
+    }
+
+    /**
+     * Returns the value of field 'conservationSelected'.
+     * 
+     * @return the value of field 'ConservationSelected'.
+     */
+    public boolean getConservationSelected(
+    ) {
+        return this._conservationSelected;
+    }
+
+    /**
+     * Returns the value of field 'followHighlight'.
+     * 
+     * @return the value of field 'FollowHighlight'.
+     */
+    public boolean getFollowHighlight(
+    ) {
+        return this._followHighlight;
+    }
+
+    /**
+     * Returns the value of field 'followSelection'.
+     * 
+     * @return the value of field 'FollowSelection'.
+     */
+    public boolean getFollowSelection(
+    ) {
+        return this._followSelection;
+    }
+
+    /**
+     * Returns the value of field 'fontName'.
+     * 
+     * @return the value of field 'FontName'.
+     */
+    public java.lang.String getFontName(
+    ) {
+        return this._fontName;
+    }
+
+    /**
+     * Returns the value of field 'fontSize'.
+     * 
+     * @return the value of field 'FontSize'.
+     */
+    public int getFontSize(
+    ) {
+        return this._fontSize;
+    }
+
+    /**
+     * Returns the value of field 'fontStyle'.
+     * 
+     * @return the value of field 'FontStyle'.
+     */
+    public int getFontStyle(
+    ) {
+        return this._fontStyle;
+    }
+
+    /**
+     * Returns the value of field 'gatheredViews'.
+     * 
+     * @return the value of field 'GatheredViews'.
+     */
+    public boolean getGatheredViews(
+    ) {
+        return this._gatheredViews;
+    }
+
+    /**
+     * Returns the value of field 'height'.
+     * 
+     * @return the value of field 'Height'.
+     */
+    public int getHeight(
+    ) {
+        return this._height;
+    }
+
+    /**
+     * Method getHiddenColumns.
+     * 
+     * @param index
+     * @throws java.lang.IndexOutOfBoundsException if the index
+     * given is outside the bounds of the collection
+     * @return the value of the
+     * jalview.schemabinding.version2.HiddenColumns at the given
+     * index
+     */
+    public jalview.schemabinding.version2.HiddenColumns getHiddenColumns(
+            final int index)
+    throws java.lang.IndexOutOfBoundsException {
+        // check bounds for index
+        if (index < 0 || index >= this._hiddenColumnsList.size()) {
+            throw new IndexOutOfBoundsException("getHiddenColumns: Index value '" + index + "' not in range [0.." + (this._hiddenColumnsList.size() - 1) + "]");
+        }
+        
+        return (jalview.schemabinding.version2.HiddenColumns) _hiddenColumnsList.get(index);
+    }
+
+    /**
+     * Method getHiddenColumns.Returns the contents of the
+     * collection in an Array.  <p>Note:  Just in case the
+     * collection contents are changing in another thread, we pass
+     * a 0-length Array of the correct type into the API call. 
+     * This way we <i>know</i> that the Array returned is of
+     * exactly the correct length.
+     * 
+     * @return this collection as an Array
+     */
+    public jalview.schemabinding.version2.HiddenColumns[] getHiddenColumns(
+    ) {
+        jalview.schemabinding.version2.HiddenColumns[] array = new jalview.schemabinding.version2.HiddenColumns[0];
+        return (jalview.schemabinding.version2.HiddenColumns[]) this._hiddenColumnsList.toArray(array);
+    }
+
+    /**
+     * Method getHiddenColumnsCount.
+     * 
+     * @return the size of this collection
+     */
+    public int getHiddenColumnsCount(
+    ) {
+        return this._hiddenColumnsList.size();
+    }
+
+    /**
+     * Returns the value of field 'id'. The field 'id' has the
+     * following description: unique id used by jalview to
+     *  synchronize between stored and
+     *  instantiated views
+     *  
+     * 
+     * @return the value of field 'Id'.
+     */
+    public java.lang.String getId(
+    ) {
+        return this._id;
+    }
+
+    /**
+     * Returns the value of field 'ignoreGapsinConsensus'.
+     * 
+     * @return the value of field 'IgnoreGapsinConsensus'.
+     */
+    public boolean getIgnoreGapsinConsensus(
+    ) {
+        return this._ignoreGapsinConsensus;
+    }
+
+    /**
+     * Returns the value of field 'normaliseSequenceLogo'.
+     * 
+     * @return the value of field 'NormaliseSequenceLogo'.
+     */
+    public boolean getNormaliseSequenceLogo(
+    ) {
+        return this._normaliseSequenceLogo;
+    }
+
+    /**
+     * Returns the value of field 'pidSelected'.
+     * 
+     * @return the value of field 'PidSelected'.
+     */
+    public boolean getPidSelected(
+    ) {
+        return this._pidSelected;
+    }
+
+    /**
+     * Returns the value of field 'pidThreshold'.
+     * 
+     * @return the value of field 'PidThreshold'.
+     */
+    public int getPidThreshold(
+    ) {
+        return this._pidThreshold;
+    }
+
+    /**
+     * Returns the value of field 'renderGaps'.
+     * 
+     * @return the value of field 'RenderGaps'.
+     */
+    public boolean getRenderGaps(
+    ) {
+        return this._renderGaps;
+    }
+
+    /**
+     * Returns the value of field 'rightAlignIds'.
+     * 
+     * @return the value of field 'RightAlignIds'.
+     */
+    public boolean getRightAlignIds(
+    ) {
+        return this._rightAlignIds;
+    }
+
+    /**
+     * Returns the value of field 'sequenceSetId'.
+     * 
+     * @return the value of field 'SequenceSetId'.
+     */
+    public java.lang.String getSequenceSetId(
+    ) {
+        return this._sequenceSetId;
+    }
+
+    /**
+     * Returns the value of field 'showAnnotation'.
+     * 
+     * @return the value of field 'ShowAnnotation'.
+     */
+    public boolean getShowAnnotation(
+    ) {
+        return this._showAnnotation;
+    }
+
+    /**
+     * Returns the value of field 'showBoxes'.
+     * 
+     * @return the value of field 'ShowBoxes'.
+     */
+    public boolean getShowBoxes(
+    ) {
+        return this._showBoxes;
+    }
+
+    /**
+     * Returns the value of field 'showColourText'.
+     * 
+     * @return the value of field 'ShowColourText'.
+     */
+    public boolean getShowColourText(
+    ) {
+        return this._showColourText;
+    }
+
+    /**
+     * Returns the value of field 'showConsensusHistogram'.
+     * 
+     * @return the value of field 'ShowConsensusHistogram'.
+     */
+    public boolean getShowConsensusHistogram(
+    ) {
+        return this._showConsensusHistogram;
+    }
+
+    /**
+     * Returns the value of field 'showDbRefTooltip'.
+     * 
+     * @return the value of field 'ShowDbRefTooltip'.
+     */
+    public boolean getShowDbRefTooltip(
+    ) {
+        return this._showDbRefTooltip;
+    }
+
+    /**
+     * Returns the value of field 'showFullId'.
+     * 
+     * @return the value of field 'ShowFullId'.
+     */
+    public boolean getShowFullId(
+    ) {
+        return this._showFullId;
+    }
+
+    /**
+     * Returns the value of field 'showGroupConsensus'.
+     * 
+     * @return the value of field 'ShowGroupConsensus'.
+     */
+    public boolean getShowGroupConsensus(
+    ) {
+        return this._showGroupConsensus;
+    }
+
+    /**
+     * Returns the value of field 'showGroupConservation'.
+     * 
+     * @return the value of field 'ShowGroupConservation'.
+     */
+    public boolean getShowGroupConservation(
+    ) {
+        return this._showGroupConservation;
+    }
+
+    /**
+     * Returns the value of field 'showNPfeatureTooltip'.
+     * 
+     * @return the value of field 'ShowNPfeatureTooltip'.
+     */
+    public boolean getShowNPfeatureTooltip(
+    ) {
+        return this._showNPfeatureTooltip;
+    }
+
+    /**
+     * Returns the value of field 'showSequenceFeatures'.
+     * 
+     * @return the value of field 'ShowSequenceFeatures'.
+     */
+    public boolean getShowSequenceFeatures(
+    ) {
+        return this._showSequenceFeatures;
+    }
+
+    /**
+     * Returns the value of field 'showSequenceLogo'.
+     * 
+     * @return the value of field 'ShowSequenceLogo'.
+     */
+    public boolean getShowSequenceLogo(
+    ) {
+        return this._showSequenceLogo;
+    }
+
+    /**
+     * Returns the value of field 'showText'.
+     * 
+     * @return the value of field 'ShowText'.
+     */
+    public boolean getShowText(
+    ) {
+        return this._showText;
+    }
+
+    /**
+     * Returns the value of field 'showUnconserved'.
+     * 
+     * @return the value of field 'ShowUnconserved'.
+     */
+    public boolean getShowUnconserved(
+    ) {
+        return this._showUnconserved;
+    }
+
+    /**
+     * Returns the value of field 'startRes'.
+     * 
+     * @return the value of field 'StartRes'.
+     */
+    public int getStartRes(
+    ) {
+        return this._startRes;
+    }
+
+    /**
+     * Returns the value of field 'startSeq'.
+     * 
+     * @return the value of field 'StartSeq'.
+     */
+    public int getStartSeq(
+    ) {
+        return this._startSeq;
+    }
+
+    /**
+     * Returns the value of field 'textCol1'.
+     * 
+     * @return the value of field 'TextCol1'.
+     */
+    public int getTextCol1(
+    ) {
+        return this._textCol1;
+    }
+
+    /**
+     * Returns the value of field 'textCol2'.
+     * 
+     * @return the value of field 'TextCol2'.
+     */
+    public int getTextCol2(
+    ) {
+        return this._textCol2;
+    }
+
+    /**
+     * Returns the value of field 'textColThreshold'.
+     * 
+     * @return the value of field 'TextColThreshold'.
+     */
+    public int getTextColThreshold(
+    ) {
+        return this._textColThreshold;
+    }
+
+    /**
+     * Returns the value of field 'title'.
+     * 
+     * @return the value of field 'Title'.
+     */
+    public java.lang.String getTitle(
+    ) {
+        return this._title;
+    }
+
+    /**
+     * Returns the value of field 'viewName'.
+     * 
+     * @return the value of field 'ViewName'.
+     */
+    public java.lang.String getViewName(
+    ) {
+        return this._viewName;
+    }
+
+    /**
+     * Returns the value of field 'width'.
+     * 
+     * @return the value of field 'Width'.
+     */
+    public int getWidth(
+    ) {
+        return this._width;
+    }
+
+    /**
+     * Returns the value of field 'wrapAlignment'.
+     * 
+     * @return the value of field 'WrapAlignment'.
+     */
+    public boolean getWrapAlignment(
+    ) {
+        return this._wrapAlignment;
+    }
+
+    /**
+     * Returns the value of field 'xpos'.
+     * 
+     * @return the value of field 'Xpos'.
+     */
+    public int getXpos(
+    ) {
+        return this._xpos;
+    }
+
+    /**
+     * Returns the value of field 'ypos'.
+     * 
+     * @return the value of field 'Ypos'.
+     */
+    public int getYpos(
+    ) {
+        return this._ypos;
+    }
+
+    /**
+     * Method hasCentreColumnLabels.
+     * 
+     * @return true if at least one CentreColumnLabels has been adde
+     */
+    public boolean hasCentreColumnLabels(
+    ) {
+        return this._has_centreColumnLabels;
+    }
+
+    /**
+     * Method hasConsThreshold.
+     * 
+     * @return true if at least one ConsThreshold has been added
+     */
+    public boolean hasConsThreshold(
+    ) {
+        return this._has_consThreshold;
+    }
+
+    /**
+     * Method hasConservationSelected.
+     * 
+     * @return true if at least one ConservationSelected has been
+     * added
+     */
+    public boolean hasConservationSelected(
+    ) {
+        return this._has_conservationSelected;
+    }
+
+    /**
+     * Method hasFollowHighlight.
+     * 
+     * @return true if at least one FollowHighlight has been added
+     */
+    public boolean hasFollowHighlight(
+    ) {
+        return this._has_followHighlight;
+    }
+
+    /**
+     * Method hasFollowSelection.
+     * 
+     * @return true if at least one FollowSelection has been added
+     */
+    public boolean hasFollowSelection(
+    ) {
+        return this._has_followSelection;
+    }
+
+    /**
+     * Method hasFontSize.
+     * 
+     * @return true if at least one FontSize has been added
+     */
+    public boolean hasFontSize(
+    ) {
+        return this._has_fontSize;
+    }
+
+    /**
+     * Method hasFontStyle.
+     * 
+     * @return true if at least one FontStyle has been added
+     */
+    public boolean hasFontStyle(
+    ) {
+        return this._has_fontStyle;
+    }
+
+    /**
+     * Method hasGatheredViews.
+     * 
+     * @return true if at least one GatheredViews has been added
+     */
+    public boolean hasGatheredViews(
+    ) {
+        return this._has_gatheredViews;
+    }
+
+    /**
+     * Method hasHeight.
+     * 
+     * @return true if at least one Height has been added
+     */
+    public boolean hasHeight(
+    ) {
+        return this._has_height;
+    }
+
+    /**
+     * Method hasIgnoreGapsinConsensus.
+     * 
+     * @return true if at least one IgnoreGapsinConsensus has been
+     * added
+     */
+    public boolean hasIgnoreGapsinConsensus(
+    ) {
+        return this._has_ignoreGapsinConsensus;
+    }
+
+    /**
+     * Method hasNormaliseSequenceLogo.
+     * 
+     * @return true if at least one NormaliseSequenceLogo has been
+     * added
+     */
+    public boolean hasNormaliseSequenceLogo(
+    ) {
+        return this._has_normaliseSequenceLogo;
+    }
+
+    /**
+     * Method hasPidSelected.
+     * 
+     * @return true if at least one PidSelected has been added
+     */
+    public boolean hasPidSelected(
+    ) {
+        return this._has_pidSelected;
+    }
+
+    /**
+     * Method hasPidThreshold.
+     * 
+     * @return true if at least one PidThreshold has been added
+     */
+    public boolean hasPidThreshold(
+    ) {
+        return this._has_pidThreshold;
+    }
+
+    /**
+     * Method hasRenderGaps.
+     * 
+     * @return true if at least one RenderGaps has been added
+     */
+    public boolean hasRenderGaps(
+    ) {
+        return this._has_renderGaps;
+    }
+
+    /**
+     * Method hasRightAlignIds.
+     * 
+     * @return true if at least one RightAlignIds has been added
+     */
+    public boolean hasRightAlignIds(
+    ) {
+        return this._has_rightAlignIds;
+    }
+
+    /**
+     * Method hasShowAnnotation.
+     * 
+     * @return true if at least one ShowAnnotation has been added
+     */
+    public boolean hasShowAnnotation(
+    ) {
+        return this._has_showAnnotation;
+    }
+
+    /**
+     * Method hasShowBoxes.
+     * 
+     * @return true if at least one ShowBoxes has been added
+     */
+    public boolean hasShowBoxes(
+    ) {
+        return this._has_showBoxes;
+    }
+
+    /**
+     * Method hasShowColourText.
+     * 
+     * @return true if at least one ShowColourText has been added
+     */
+    public boolean hasShowColourText(
+    ) {
+        return this._has_showColourText;
+    }
+
+    /**
+     * Method hasShowConsensusHistogram.
+     * 
+     * @return true if at least one ShowConsensusHistogram has been
+     * added
+     */
+    public boolean hasShowConsensusHistogram(
+    ) {
+        return this._has_showConsensusHistogram;
+    }
+
+    /**
+     * Method hasShowDbRefTooltip.
+     * 
+     * @return true if at least one ShowDbRefTooltip has been added
+     */
+    public boolean hasShowDbRefTooltip(
+    ) {
+        return this._has_showDbRefTooltip;
+    }
+
+    /**
+     * Method hasShowFullId.
+     * 
+     * @return true if at least one ShowFullId has been added
+     */
+    public boolean hasShowFullId(
+    ) {
+        return this._has_showFullId;
+    }
+
+    /**
+     * Method hasShowGroupConsensus.
+     * 
+     * @return true if at least one ShowGroupConsensus has been adde
+     */
+    public boolean hasShowGroupConsensus(
+    ) {
+        return this._has_showGroupConsensus;
+    }
+
+    /**
+     * Method hasShowGroupConservation.
+     * 
+     * @return true if at least one ShowGroupConservation has been
+     * added
+     */
+    public boolean hasShowGroupConservation(
+    ) {
+        return this._has_showGroupConservation;
+    }
+
+    /**
+     * Method hasShowNPfeatureTooltip.
+     * 
+     * @return true if at least one ShowNPfeatureTooltip has been
+     * added
+     */
+    public boolean hasShowNPfeatureTooltip(
+    ) {
+        return this._has_showNPfeatureTooltip;
+    }
+
+    /**
+     * Method hasShowSequenceFeatures.
+     * 
+     * @return true if at least one ShowSequenceFeatures has been
+     * added
+     */
+    public boolean hasShowSequenceFeatures(
+    ) {
+        return this._has_showSequenceFeatures;
+    }
+
+    /**
+     * Method hasShowSequenceLogo.
+     * 
+     * @return true if at least one ShowSequenceLogo has been added
+     */
+    public boolean hasShowSequenceLogo(
+    ) {
+        return this._has_showSequenceLogo;
+    }
+
+    /**
+     * Method hasShowText.
+     * 
+     * @return true if at least one ShowText has been added
+     */
+    public boolean hasShowText(
+    ) {
+        return this._has_showText;
+    }
+
+    /**
+     * Method hasShowUnconserved.
+     * 
+     * @return true if at least one ShowUnconserved has been added
+     */
+    public boolean hasShowUnconserved(
+    ) {
+        return this._has_showUnconserved;
+    }
+
+    /**
+     * Method hasStartRes.
+     * 
+     * @return true if at least one StartRes has been added
+     */
+    public boolean hasStartRes(
+    ) {
+        return this._has_startRes;
+    }
+
+    /**
+     * Method hasStartSeq.
+     * 
+     * @return true if at least one StartSeq has been added
+     */
+    public boolean hasStartSeq(
+    ) {
+        return this._has_startSeq;
+    }
+
+    /**
+     * Method hasTextCol1.
+     * 
+     * @return true if at least one TextCol1 has been added
+     */
+    public boolean hasTextCol1(
+    ) {
+        return this._has_textCol1;
+    }
+
+    /**
+     * Method hasTextCol2.
+     * 
+     * @return true if at least one TextCol2 has been added
+     */
+    public boolean hasTextCol2(
+    ) {
+        return this._has_textCol2;
+    }
+
+    /**
+     * Method hasTextColThreshold.
+     * 
+     * @return true if at least one TextColThreshold has been added
+     */
+    public boolean hasTextColThreshold(
+    ) {
+        return this._has_textColThreshold;
+    }
+
+    /**
+     * Method hasWidth.
+     * 
+     * @return true if at least one Width has been added
+     */
+    public boolean hasWidth(
+    ) {
+        return this._has_width;
+    }
+
+    /**
+     * Method hasWrapAlignment.
+     * 
+     * @return true if at least one WrapAlignment has been added
+     */
+    public boolean hasWrapAlignment(
+    ) {
+        return this._has_wrapAlignment;
+    }
+
+    /**
+     * Method hasXpos.
+     * 
+     * @return true if at least one Xpos has been added
+     */
+    public boolean hasXpos(
+    ) {
+        return this._has_xpos;
+    }
+
+    /**
+     * Method hasYpos.
+     * 
+     * @return true if at least one Ypos has been added
+     */
+    public boolean hasYpos(
+    ) {
+        return this._has_ypos;
+    }
+
+    /**
+     * Returns the value of field 'centreColumnLabels'.
+     * 
+     * @return the value of field 'CentreColumnLabels'.
+     */
+    public boolean isCentreColumnLabels(
+    ) {
+        return this._centreColumnLabels;
+    }
+
+    /**
+     * Returns the value of field 'conservationSelected'.
+     * 
+     * @return the value of field 'ConservationSelected'.
+     */
+    public boolean isConservationSelected(
+    ) {
+        return this._conservationSelected;
+    }
+
+    /**
+     * Returns the value of field 'followHighlight'.
+     * 
+     * @return the value of field 'FollowHighlight'.
+     */
+    public boolean isFollowHighlight(
+    ) {
+        return this._followHighlight;
+    }
+
+    /**
+     * Returns the value of field 'followSelection'.
+     * 
+     * @return the value of field 'FollowSelection'.
+     */
+    public boolean isFollowSelection(
+    ) {
+        return this._followSelection;
+    }
+
+    /**
+     * Returns the value of field 'gatheredViews'.
+     * 
+     * @return the value of field 'GatheredViews'.
+     */
+    public boolean isGatheredViews(
+    ) {
+        return this._gatheredViews;
+    }
+
+    /**
+     * Returns the value of field 'ignoreGapsinConsensus'.
+     * 
+     * @return the value of field 'IgnoreGapsinConsensus'.
+     */
+    public boolean isIgnoreGapsinConsensus(
+    ) {
+        return this._ignoreGapsinConsensus;
+    }
+
+    /**
+     * Returns the value of field 'normaliseSequenceLogo'.
+     * 
+     * @return the value of field 'NormaliseSequenceLogo'.
+     */
+    public boolean isNormaliseSequenceLogo(
+    ) {
+        return this._normaliseSequenceLogo;
+    }
+
+    /**
+     * Returns the value of field 'pidSelected'.
+     * 
+     * @return the value of field 'PidSelected'.
+     */
+    public boolean isPidSelected(
+    ) {
+        return this._pidSelected;
+    }
+
+    /**
+     * Returns the value of field 'renderGaps'.
+     * 
+     * @return the value of field 'RenderGaps'.
+     */
+    public boolean isRenderGaps(
+    ) {
+        return this._renderGaps;
+    }
+
+    /**
+     * Returns the value of field 'rightAlignIds'.
+     * 
+     * @return the value of field 'RightAlignIds'.
+     */
+    public boolean isRightAlignIds(
+    ) {
+        return this._rightAlignIds;
+    }
+
+    /**
+     * Returns the value of field 'showAnnotation'.
+     * 
+     * @return the value of field 'ShowAnnotation'.
+     */
+    public boolean isShowAnnotation(
+    ) {
+        return this._showAnnotation;
+    }
+
+    /**
+     * Returns the value of field 'showBoxes'.
+     * 
+     * @return the value of field 'ShowBoxes'.
+     */
+    public boolean isShowBoxes(
+    ) {
+        return this._showBoxes;
+    }
+
+    /**
+     * Returns the value of field 'showColourText'.
+     * 
+     * @return the value of field 'ShowColourText'.
+     */
+    public boolean isShowColourText(
+    ) {
+        return this._showColourText;
+    }
+
+    /**
+     * Returns the value of field 'showConsensusHistogram'.
+     * 
+     * @return the value of field 'ShowConsensusHistogram'.
+     */
+    public boolean isShowConsensusHistogram(
+    ) {
+        return this._showConsensusHistogram;
+    }
+
+    /**
+     * Returns the value of field 'showDbRefTooltip'.
+     * 
+     * @return the value of field 'ShowDbRefTooltip'.
+     */
+    public boolean isShowDbRefTooltip(
+    ) {
+        return this._showDbRefTooltip;
+    }
+
+    /**
+     * Returns the value of field 'showFullId'.
+     * 
+     * @return the value of field 'ShowFullId'.
+     */
+    public boolean isShowFullId(
+    ) {
+        return this._showFullId;
+    }
+
+    /**
+     * Returns the value of field 'showGroupConsensus'.
+     * 
+     * @return the value of field 'ShowGroupConsensus'.
+     */
+    public boolean isShowGroupConsensus(
+    ) {
+        return this._showGroupConsensus;
+    }
+
+    /**
+     * Returns the value of field 'showGroupConservation'.
+     * 
+     * @return the value of field 'ShowGroupConservation'.
+     */
+    public boolean isShowGroupConservation(
+    ) {
+        return this._showGroupConservation;
+    }
+
+    /**
+     * Returns the value of field 'showNPfeatureTooltip'.
+     * 
+     * @return the value of field 'ShowNPfeatureTooltip'.
+     */
+    public boolean isShowNPfeatureTooltip(
+    ) {
+        return this._showNPfeatureTooltip;
+    }
+
+    /**
+     * Returns the value of field 'showSequenceFeatures'.
+     * 
+     * @return the value of field 'ShowSequenceFeatures'.
+     */
+    public boolean isShowSequenceFeatures(
+    ) {
+        return this._showSequenceFeatures;
+    }
+
+    /**
+     * Returns the value of field 'showSequenceLogo'.
+     * 
+     * @return the value of field 'ShowSequenceLogo'.
+     */
+    public boolean isShowSequenceLogo(
+    ) {
+        return this._showSequenceLogo;
+    }
+
+    /**
+     * Returns the value of field 'showText'.
+     * 
+     * @return the value of field 'ShowText'.
+     */
+    public boolean isShowText(
+    ) {
+        return this._showText;
+    }
+
+    /**
+     * Returns the value of field 'showUnconserved'.
+     * 
+     * @return the value of field 'ShowUnconserved'.
+     */
+    public boolean isShowUnconserved(
+    ) {
+        return this._showUnconserved;
+    }
+
+    /**
+     * Method isValid.
+     * 
+     * @return true if this object is valid according to the schema
+     */
+    public boolean isValid(
+    ) {
+        try {
+            validate();
+        } catch (org.exolab.castor.xml.ValidationException vex) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns the value of field 'wrapAlignment'.
+     * 
+     * @return the value of field 'WrapAlignment'.
+     */
+    public boolean isWrapAlignment(
+    ) {
+        return this._wrapAlignment;
+    }
+
+    /**
+     * 
+     * 
+     * @param out
+     * @throws org.exolab.castor.xml.MarshalException if object is
+     * null or if any SAXException is thrown during marshaling
+     * @throws org.exolab.castor.xml.ValidationException if this
+     * object is an invalid instance according to the schema
+     */
+    public void marshal(
+            final java.io.Writer out)
+    throws org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
+        Marshaller.marshal(this, out);
+    }
+
+    /**
+     * 
+     * 
+     * @param handler
+     * @throws java.io.IOException if an IOException occurs during
+     * marshaling
+     * @throws org.exolab.castor.xml.ValidationException if this
+     * object is an invalid instance according to the schema
+     * @throws org.exolab.castor.xml.MarshalException if object is
+     * null or if any SAXException is thrown during marshaling
+     */
+    public void marshal(
+            final org.xml.sax.ContentHandler handler)
+    throws java.io.IOException, org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
+        Marshaller.marshal(this, handler);
+    }
+
+    /**
+     */
+    public void removeAllCalcIdParam(
+    ) {
+        this._calcIdParamList.clear();
+    }
+
+    /**
+     */
+    public void removeAllHiddenColumns(
+    ) {
+        this._hiddenColumnsList.clear();
+    }
+
+    /**
+     * Method removeCalcIdParam.
+     * 
+     * @param vCalcIdParam
+     * @return true if the object was removed from the collection.
+     */
+    public boolean removeCalcIdParam(
+            final jalview.schemabinding.version2.CalcIdParam vCalcIdParam) {
+        boolean removed = _calcIdParamList.remove(vCalcIdParam);
+        return removed;
+    }
+
+    /**
+     * Method removeCalcIdParamAt.
+     * 
+     * @param index
+     * @return the element removed from the collection
+     */
+    public jalview.schemabinding.version2.CalcIdParam removeCalcIdParamAt(
+            final int index) {
+        java.lang.Object obj = this._calcIdParamList.remove(index);
+        return (jalview.schemabinding.version2.CalcIdParam) obj;
+    }
+
+    /**
+     * Method removeHiddenColumns.
+     * 
+     * @param vHiddenColumns
+     * @return true if the object was removed from the collection.
+     */
+    public boolean removeHiddenColumns(
+            final jalview.schemabinding.version2.HiddenColumns vHiddenColumns) {
+        boolean removed = _hiddenColumnsList.remove(vHiddenColumns);
+        return removed;
+    }
+
+    /**
+     * Method removeHiddenColumnsAt.
+     * 
+     * @param index
+     * @return the element removed from the collection
+     */
+    public jalview.schemabinding.version2.HiddenColumns removeHiddenColumnsAt(
+            final int index) {
+        java.lang.Object obj = this._hiddenColumnsList.remove(index);
+        return (jalview.schemabinding.version2.HiddenColumns) obj;
+    }
+
+    /**
+     * Sets the value of field 'annotationColours'.
+     * 
+     * @param annotationColours the value of field
+     * 'annotationColours'.
+     */
+    public void setAnnotationColours(
+            final jalview.schemabinding.version2.AnnotationColours annotationColours) {
+        this._annotationColours = annotationColours;
+    }
+
+    /**
+     * Sets the value of field 'bgColour'.
+     * 
+     * @param bgColour the value of field 'bgColour'.
+     */
+    public void setBgColour(
+            final java.lang.String bgColour) {
+        this._bgColour = bgColour;
+    }
+
+    /**
+     * 
+     * 
+     * @param index
+     * @param vCalcIdParam
+     * @throws java.lang.IndexOutOfBoundsException if the index
+     * given is outside the bounds of the collection
+     */
+    public void setCalcIdParam(
+            final int index,
+            final jalview.schemabinding.version2.CalcIdParam vCalcIdParam)
+    throws java.lang.IndexOutOfBoundsException {
+        // check bounds for index
+        if (index < 0 || index >= this._calcIdParamList.size()) {
+            throw new IndexOutOfBoundsException("setCalcIdParam: Index value '" + index + "' not in range [0.." + (this._calcIdParamList.size() - 1) + "]");
+        }
+        
+        this._calcIdParamList.set(index, vCalcIdParam);
+    }
+
+    /**
+     * 
+     * 
+     * @param vCalcIdParamArray
+     */
+    public void setCalcIdParam(
+            final jalview.schemabinding.version2.CalcIdParam[] vCalcIdParamArray) {
+        //-- copy array
+        _calcIdParamList.clear();
+        
+        for (int i = 0; i < vCalcIdParamArray.length; i++) {
+                this._calcIdParamList.add(vCalcIdParamArray[i]);
+        }
+    }
+
+    /**
+     * Sets the value of field 'centreColumnLabels'.
+     * 
+     * @param centreColumnLabels the value of field
+     * 'centreColumnLabels'.
+     */
+    public void setCentreColumnLabels(
+            final boolean centreColumnLabels) {
+        this._centreColumnLabels = centreColumnLabels;
+        this._has_centreColumnLabels = true;
+    }
+
+    /**
+     * Sets the value of field 'complementId'. The field
+     * 'complementId' has the following description: The viewport
+     * id of this viewport's (cdna/protein) coding complement, if
+     * any
+     *  
+     * 
+     * @param complementId the value of field 'complementId'.
+     */
+    public void setComplementId(
+            final java.lang.String complementId) {
+        this._complementId = complementId;
+    }
+
+    /**
+     * Sets the value of field 'consThreshold'.
+     * 
+     * @param consThreshold the value of field 'consThreshold'.
+     */
+    public void setConsThreshold(
+            final int consThreshold) {
+        this._consThreshold = consThreshold;
+        this._has_consThreshold = true;
+    }
+
+    /**
+     * Sets the value of field 'conservationSelected'.
+     * 
+     * @param conservationSelected the value of field
+     * 'conservationSelected'.
+     */
+    public void setConservationSelected(
+            final boolean conservationSelected) {
+        this._conservationSelected = conservationSelected;
+        this._has_conservationSelected = true;
+    }
+
+    /**
+     * Sets the value of field 'followHighlight'.
+     * 
+     * @param followHighlight the value of field 'followHighlight'.
+     */
+    public void setFollowHighlight(
+            final boolean followHighlight) {
+        this._followHighlight = followHighlight;
+        this._has_followHighlight = true;
+    }
+
+    /**
+     * Sets the value of field 'followSelection'.
+     * 
+     * @param followSelection the value of field 'followSelection'.
+     */
+    public void setFollowSelection(
+            final boolean followSelection) {
+        this._followSelection = followSelection;
+        this._has_followSelection = true;
+    }
+
+    /**
+     * Sets the value of field 'fontName'.
+     * 
+     * @param fontName the value of field 'fontName'.
+     */
+    public void setFontName(
+            final java.lang.String fontName) {
+        this._fontName = fontName;
+    }
+
+    /**
+     * Sets the value of field 'fontSize'.
+     * 
+     * @param fontSize the value of field 'fontSize'.
+     */
+    public void setFontSize(
+            final int fontSize) {
+        this._fontSize = fontSize;
+        this._has_fontSize = true;
+    }
+
+    /**
+     * Sets the value of field 'fontStyle'.
+     * 
+     * @param fontStyle the value of field 'fontStyle'.
+     */
+    public void setFontStyle(
+            final int fontStyle) {
+        this._fontStyle = fontStyle;
+        this._has_fontStyle = true;
+    }
+
+    /**
+     * Sets the value of field 'gatheredViews'.
+     * 
+     * @param gatheredViews the value of field 'gatheredViews'.
+     */
+    public void setGatheredViews(
+            final boolean gatheredViews) {
+        this._gatheredViews = gatheredViews;
+        this._has_gatheredViews = true;
+    }
+
+    /**
+     * Sets the value of field 'height'.
+     * 
+     * @param height the value of field 'height'.
+     */
+    public void setHeight(
+            final int height) {
+        this._height = height;
+        this._has_height = true;
+    }
+
+    /**
+     * 
+     * 
+     * @param index
+     * @param vHiddenColumns
+     * @throws java.lang.IndexOutOfBoundsException if the index
+     * given is outside the bounds of the collection
+     */
+    public void setHiddenColumns(
+            final int index,
+            final jalview.schemabinding.version2.HiddenColumns vHiddenColumns)
+    throws java.lang.IndexOutOfBoundsException {
+        // check bounds for index
+        if (index < 0 || index >= this._hiddenColumnsList.size()) {
+            throw new IndexOutOfBoundsException("setHiddenColumns: Index value '" + index + "' not in range [0.." + (this._hiddenColumnsList.size() - 1) + "]");
+        }
+        
+        this._hiddenColumnsList.set(index, vHiddenColumns);
+    }
+
+    /**
+     * 
+     * 
+     * @param vHiddenColumnsArray
+     */
+    public void setHiddenColumns(
+            final jalview.schemabinding.version2.HiddenColumns[] vHiddenColumnsArray) {
+        //-- copy array
+        _hiddenColumnsList.clear();
+        
+        for (int i = 0; i < vHiddenColumnsArray.length; i++) {
+                this._hiddenColumnsList.add(vHiddenColumnsArray[i]);
+        }
+    }
+
+    /**
+     * Sets the value of field 'id'. The field 'id' has the
+     * following description: unique id used by jalview to
+     *  synchronize between stored and
+     *  instantiated views
+     *  
+     * 
+     * @param id the value of field 'id'.
+     */
+    public void setId(
+            final java.lang.String id) {
+        this._id = id;
+    }
+
+    /**
+     * Sets the value of field 'ignoreGapsinConsensus'.
+     * 
+     * @param ignoreGapsinConsensus the value of field
+     * 'ignoreGapsinConsensus'.
+     */
+    public void setIgnoreGapsinConsensus(
+            final boolean ignoreGapsinConsensus) {
+        this._ignoreGapsinConsensus = ignoreGapsinConsensus;
+        this._has_ignoreGapsinConsensus = true;
+    }
+
+    /**
+     * Sets the value of field 'normaliseSequenceLogo'.
+     * 
+     * @param normaliseSequenceLogo the value of field
+     * 'normaliseSequenceLogo'.
+     */
+    public void setNormaliseSequenceLogo(
+            final boolean normaliseSequenceLogo) {
+        this._normaliseSequenceLogo = normaliseSequenceLogo;
+        this._has_normaliseSequenceLogo = true;
+    }
+
+    /**
+     * Sets the value of field 'pidSelected'.
+     * 
+     * @param pidSelected the value of field 'pidSelected'.
+     */
+    public void setPidSelected(
+            final boolean pidSelected) {
+        this._pidSelected = pidSelected;
+        this._has_pidSelected = true;
+    }
+
+    /**
+     * Sets the value of field 'pidThreshold'.
+     * 
+     * @param pidThreshold the value of field 'pidThreshold'.
+     */
+    public void setPidThreshold(
+            final int pidThreshold) {
+        this._pidThreshold = pidThreshold;
+        this._has_pidThreshold = true;
+    }
+
+    /**
+     * Sets the value of field 'renderGaps'.
+     * 
+     * @param renderGaps the value of field 'renderGaps'.
+     */
+    public void setRenderGaps(
+            final boolean renderGaps) {
+        this._renderGaps = renderGaps;
+        this._has_renderGaps = true;
+    }
+
+    /**
+     * Sets the value of field 'rightAlignIds'.
+     * 
+     * @param rightAlignIds the value of field 'rightAlignIds'.
+     */
+    public void setRightAlignIds(
+            final boolean rightAlignIds) {
+        this._rightAlignIds = rightAlignIds;
+        this._has_rightAlignIds = true;
+    }
+
+    /**
+     * Sets the value of field 'sequenceSetId'.
+     * 
+     * @param sequenceSetId the value of field 'sequenceSetId'.
+     */
+    public void setSequenceSetId(
+            final java.lang.String sequenceSetId) {
+        this._sequenceSetId = sequenceSetId;
+    }
+
+    /**
+     * Sets the value of field 'showAnnotation'.
+     * 
+     * @param showAnnotation the value of field 'showAnnotation'.
+     */
+    public void setShowAnnotation(
+            final boolean showAnnotation) {
+        this._showAnnotation = showAnnotation;
+        this._has_showAnnotation = true;
+    }
+
+    /**
+     * Sets the value of field 'showBoxes'.
+     * 
+     * @param showBoxes the value of field 'showBoxes'.
+     */
+    public void setShowBoxes(
+            final boolean showBoxes) {
+        this._showBoxes = showBoxes;
+        this._has_showBoxes = true;
+    }
+
+    /**
+     * Sets the value of field 'showColourText'.
+     * 
+     * @param showColourText the value of field 'showColourText'.
+     */
+    public void setShowColourText(
+            final boolean showColourText) {
+        this._showColourText = showColourText;
+        this._has_showColourText = true;
+    }
+
+    /**
+     * Sets the value of field 'showConsensusHistogram'.
+     * 
+     * @param showConsensusHistogram the value of field
+     * 'showConsensusHistogram'.
+     */
+    public void setShowConsensusHistogram(
+            final boolean showConsensusHistogram) {
+        this._showConsensusHistogram = showConsensusHistogram;
+        this._has_showConsensusHistogram = true;
+    }
+
+    /**
+     * Sets the value of field 'showDbRefTooltip'.
+     * 
+     * @param showDbRefTooltip the value of field 'showDbRefTooltip'
+     */
+    public void setShowDbRefTooltip(
+            final boolean showDbRefTooltip) {
+        this._showDbRefTooltip = showDbRefTooltip;
+        this._has_showDbRefTooltip = true;
+    }
+
+    /**
+     * Sets the value of field 'showFullId'.
+     * 
+     * @param showFullId the value of field 'showFullId'.
+     */
+    public void setShowFullId(
+            final boolean showFullId) {
+        this._showFullId = showFullId;
+        this._has_showFullId = true;
+    }
+
+    /**
+     * Sets the value of field 'showGroupConsensus'.
+     * 
+     * @param showGroupConsensus the value of field
+     * 'showGroupConsensus'.
+     */
+    public void setShowGroupConsensus(
+            final boolean showGroupConsensus) {
+        this._showGroupConsensus = showGroupConsensus;
+        this._has_showGroupConsensus = true;
+    }
+
+    /**
+     * Sets the value of field 'showGroupConservation'.
+     * 
+     * @param showGroupConservation the value of field
+     * 'showGroupConservation'.
+     */
+    public void setShowGroupConservation(
+            final boolean showGroupConservation) {
+        this._showGroupConservation = showGroupConservation;
+        this._has_showGroupConservation = true;
+    }
+
+    /**
+     * Sets the value of field 'showNPfeatureTooltip'.
+     * 
+     * @param showNPfeatureTooltip the value of field
+     * 'showNPfeatureTooltip'.
+     */
+    public void setShowNPfeatureTooltip(
+            final boolean showNPfeatureTooltip) {
+        this._showNPfeatureTooltip = showNPfeatureTooltip;
+        this._has_showNPfeatureTooltip = true;
+    }
+
+    /**
+     * Sets the value of field 'showSequenceFeatures'.
+     * 
+     * @param showSequenceFeatures the value of field
+     * 'showSequenceFeatures'.
+     */
+    public void setShowSequenceFeatures(
+            final boolean showSequenceFeatures) {
+        this._showSequenceFeatures = showSequenceFeatures;
+        this._has_showSequenceFeatures = true;
+    }
+
+    /**
+     * Sets the value of field 'showSequenceLogo'.
+     * 
+     * @param showSequenceLogo the value of field 'showSequenceLogo'
+     */
+    public void setShowSequenceLogo(
+            final boolean showSequenceLogo) {
+        this._showSequenceLogo = showSequenceLogo;
+        this._has_showSequenceLogo = true;
+    }
+
+    /**
+     * Sets the value of field 'showText'.
+     * 
+     * @param showText the value of field 'showText'.
+     */
+    public void setShowText(
+            final boolean showText) {
+        this._showText = showText;
+        this._has_showText = true;
+    }
+
+    /**
+     * Sets the value of field 'showUnconserved'.
+     * 
+     * @param showUnconserved the value of field 'showUnconserved'.
+     */
+    public void setShowUnconserved(
+            final boolean showUnconserved) {
+        this._showUnconserved = showUnconserved;
+        this._has_showUnconserved = true;
+    }
+
+    /**
+     * Sets the value of field 'startRes'.
+     * 
+     * @param startRes the value of field 'startRes'.
+     */
+    public void setStartRes(
+            final int startRes) {
+        this._startRes = startRes;
+        this._has_startRes = true;
+    }
+
+    /**
+     * Sets the value of field 'startSeq'.
+     * 
+     * @param startSeq the value of field 'startSeq'.
+     */
+    public void setStartSeq(
+            final int startSeq) {
+        this._startSeq = startSeq;
+        this._has_startSeq = true;
+    }
+
+    /**
+     * Sets the value of field 'textCol1'.
+     * 
+     * @param textCol1 the value of field 'textCol1'.
+     */
+    public void setTextCol1(
+            final int textCol1) {
+        this._textCol1 = textCol1;
+        this._has_textCol1 = true;
+    }
+
+    /**
+     * Sets the value of field 'textCol2'.
+     * 
+     * @param textCol2 the value of field 'textCol2'.
+     */
+    public void setTextCol2(
+            final int textCol2) {
+        this._textCol2 = textCol2;
+        this._has_textCol2 = true;
+    }
+
+    /**
+     * Sets the value of field 'textColThreshold'.
+     * 
+     * @param textColThreshold the value of field 'textColThreshold'
+     */
+    public void setTextColThreshold(
+            final int textColThreshold) {
+        this._textColThreshold = textColThreshold;
+        this._has_textColThreshold = true;
+    }
+
+    /**
+     * Sets the value of field 'title'.
+     * 
+     * @param title the value of field 'title'.
+     */
+    public void setTitle(
+            final java.lang.String title) {
+        this._title = title;
+    }
+
+    /**
+     * Sets the value of field 'viewName'.
+     * 
+     * @param viewName the value of field 'viewName'.
+     */
+    public void setViewName(
+            final java.lang.String viewName) {
+        this._viewName = viewName;
+    }
+
+    /**
+     * Sets the value of field 'width'.
+     * 
+     * @param width the value of field 'width'.
+     */
+    public void setWidth(
+            final int width) {
+        this._width = width;
+        this._has_width = true;
+    }
+
+    /**
+     * Sets the value of field 'wrapAlignment'.
+     * 
+     * @param wrapAlignment the value of field 'wrapAlignment'.
+     */
+    public void setWrapAlignment(
+            final boolean wrapAlignment) {
+        this._wrapAlignment = wrapAlignment;
+        this._has_wrapAlignment = true;
+    }
+
+    /**
+     * Sets the value of field 'xpos'.
+     * 
+     * @param xpos the value of field 'xpos'.
+     */
+    public void setXpos(
+            final int xpos) {
+        this._xpos = xpos;
+        this._has_xpos = true;
+    }
+
+    /**
+     * Sets the value of field 'ypos'.
+     * 
+     * @param ypos the value of field 'ypos'.
+     */
+    public void setYpos(
+            final int ypos) {
+        this._ypos = ypos;
+        this._has_ypos = true;
+    }
+
+    /**
+     * Method unmarshal.
+     * 
+     * @param reader
+     * @throws org.exolab.castor.xml.MarshalException if object is
+     * null or if any SAXException is thrown during marshaling
+     * @throws org.exolab.castor.xml.ValidationException if this
+     * object is an invalid instance according to the schema
+     * @return the unmarshaled
+     * jalview.schemabinding.version2.Viewport
+     */
+    public static jalview.schemabinding.version2.Viewport unmarshal(
+            final java.io.Reader reader)
+    throws org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
+        return (jalview.schemabinding.version2.Viewport) Unmarshaller.unmarshal(jalview.schemabinding.version2.Viewport.class, reader);
+    }
+
+    /**
+     * 
+     * 
+     * @throws org.exolab.castor.xml.ValidationException if this
+     * object is an invalid instance according to the schema
+     */
+    public void validate(
+    )
+    throws org.exolab.castor.xml.ValidationException {
+        org.exolab.castor.xml.Validator validator = new org.exolab.castor.xml.Validator();
+        validator.validate(this);
+    }
 
 }
index 2204639..81615a1 100644 (file)
@@ -20,8 +20,8 @@
  */
 package jalview.schemabinding.version2.descriptors;
 
-//---------------------------------/
-//- Imported classes and packages -/
+  //---------------------------------/
+ //- Imported classes and packages -/
 //---------------------------------/
 
 import jalview.schemabinding.version2.Viewport;
@@ -31,2600 +31,2061 @@ import jalview.schemabinding.version2.Viewport;
  * 
  * @version $Revision$ $Date$
  */
-public class ViewportDescriptor extends
-        org.exolab.castor.xml.util.XMLClassDescriptorImpl
-{
-
-  // --------------------------/
-  // - Class/Member Variables -/
-  // --------------------------/
-
-  /**
-   * Field _elementDefinition.
-   */
-  private boolean _elementDefinition;
-
-  /**
-   * Field _nsPrefix.
-   */
-  private java.lang.String _nsPrefix;
-
-  /**
-   * Field _nsURI.
-   */
-  private java.lang.String _nsURI;
-
-  /**
-   * Field _xmlName.
-   */
-  private java.lang.String _xmlName;
-
-  // ----------------/
-  // - Constructors -/
-  // ----------------/
-
-  public ViewportDescriptor()
-  {
-    super();
-    _nsURI = "www.jalview.org";
-    _xmlName = "Viewport";
-    _elementDefinition = true;
-
-    // -- set grouping compositor
-    setCompositorAsSequence();
-    org.exolab.castor.xml.util.XMLFieldDescriptorImpl desc = null;
-    org.exolab.castor.mapping.FieldHandler handler = null;
-    org.exolab.castor.xml.FieldValidator fieldValidator = null;
-    // -- initialize attribute descriptors
-
-    // -- _conservationSelected
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_conservationSelected",
-            "conservationSelected",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasConservationSelected())
-        {
-          return null;
-        }
-        return (target.getConservationSelected() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteConservationSelected();
-            return;
-          }
-          target.setConservationSelected(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _conservationSelected
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
+public class ViewportDescriptor extends org.exolab.castor.xml.util.XMLClassDescriptorImpl {
+
+
+      //--------------------------/
+     //- Class/Member Variables -/
+    //--------------------------/
+
+    /**
+     * Field _elementDefinition.
+     */
+    private boolean _elementDefinition;
+
+    /**
+     * Field _nsPrefix.
+     */
+    private java.lang.String _nsPrefix;
+
+    /**
+     * Field _nsURI.
+     */
+    private java.lang.String _nsURI;
+
+    /**
+     * Field _xmlName.
+     */
+    private java.lang.String _xmlName;
+
+
+      //----------------/
+     //- Constructors -/
+    //----------------/
+
+    public ViewportDescriptor() {
+        super();
+        _nsURI = "www.jalview.org";
+        _xmlName = "Viewport";
+        _elementDefinition = true;
+        
+        //-- set grouping compositor
+        setCompositorAsSequence();
+        org.exolab.castor.xml.util.XMLFieldDescriptorImpl  desc           = null;
+        org.exolab.castor.mapping.FieldHandler             handler        = null;
+        org.exolab.castor.xml.FieldValidator               fieldValidator = null;
+        //-- initialize attribute descriptors
+        
+        //-- _conservationSelected
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_conservationSelected", "conservationSelected", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasConservationSelected()) { return null; }
+                return (target.getConservationSelected() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteConservationSelected();
+                        return;
+                    }
+                    target.setConservationSelected( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _conservationSelected
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _pidSelected
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_pidSelected", "pidSelected", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasPidSelected()) { return null; }
+                return (target.getPidSelected() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deletePidSelected();
+                        return;
+                    }
+                    target.setPidSelected( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _pidSelected
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _bgColour
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.String.class, "_bgColour", "bgColour", org.exolab.castor.xml.NodeType.Attribute);
+        desc.setImmutable(true);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                return target.getBgColour();
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.setBgColour( (java.lang.String) value);
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _bgColour
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.StringValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.StringValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setWhiteSpace("preserve");
+        }
+        desc.setValidator(fieldValidator);
+        //-- _consThreshold
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_consThreshold", "consThreshold", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasConsThreshold()) { return null; }
+                return new java.lang.Integer(target.getConsThreshold());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteConsThreshold();
+                        return;
+                    }
+                    target.setConsThreshold( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _consThreshold
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _pidThreshold
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_pidThreshold", "pidThreshold", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasPidThreshold()) { return null; }
+                return new java.lang.Integer(target.getPidThreshold());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deletePidThreshold();
+                        return;
+                    }
+                    target.setPidThreshold( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _pidThreshold
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _title
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.String.class, "_title", "title", org.exolab.castor.xml.NodeType.Attribute);
+        desc.setImmutable(true);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                return target.getTitle();
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.setTitle( (java.lang.String) value);
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _title
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.StringValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.StringValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setWhiteSpace("preserve");
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showFullId
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showFullId", "showFullId", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowFullId()) { return null; }
+                return (target.getShowFullId() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowFullId();
+                        return;
+                    }
+                    target.setShowFullId( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showFullId
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _rightAlignIds
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_rightAlignIds", "rightAlignIds", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasRightAlignIds()) { return null; }
+                return (target.getRightAlignIds() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteRightAlignIds();
+                        return;
+                    }
+                    target.setRightAlignIds( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _rightAlignIds
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showText
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showText", "showText", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowText()) { return null; }
+                return (target.getShowText() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowText();
+                        return;
+                    }
+                    target.setShowText( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showText
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showColourText
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showColourText", "showColourText", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowColourText()) { return null; }
+                return (target.getShowColourText() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowColourText();
+                        return;
+                    }
+                    target.setShowColourText( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showColourText
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showUnconserved
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showUnconserved", "showUnconserved", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowUnconserved()) { return null; }
+                return (target.getShowUnconserved() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowUnconserved();
+                        return;
+                    }
+                    target.setShowUnconserved( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showUnconserved
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showBoxes
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showBoxes", "showBoxes", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowBoxes()) { return null; }
+                return (target.getShowBoxes() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowBoxes();
+                        return;
+                    }
+                    target.setShowBoxes( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showBoxes
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _wrapAlignment
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_wrapAlignment", "wrapAlignment", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasWrapAlignment()) { return null; }
+                return (target.getWrapAlignment() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteWrapAlignment();
+                        return;
+                    }
+                    target.setWrapAlignment( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _wrapAlignment
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _renderGaps
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_renderGaps", "renderGaps", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasRenderGaps()) { return null; }
+                return (target.getRenderGaps() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteRenderGaps();
+                        return;
+                    }
+                    target.setRenderGaps( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _renderGaps
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showSequenceFeatures
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showSequenceFeatures", "showSequenceFeatures", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowSequenceFeatures()) { return null; }
+                return (target.getShowSequenceFeatures() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowSequenceFeatures();
+                        return;
+                    }
+                    target.setShowSequenceFeatures( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showSequenceFeatures
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showNPfeatureTooltip
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showNPfeatureTooltip", "showNPfeatureTooltip", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowNPfeatureTooltip()) { return null; }
+                return (target.getShowNPfeatureTooltip() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowNPfeatureTooltip();
+                        return;
+                    }
+                    target.setShowNPfeatureTooltip( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showNPfeatureTooltip
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showDbRefTooltip
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showDbRefTooltip", "showDbRefTooltip", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowDbRefTooltip()) { return null; }
+                return (target.getShowDbRefTooltip() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowDbRefTooltip();
+                        return;
+                    }
+                    target.setShowDbRefTooltip( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showDbRefTooltip
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _followHighlight
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_followHighlight", "followHighlight", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasFollowHighlight()) { return null; }
+                return (target.getFollowHighlight() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteFollowHighlight();
+                        return;
+                    }
+                    target.setFollowHighlight( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _followHighlight
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _followSelection
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_followSelection", "followSelection", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasFollowSelection()) { return null; }
+                return (target.getFollowSelection() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteFollowSelection();
+                        return;
+                    }
+                    target.setFollowSelection( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _followSelection
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showAnnotation
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showAnnotation", "showAnnotation", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowAnnotation()) { return null; }
+                return (target.getShowAnnotation() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowAnnotation();
+                        return;
+                    }
+                    target.setShowAnnotation( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showAnnotation
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _centreColumnLabels
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_centreColumnLabels", "centreColumnLabels", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasCentreColumnLabels()) { return null; }
+                return (target.getCentreColumnLabels() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteCentreColumnLabels();
+                        return;
+                    }
+                    target.setCentreColumnLabels( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _centreColumnLabels
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showGroupConservation
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showGroupConservation", "showGroupConservation", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowGroupConservation()) { return null; }
+                return (target.getShowGroupConservation() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowGroupConservation();
+                        return;
+                    }
+                    target.setShowGroupConservation( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showGroupConservation
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showGroupConsensus
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showGroupConsensus", "showGroupConsensus", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowGroupConsensus()) { return null; }
+                return (target.getShowGroupConsensus() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowGroupConsensus();
+                        return;
+                    }
+                    target.setShowGroupConsensus( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showGroupConsensus
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showConsensusHistogram
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showConsensusHistogram", "showConsensusHistogram", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowConsensusHistogram()) { return null; }
+                return (target.getShowConsensusHistogram() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowConsensusHistogram();
+                        return;
+                    }
+                    target.setShowConsensusHistogram( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showConsensusHistogram
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _showSequenceLogo
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_showSequenceLogo", "showSequenceLogo", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasShowSequenceLogo()) { return null; }
+                return (target.getShowSequenceLogo() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteShowSequenceLogo();
+                        return;
+                    }
+                    target.setShowSequenceLogo( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _showSequenceLogo
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _normaliseSequenceLogo
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_normaliseSequenceLogo", "normaliseSequenceLogo", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasNormaliseSequenceLogo()) { return null; }
+                return (target.getNormaliseSequenceLogo() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteNormaliseSequenceLogo();
+                        return;
+                    }
+                    target.setNormaliseSequenceLogo( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _normaliseSequenceLogo
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _ignoreGapsinConsensus
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_ignoreGapsinConsensus", "ignoreGapsinConsensus", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasIgnoreGapsinConsensus()) { return null; }
+                return (target.getIgnoreGapsinConsensus() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteIgnoreGapsinConsensus();
+                        return;
+                    }
+                    target.setIgnoreGapsinConsensus( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _ignoreGapsinConsensus
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _startRes
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_startRes", "startRes", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasStartRes()) { return null; }
+                return new java.lang.Integer(target.getStartRes());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteStartRes();
+                        return;
+                    }
+                    target.setStartRes( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _startRes
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _startSeq
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_startSeq", "startSeq", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasStartSeq()) { return null; }
+                return new java.lang.Integer(target.getStartSeq());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteStartSeq();
+                        return;
+                    }
+                    target.setStartSeq( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _startSeq
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _fontName
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.String.class, "_fontName", "fontName", org.exolab.castor.xml.NodeType.Attribute);
+        desc.setImmutable(true);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                return target.getFontName();
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.setFontName( (java.lang.String) value);
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _fontName
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.StringValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.StringValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setWhiteSpace("preserve");
+        }
+        desc.setValidator(fieldValidator);
+        //-- _fontSize
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_fontSize", "fontSize", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasFontSize()) { return null; }
+                return new java.lang.Integer(target.getFontSize());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteFontSize();
+                        return;
+                    }
+                    target.setFontSize( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _fontSize
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _fontStyle
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_fontStyle", "fontStyle", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasFontStyle()) { return null; }
+                return new java.lang.Integer(target.getFontStyle());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteFontStyle();
+                        return;
+                    }
+                    target.setFontStyle( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _fontStyle
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _viewName
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.String.class, "_viewName", "viewName", org.exolab.castor.xml.NodeType.Attribute);
+        desc.setImmutable(true);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                return target.getViewName();
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.setViewName( (java.lang.String) value);
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _viewName
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.StringValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.StringValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setWhiteSpace("preserve");
+        }
+        desc.setValidator(fieldValidator);
+        //-- _sequenceSetId
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.String.class, "_sequenceSetId", "sequenceSetId", org.exolab.castor.xml.NodeType.Attribute);
+        desc.setImmutable(true);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                return target.getSequenceSetId();
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.setSequenceSetId( (java.lang.String) value);
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _sequenceSetId
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.StringValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.StringValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setWhiteSpace("preserve");
+        }
+        desc.setValidator(fieldValidator);
+        //-- _gatheredViews
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Boolean.TYPE, "_gatheredViews", "gatheredViews", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasGatheredViews()) { return null; }
+                return (target.getGatheredViews() ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE);
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteGatheredViews();
+                        return;
+                    }
+                    target.setGatheredViews( ((java.lang.Boolean) value).booleanValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _gatheredViews
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _textCol1
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_textCol1", "textCol1", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasTextCol1()) { return null; }
+                return new java.lang.Integer(target.getTextCol1());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteTextCol1();
+                        return;
+                    }
+                    target.setTextCol1( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _textCol1
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _textCol2
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_textCol2", "textCol2", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasTextCol2()) { return null; }
+                return new java.lang.Integer(target.getTextCol2());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteTextCol2();
+                        return;
+                    }
+                    target.setTextCol2( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _textCol2
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _textColThreshold
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_textColThreshold", "textColThreshold", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasTextColThreshold()) { return null; }
+                return new java.lang.Integer(target.getTextColThreshold());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteTextColThreshold();
+                        return;
+                    }
+                    target.setTextColThreshold( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _textColThreshold
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _id
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.String.class, "_id", "id", org.exolab.castor.xml.NodeType.Attribute);
+        super.setIdentity(desc);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                return target.getId();
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.setId( (java.lang.String) value);
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return new java.lang.String();
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _id
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IdValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IdValidator();
+            fieldValidator.setValidator(typeValidator);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _complementId
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.String.class, "_complementId", "complementId", org.exolab.castor.xml.NodeType.Attribute);
+        desc.setImmutable(true);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                return target.getComplementId();
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.setComplementId( (java.lang.String) value);
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _complementId
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.StringValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.StringValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setWhiteSpace("preserve");
+        }
+        desc.setValidator(fieldValidator);
+        //-- _width
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_width", "width", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasWidth()) { return null; }
+                return new java.lang.Integer(target.getWidth());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteWidth();
+                        return;
+                    }
+                    target.setWidth( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _width
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _height
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_height", "height", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasHeight()) { return null; }
+                return new java.lang.Integer(target.getHeight());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteHeight();
+                        return;
+                    }
+                    target.setHeight( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _height
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _xpos
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_xpos", "xpos", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasXpos()) { return null; }
+                return new java.lang.Integer(target.getXpos());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteXpos();
+                        return;
+                    }
+                    target.setXpos( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _xpos
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- _ypos
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(java.lang.Integer.TYPE, "_ypos", "ypos", org.exolab.castor.xml.NodeType.Attribute);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                if (!target.hasYpos()) { return null; }
+                return new java.lang.Integer(target.getYpos());
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    // if null, use delete method for optional primitives 
+                    if (value == null) {
+                        target.deleteYpos();
+                        return;
+                    }
+                    target.setYpos( ((java.lang.Integer) value).intValue());
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return null;
+            }
+        };
+        desc.setHandler(handler);
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _ypos
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+            org.exolab.castor.xml.validators.IntValidator typeValidator;
+            typeValidator = new org.exolab.castor.xml.validators.IntValidator();
+            fieldValidator.setValidator(typeValidator);
+            typeValidator.setMinInclusive(-2147483648);
+            typeValidator.setMaxInclusive(2147483647);
+        }
+        desc.setValidator(fieldValidator);
+        //-- initialize element descriptors
+        
+        //-- _annotationColours
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(jalview.schemabinding.version2.AnnotationColours.class, "_annotationColours", "AnnotationColours", org.exolab.castor.xml.NodeType.Element);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                return target.getAnnotationColours();
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.setAnnotationColours( (jalview.schemabinding.version2.AnnotationColours) value);
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return new jalview.schemabinding.version2.AnnotationColours();
+            }
+        };
+        desc.setHandler(handler);
+        desc.setNameSpaceURI("www.jalview.org");
+        desc.setMultivalued(false);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _annotationColours
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        { //-- local scope
+        }
+        desc.setValidator(fieldValidator);
+        //-- _hiddenColumnsList
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(jalview.schemabinding.version2.HiddenColumns.class, "_hiddenColumnsList", "hiddenColumns", org.exolab.castor.xml.NodeType.Element);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                return target.getHiddenColumns();
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.addHiddenColumns( (jalview.schemabinding.version2.HiddenColumns) value);
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public void resetValue(Object object) throws IllegalStateException, IllegalArgumentException {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.removeAllHiddenColumns();
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return new jalview.schemabinding.version2.HiddenColumns();
+            }
+        };
+        desc.setHandler(handler);
+        desc.setNameSpaceURI("www.jalview.org");
+        desc.setMultivalued(true);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _hiddenColumnsList
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        fieldValidator.setMinOccurs(0);
+        { //-- local scope
+        }
+        desc.setValidator(fieldValidator);
+        //-- _calcIdParamList
+        desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(jalview.schemabinding.version2.CalcIdParam.class, "_calcIdParamList", "calcIdParam", org.exolab.castor.xml.NodeType.Element);
+        handler = new org.exolab.castor.xml.XMLFieldHandler() {
+            public java.lang.Object getValue( java.lang.Object object ) 
+                throws IllegalStateException
+            {
+                Viewport target = (Viewport) object;
+                return target.getCalcIdParam();
+            }
+            public void setValue( java.lang.Object object, java.lang.Object value) 
+                throws IllegalStateException, IllegalArgumentException
+            {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.addCalcIdParam( (jalview.schemabinding.version2.CalcIdParam) value);
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public void resetValue(Object object) throws IllegalStateException, IllegalArgumentException {
+                try {
+                    Viewport target = (Viewport) object;
+                    target.removeAllCalcIdParam();
+                } catch (java.lang.Exception ex) {
+                    throw new IllegalStateException(ex.toString());
+                }
+            }
+            public java.lang.Object newInstance(java.lang.Object parent) {
+                return new jalview.schemabinding.version2.CalcIdParam();
+            }
+        };
+        desc.setHandler(handler);
+        desc.setNameSpaceURI("www.jalview.org");
+        desc.setMultivalued(true);
+        addFieldDescriptor(desc);
+        
+        //-- validation code for: _calcIdParamList
+        fieldValidator = new org.exolab.castor.xml.FieldValidator();
+        fieldValidator.setMinOccurs(0);
+        { //-- local scope
+        }
+        desc.setValidator(fieldValidator);
     }
-    desc.setValidator(fieldValidator);
-    // -- _pidSelected
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_pidSelected", "pidSelected",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasPidSelected())
-        {
-          return null;
-        }
-        return (target.getPidSelected() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deletePidSelected();
-            return;
-          }
-          target.setPidSelected(((java.lang.Boolean) value).booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
 
-    // -- validation code for: _pidSelected
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _bgColour
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.String.class, "_bgColour", "bgColour",
-            org.exolab.castor.xml.NodeType.Attribute);
-    desc.setImmutable(true);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        return target.getBgColour();
-      }
 
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.setBgColour((java.lang.String) value);
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
+      //-----------/
+     //- Methods -/
+    //-----------/
 
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
+    /**
+     * Method getAccessMode.
+     * 
+     * @return the access mode specified for this class.
+     */
+    public org.exolab.castor.mapping.AccessMode getAccessMode(
+    ) {
         return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _bgColour
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.StringValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.StringValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setWhiteSpace("preserve");
     }
-    desc.setValidator(fieldValidator);
-    // -- _consThreshold
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_consThreshold", "consThreshold",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasConsThreshold())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getConsThreshold());
-      }
 
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteConsThreshold();
-            return;
-          }
-          target.setConsThreshold(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _consThreshold
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
+    /**
+     * Method getIdentity.
+     * 
+     * @return the identity field, null if this class has no
+     * identity.
+     */
+    public org.exolab.castor.mapping.FieldDescriptor getIdentity(
+    ) {
+        return super.getIdentity();
     }
-    desc.setValidator(fieldValidator);
-    // -- _pidThreshold
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_pidThreshold", "pidThreshold",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasPidThreshold())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getPidThreshold());
-      }
 
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deletePidThreshold();
-            return;
-          }
-          target.setPidThreshold(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _pidThreshold
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
+    /**
+     * Method getJavaClass.
+     * 
+     * @return the Java class represented by this descriptor.
+     */
+    public java.lang.Class getJavaClass(
+    ) {
+        return jalview.schemabinding.version2.Viewport.class;
     }
-    desc.setValidator(fieldValidator);
-    // -- _title
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.String.class, "_title", "title",
-            org.exolab.castor.xml.NodeType.Attribute);
-    desc.setImmutable(true);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        return target.getTitle();
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.setTitle((java.lang.String) value);
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
 
-    // -- validation code for: _title
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.StringValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.StringValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setWhiteSpace("preserve");
+    /**
+     * Method getNameSpacePrefix.
+     * 
+     * @return the namespace prefix to use when marshaling as XML.
+     */
+    public java.lang.String getNameSpacePrefix(
+    ) {
+        return _nsPrefix;
     }
-    desc.setValidator(fieldValidator);
-    // -- _showFullId
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showFullId", "showFullId",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowFullId())
-        {
-          return null;
-        }
-        return (target.getShowFullId() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
 
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowFullId();
-            return;
-          }
-          target.setShowFullId(((java.lang.Boolean) value).booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showFullId
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _rightAlignIds
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_rightAlignIds", "rightAlignIds",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasRightAlignIds())
-        {
-          return null;
-        }
-        return (target.getRightAlignIds() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteRightAlignIds();
-            return;
-          }
-          target.setRightAlignIds(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _rightAlignIds
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
+    /**
+     * Method getNameSpaceURI.
+     * 
+     * @return the namespace URI used when marshaling and
+     * unmarshaling as XML.
+     */
+    public java.lang.String getNameSpaceURI(
+    ) {
+        return _nsURI;
     }
-    desc.setValidator(fieldValidator);
-    // -- _showText
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showText", "showText",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowText())
-        {
-          return null;
-        }
-        return (target.getShowText() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowText();
-            return;
-          }
-          target.setShowText(((java.lang.Boolean) value).booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
 
-    // -- validation code for: _showText
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
+    /**
+     * Method getValidator.
+     * 
+     * @return a specific validator for the class described by this
+     * ClassDescriptor.
+     */
+    public org.exolab.castor.xml.TypeValidator getValidator(
+    ) {
+        return this;
     }
-    desc.setValidator(fieldValidator);
-    // -- _showColourText
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showColourText", "showColourText",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowColourText())
-        {
-          return null;
-        }
-        return (target.getShowColourText() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowColourText();
-            return;
-          }
-          target.setShowColourText(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
 
-    // -- validation code for: _showColourText
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
+    /**
+     * Method getXMLName.
+     * 
+     * @return the XML Name for the Class being described.
+     */
+    public java.lang.String getXMLName(
+    ) {
+        return _xmlName;
     }
-    desc.setValidator(fieldValidator);
-    // -- _showUnconserved
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showUnconserved", "showUnconserved",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowUnconserved())
-        {
-          return null;
-        }
-        return (target.getShowUnconserved() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowUnconserved();
-            return;
-          }
-          target.setShowUnconserved(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showUnconserved
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _showBoxes
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showBoxes", "showBoxes",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowBoxes())
-        {
-          return null;
-        }
-        return (target.getShowBoxes() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowBoxes();
-            return;
-          }
-          target.setShowBoxes(((java.lang.Boolean) value).booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showBoxes
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _wrapAlignment
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_wrapAlignment", "wrapAlignment",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasWrapAlignment())
-        {
-          return null;
-        }
-        return (target.getWrapAlignment() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteWrapAlignment();
-            return;
-          }
-          target.setWrapAlignment(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
 
-    // -- validation code for: _wrapAlignment
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
+    /**
+     * Method isElementDefinition.
+     * 
+     * @return true if XML schema definition of this Class is that
+     * of a global
+     * element or element with anonymous type definition.
+     */
+    public boolean isElementDefinition(
+    ) {
+        return _elementDefinition;
     }
-    desc.setValidator(fieldValidator);
-    // -- _renderGaps
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_renderGaps", "renderGaps",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasRenderGaps())
-        {
-          return null;
-        }
-        return (target.getRenderGaps() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteRenderGaps();
-            return;
-          }
-          target.setRenderGaps(((java.lang.Boolean) value).booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _renderGaps
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _showSequenceFeatures
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showSequenceFeatures",
-            "showSequenceFeatures",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowSequenceFeatures())
-        {
-          return null;
-        }
-        return (target.getShowSequenceFeatures() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowSequenceFeatures();
-            return;
-          }
-          target.setShowSequenceFeatures(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showSequenceFeatures
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _showNPfeatureTooltip
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showNPfeatureTooltip",
-            "showNPfeatureTooltip",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowNPfeatureTooltip())
-        {
-          return null;
-        }
-        return (target.getShowNPfeatureTooltip() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowNPfeatureTooltip();
-            return;
-          }
-          target.setShowNPfeatureTooltip(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showNPfeatureTooltip
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _showDbRefTooltip
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showDbRefTooltip",
-            "showDbRefTooltip", org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowDbRefTooltip())
-        {
-          return null;
-        }
-        return (target.getShowDbRefTooltip() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowDbRefTooltip();
-            return;
-          }
-          target.setShowDbRefTooltip(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showDbRefTooltip
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _followHighlight
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_followHighlight", "followHighlight",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasFollowHighlight())
-        {
-          return null;
-        }
-        return (target.getFollowHighlight() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteFollowHighlight();
-            return;
-          }
-          target.setFollowHighlight(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _followHighlight
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _followSelection
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_followSelection", "followSelection",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasFollowSelection())
-        {
-          return null;
-        }
-        return (target.getFollowSelection() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteFollowSelection();
-            return;
-          }
-          target.setFollowSelection(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _followSelection
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _showAnnotation
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showAnnotation", "showAnnotation",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowAnnotation())
-        {
-          return null;
-        }
-        return (target.getShowAnnotation() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowAnnotation();
-            return;
-          }
-          target.setShowAnnotation(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showAnnotation
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _centreColumnLabels
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_centreColumnLabels",
-            "centreColumnLabels", org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasCentreColumnLabels())
-        {
-          return null;
-        }
-        return (target.getCentreColumnLabels() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteCentreColumnLabels();
-            return;
-          }
-          target.setCentreColumnLabels(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _centreColumnLabels
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _showGroupConservation
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showGroupConservation",
-            "showGroupConservation",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowGroupConservation())
-        {
-          return null;
-        }
-        return (target.getShowGroupConservation() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowGroupConservation();
-            return;
-          }
-          target.setShowGroupConservation(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showGroupConservation
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _showGroupConsensus
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showGroupConsensus",
-            "showGroupConsensus", org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowGroupConsensus())
-        {
-          return null;
-        }
-        return (target.getShowGroupConsensus() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowGroupConsensus();
-            return;
-          }
-          target.setShowGroupConsensus(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showGroupConsensus
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _showConsensusHistogram
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showConsensusHistogram",
-            "showConsensusHistogram",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowConsensusHistogram())
-        {
-          return null;
-        }
-        return (target.getShowConsensusHistogram() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowConsensusHistogram();
-            return;
-          }
-          target.setShowConsensusHistogram(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showConsensusHistogram
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _showSequenceLogo
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_showSequenceLogo",
-            "showSequenceLogo", org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasShowSequenceLogo())
-        {
-          return null;
-        }
-        return (target.getShowSequenceLogo() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteShowSequenceLogo();
-            return;
-          }
-          target.setShowSequenceLogo(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _showSequenceLogo
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _normaliseSequenceLogo
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_normaliseSequenceLogo",
-            "normaliseSequenceLogo",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasNormaliseSequenceLogo())
-        {
-          return null;
-        }
-        return (target.getNormaliseSequenceLogo() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteNormaliseSequenceLogo();
-            return;
-          }
-          target.setNormaliseSequenceLogo(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _normaliseSequenceLogo
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _ignoreGapsinConsensus
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_ignoreGapsinConsensus",
-            "ignoreGapsinConsensus",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasIgnoreGapsinConsensus())
-        {
-          return null;
-        }
-        return (target.getIgnoreGapsinConsensus() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteIgnoreGapsinConsensus();
-            return;
-          }
-          target.setIgnoreGapsinConsensus(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _ignoreGapsinConsensus
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _startRes
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_startRes", "startRes",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasStartRes())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getStartRes());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteStartRes();
-            return;
-          }
-          target.setStartRes(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _startRes
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _startSeq
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_startSeq", "startSeq",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasStartSeq())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getStartSeq());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteStartSeq();
-            return;
-          }
-          target.setStartSeq(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _startSeq
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _fontName
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.String.class, "_fontName", "fontName",
-            org.exolab.castor.xml.NodeType.Attribute);
-    desc.setImmutable(true);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        return target.getFontName();
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.setFontName((java.lang.String) value);
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _fontName
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.StringValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.StringValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setWhiteSpace("preserve");
-    }
-    desc.setValidator(fieldValidator);
-    // -- _fontSize
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_fontSize", "fontSize",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasFontSize())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getFontSize());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteFontSize();
-            return;
-          }
-          target.setFontSize(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _fontSize
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _fontStyle
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_fontStyle", "fontStyle",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasFontStyle())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getFontStyle());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteFontStyle();
-            return;
-          }
-          target.setFontStyle(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _fontStyle
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _viewName
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.String.class, "_viewName", "viewName",
-            org.exolab.castor.xml.NodeType.Attribute);
-    desc.setImmutable(true);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        return target.getViewName();
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.setViewName((java.lang.String) value);
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _viewName
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.StringValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.StringValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setWhiteSpace("preserve");
-    }
-    desc.setValidator(fieldValidator);
-    // -- _sequenceSetId
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.String.class, "_sequenceSetId", "sequenceSetId",
-            org.exolab.castor.xml.NodeType.Attribute);
-    desc.setImmutable(true);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        return target.getSequenceSetId();
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.setSequenceSetId((java.lang.String) value);
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _sequenceSetId
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.StringValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.StringValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setWhiteSpace("preserve");
-    }
-    desc.setValidator(fieldValidator);
-    // -- _gatheredViews
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Boolean.TYPE, "_gatheredViews", "gatheredViews",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasGatheredViews())
-        {
-          return null;
-        }
-        return (target.getGatheredViews() ? java.lang.Boolean.TRUE
-                : java.lang.Boolean.FALSE);
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteGatheredViews();
-            return;
-          }
-          target.setGatheredViews(((java.lang.Boolean) value)
-                  .booleanValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _gatheredViews
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _textCol1
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_textCol1", "textCol1",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasTextCol1())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getTextCol1());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteTextCol1();
-            return;
-          }
-          target.setTextCol1(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _textCol1
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _textCol2
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_textCol2", "textCol2",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasTextCol2())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getTextCol2());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteTextCol2();
-            return;
-          }
-          target.setTextCol2(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _textCol2
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _textColThreshold
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_textColThreshold",
-            "textColThreshold", org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasTextColThreshold())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getTextColThreshold());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteTextColThreshold();
-            return;
-          }
-          target.setTextColThreshold(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _textColThreshold
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _id
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.String.class, "_id", "id",
-            org.exolab.castor.xml.NodeType.Attribute);
-    super.setIdentity(desc);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        return target.getId();
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.setId((java.lang.String) value);
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return new java.lang.String();
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _id
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IdValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IdValidator();
-      fieldValidator.setValidator(typeValidator);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _width
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_width", "width",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasWidth())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getWidth());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteWidth();
-            return;
-          }
-          target.setWidth(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _width
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _height
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_height", "height",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasHeight())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getHeight());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteHeight();
-            return;
-          }
-          target.setHeight(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _height
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _xpos
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_xpos", "xpos",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasXpos())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getXpos());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteXpos();
-            return;
-          }
-          target.setXpos(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _xpos
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- _ypos
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            java.lang.Integer.TYPE, "_ypos", "ypos",
-            org.exolab.castor.xml.NodeType.Attribute);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        if (!target.hasYpos())
-        {
-          return null;
-        }
-        return new java.lang.Integer(target.getYpos());
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          // if null, use delete method for optional primitives
-          if (value == null)
-          {
-            target.deleteYpos();
-            return;
-          }
-          target.setYpos(((java.lang.Integer) value).intValue());
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return null;
-      }
-    };
-    desc.setHandler(handler);
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _ypos
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-      org.exolab.castor.xml.validators.IntValidator typeValidator;
-      typeValidator = new org.exolab.castor.xml.validators.IntValidator();
-      fieldValidator.setValidator(typeValidator);
-      typeValidator.setMinInclusive(-2147483648);
-      typeValidator.setMaxInclusive(2147483647);
-    }
-    desc.setValidator(fieldValidator);
-    // -- initialize element descriptors
-
-    // -- _annotationColours
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            jalview.schemabinding.version2.AnnotationColours.class,
-            "_annotationColours", "AnnotationColours",
-            org.exolab.castor.xml.NodeType.Element);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        return target.getAnnotationColours();
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.setAnnotationColours((jalview.schemabinding.version2.AnnotationColours) value);
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return new jalview.schemabinding.version2.AnnotationColours();
-      }
-    };
-    desc.setHandler(handler);
-    desc.setNameSpaceURI("www.jalview.org");
-    desc.setMultivalued(false);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _annotationColours
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    { // -- local scope
-    }
-    desc.setValidator(fieldValidator);
-    // -- _hiddenColumnsList
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            jalview.schemabinding.version2.HiddenColumns.class,
-            "_hiddenColumnsList", "hiddenColumns",
-            org.exolab.castor.xml.NodeType.Element);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        return target.getHiddenColumns();
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.addHiddenColumns((jalview.schemabinding.version2.HiddenColumns) value);
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public void resetValue(Object object) throws IllegalStateException,
-              IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.removeAllHiddenColumns();
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return new jalview.schemabinding.version2.HiddenColumns();
-      }
-    };
-    desc.setHandler(handler);
-    desc.setNameSpaceURI("www.jalview.org");
-    desc.setMultivalued(true);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _hiddenColumnsList
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    fieldValidator.setMinOccurs(0);
-    { // -- local scope
-    }
-    desc.setValidator(fieldValidator);
-    // -- _calcIdParamList
-    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
-            jalview.schemabinding.version2.CalcIdParam.class,
-            "_calcIdParamList", "calcIdParam",
-            org.exolab.castor.xml.NodeType.Element);
-    handler = new org.exolab.castor.xml.XMLFieldHandler()
-    {
-      public java.lang.Object getValue(java.lang.Object object)
-              throws IllegalStateException
-      {
-        Viewport target = (Viewport) object;
-        return target.getCalcIdParam();
-      }
-
-      public void setValue(java.lang.Object object, java.lang.Object value)
-              throws IllegalStateException, IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.addCalcIdParam((jalview.schemabinding.version2.CalcIdParam) value);
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public void resetValue(Object object) throws IllegalStateException,
-              IllegalArgumentException
-      {
-        try
-        {
-          Viewport target = (Viewport) object;
-          target.removeAllCalcIdParam();
-        } catch (java.lang.Exception ex)
-        {
-          throw new IllegalStateException(ex.toString());
-        }
-      }
-
-      public java.lang.Object newInstance(java.lang.Object parent)
-      {
-        return new jalview.schemabinding.version2.CalcIdParam();
-      }
-    };
-    desc.setHandler(handler);
-    desc.setNameSpaceURI("www.jalview.org");
-    desc.setMultivalued(true);
-    addFieldDescriptor(desc);
-
-    // -- validation code for: _calcIdParamList
-    fieldValidator = new org.exolab.castor.xml.FieldValidator();
-    fieldValidator.setMinOccurs(0);
-    { // -- local scope
-    }
-    desc.setValidator(fieldValidator);
-  }
-
-  // -----------/
-  // - Methods -/
-  // -----------/
-
-  /**
-   * Method getAccessMode.
-   * 
-   * @return the access mode specified for this class.
-   */
-  public org.exolab.castor.mapping.AccessMode getAccessMode()
-  {
-    return null;
-  }
-
-  /**
-   * Method getIdentity.
-   * 
-   * @return the identity field, null if this class has no identity.
-   */
-  public org.exolab.castor.mapping.FieldDescriptor getIdentity()
-  {
-    return super.getIdentity();
-  }
-
-  /**
-   * Method getJavaClass.
-   * 
-   * @return the Java class represented by this descriptor.
-   */
-  public java.lang.Class getJavaClass()
-  {
-    return jalview.schemabinding.version2.Viewport.class;
-  }
-
-  /**
-   * Method getNameSpacePrefix.
-   * 
-   * @return the namespace prefix to use when marshaling as XML.
-   */
-  public java.lang.String getNameSpacePrefix()
-  {
-    return _nsPrefix;
-  }
-
-  /**
-   * Method getNameSpaceURI.
-   * 
-   * @return the namespace URI used when marshaling and unmarshaling as XML.
-   */
-  public java.lang.String getNameSpaceURI()
-  {
-    return _nsURI;
-  }
-
-  /**
-   * Method getValidator.
-   * 
-   * @return a specific validator for the class described by this
-   *         ClassDescriptor.
-   */
-  public org.exolab.castor.xml.TypeValidator getValidator()
-  {
-    return this;
-  }
-
-  /**
-   * Method getXMLName.
-   * 
-   * @return the XML Name for the Class being described.
-   */
-  public java.lang.String getXMLName()
-  {
-    return _xmlName;
-  }
-
-  /**
-   * Method isElementDefinition.
-   * 
-   * @return true if XML schema definition of this Class is that of a global
-   *         element or element with anonymous type definition.
-   */
-  public boolean isElementDefinition()
-  {
-    return _elementDefinition;
-  }
 
 }
index d766596..ca4316f 100755 (executable)
@@ -25,41 +25,47 @@ import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
 
 import java.awt.Color;
-import java.util.Hashtable;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Vector;
 
-public class ClustalxColourScheme extends ResidueColourScheme // implements
-// IParameterizable
+public class ClustalxColourScheme extends ResidueColourScheme
 {
-  public static Hashtable colhash = new Hashtable();
+  private static final int EIGHTY_FIVE = 85;
 
-  Hashtable[] cons;
+  private static final int FIFTY = 50;
 
-  int[][] cons2;
+  private static final int EIGHTY = 80;
 
-  ConsensusColour[] colours;
+  private static final int SIXTY = 60;
 
-  ConsensusColour[] ResidueColour;
+  /*
+   * Map from conventional colour names to Clustal version of the same
+   */
+  private static Map<Color, Color> colhash = new HashMap<Color, Color>();
+
+  private int[][] cons2;
+
+  private ConsensusColour[] colours;
 
-  int size;
+  private ConsensusColour[] residueColour;
 
-  Consensus[] conses = new Consensus[32];
+  private int size;
 
-  Vector colourTable = new Vector();
+  private Consensus[] conses = new Consensus[32];
 
   private boolean includeGaps = true;
 
+  static
   {
-    colhash.put("RED", new Color((float) 0.9, (float) 0.2, (float) 0.1));
-    colhash.put("BLUE", new Color((float) 0.5, (float) 0.7, (float) 0.9));
-    colhash.put("GREEN", new Color((float) 0.1, (float) 0.8, (float) 0.1));
-    colhash.put("ORANGE", new Color((float) 0.9, (float) 0.6, (float) 0.3));
-    colhash.put("CYAN", new Color((float) 0.1, (float) 0.7, (float) 0.7));
-    colhash.put("PINK", new Color((float) 0.9, (float) 0.5, (float) 0.5));
-    colhash.put("MAGENTA", new Color((float) 0.8, (float) 0.3, (float) 0.8));
-    colhash.put("YELLOW", new Color((float) 0.8, (float) 0.8, (float) 0.0));
+    colhash.put(Color.RED, new Color(0.9f, 0.2f, 0.1f));
+    colhash.put(Color.BLUE, new Color(0.5f, 0.7f, 0.9f));
+    colhash.put(Color.GREEN, new Color(0.1f, 0.8f, 0.1f));
+    colhash.put(Color.ORANGE, new Color(0.9f, 0.6f, 0.3f));
+    colhash.put(Color.CYAN, new Color(0.1f, 0.7f, 0.7f));
+    colhash.put(Color.PINK, new Color(0.9f, 0.5f, 0.5f));
+    colhash.put(Color.MAGENTA, new Color(0.8f, 0.3f, 0.8f));
+    colhash.put(Color.YELLOW, new Color(0.8f, 0.8f, 0.0f));
   }
 
   public ClustalxColourScheme(AnnotatedCollectionI alignment,
@@ -122,36 +128,36 @@ public class ClustalxColourScheme extends ResidueColourScheme // implements
 
   public void makeColours()
   {
-    conses[0] = new Consensus("WLVIMAFCYHP", 60);
-    conses[1] = new Consensus("WLVIMAFCYHP", 80);
-    conses[2] = new Consensus("ED", 50);
-    conses[3] = new Consensus("KR", 60);
-    conses[4] = new Consensus("G", 50);
-    conses[5] = new Consensus("N", 50);
-    conses[6] = new Consensus("QE", 50);
-    conses[7] = new Consensus("P", 50);
-    conses[8] = new Consensus("TS", 50);
-
-    conses[26] = new Consensus("A", 85);
-    conses[27] = new Consensus("C", 85);
-    conses[10] = new Consensus("E", 85);
-    conses[11] = new Consensus("F", 85);
-    conses[12] = new Consensus("G", 85);
-    conses[13] = new Consensus("H", 85);
-    conses[14] = new Consensus("I", 85);
-    conses[15] = new Consensus("L", 85);
-    conses[16] = new Consensus("M", 85);
-    conses[17] = new Consensus("N", 85);
-    conses[18] = new Consensus("P", 85);
-    conses[19] = new Consensus("Q", 85);
-    conses[20] = new Consensus("R", 85);
-    conses[21] = new Consensus("S", 85);
-    conses[22] = new Consensus("T", 85);
-    conses[23] = new Consensus("V", 85);
-    conses[24] = new Consensus("W", 85);
-    conses[25] = new Consensus("Y", 85);
-    conses[28] = new Consensus("K", 85);
-    conses[29] = new Consensus("D", 85);
+    conses[0] = new Consensus("WLVIMAFCYHP", SIXTY);
+    conses[1] = new Consensus("WLVIMAFCYHP", EIGHTY);
+    conses[2] = new Consensus("ED", FIFTY);
+    conses[3] = new Consensus("KR", SIXTY);
+    conses[4] = new Consensus("G", FIFTY);
+    conses[5] = new Consensus("N", FIFTY);
+    conses[6] = new Consensus("QE", FIFTY);
+    conses[7] = new Consensus("P", FIFTY);
+    conses[8] = new Consensus("TS", FIFTY);
+
+    conses[26] = new Consensus("A", EIGHTY_FIVE);
+    conses[27] = new Consensus("C", EIGHTY_FIVE);
+    conses[10] = new Consensus("E", EIGHTY_FIVE);
+    conses[11] = new Consensus("F", EIGHTY_FIVE);
+    conses[12] = new Consensus("G", EIGHTY_FIVE);
+    conses[13] = new Consensus("H", EIGHTY_FIVE);
+    conses[14] = new Consensus("I", EIGHTY_FIVE);
+    conses[15] = new Consensus("L", EIGHTY_FIVE);
+    conses[16] = new Consensus("M", EIGHTY_FIVE);
+    conses[17] = new Consensus("N", EIGHTY_FIVE);
+    conses[18] = new Consensus("P", EIGHTY_FIVE);
+    conses[19] = new Consensus("Q", EIGHTY_FIVE);
+    conses[20] = new Consensus("R", EIGHTY_FIVE);
+    conses[21] = new Consensus("S", EIGHTY_FIVE);
+    conses[22] = new Consensus("T", EIGHTY_FIVE);
+    conses[23] = new Consensus("V", EIGHTY_FIVE);
+    conses[24] = new Consensus("W", EIGHTY_FIVE);
+    conses[25] = new Consensus("Y", EIGHTY_FIVE);
+    conses[28] = new Consensus("K", EIGHTY_FIVE);
+    conses[29] = new Consensus("D", EIGHTY_FIVE);
 
     conses[30] = new Consensus("G", 0);
     conses[31] = new Consensus("P", 0);
@@ -161,15 +167,15 @@ public class ClustalxColourScheme extends ResidueColourScheme // implements
 
     Consensus[] tmp8 = new Consensus[1];
     tmp8[0] = conses[30]; // G
-    colours[7] = new ConsensusColour((Color) colhash.get("ORANGE"), tmp8);
+    colours[7] = new ConsensusColour(colhash.get(Color.ORANGE), tmp8);
 
     Consensus[] tmp9 = new Consensus[1];
     tmp9[0] = conses[31]; // P
-    colours[8] = new ConsensusColour((Color) colhash.get("YELLOW"), tmp9);
+    colours[8] = new ConsensusColour(colhash.get(Color.YELLOW), tmp9);
 
     Consensus[] tmp10 = new Consensus[1];
     tmp10[0] = conses[27]; // C
-    colours[9] = new ConsensusColour((Color) colhash.get("PINK"), tmp8);
+    colours[9] = new ConsensusColour(colhash.get(Color.PINK), tmp8);
 
     Consensus[] tmp1 = new Consensus[14];
     tmp1[0] = conses[0]; // %
@@ -186,9 +192,9 @@ public class ClustalxColourScheme extends ResidueColourScheme // implements
     tmp1[11] = conses[25]; // Y
     tmp1[12] = conses[18]; // P
     tmp1[13] = conses[19]; // p
-    colours[0] = new ConsensusColour((Color) colhash.get("BLUE"), tmp1);
+    colours[0] = new ConsensusColour(colhash.get(Color.BLUE), tmp1);
 
-    colours[10] = new ConsensusColour((Color) colhash.get("CYAN"), tmp1);
+    colours[10] = new ConsensusColour(colhash.get(Color.CYAN), tmp1);
 
     Consensus[] tmp2 = new Consensus[5];
     tmp2[0] = conses[8]; // t
@@ -196,14 +202,14 @@ public class ClustalxColourScheme extends ResidueColourScheme // implements
     tmp2[2] = conses[22]; // T
     tmp2[3] = conses[0]; // %
     tmp2[4] = conses[1]; // #
-    colours[1] = new ConsensusColour((Color) colhash.get("GREEN"), tmp2);
+    colours[1] = new ConsensusColour(colhash.get(Color.GREEN), tmp2);
 
     Consensus[] tmp3 = new Consensus[3];
 
     tmp3[0] = conses[17]; // N
     tmp3[1] = conses[29]; // D
     tmp3[2] = conses[5]; // n
-    colours[2] = new ConsensusColour((Color) colhash.get("GREEN"), tmp3);
+    colours[2] = new ConsensusColour(colhash.get(Color.GREEN), tmp3);
 
     Consensus[] tmp4 = new Consensus[6];
     tmp4[0] = conses[6]; // q = QE
@@ -212,14 +218,14 @@ public class ClustalxColourScheme extends ResidueColourScheme // implements
     tmp4[3] = conses[3]; // +
     tmp4[4] = conses[28]; // K
     tmp4[5] = conses[20]; // R
-    colours[3] = new ConsensusColour((Color) colhash.get("GREEN"), tmp4);
+    colours[3] = new ConsensusColour(colhash.get(Color.GREEN), tmp4);
 
     Consensus[] tmp5 = new Consensus[4];
     tmp5[0] = conses[3]; // +
     tmp5[1] = conses[28]; // K
     tmp5[2] = conses[20]; // R
     tmp5[3] = conses[19]; // Q
-    colours[4] = new ConsensusColour((Color) colhash.get("RED"), tmp5);
+    colours[4] = new ConsensusColour(colhash.get(Color.RED), tmp5);
 
     Consensus[] tmp6 = new Consensus[6];
     tmp6[0] = conses[3]; // -
@@ -228,7 +234,7 @@ public class ClustalxColourScheme extends ResidueColourScheme // implements
     tmp6[3] = conses[6]; // QE
     tmp6[4] = conses[19]; // Q
     tmp6[5] = conses[2]; // DE
-    colours[5] = new ConsensusColour((Color) colhash.get("MAGENTA"), tmp6);
+    colours[5] = new ConsensusColour(colhash.get(Color.MAGENTA), tmp6);
 
     Consensus[] tmp7 = new Consensus[5];
     tmp7[0] = conses[3]; // -
@@ -236,30 +242,30 @@ public class ClustalxColourScheme extends ResidueColourScheme // implements
     tmp7[2] = conses[10]; // E
     tmp7[3] = conses[17]; // N
     tmp7[4] = conses[2]; // DE
-    colours[6] = new ConsensusColour((Color) colhash.get("MAGENTA"), tmp7);
+    colours[6] = new ConsensusColour(colhash.get(Color.MAGENTA), tmp7);
 
     // Now attach the ConsensusColours to the residue letters
-    ResidueColour = new ConsensusColour[20];
-    ResidueColour[0] = colours[0]; // A
-    ResidueColour[1] = colours[4]; // R
-    ResidueColour[2] = colours[2]; // N
-    ResidueColour[3] = colours[6]; // D
-    ResidueColour[4] = colours[0]; // C
-    ResidueColour[5] = colours[3]; // Q
-    ResidueColour[6] = colours[5]; // E
-    ResidueColour[7] = colours[7]; // G
-    ResidueColour[8] = colours[10]; // H
-    ResidueColour[9] = colours[0]; // I
-    ResidueColour[10] = colours[0]; // L
-    ResidueColour[11] = colours[4]; // K
-    ResidueColour[12] = colours[0]; // M
-    ResidueColour[13] = colours[0]; // F
-    ResidueColour[14] = colours[8]; // P
-    ResidueColour[15] = colours[1]; // S
-    ResidueColour[16] = colours[1]; // T
-    ResidueColour[17] = colours[0]; // W
-    ResidueColour[18] = colours[10]; // Y
-    ResidueColour[19] = colours[0]; // V
+    residueColour = new ConsensusColour[20];
+    residueColour[0] = colours[0]; // A
+    residueColour[1] = colours[4]; // R
+    residueColour[2] = colours[2]; // N
+    residueColour[3] = colours[6]; // D
+    residueColour[4] = colours[0]; // C
+    residueColour[5] = colours[3]; // Q
+    residueColour[6] = colours[5]; // E
+    residueColour[7] = colours[7]; // G
+    residueColour[8] = colours[10]; // H
+    residueColour[9] = colours[0]; // I
+    residueColour[10] = colours[0]; // L
+    residueColour[11] = colours[4]; // K
+    residueColour[12] = colours[0]; // M
+    residueColour[13] = colours[0]; // F
+    residueColour[14] = colours[8]; // P
+    residueColour[15] = colours[1]; // S
+    residueColour[16] = colours[1]; // T
+    residueColour[17] = colours[0]; // W
+    residueColour[18] = colours[10]; // Y
+    residueColour[19] = colours[0]; // V
   }
 
   @Override
@@ -288,12 +294,12 @@ public class ClustalxColourScheme extends ResidueColourScheme // implements
       return currentColour;
     }
 
-    for (int k = 0; k < ResidueColour[i].conses.length; k++)
+    for (int k = 0; k < residueColour[i].conses.length; k++)
     {
-      if (ResidueColour[i].conses[k].isConserved(cons2, j, size,
+      if (residueColour[i].conses[k].isConserved(cons2, j, size,
               includeGaps))
       {
-        currentColour = ResidueColour[i].c;
+        currentColour = residueColour[i].c;
       }
     }
 
@@ -301,7 +307,7 @@ public class ClustalxColourScheme extends ResidueColourScheme // implements
     {
       if (conses[27].isConserved(cons2, j, size, includeGaps))
       {
-        currentColour = (Color) colhash.get("PINK");
+        currentColour = colhash.get(Color.PINK);
       }
     }
 
index 593e83d..0f34824 100755 (executable)
@@ -27,6 +27,7 @@ import jalview.api.analysis.ScoreModelI;
 import java.awt.Color;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -43,11 +44,11 @@ public class ResidueProperties
 
   public static final int[] purinepyrimidineIndex;
 
-  public static final Hashtable aa3Hash = new Hashtable();
+  public static final Map<String, Integer> aa3Hash = new HashMap<String, Integer>();
 
-  public static final Hashtable aa2Triplet = new Hashtable();
+  public static final Map<String, String> aa2Triplet = new HashMap<String, String>();
 
-  public static final Hashtable nucleotideName = new Hashtable();
+  public static final Map<String, String> nucleotideName = new HashMap<String, String>();
 
   static
   {
@@ -632,49 +633,51 @@ public class ResidueProperties
   public static final float[] pidThresholds =
   { 80, 60, 40, };
 
-  public static Hashtable codonHash = new Hashtable();
+  public static Map<String, List<String>> codonHash = new HashMap<String, List<String>>();
 
-  public static Vector Lys = new Vector();
+  private static List<String> Lys = new ArrayList<String>();
 
-  public static Vector Asn = new Vector();
+  private static List<String> Asn = new ArrayList<String>();
 
-  public static Vector Gln = new Vector();
+  private static List<String> Gln = new ArrayList<String>();
 
-  public static Vector His = new Vector();
+  private static List<String> His = new ArrayList<String>();
 
-  public static Vector Glu = new Vector();
+  private static List<String> Glu = new ArrayList<String>();
 
-  public static Vector Asp = new Vector();
+  private static List<String> Asp = new ArrayList<String>();
 
-  public static Vector Tyr = new Vector();
+  private static List<String> Tyr = new ArrayList<String>();
 
-  public static Vector Thr = new Vector();
+  private static List<String> Thr = new ArrayList<String>();
 
-  public static Vector Pro = new Vector();
+  private static List<String> Pro = new ArrayList<String>();
 
-  public static Vector Ala = new Vector();
+  private static List<String> Ala = new ArrayList<String>();
 
-  public static Vector Ser = new Vector();
+  private static List<String> Ser = new ArrayList<String>();
 
-  public static Vector Arg = new Vector();
+  private static List<String> Arg = new ArrayList<String>();
 
-  public static Vector Gly = new Vector();
+  private static List<String> Gly = new ArrayList<String>();
 
-  public static Vector Trp = new Vector();
+  private static List<String> Trp = new ArrayList<String>();
 
-  public static Vector Cys = new Vector();
+  private static List<String> Cys = new ArrayList<String>();
 
-  public static Vector Ile = new Vector();
+  private static List<String> Ile = new ArrayList<String>();
 
-  public static Vector Met = new Vector();
+  private static List<String> Met = new ArrayList<String>();
 
-  public static Vector Leu = new Vector();
+  private static List<String> Leu = new ArrayList<String>();
 
-  public static Vector Val = new Vector();
+  private static List<String> Val = new ArrayList<String>();
 
-  public static Vector Phe = new Vector();
+  private static List<String> Phe = new ArrayList<String>();
 
-  public static Vector STOP = new Vector();
+  public static List<String> STOP = new ArrayList<String>();
+
+  public static String START = "ATG";
 
   static
   {
@@ -704,7 +707,7 @@ public class ResidueProperties
   /**
    * Nucleotide Ambiguity Codes
    */
-  public static final Hashtable<String, String[]> ambiguityCodes = new Hashtable<String, String[]>();
+  public static final Map<String, String[]> ambiguityCodes = new Hashtable<String, String[]>();
 
   /**
    * Codon triplets with additional symbols for unambiguous codons that include
@@ -719,105 +722,32 @@ public class ResidueProperties
 
   static
   {
-    /**
-     * 3.2. Purine (adenine or guanine): R
-     * 
-     * R is the symbol previously recommended [1].
+    /*
+     * Ambiguity codes as per http://www.chem.qmul.ac.uk/iubmb/misc/naseq.html
      */
     ambiguityCodes.put("R", new String[]
     { "A", "G" });
-
-    /**
-     * 3.3. Pyrimidine (thymine or cytosine): Y
-     * 
-     * Y is the symbol previously recommended [1].
-     */
     ambiguityCodes.put("Y", new String[]
     { "T", "C" });
-    /**
-     * 3.4. Adenine or thymine: W
-     * 
-     * Although several diverse symbols have been used for this pair, (and for
-     * the reciprocal pair G+C), only two symbols have a rational basis, L and
-     * W: L derives from DNA density (light; G+C - heavy - would thus be H); W
-     * derives from the strength of the hydrogen bonding interaction between the
-     * base pairs (weak for A+T: G +C - strong - would thus be S). However, the
-     * system recommended for the three-base series (not-A = B, etc., see below,
-     * section 3.8) rules out H as this would be not-G. W is thus recommended.
-     */
     ambiguityCodes.put("W", new String[]
     { "A", "T" });
-    /**
-     * 3.5. Guanine or cytosine: S
-     * 
-     * The choice of this symbol is discussed above in section 3.4.
-     */
     ambiguityCodes.put("S", new String[]
     { "G", "C" });
-    /**
-     * 3.6. Adenine or cytosine: M
-     * 
-     * There are few common features between A and C. The presence of an NH2
-     * group in similar positions on both bases (Fig. 1) makes possible a
-     * logically derived symbol. A and N being ruled out, M (from aMino) is
-     * recommended.
-     * 
-     * 
-     * Fig. 1. Origin of the symbols M and K The four bases are drawn so as to
-     * show the relationship between adenine and cytosine on the one hand, which
-     * both have aMino groups at the ring position most distant from the point
-     * of attachment to the sugar, and between guanine and thymine on the other,
-     * which both have Keto groups at the corresponding position. The ring atoms
-     * are numbered as recommended [24-26], although for the present purpose
-     * this has the disadvantage of giving discordant numbers to the
-     * corresponding positions.
-     */
     ambiguityCodes.put("M", new String[]
     { "A", "C" });
-    /**
-     * 3.7. Guanine or thymine: K By analogy with A and C (section 3.6), both G
-     * and T have Keto groups in similar positions (Fig. 1).
-     */
     ambiguityCodes.put("K", new String[]
     { "G", "T" });
-    /**
-     * 3.8. Adenine or thymine or cytosine: H
-     * 
-     * Not-G is the most simple means of memorising this combination and symbols
-     * logically related to G were examined. F and H would both be suitable, as
-     * the letters before and after G in the alphabet, but A would have no
-     * equivalent to F. The use of H has historical precedence [2].
-     */
     ambiguityCodes.put("H", new String[]
     { "A", "T", "C" });
-    /**
-     * 3.9. Guanine or cytosine or thymine: B
-     * 
-     * Not-A as above (section 3.8).
-     */
     ambiguityCodes.put("B", new String[]
     { "G", "T", "C" });
-    /**
-     * 3.10. Guanine or adenine or cytosine: V
-     * 
-     * Not-T by analogy with not-G (section 3.8) would be U but this is ruled
-     * out to eliminate confusion with uracil. V is the next logical choice.
-     * Note that T and U may in some cases be considered to be synonyms.
-     */
     ambiguityCodes.put("V", new String[]
     { "G", "A", "C" });
-    /**
-     * 3.11. Guanine or adenine or thymine: D
-     * 
-     * Not-C as above (section 3.8).
-     */
     ambiguityCodes.put("D", new String[]
     { "G", "A", "T" });
-    /**
-     * 3.12. Guanine or adenine or thymine or cytosine: N
-     */
-    ambiguityCodes.put("R", new String[]
+    ambiguityCodes.put("N", new String[]
     { "G", "A", "T", "C" });
+
     // Now build codon translation table
     codonHash2.put("AAA", "K");
     codonHash2.put("AAG", "K");
@@ -1044,87 +974,87 @@ public class ResidueProperties
 
   static
   {
-    Lys.addElement("AAA");
-    Lys.addElement("AAG");
-    Asn.addElement("AAC");
-    Asn.addElement("AAT");
-
-    Gln.addElement("CAA");
-    Gln.addElement("CAG");
-    His.addElement("CAC");
-    His.addElement("CAT");
-
-    Glu.addElement("GAA");
-    Glu.addElement("GAG");
-    Asp.addElement("GAC");
-    Asp.addElement("GAT");
-
-    Tyr.addElement("TAC");
-    Tyr.addElement("TAT");
-
-    Thr.addElement("ACA");
-    Thr.addElement("ACG");
-    Thr.addElement("ACC");
-    Thr.addElement("ACT");
-
-    Pro.addElement("CCA");
-    Pro.addElement("CCG");
-    Pro.addElement("CCC");
-    Pro.addElement("CCT");
-
-    Ala.addElement("GCA");
-    Ala.addElement("GCG");
-    Ala.addElement("GCC");
-    Ala.addElement("GCT");
-
-    Ser.addElement("TCA");
-    Ser.addElement("TCG");
-    Ser.addElement("TCC");
-    Ser.addElement("TCT");
-    Ser.addElement("AGC");
-    Ser.addElement("AGT");
-
-    Arg.addElement("AGA");
-    Arg.addElement("AGG");
-    Arg.addElement("CGA");
-    Arg.addElement("CGG");
-    Arg.addElement("CGC");
-    Arg.addElement("CGT");
-
-    Gly.addElement("GGA");
-    Gly.addElement("GGG");
-    Gly.addElement("GGC");
-    Gly.addElement("GGT");
-
-    STOP.addElement("TGA");
-    STOP.addElement("TAA");
-    STOP.addElement("TAG");
-
-    Trp.addElement("TGG");
-
-    Cys.addElement("TGC");
-    Cys.addElement("TGT");
-
-    Ile.addElement("ATA");
-    Ile.addElement("ATC");
-    Ile.addElement("ATT");
-
-    Met.addElement("ATG");
-
-    Leu.addElement("CTA");
-    Leu.addElement("CTG");
-    Leu.addElement("CTC");
-    Leu.addElement("CTT");
-    Leu.addElement("TTA");
-    Leu.addElement("TTG");
-
-    Val.addElement("GTA");
-    Val.addElement("GTG");
-    Val.addElement("GTC");
-    Val.addElement("GTT");
-
-    Phe.addElement("TTC");
-    Phe.addElement("TTT");
+    Lys.add("AAA");
+    Lys.add("AAG");
+    Asn.add("AAC");
+    Asn.add("AAT");
+
+    Gln.add("CAA");
+    Gln.add("CAG");
+    His.add("CAC");
+    His.add("CAT");
+
+    Glu.add("GAA");
+    Glu.add("GAG");
+    Asp.add("GAC");
+    Asp.add("GAT");
+
+    Tyr.add("TAC");
+    Tyr.add("TAT");
+
+    Thr.add("ACA");
+    Thr.add("ACG");
+    Thr.add("ACC");
+    Thr.add("ACT");
+
+    Pro.add("CCA");
+    Pro.add("CCG");
+    Pro.add("CCC");
+    Pro.add("CCT");
+
+    Ala.add("GCA");
+    Ala.add("GCG");
+    Ala.add("GCC");
+    Ala.add("GCT");
+
+    Ser.add("TCA");
+    Ser.add("TCG");
+    Ser.add("TCC");
+    Ser.add("TCT");
+    Ser.add("AGC");
+    Ser.add("AGT");
+
+    Arg.add("AGA");
+    Arg.add("AGG");
+    Arg.add("CGA");
+    Arg.add("CGG");
+    Arg.add("CGC");
+    Arg.add("CGT");
+
+    Gly.add("GGA");
+    Gly.add("GGG");
+    Gly.add("GGC");
+    Gly.add("GGT");
+
+    STOP.add("TGA");
+    STOP.add("TAA");
+    STOP.add("TAG");
+
+    Trp.add("TGG");
+
+    Cys.add("TGC");
+    Cys.add("TGT");
+
+    Ile.add("ATA");
+    Ile.add("ATC");
+    Ile.add("ATT");
+
+    Met.add("ATG");
+
+    Leu.add("CTA");
+    Leu.add("CTG");
+    Leu.add("CTC");
+    Leu.add("CTT");
+    Leu.add("TTA");
+    Leu.add("TTG");
+
+    Val.add("GTA");
+    Val.add("GTG");
+    Val.add("GTC");
+    Val.add("GTT");
+
+    Phe.add("TTC");
+    Phe.add("TTT");
   }
 
   // Stores residue codes/names and colours and other things
@@ -1521,7 +1451,7 @@ public class ResidueProperties
     return hyd;
   }
 
-  public static Hashtable getAA3Hash()
+  public static Map<String, Integer> getAA3Hash()
   {
     return aa3Hash;
   }
@@ -1591,14 +1521,9 @@ public class ResidueProperties
     {
       return "X";
     }
-    Enumeration e = codonHash.keys();
-
-    while (e.hasMoreElements())
+    for (String key : codonHash.keySet())
     {
-      String key = (String) e.nextElement();
-      Vector tmp = (Vector) codonHash.get(key);
-
-      if (tmp.contains(codon))
+      if (codonHash.get(key).contains(codon))
       {
         return key;
       }
diff --git a/src/jalview/structure/AtomSpec.java b/src/jalview/structure/AtomSpec.java
new file mode 100644 (file)
index 0000000..d3e8d42
--- /dev/null
@@ -0,0 +1,64 @@
+package jalview.structure;
+
+/**
+ * Java bean representing an atom in a PDB (or similar) structure model.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class AtomSpec
+{
+  // TODO clarify do we want pdbFile here, or pdbId?
+  // compare highlightAtom in 2.8.2 for JalviewJmolBinding and
+  // javascript.MouseOverStructureListener
+  private String pdbFile;
+
+  private String chain;
+
+  private int pdbResNum;
+
+  private int atomIndex;
+
+  /**
+   * Constructor
+   * 
+   * @param pdbFile
+   * @param chain
+   * @param resNo
+   * @param atomNo
+   */
+  public AtomSpec(String pdbFile, String chain, int resNo, int atomNo)
+  {
+    this.pdbFile = pdbFile;
+    this.chain = chain;
+    this.pdbResNum = resNo;
+    this.atomIndex = atomNo;
+  }
+
+  public String getPdbFile()
+  {
+    return pdbFile;
+  }
+
+  public String getChain()
+  {
+    return chain;
+  }
+
+  public int getPdbResNum()
+  {
+    return pdbResNum;
+  }
+
+  public int getAtomIndex()
+  {
+    return atomIndex;
+  }
+
+  @Override
+  public String toString()
+  {
+    return "pdbFile: " + pdbFile + ", chain: " + chain + ", res: "
+            + pdbResNum + ", atom: " + atomIndex;
+  }
+}
diff --git a/src/jalview/structure/CommandListener.java b/src/jalview/structure/CommandListener.java
new file mode 100644 (file)
index 0000000..e5f3e36
--- /dev/null
@@ -0,0 +1,35 @@
+package jalview.structure;
+
+import jalview.commands.CommandI;
+
+/**
+ * Defines a listener for commands performed on another alignment. This is to
+ * support linked editing of two alternative representations of an alignment (in
+ * particular, cDNA and protein).
+ * 
+ * @author gmcarstairs
+ *
+ */
+public interface CommandListener
+{
+  /**
+   * The listener may attempt to perform the specified command; the region acted
+   * on is determined by a callback to the StructureSelectionManager (which
+   * holds mappings between alignments).
+   * 
+   * @param command
+   * @param undo
+   * @param ssm
+   * @param source
+   *          the originator of the command
+   */
+  public void mirrorCommand(CommandI command, boolean undo,
+          StructureSelectionManager ssm, VamsasSource source);
+
+  /**
+   * Temporary workaround to make check for source == listener work.
+   * 
+   * @return
+   */
+  public VamsasSource getVamsasSource();
+}
index 6f5ea50..cb65032 100644 (file)
  */
 package jalview.structure;
 
-import jalview.datamodel.*;
+import jalview.datamodel.SequenceI;
+
 
 public interface SequenceListener
 {
+  // TODO remove this? never called on SequenceListener type
   public void mouseOverSequence(SequenceI sequence, int index, int pos);
 
   public void highlightSequence(jalview.datamodel.SearchResults results);
 
+  // TODO remove this? never called
   public void updateColours(SequenceI sequence, int index);
+
+  public VamsasSource getVamsasSource();
+
 }
index 73576e9..69658bf 100644 (file)
  */
 package jalview.structure;
 
+import java.util.List;
+
 public interface StructureListener
 {
   /**
-   * 
-   * @return list of structure files (unique IDs/filenames) that this listener
-   *         handles messages for, or null if generic listener (only used by
-   *         removeListener method)
+   * Returns a list of structure files (unique IDs/filenames) that this listener
+   * handles messages for, or null if generic listener (only used by
+   * removeListener method)
    */
   public String[] getPdbFile();
 
   /**
-   * NOT A LISTENER METHOD! called by structure viewer when the given
-   * atom/structure has been moused over. Typically, implementors call
-   * StructureSelectionManager.mouseOverStructure
-   * 
-   * @param atomIndex
-   * @param strInfo
-   */
-  public void mouseOverStructure(int atomIndex, String strInfo);
-
-  /**
-   * called by StructureSelectionManager to inform viewer to highlight given
-   * atomspec
+   * Called by StructureSelectionManager to inform viewer to highlight given
+   * atom positions
    * 
-   * @param atomIndex
-   * @param pdbResNum
-   * @param chain
-   * @param pdbId
+   * @param atoms
    */
-  public void highlightAtom(int atomIndex, int pdbResNum, String chain,
-          String pdbId);
+  public void highlightAtoms(List<AtomSpec> atoms);
 
   /**
-   * called by StructureSelectionManager when the colours of a sequence
+   * Called by StructureSelectionManager when the colours of a sequence
    * associated with a structure have changed.
    * 
    * @param source
@@ -62,19 +49,7 @@ public interface StructureListener
   public void updateColours(Object source);
 
   /**
-   * called by Jalview to get the colour for the given atomspec
-   * 
-   * @param atomIndex
-   * @param pdbResNum
-   * @param chain
-   * @param pdbId
-   * @return
-   */
-  public java.awt.Color getColour(int atomIndex, int pdbResNum,
-          String chain, String pdbId);
-
-  /**
-   * called by structureSelectionManager to instruct implementor to release any
+   * Called by structureSelectionManager to instruct implementor to release any
    * direct references it may hold to the given object (typically, these are
    * Jalview alignment panels).
    * 
index 0434aac..ed5ee2d 100644 (file)
@@ -22,19 +22,29 @@ package jalview.structure;
 
 import jalview.analysis.AlignSeq;
 import jalview.api.StructureSelectionManagerProvider;
+import jalview.commands.CommandI;
+import jalview.commands.EditCommand;
+import jalview.commands.OrderCommand;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.SequenceI;
 import jalview.io.AppletFormatAdapter;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 
 import java.io.PrintStream;
+import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.Vector;
 
 import MCview.Atom;
@@ -44,10 +54,28 @@ public class StructureSelectionManager
 {
   static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
 
-  StructureMapping[] mappings;
+  private List<StructureMapping> mappings = new ArrayList<StructureMapping>();
 
-  private boolean processSecondaryStructure = false,
-          secStructServices = false, addTempFacAnnot = false;
+  private boolean processSecondaryStructure = false;
+
+  private boolean secStructServices = false;
+
+  private boolean addTempFacAnnot = false;
+
+  /*
+   * Set of any registered mappings between (dataset) sequences.
+   */
+  Set<AlignedCodonFrame> seqmappings = new LinkedHashSet<AlignedCodonFrame>();
+
+  /*
+   * Reference counters for the above mappings. Remove mappings when ref count
+   * goes to zero.
+   */
+  Map<AlignedCodonFrame, Integer> seqMappingRefCounts = new HashMap<AlignedCodonFrame, Integer>();
+
+  private List<CommandListener> commandListeners = new ArrayList<CommandListener>();
+
+  private List<SelectionListener> sel_listeners = new ArrayList<SelectionListener>();
 
   /**
    * @return true if will try to use external services for processing secondary
@@ -115,17 +143,18 @@ public class StructureSelectionManager
    */
   public void reportMapping()
   {
-    if (mappings == null)
+    if (mappings.isEmpty())
     {
       System.err.println("reportMapping: No PDB/Sequence mappings.");
     }
     else
     {
-      System.err.println("reportMapping: There are " + mappings.length
+      System.err.println("reportMapping: There are " + mappings.size()
               + " mappings.");
-      for (int m = 0; m < mappings.length; m++)
+      int i = 0;
+      for (StructureMapping sm : mappings)
       {
-        System.err.println("mapping " + m + " : " + mappings[m].pdbfile);
+        System.err.println("mapping " + i++ + " : " + sm.pdbfile);
       }
     }
   }
@@ -246,16 +275,19 @@ public class StructureSelectionManager
     }
   }
 
+  /**
+   * Returns the file name for a mapped PDB id (or null if not mapped).
+   * 
+   * @param pdbid
+   * @return
+   */
   public String alreadyMappedToFile(String pdbid)
   {
-    if (mappings != null)
+    for (StructureMapping sm : mappings)
     {
-      for (int i = 0; i < mappings.length; i++)
+      if (sm.getPdbId().equals(pdbid))
       {
-        if (mappings[i].getPdbId().equals(pdbid))
-        {
-          return mappings[i].pdbfile;
-        }
+        return sm.pdbfile;
       }
     }
     return null;
@@ -482,19 +514,7 @@ public class StructureSelectionManager
               mappingDetails.toString());
       if (forStructureView)
       {
-
-        if (mappings == null)
-        {
-          mappings = new StructureMapping[1];
-        }
-        else
-        {
-          StructureMapping[] tmp = new StructureMapping[mappings.length + 1];
-          System.arraycopy(mappings, 0, tmp, 0, mappings.length);
-          mappings = tmp;
-        }
-
-        mappings[mappings.length - 1] = newMapping;
+        mappings.add(newMapping);
       }
       maxChain.transferResidueAnnotation(newMapping, sqmpping);
     }
@@ -546,19 +566,18 @@ public class StructureSelectionManager
       }
     }
 
-    if (pdbs.size() > 0 && mappings != null)
+    if (pdbs.size() > 0)
     {
-      Vector tmp = new Vector();
-      for (int i = 0; i < mappings.length; i++)
+      List<StructureMapping> tmp = new ArrayList<StructureMapping>();
+      for (StructureMapping sm : mappings)
       {
-        if (!pdbs.contains(mappings[i].pdbfile))
+        if (!pdbs.contains(sm.pdbfile))
         {
-          tmp.addElement(mappings[i]);
+          tmp.add(sm);
         }
       }
 
-      mappings = new StructureMapping[tmp.size()];
-      tmp.copyInto(mappings);
+      mappings = tmp;
     }
   }
 
@@ -569,7 +588,6 @@ public class StructureSelectionManager
       // old or prematurely sent event
       return;
     }
-    boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
     SearchResults results = null;
     SequenceI lastseq = null;
     int lastipos = -1, indexpos;
@@ -581,33 +599,21 @@ public class StructureSelectionManager
         {
           results = new SearchResults();
         }
-        if (mappings != null)
+        for (StructureMapping sm : mappings)
         {
-          for (int j = 0; j < mappings.length; j++)
+          if (sm.pdbfile.equals(pdbfile) && sm.pdbchain.equals(chain))
           {
-            if (mappings[j].pdbfile.equals(pdbfile)
-                    && mappings[j].pdbchain.equals(chain))
+            indexpos = sm.getSeqPos(pdbResNum);
+            if (lastipos != indexpos && lastseq != sm.sequence)
             {
-              indexpos = mappings[j].getSeqPos(pdbResNum);
-              if (lastipos != indexpos && lastseq != mappings[j].sequence)
+              results.addResult(sm.sequence, indexpos, indexpos);
+              lastipos = indexpos;
+              lastseq = sm.sequence;
+              // construct highlighted sequence list
+              for (AlignedCodonFrame acf : seqmappings)
               {
-                results.addResult(mappings[j].sequence, indexpos, indexpos);
-                lastipos = indexpos;
-                lastseq = mappings[j].sequence;
-                // construct highlighted sequence list
-                if (seqmappings != null)
-                {
-
-                  Enumeration e = seqmappings.elements();
-                  while (e.hasMoreElements())
-
-                  {
-                    ((AlignedCodonFrame) e.nextElement()).markMappedRegion(
-                            mappings[j].sequence, indexpos, results);
-                  }
-                }
+                acf.markMappedRegion(sm.sequence, indexpos, results);
               }
-
             }
           }
         }
@@ -626,13 +632,11 @@ public class StructureSelectionManager
     }
   }
 
-  Vector seqmappings = null; // should be a simpler list of mapped seuqence
-
   /**
    * highlight regions associated with a position (indexpos) in seq
    * 
    * @param seq
-   *          the sequeence that the mouse over occured on
+   *          the sequence that the mouse over occurred on
    * @param indexpos
    *          the absolute position being mouseovered in seq (0 to seq.length())
    * @param index
@@ -642,93 +646,54 @@ public class StructureSelectionManager
   public void mouseOverSequence(SequenceI seq, int indexpos, int index,
           VamsasSource source)
   {
-    boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
+    boolean hasSequenceListeners = handlingVamsasMo
+            || !seqmappings.isEmpty();
     SearchResults results = null;
     if (index == -1)
     {
       index = seq.findPosition(indexpos);
     }
-    StructureListener sl;
-    int atomNo = 0;
     for (int i = 0; i < listeners.size(); i++)
     {
       Object listener = listeners.elementAt(i);
       if (listener == source)
       {
+        // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
+        // Temporary fudge with SequenceListener.getVamsasSource()
         continue;
       }
       if (listener instanceof StructureListener)
       {
-        sl = (StructureListener) listener;
-        if (mappings == null)
-        {
-          continue;
-        }
-        for (int j = 0; j < mappings.length; j++)
-        {
-          if (mappings[j].sequence == seq
-                  || mappings[j].sequence == seq.getDatasetSequence())
-          {
-            atomNo = mappings[j].getAtomNum(index);
-
-            if (atomNo > 0)
-            {
-              sl.highlightAtom(atomNo, mappings[j].getPDBResNum(index),
-                      mappings[j].pdbchain, mappings[j].pdbfile);
-            }
-          }
-        }
+        highlightStructure((StructureListener) listener, seq, index);
       }
       else
       {
-        if (relaySeqMappings && hasSequenceListeners
-                && listener instanceof SequenceListener)
+        if (listener instanceof SequenceListener)
         {
-          // DEBUG
-          // System.err.println("relay Seq " + seq.getDisplayId(false) + " " +
-          // index);
-
-          if (results == null)
+          final SequenceListener seqListener = (SequenceListener) listener;
+          if (hasSequenceListeners
+                  && seqListener.getVamsasSource() != source)
           {
-            results = new SearchResults();
-            if (index >= seq.getStart() && index <= seq.getEnd())
+            if (relaySeqMappings)
             {
-              // construct highlighted sequence list
-
-              if (seqmappings != null)
+              if (results == null)
               {
-                Enumeration e = seqmappings.elements();
-                while (e.hasMoreElements())
-
-                {
-                  ((AlignedCodonFrame) e.nextElement()).markMappedRegion(
-                          seq, index, results);
-                }
+                results = MappingUtils.buildSearchResults(seq, index,
+                        seqmappings);
               }
-              // hasSequenceListeners = results.getSize() > 0;
               if (handlingVamsasMo)
               {
-                // maybe have to resolve seq to a dataset seqeunce...
-                // add in additional direct sequence and/or dataset sequence
-                // highlighting
                 results.addResult(seq, index, index);
+
               }
+              seqListener.highlightSequence(results);
             }
           }
-          if (hasSequenceListeners)
-          {
-            ((SequenceListener) listener).highlightSequence(results);
-          }
         }
         else if (listener instanceof VamsasListener && !handlingVamsasMo)
         {
-          // DEBUG
-          // System.err.println("Vamsas from Seq " + seq.getDisplayId(false) + "
-          // " +
-          // index);
-          // pass the mouse over and absolute position onto the
-          // VamsasListener(s)
-          ((VamsasListener) listener).mouseOver(seq, indexpos, source);
+          ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
+                  source);
         }
         else if (listener instanceof SecondaryStructureListener)
         {
@@ -740,6 +705,35 @@ public class StructureSelectionManager
   }
 
   /**
+   * Send suitable messages to a StructureListener to highlight atoms
+   * corresponding to the given sequence position.
+   * 
+   * @param sl
+   * @param seq
+   * @param index
+   */
+  protected void highlightStructure(StructureListener sl, SequenceI seq,
+          int index)
+  {
+    int atomNo;
+    List<AtomSpec> atoms = new ArrayList<AtomSpec>();
+    for (StructureMapping sm : mappings)
+    {
+      if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence())
+      {
+        atomNo = sm.getAtomNum(index);
+
+        if (atomNo > 0)
+        {
+          atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, sm
+                  .getPDBResNum(index), atomNo));
+        }
+      }
+    }
+    sl.highlightAtoms(atoms);
+  }
+
+  /**
    * true if a mouse over event from an external (ie Vamsas) source is being
    * handled
    */
@@ -825,119 +819,114 @@ public class StructureSelectionManager
 
   public StructureMapping[] getMapping(String pdbfile)
   {
-    Vector tmp = new Vector();
-    if (mappings != null)
-    {
-      for (int i = 0; i < mappings.length; i++)
+    List<StructureMapping> tmp = new ArrayList<StructureMapping>();
+    for (StructureMapping sm : mappings)
       {
-        if (mappings[i].pdbfile.equals(pdbfile))
+      if (sm.pdbfile.equals(pdbfile))
         {
-          tmp.addElement(mappings[i]);
+        tmp.add(sm);
         }
-      }
     }
-    StructureMapping[] ret = new StructureMapping[tmp.size()];
-    for (int i = 0; i < tmp.size(); i++)
-    {
-      ret[i] = (StructureMapping) tmp.elementAt(i);
-    }
-
-    return ret;
+    return tmp.toArray(new StructureMapping[tmp.size()]);
   }
 
   public String printMapping(String pdbfile)
   {
-    StringBuffer sb = new StringBuffer();
-    for (int i = 0; i < mappings.length; i++)
+    StringBuilder sb = new StringBuilder(64);
+    for (StructureMapping sm : mappings)
     {
-      if (mappings[i].pdbfile.equals(pdbfile))
+      if (sm.pdbfile.equals(pdbfile))
       {
-        sb.append(mappings[i].mappingDetails);
+        sb.append(sm.mappingDetails);
       }
     }
 
     return sb.toString();
   }
 
-  private int[] seqmappingrefs = null; // refcount for seqmappings elements
-
-  private synchronized void modifySeqMappingList(boolean add,
-          AlignedCodonFrame[] codonFrames)
+  /**
+   * Decrement the reference counter for each of the given mappings, and remove
+   * it entirely if its reference counter reduces to zero.
+   * 
+   * @param set
+   */
+  public void removeMappings(Set<AlignedCodonFrame> set)
   {
-    if (!add && (seqmappings == null || seqmappings.size() == 0))
-    {
-      return;
-    }
-    if (seqmappings == null)
+    if (set != null)
     {
-      seqmappings = new Vector();
+      for (AlignedCodonFrame acf : set)
+      {
+        removeMapping(acf);
+      }
     }
-    if (codonFrames != null && codonFrames.length > 0)
+  }
+
+  /**
+   * Decrement the reference counter for the given mapping, and remove it
+   * entirely if its reference counter reduces to zero.
+   * 
+   * @param acf
+   */
+  public void removeMapping(AlignedCodonFrame acf)
+  {
+    if (acf != null && seqmappings.contains(acf))
     {
-      for (int cf = 0; cf < codonFrames.length; cf++)
+      int count = seqMappingRefCounts.get(acf);
+      count--;
+      if (count > 0)
       {
-        if (seqmappings.contains(codonFrames[cf]))
-        {
-          if (add)
-          {
-            seqmappingrefs[seqmappings.indexOf(codonFrames[cf])]++;
-          }
-          else
-          {
-            if (--seqmappingrefs[seqmappings.indexOf(codonFrames[cf])] <= 0)
-            {
-              int pos = seqmappings.indexOf(codonFrames[cf]);
-              int[] nr = new int[seqmappingrefs.length - 1];
-              if (pos > 0)
-              {
-                System.arraycopy(seqmappingrefs, 0, nr, 0, pos);
-              }
-              if (pos < seqmappingrefs.length - 1)
-              {
-                System.arraycopy(seqmappingrefs, pos + 1, nr, 0,
-                        seqmappingrefs.length - pos - 2);
-              }
-            }
-          }
-        }
-        else
-        {
-          if (add)
-          {
-            seqmappings.addElement(codonFrames[cf]);
-
-            int[] nsr = new int[(seqmappingrefs == null) ? 1
-                    : seqmappingrefs.length + 1];
-            if (seqmappingrefs != null && seqmappingrefs.length > 0)
-            {
-              System.arraycopy(seqmappingrefs, 0, nsr, 0,
-                      seqmappingrefs.length);
-            }
-            nsr[(seqmappingrefs == null) ? 0 : seqmappingrefs.length] = 1;
-            seqmappingrefs = nsr;
-          }
-        }
+        seqMappingRefCounts.put(acf, count);
+      }
+      else
+      {
+        seqmappings.remove(acf);
+        seqMappingRefCounts.remove(acf);
       }
     }
   }
 
-  public void removeMappings(AlignedCodonFrame[] codonFrames)
+  /**
+   * Add each of the given codonFrames to the stored set. If not aready present,
+   * increments its reference count instead.
+   * 
+   * @param set
+   */
+  public void addMappings(Set<AlignedCodonFrame> set)
   {
-    modifySeqMappingList(false, codonFrames);
+    if (set != null)
+    {
+      for (AlignedCodonFrame acf : set)
+      {
+        addMapping(acf);
+      }
+    }
   }
 
-  public void addMappings(AlignedCodonFrame[] codonFrames)
+  /**
+   * Add the given mapping to the stored set, or if already stored, increment
+   * its reference counter.
+   */
+  public void addMapping(AlignedCodonFrame acf)
   {
-    modifySeqMappingList(true, codonFrames);
+    if (acf != null)
+    {
+      if (seqmappings.contains(acf))
+      {
+        seqMappingRefCounts.put(acf, seqMappingRefCounts.get(acf) + 1);
+      }
+      else
+      {
+        seqmappings.add(acf);
+        seqMappingRefCounts.put(acf, 1);
+      }
+    }
   }
 
-  Vector<SelectionListener> sel_listeners = new Vector<SelectionListener>();
-
   public void addSelectionListener(SelectionListener selecter)
   {
     if (!sel_listeners.contains(selecter))
     {
-      sel_listeners.addElement(selecter);
+      sel_listeners.add(selecter);
     }
   }
 
@@ -945,7 +934,7 @@ public class StructureSelectionManager
   {
     if (sel_listeners.contains(toremove))
     {
-      sel_listeners.removeElement(toremove);
+      sel_listeners.remove(toremove);
     }
   }
 
@@ -953,18 +942,11 @@ public class StructureSelectionManager
           jalview.datamodel.SequenceGroup selection,
           jalview.datamodel.ColumnSelection colsel, SelectionSource source)
   {
-    if (sel_listeners != null && sel_listeners.size() > 0)
+    for (SelectionListener slis : sel_listeners)
     {
-      Enumeration listeners = sel_listeners.elements();
-      while (listeners.hasMoreElements())
+      if (slis != source)
       {
-        SelectionListener slis = ((SelectionListener) listeners
-                .nextElement());
-        if (slis != source)
-        {
-          slis.selection(selection, colsel, source);
-        }
-        ;
+        slis.selection(selection, colsel, source);
       }
     }
   }
@@ -992,32 +974,6 @@ public class StructureSelectionManager
     }
   }
 
-  public void finalize() throws Throwable
-  {
-    if (listeners != null)
-    {
-      listeners.clear();
-      listeners = null;
-    }
-    if (pdbIdFileName != null)
-    {
-      pdbIdFileName.clear();
-      pdbIdFileName = null;
-    }
-    if (sel_listeners != null)
-    {
-      sel_listeners.clear();
-      sel_listeners = null;
-    }
-    if (view_listeners != null)
-    {
-      view_listeners.clear();
-      view_listeners = null;
-    }
-    mappings = null;
-    seqmappingrefs = null;
-  }
-
   /**
    * release all references associated with this manager provider
    * 
@@ -1041,7 +997,6 @@ public class StructureSelectionManager
         } catch (Throwable x)
         {
         }
-        ;
       }
     }
   }
@@ -1055,4 +1010,67 @@ public class StructureSelectionManager
     }
   }
 
+  public void addCommandListener(CommandListener cl)
+  {
+    if (!commandListeners.contains(cl))
+    {
+      commandListeners.add(cl);
+    }
+  }
+
+  public boolean hasCommandListener(CommandListener cl)
+  {
+    return this.commandListeners.contains(cl);
+  }
+
+  public boolean removeCommandListener(CommandListener l)
+  {
+    return commandListeners.remove(l);
+  }
+
+  /**
+   * Forward a command to any command listeners (except for the command's
+   * source).
+   * 
+   * @param command
+   *          the command to be broadcast (in its form after being performed)
+   * @param undo
+   *          if true, the command was being 'undone'
+   * @param source
+   */
+  public void commandPerformed(CommandI command, boolean undo,
+          VamsasSource source)
+  {
+    for (CommandListener listener : commandListeners)
+    {
+      listener.mirrorCommand(command, undo, this, source);
+    }
+  }
+
+  /**
+   * Returns a new CommandI representing the given command as mapped to the
+   * given sequences. If no mapping could be made, or the command is not of a
+   * mappable kind, returns null.
+   * 
+   * @param command
+   * @param undo
+   * @param mapTo
+   * @param gapChar
+   * @return
+   */
+  public CommandI mapCommand(CommandI command, boolean undo,
+          final AlignmentI mapTo, char gapChar)
+  {
+    if (command instanceof EditCommand)
+    {
+      return MappingUtils.mapEditCommand((EditCommand) command, undo,
+              mapTo, gapChar, seqmappings);
+    }
+    else if (command instanceof OrderCommand)
+    {
+      return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
+              mapTo, seqmappings);
+    }
+    return null;
+  }
 }
index 8a390c4..c328c43 100644 (file)
@@ -36,5 +36,6 @@ import jalview.datamodel.SequenceI;
  */
 public interface VamsasListener
 {
-  public void mouseOver(SequenceI seq, int index, VamsasSource source);
+  public void mouseOverSequence(SequenceI seq, int index,
+          VamsasSource source);
 }
index 664c903..653ec2d 100644 (file)
@@ -3,15 +3,16 @@ package jalview.structures.models;
 import jalview.api.StructureSelectionManagerProvider;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.structure.AtomSpec;
 import jalview.structure.StructureListener;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 
-import java.awt.event.ComponentEvent;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
+ * 
  * A base class to hold common function for protein structure model binding.
  * Initial version created by refactoring JMol and Chimera binding models, but
  * other structure viewers could in principle be accommodated in future.
@@ -361,4 +362,21 @@ public abstract class AAStructureBindingModel extends
     }
   }
 
+  @Override
+  public void highlightAtoms(List<AtomSpec> atoms)
+  {
+    if (atoms != null)
+    {
+      for (AtomSpec atom : atoms)
+      {
+        highlightAtom(atom.getAtomIndex(), atom.getPdbResNum(),
+                atom.getChain(), atom.getPdbFile());
+      }
+    }
+  }
+
+  // TODO Jmol and Chimera seem to expect pdbFile, javascript listener pdbId
+  protected abstract void highlightAtom(int atomIndex, int pdbResNum,
+          String chain, String pdbFile);
+
 }
\ No newline at end of file
index 1934583..e224b71 100644 (file)
  */
 package jalview.util;
 
-import jalview.datamodel.*;
+import jalview.datamodel.SequenceI;
 
 /**
- * DOCUMENT ME!
- * 
- * @author $author$
- * @version $Revision$
+ * Assorted methods for analysing or comparing sequences.
  */
 public class Comparison
 {
-  /** DOCUMENT ME!! */
-  public static final String GapChars = " .-";
+  private static final int EIGHTY_FIVE = 85;
+
+  private static final int TO_UPPER_CASE = 'a' - 'A';
+
+  private static final char GAP_SPACE = ' ';
+
+  private static final char GAP_DOT = '.';
+
+  private static final char GAP_DASH = '-';
+
+  public static final String GapChars = new String(new char[]
+  { GAP_SPACE, GAP_DOT, GAP_DASH });
 
   /**
    * DOCUMENT ME!
@@ -69,12 +76,12 @@ public class Comparison
     int ilen = si.length() - 1;
     int jlen = sj.length() - 1;
 
-    while (jalview.util.Comparison.isGap(si.charAt(start + ilen)))
+    while (Comparison.isGap(si.charAt(start + ilen)))
     {
       ilen--;
     }
 
-    while (jalview.util.Comparison.isGap(sj.charAt(start + jlen)))
+    while (Comparison.isGap(sj.charAt(start + jlen)))
     {
       jlen--;
     }
@@ -225,47 +232,60 @@ public class Comparison
   }
 
   /**
-   * DOCUMENT ME!
+   * Answers true if the supplied character is a recognised gap character, else
+   * false. Currently hard-coded to recognise '-', '-' or ' ' (hyphen / dot /
+   * space).
    * 
    * @param c
-   *          DOCUMENT ME!
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
   public static final boolean isGap(char c)
   {
-    return (c == '-' || c == '.' || c == ' ') ? true : false;
+    return (c == GAP_DASH || c == GAP_DOT || c == GAP_SPACE) ? true : false;
   }
 
+  /**
+   * Answers true if more than 85% of the sequence residues (ignoring gaps) are
+   * A, G, C, T or U, else false. This is just a heuristic guess and may give a
+   * wrong answer (as AGCT are also animo acid codes).
+   * 
+   * @param seqs
+   * @return
+   */
   public static final boolean isNucleotide(SequenceI[] seqs)
   {
-    int i = 0, iSize = seqs.length, j, jSize;
-    float nt = 0, aa = 0;
-    char c;
-    while (i < iSize)
+    if (seqs == null)
+    {
+      return false;
+    }
+    int ntCount = 0;
+    int aaCount = 0;
+    for (SequenceI seq : seqs)
     {
-      jSize = seqs[i].getLength();
-      for (j = 0; j < jSize; j++)
+      for (char c : seq.getSequence())
       {
-        c = seqs[i].getCharAt(j);
         if ('a' <= c && c <= 'z')
         {
-          c -= ('a' - 'A');
+          c -= TO_UPPER_CASE;
         }
 
         if (c == 'A' || c == 'G' || c == 'C' || c == 'T' || c == 'U')
         {
-          nt++;
+          ntCount++;
         }
-        else if (!jalview.util.Comparison.isGap(seqs[i].getCharAt(j)))
+        else if (!Comparison.isGap(c))
         {
-          aa++;
+          aaCount++;
         }
       }
-      i++;
     }
 
-    if ((nt / (nt + aa)) > 0.85f)
+    /*
+     * Check for nucleotide count > 85% of total count (in a form that evades
+     * int / float conversion or divide by zero).
+     */
+    if (ntCount * 100 > EIGHTY_FIVE * (ntCount + aaCount))
     {
       return true;
     }
index 9a4ffc8..8163f05 100755 (executable)
  */
 package jalview.util;
 
-import java.util.*;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
 
-import jalview.datamodel.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Vector;
 
 public class DBRefUtils
 {
@@ -48,8 +54,8 @@ public class DBRefUtils
     {
       return dbrefs;
     }
-    Hashtable srcs = new Hashtable();
-    Vector res = new Vector();
+    Map<String, Integer> srcs = new HashMap<String, Integer>();
+    ArrayList<DBRefEntry> res = new ArrayList<DBRefEntry>();
 
     for (int i = 0; i < sources.length; i++)
     {
@@ -59,18 +65,14 @@ public class DBRefUtils
     {
       if (srcs.containsKey(dbrefs[i].getSource()))
       {
-        res.addElement(dbrefs[i]);
+        res.add(dbrefs[i]);
       }
     }
 
     if (res.size() > 0)
     {
       DBRefEntry[] reply = new DBRefEntry[res.size()];
-      for (int i = 0; i < res.size(); i++)
-      {
-        reply[i] = (DBRefEntry) res.elementAt(i);
-      }
-      return reply;
+      return res.toArray(reply);
     }
     res = null;
     // there are probable memory leaks in the hashtable!
@@ -169,7 +171,9 @@ public class DBRefUtils
           DbRefComp comparator)
   {
     if (ref == null || entry == null)
+    {
       return null;
+    }
     Vector rfs = new Vector();
     for (int i = 0; i < ref.length; i++)
     {
@@ -303,6 +307,7 @@ public class DBRefUtils
         {
           if ((refa.getMap() == null && refb.getMap() == null)
                   || (refa.getMap() != null && refb.getMap() != null))
+          {
             if ((refb.getMap().getMap() == null && refa.getMap().getMap() == null)
                     || (refb.getMap().getMap() != null
                             && refa.getMap().getMap() != null && refb
@@ -311,6 +316,7 @@ public class DBRefUtils
             {
               return true;
             }
+          }
         }
       }
       return false;
index 98500da..d14e4ad 100755 (executable)
@@ -54,6 +54,8 @@ public class Format
 
   private char fmt; // one of cdeEfgGiosxXos
 
+  private final String formatString;
+
   /**
    * Creates a new Format object.
    * 
@@ -62,6 +64,7 @@ public class Format
    */
   public Format(String s)
   {
+    formatString = s;
     width = 0;
     precision = -1;
     pre = "";
@@ -622,7 +625,7 @@ public class Format
   /**
    * Formats a character into a string (like sprintf in C)
    * 
-   * @param x
+   * @param debounceTrap
    *          the value to format
    * @return the formatted string
    */
@@ -641,7 +644,7 @@ public class Format
   /**
    * Formats a string into a larger string (like sprintf in C)
    * 
-   * @param x
+   * @param debounceTrap
    *          the value to format
    * @return the formatted string
    */
@@ -938,4 +941,10 @@ public class Format
 
     return f + p.substring(p.length() - 3, p.length());
   }
+
+  @Override
+  public String toString()
+  {
+    return formatString;
+  }
 }
index c566c84..2641659 100644 (file)
  */
 package jalview.util;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 /**
- * MapList Simple way of bijectively mapping a non-contiguous linear range to
- * another non-contiguous linear range Use at your own risk! TODO: efficient
- * implementation of private posMap method TODO: test/ensure that sense of from
- * and to ratio start position is conserved (codon start position recovery)
- * TODO: optimize to use int[][] arrays rather than vectors.
+ * A simple way of bijectively mapping a non-contiguous linear range to another
+ * non-contiguous linear range.
+ * 
+ * Use at your own risk!
+ * 
+ * TODO: efficient implementation of private posMap method
+ * 
+ * TODO: test/ensure that sense of from and to ratio start position is conserved
+ * (codon start position recovery)
  */
 public class MapList
 {
+
   /*
-   * (non-Javadoc)
-   * 
-   * @see java.lang.Object#equals(java.lang.Object)
+   * Subregions (base 1) described as { [start1, end1], [start2, end2], ...}
+   */
+  private List<int[]> fromShifts = new ArrayList<int[]>();
+
+  /*
+   * Same format as fromShifts, for the 'mapped to' sequence
+   */
+  private List<int[]> toShifts = new ArrayList<int[]>();
+
+  /*
+   * number of steps in fromShifts to one toRatio unit
+   */
+  private int fromRatio;
+
+  /*
+   * number of steps in toShifts to one fromRatio
+   */
+  private int toRatio;
+
+  /*
+   * lowest and highest value in the from Map
+   */
+  private int fromLowest;
+
+  private int fromHighest;
+
+  /*
+   * lowest and highest value in the to Map
+   */
+  private int toLowest;
+
+  private int toHighest;
+
+  /**
+   * Two MapList objects are equal if they are the same object, or they both
+   * have populated shift ranges and all values are the same.
    */
-  public boolean equals(MapList obj)
+  @Override
+  public boolean equals(Object o)
   {
+    // TODO should also override hashCode to ensure equal objects have equal
+    // hashcodes
+    if (o == null || !(o instanceof MapList))
+    {
+      return false;
+    }
+
+    MapList obj = (MapList) o;
     if (obj == this)
-      return true;
-    if (obj != null && obj.fromRatio == fromRatio && obj.toRatio == toRatio
-            && obj.fromShifts != null && obj.toShifts != null)
     {
-      int i, iSize = fromShifts.size(), j, jSize = obj.fromShifts.size();
-      if (iSize != jSize)
-        return false;
-      for (i = 0, iSize = fromShifts.size(), j = 0, jSize = obj.fromShifts
-              .size(); i < iSize;)
-      {
-        int[] mi = (int[]) fromShifts.elementAt(i++);
-        int[] mj = (int[]) obj.fromShifts.elementAt(j++);
-        if (mi[0] != mj[0] || mi[1] != mj[1])
-          return false;
-      }
-      iSize = toShifts.size();
-      jSize = obj.toShifts.size();
-      if (iSize != jSize)
-        return false;
-      for (i = 0, j = 0; i < iSize;)
-      {
-        int[] mi = (int[]) toShifts.elementAt(i++);
-        int[] mj = (int[]) obj.toShifts.elementAt(j++);
-        if (mi[0] != mj[0] || mi[1] != mj[1])
-          return false;
-      }
       return true;
     }
-    return false;
+    if (obj.fromRatio != fromRatio || obj.toRatio != toRatio
+            || obj.fromShifts == null || obj.toShifts == null)
+    {
+      return false;
+    }
+    return Arrays
+            .deepEquals(fromShifts.toArray(), obj.fromShifts.toArray())
+            && Arrays
+                    .deepEquals(toShifts.toArray(), obj.toShifts.toArray());
   }
 
-  public Vector fromShifts;
-
-  public Vector toShifts;
-
-  int fromRatio; // number of steps in fromShifts to one toRatio unit
-
-  int toRatio; // number of steps in toShifts to one fromRatio
-
   /**
+   * Returns the 'from' ranges as {[start1, end1], [start2, end2], ...}
    * 
-   * @return series of intervals mapped in from
+   * @return
    */
-  public int[] getFromRanges()
+  public List<int[]> getFromRanges()
   {
-    return getRanges(fromShifts);
+    return fromShifts;
   }
 
-  public int[] getToRanges()
+  /**
+   * Returns the 'to' ranges as {[start1, end1], [start2, end2], ...}
+   * 
+   * @return
+   */
+  public List<int[]> getToRanges()
   {
-    return getRanges(toShifts);
+    return toShifts;
   }
 
-  private int[] getRanges(Vector shifts)
+  /**
+   * Flattens a list of [start, end] into a single [start1, end1, start2,
+   * end2,...] array.
+   * 
+   * @param shifts
+   * @return
+   */
+  protected static int[] getRanges(List<int[]> shifts)
   {
     int[] rnges = new int[2 * shifts.size()];
-    Enumeration e = shifts.elements();
     int i = 0;
-    while (e.hasMoreElements())
+    for (int[] r : shifts)
     {
-      int r[] = (int[]) e.nextElement();
       rnges[i++] = r[0];
       rnges[i++] = r[1];
     }
@@ -107,16 +142,6 @@ public class MapList
   }
 
   /**
-   * lowest and highest value in the from Map
-   */
-  int[] fromRange = null;
-
-  /**
-   * lowest and highest value in the to Map
-   */
-  int[] toRange = null;
-
-  /**
    * 
    * @return length of mapped phrase in from
    */
@@ -136,100 +161,136 @@ public class MapList
 
   public int getFromLowest()
   {
-    return fromRange[0];
+    return fromLowest;
   }
 
   public int getFromHighest()
   {
-    return fromRange[1];
+    return fromHighest;
   }
 
   public int getToLowest()
   {
-    return toRange[0];
+    return toLowest;
   }
 
   public int getToHighest()
   {
-    return toRange[1];
-  }
-
-  private void ensureRange(int[] limits, int pos)
-  {
-    if (limits[0] > pos)
-      limits[0] = pos;
-    if (limits[1] < pos)
-      limits[1] = pos;
+    return toHighest;
   }
 
+  /**
+   * Constructor.
+   * 
+   * @param from
+   *          contiguous regions as [start1, end1, start2, end2, ...]
+   * @param to
+   *          same format as 'from'
+   * @param fromRatio
+   *          phrase length in 'from' (e.g. 3 for dna)
+   * @param toRatio
+   *          phrase length in 'to' (e.g. 1 for protein)
+   */
   public MapList(int from[], int to[], int fromRatio, int toRatio)
   {
-    fromRange = new int[]
-    { from[0], from[1] };
-    toRange = new int[]
-    { to[0], to[1] };
-
-    fromShifts = new Vector();
+    this.fromRatio = fromRatio;
+    this.toRatio = toRatio;
+    fromLowest = from[0];
+    fromHighest = from[1];
     for (int i = 0; i < from.length; i += 2)
     {
-      ensureRange(fromRange, from[i]);
-      ensureRange(fromRange, from[i + 1]);
+      fromLowest = Math.min(fromLowest, from[i]);
+      fromHighest = Math.max(fromHighest, from[i + 1]);
 
-      fromShifts.addElement(new int[]
+      fromShifts.add(new int[]
       { from[i], from[i + 1] });
     }
-    toShifts = new Vector();
+
+    toLowest = to[0];
+    toHighest = to[1];
     for (int i = 0; i < to.length; i += 2)
     {
-      ensureRange(toRange, to[i]);
-      ensureRange(toRange, to[i + 1]);
-      toShifts.addElement(new int[]
+      toLowest = Math.min(toLowest, to[i]);
+      toHighest = Math.max(toHighest, to[i + 1]);
+      toShifts.add(new int[]
       { to[i], to[i + 1] });
     }
-    this.fromRatio = fromRatio;
-    this.toRatio = toRatio;
   }
 
+  /**
+   * Copy constructor. Creates an identical mapping.
+   * 
+   * @param map
+   */
   public MapList(MapList map)
   {
-    this.fromRange = new int[]
-    { map.fromRange[0], map.fromRange[1] };
-    this.toRange = new int[]
-    { map.toRange[0], map.toRange[1] };
+    // TODO not used - remove?
+    this.fromLowest = map.fromLowest;
+    this.fromHighest = map.fromHighest;
+    this.toLowest = map.toLowest;
+    this.toHighest = map.toHighest;
+
     this.fromRatio = map.fromRatio;
     this.toRatio = map.toRatio;
     if (map.fromShifts != null)
     {
-      this.fromShifts = new Vector();
-      Enumeration e = map.fromShifts.elements();
-      while (e.hasMoreElements())
+      for (int[] r : map.fromShifts)
       {
-        int[] el = (int[]) e.nextElement();
-        fromShifts.addElement(new int[]
-        { el[0], el[1] });
+        fromShifts.add(new int[]
+        { r[0], r[1] });
       }
     }
     if (map.toShifts != null)
     {
-      this.toShifts = new Vector();
-      Enumeration e = map.toShifts.elements();
-      while (e.hasMoreElements())
+      for (int[] r : map.toShifts)
       {
-        int[] el = (int[]) e.nextElement();
-        toShifts.addElement(new int[]
-        { el[0], el[1] });
+        toShifts.add(new int[]
+        { r[0], r[1] });
       }
     }
   }
 
   /**
+   * Constructor given ranges as lists of [start, end] positions
+   * 
+   * @param fromRange
+   * @param toRange
+   * @param fromRatio
+   * @param toRatio
+   */
+  public MapList(List<int[]> fromRange, List<int[]> toRange,
+          int fromRatio, int toRatio)
+  {
+    this.fromShifts = fromRange;
+    this.toShifts = toRange;
+    this.fromRatio = fromRatio;
+    this.toRatio = toRatio;
+
+    fromLowest = Integer.MAX_VALUE;
+    fromHighest = 0;
+    for (int[] range : fromRange) {
+      fromLowest = Math.min(fromLowest, range[0]);
+      fromHighest = Math.max(fromHighest, range[1]);
+    }
+
+    toLowest = Integer.MAX_VALUE;
+    toHighest = 0;
+    for (int[] range : toRange)
+    {
+      toLowest = Math.min(toLowest, range[0]);
+      toHighest = Math.max(toHighest, range[1]);
+    }
+  }
+
+  /**
    * get all mapped positions from 'from' to 'to'
    * 
    * @return int[][] { int[] { fromStart, fromFinish, toStart, toFinish }, int
    *         [fromFinish-fromStart+2] { toStart..toFinish mappings}}
    */
-  public int[][] makeFromMap()
+  protected int[][] makeFromMap()
   {
+    // TODO not used - remove??
     return posMap(fromShifts, fromRatio, toShifts, toRatio);
   }
 
@@ -238,27 +299,30 @@ public class MapList
    * 
    * @return int[to position]=position mapped in from
    */
-  public int[][] makeToMap()
+  protected int[][] makeToMap()
   {
+    // TODO not used - remove??
     return posMap(toShifts, toRatio, fromShifts, fromRatio);
   }
 
   /**
    * construct an int map for intervals in intVals
    * 
-   * @param intVals
+   * @param shiftTo
    * @return int[] { from, to pos in range }, int[range.to-range.from+1]
    *         returning mapped position
    */
-  private int[][] posMap(Vector intVals, int ratio, Vector toIntVals,
+  private int[][] posMap(List<int[]> shiftTo, int ratio,
+          List<int[]> shiftFrom,
           int toRatio)
   {
-    int iv = 0, ivSize = intVals.size();
+    // TODO not used - remove??
+    int iv = 0, ivSize = shiftTo.size();
     if (iv >= ivSize)
     {
       return null;
     }
-    int[] intv = (int[]) intVals.elementAt(iv++);
+    int[] intv = shiftTo.get(iv++);
     int from = intv[0], to = intv[1];
     if (from > to)
     {
@@ -267,7 +331,7 @@ public class MapList
     }
     while (iv < ivSize)
     {
-      intv = (int[]) intVals.elementAt(iv++);
+      intv = shiftTo.get(iv++);
       if (intv[0] < from)
       {
         from = intv[0];
@@ -289,7 +353,7 @@ public class MapList
     int mp[][] = new int[to - from + 2][];
     for (int i = 0; i < mp.length; i++)
     {
-      int[] m = shift(i + from, intVals, ratio, toIntVals, toRatio);
+      int[] m = shift(i + from, shiftTo, ratio, shiftFrom, toRatio);
       if (m != null)
       {
         if (i == 0)
@@ -345,6 +409,7 @@ public class MapList
    *          shifts.insertElementAt(new int[] { pos, shift}, sidx); else
    *          rshift[1]+=shift; }
    */
+
   /**
    * shift from pos to To(pos)
    * 
@@ -373,23 +438,24 @@ public class MapList
 
   /**
    * 
-   * @param fromShifts
+   * @param shiftTo
    * @param fromRatio
-   * @param toShifts
+   * @param shiftFrom
    * @param toRatio
    * @return
    */
-  private int[] shift(int pos, Vector fromShifts, int fromRatio,
-          Vector toShifts, int toRatio)
+  protected static int[] shift(int pos, List<int[]> shiftTo, int fromRatio,
+          List<int[]> shiftFrom, int toRatio)
   {
-    int[] fromCount = countPos(fromShifts, pos);
+    // TODO: javadoc; tests
+    int[] fromCount = countPos(shiftTo, pos);
     if (fromCount == null)
     {
       return null;
     }
     int fromRemainder = (fromCount[0] - 1) % fromRatio;
     int toCount = 1 + (((fromCount[0] - 1) / fromRatio) * toRatio);
-    int[] toPos = countToPos(toShifts, toCount);
+    int[] toPos = countToPos(shiftFrom, toCount);
     if (toPos == null)
     {
       return null; // throw new Error("Bad Mapping!");
@@ -402,16 +468,16 @@ public class MapList
   /**
    * count how many positions pos is along the series of intervals.
    * 
-   * @param intVals
+   * @param shiftTo
    * @param pos
    * @return number of positions or null if pos is not within intervals
    */
-  private int[] countPos(Vector intVals, int pos)
+  protected static int[] countPos(List<int[]> shiftTo, int pos)
   {
-    int count = 0, intv[], iv = 0, ivSize = intVals.size();
+    int count = 0, intv[], iv = 0, ivSize = shiftTo.size();
     while (iv < ivSize)
     {
-      intv = (int[]) intVals.elementAt(iv++);
+      intv = shiftTo.get(iv++);
       if (intv[0] <= intv[1])
       {
         if (pos >= intv[0] && pos <= intv[1])
@@ -443,17 +509,18 @@ public class MapList
   /**
    * count out pos positions into a series of intervals and return the position
    * 
-   * @param intVals
+   * @param shiftFrom
    * @param pos
    * @return position pos in interval set
    */
-  private int[] countToPos(Vector intVals, int pos)
+  protected static int[] countToPos(List<int[]> shiftFrom, int pos)
   {
-    int count = 0, diff = 0, iv = 0, ivSize = intVals.size(), intv[] =
+    int count = 0, diff = 0, iv = 0, ivSize = shiftFrom.size();
+    int[] intv =
     { 0, 0 };
     while (iv < ivSize)
     {
-      intv = (int[]) intVals.elementAt(iv++);
+      intv = shiftFrom.get(iv++);
       diff = intv[1] - intv[0];
       if (diff >= 0)
       {
@@ -487,69 +554,68 @@ public class MapList
    * find series of intervals mapping from start-end in the From map.
    * 
    * @param start
-   *          position in to map
+   *          position mapped 'to'
    * @param end
-   *          position in to map
-   * @return series of ranges in from map
+   *          position mapped 'to'
+   * @return series of [start, end] ranges in sequence mapped 'from'
    */
   public int[] locateInFrom(int start, int end)
   {
     // inefficient implementation
     int fromStart[] = shiftTo(start);
-    int fromEnd[] = shiftTo(end); // needs to be inclusive of end of symbol
-    // position
-    if (fromStart == null || fromEnd == null)
-      return null;
-    int iv[] = getIntervals(fromShifts, fromStart, fromEnd, fromRatio);
-    return iv;
+    // needs to be inclusive of end of symbol position
+    int fromEnd[] = shiftTo(end);
+
+    return getIntervals(fromShifts, fromStart, fromEnd, fromRatio);
   }
 
   /**
    * find series of intervals mapping from start-end in the to map.
    * 
    * @param start
-   *          position in from map
+   *          position mapped 'from'
    * @param end
-   *          position in from map
-   * @return series of ranges in to map
+   *          position mapped 'from'
+   * @return series of [start, end] ranges in sequence mapped 'to'
    */
   public int[] locateInTo(int start, int end)
   {
-    // inefficient implementation
     int toStart[] = shiftFrom(start);
     int toEnd[] = shiftFrom(end);
-    if (toStart == null || toEnd == null)
-      return null;
-    int iv[] = getIntervals(toShifts, toStart, toEnd, toRatio);
-    return iv;
+    return getIntervals(toShifts, toStart, toEnd, toRatio);
   }
 
   /**
    * like shift - except returns the intervals in the given vector of shifts
    * which were spanned in traversing fromStart to fromEnd
    * 
-   * @param fromShifts2
+   * @param shiftFrom
    * @param fromStart
    * @param fromEnd
    * @param fromRatio2
    * @return series of from,to intervals from from first position of starting
    *         region to final position of ending region inclusive
    */
-  private int[] getIntervals(Vector fromShifts2, int[] fromStart,
+  protected static int[] getIntervals(List<int[]> shiftFrom,
+          int[] fromStart,
           int[] fromEnd, int fromRatio2)
   {
+    if (fromStart == null || fromEnd == null)
+    {
+      return null;
+    }
     int startpos, endpos;
     startpos = fromStart[0]; // first position in fromStart
     endpos = fromEnd[0]; // last position in fromEnd
     int endindx = (fromRatio2 - 1); // additional positions to get to last
     // position from endpos
-    int intv = 0, intvSize = fromShifts2.size();
+    int intv = 0, intvSize = shiftFrom.size();
     int iv[], i = 0, fs = -1, fe_s = -1, fe = -1; // containing intervals
     // search intervals to locate ones containing startpos and count endindx
     // positions on from endpos
     while (intv < intvSize && (fs == -1 || fe == -1))
     {
-      iv = (int[]) fromShifts2.elementAt(intv++);
+      iv = shiftFrom.get(intv++);
       if (fe_s > -1)
       {
         endpos = iv[0]; // start counting from beginning of interval
@@ -612,39 +678,45 @@ public class MapList
       i++;
     }
     if (fs == fe && fe == -1)
+    {
       return null;
-    Vector ranges = new Vector();
+    }
+    List<int[]> ranges = new ArrayList<int[]>();
     if (fs <= fe)
     {
       intv = fs;
       i = fs;
       // truncate initial interval
-      iv = (int[]) fromShifts2.elementAt(intv++);
+      iv = shiftFrom.get(intv++);
       iv = new int[]
       { iv[0], iv[1] };// clone
       if (i == fs)
+      {
         iv[0] = startpos;
+      }
       while (i != fe)
       {
-        ranges.addElement(iv); // add initial range
-        iv = (int[]) fromShifts2.elementAt(intv++); // get next interval
+        ranges.add(iv); // add initial range
+        iv = shiftFrom.get(intv++); // get next interval
         iv = new int[]
         { iv[0], iv[1] };// clone
         i++;
       }
       if (i == fe)
+      {
         iv[1] = endpos;
-      ranges.addElement(iv); // add only - or final range
+      }
+      ranges.add(iv); // add only - or final range
     }
     else
     {
       // walk from end of interval.
-      i = fromShifts2.size() - 1;
+      i = shiftFrom.size() - 1;
       while (i > fs)
       {
         i--;
       }
-      iv = (int[]) fromShifts2.elementAt(i);
+      iv = shiftFrom.get(i);
       iv = new int[]
       { iv[1], iv[0] };// reverse and clone
       // truncate initial interval
@@ -654,8 +726,8 @@ public class MapList
       }
       while (--i != fe)
       { // fix apparent logic bug when fe==-1
-        ranges.addElement(iv); // add (truncated) reversed interval
-        iv = (int[]) fromShifts2.elementAt(i);
+        ranges.add(iv); // add (truncated) reversed interval
+        iv = shiftFrom.get(i);
         iv = new int[]
         { iv[1], iv[0] }; // reverse and clone
       }
@@ -664,7 +736,7 @@ public class MapList
         // interval is already reversed
         iv[1] = endpos;
       }
-      ranges.addElement(iv); // add only - or final range
+      ranges.add(iv); // add only - or final range
     }
     // create array of start end intervals.
     int[] range = null;
@@ -676,10 +748,10 @@ public class MapList
       i = 0;
       while (intv < intvSize)
       {
-        iv = (int[]) ranges.elementAt(intv);
+        iv = ranges.get(intv);
         range[i++] = iv[0];
         range[i++] = iv[1];
-        ranges.setElementAt(null, intv++); // remove
+        ranges.set(intv++, null); // remove
       }
     }
     return range;
@@ -694,6 +766,7 @@ public class MapList
    */
   public int getToPosition(int mpos)
   {
+    // TODO not used - remove??
     int[] mp = shiftTo(mpos);
     if (mp != null)
     {
@@ -730,6 +803,7 @@ public class MapList
    */
   public int getMappedPosition(int pos)
   {
+    // TODO not used - remove??
     int[] mp = shiftFrom(pos);
     if (mp != null)
     {
@@ -740,6 +814,7 @@ public class MapList
 
   public int[] getMappedWord(int pos)
   {
+    // TODO not used - remove??
     int[] mp = shiftFrom(pos);
     if (mp != null)
     {
@@ -750,223 +825,6 @@ public class MapList
   }
 
   /**
-   * test routine. not incremental.
-   * 
-   * @param ml
-   * @param fromS
-   * @param fromE
-   */
-  public static void testMap(MapList ml, int fromS, int fromE)
-  {
-    for (int from = 1; from <= 25; from++)
-    {
-      int[] too = ml.shiftFrom(from);
-      System.out.print("ShiftFrom(" + from + ")==");
-      if (too == null)
-      {
-        System.out.print("NaN\n");
-      }
-      else
-      {
-        System.out.print(too[0] + " % " + too[1] + " (" + too[2] + ")");
-        System.out.print("\t+--+\t");
-        int[] toofrom = ml.shiftTo(too[0]);
-        if (toofrom != null)
-        {
-          if (toofrom[0] != from)
-          {
-            System.err.println("Mapping not reflexive:" + from + " "
-                    + too[0] + "->" + toofrom[0]);
-          }
-          System.out.println("ShiftTo(" + too[0] + ")==" + toofrom[0]
-                  + " % " + toofrom[1] + " (" + toofrom[2] + ")");
-        }
-        else
-        {
-          System.out.println("ShiftTo(" + too[0] + ")=="
-                  + "NaN! - not Bijective Mapping!");
-        }
-      }
-    }
-    int mmap[][] = ml.makeFromMap();
-    System.out.println("FromMap : (" + mmap[0][0] + " " + mmap[0][1] + " "
-            + mmap[0][2] + " " + mmap[0][3] + " ");
-    for (int i = 1; i <= mmap[1].length; i++)
-    {
-      if (mmap[1][i - 1] == -1)
-      {
-        System.out.print(i + "=XXX");
-
-      }
-      else
-      {
-        System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1]));
-      }
-      if (i % 20 == 0)
-      {
-        System.out.print("\n");
-      }
-      else
-      {
-        System.out.print(",");
-      }
-    }
-    // test range function
-    System.out.print("\nTest locateInFrom\n");
-    {
-      int f = mmap[0][2], t = mmap[0][3];
-      while (f <= t)
-      {
-        System.out.println("Range " + f + " to " + t);
-        int rng[] = ml.locateInFrom(f, t);
-        if (rng != null)
-        {
-          for (int i = 0; i < rng.length; i++)
-          {
-            System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
-          }
-        }
-        else
-        {
-          System.out.println("No range!");
-        }
-        System.out.print("\nReversed\n");
-        rng = ml.locateInFrom(t, f);
-        if (rng != null)
-        {
-          for (int i = 0; i < rng.length; i++)
-          {
-            System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
-          }
-        }
-        else
-        {
-          System.out.println("No range!");
-        }
-        System.out.print("\n");
-        f++;
-        t--;
-      }
-    }
-    System.out.print("\n");
-    mmap = ml.makeToMap();
-    System.out.println("ToMap : (" + mmap[0][0] + " " + mmap[0][1] + " "
-            + mmap[0][2] + " " + mmap[0][3] + " ");
-    for (int i = 1; i <= mmap[1].length; i++)
-    {
-      if (mmap[1][i - 1] == -1)
-      {
-        System.out.print(i + "=XXX");
-
-      }
-      else
-      {
-        System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1]));
-      }
-      if (i % 20 == 0)
-      {
-        System.out.print("\n");
-      }
-      else
-      {
-        System.out.print(",");
-      }
-    }
-    System.out.print("\n");
-    // test range function
-    System.out.print("\nTest locateInTo\n");
-    {
-      int f = mmap[0][2], t = mmap[0][3];
-      while (f <= t)
-      {
-        System.out.println("Range " + f + " to " + t);
-        int rng[] = ml.locateInTo(f, t);
-        if (rng != null)
-        {
-          for (int i = 0; i < rng.length; i++)
-          {
-            System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
-          }
-        }
-        else
-        {
-          System.out.println("No range!");
-        }
-        System.out.print("\nReversed\n");
-        rng = ml.locateInTo(t, f);
-        if (rng != null)
-        {
-          for (int i = 0; i < rng.length; i++)
-          {
-            System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
-          }
-        }
-        else
-        {
-          System.out.println("No range!");
-        }
-        f++;
-        t--;
-        System.out.print("\n");
-      }
-    }
-
-  }
-
-  public static void main(String argv[])
-  {
-    MapList ml = new MapList(new int[]
-    { 1, 5, 10, 15, 25, 20 }, new int[]
-    { 51, 1 }, 1, 3);
-    MapList ml1 = new MapList(new int[]
-    { 1, 3, 17, 4 }, new int[]
-    { 51, 1 }, 1, 3);
-    MapList ml2 = new MapList(new int[]
-    { 1, 60 }, new int[]
-    { 1, 20 }, 3, 1);
-    // test internal consistency
-    int to[] = new int[51];
-    MapList.testMap(ml, 1, 60);
-    MapList mldna = new MapList(new int[]
-    { 2, 2, 6, 8, 12, 16 }, new int[]
-    { 1, 3 }, 3, 1);
-    int[] frm = mldna.locateInFrom(1, 1);
-    testLocateFrom(mldna, 1, 1, new int[]
-    { 2, 2, 6, 7 });
-    MapList.testMap(mldna, 1, 3);
-    /*
-     * for (int from=1; from<=51; from++) { int[] too=ml.shiftTo(from); int[]
-     * toofrom=ml.shiftFrom(too[0]);
-     * System.out.println("ShiftFrom("+from+")=="+too[0]+" %
-     * "+too[1]+"\t+-+\tShiftTo("+too[0]+")=="+toofrom[0]+" % "+toofrom[1]); }
-     */
-    System.out.print("Success?\n"); // if we get here - something must be
-    // working!
-  }
-
-  private static void testLocateFrom(MapList mldna, int i, int j, int[] ks)
-  {
-    int[] frm = mldna.locateInFrom(i, j);
-    if (frm == ks || java.util.Arrays.equals(frm, ks))
-    {
-      System.out.println("Success test locate from " + i + " to " + j);
-    }
-    else
-    {
-      System.err.println("Failed test locate from " + i + " to " + j);
-      for (int c = 0; c < frm.length; c++)
-      {
-        System.err.print(frm[c] + ((c % 2 == 0) ? "," : ";"));
-      }
-      System.err.println("Expected");
-      for (int c = 0; c < ks.length; c++)
-      {
-        System.err.print(ks[c] + ((c % 2 == 0) ? "," : ";"));
-      }
-    }
-  }
-
-  /**
    * 
    * @return a MapList whose From range is this maplist's To Range, and vice
    *         versa
@@ -987,6 +845,7 @@ public class MapList
    */
   public boolean containsEither(boolean local, MapList map)
   {
+    // TODO not used - remove?
     if (local)
     {
       return ((getFromLowest() >= map.getFromLowest() && getFromHighest() <= map
diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java
new file mode 100644 (file)
index 0000000..4cfb49e
--- /dev/null
@@ -0,0 +1,545 @@
+package jalview.util;
+
+import jalview.analysis.AlignmentSorter;
+import jalview.api.AlignViewportI;
+import jalview.commands.CommandI;
+import jalview.commands.EditCommand;
+import jalview.commands.EditCommand.Action;
+import jalview.commands.EditCommand.Edit;
+import jalview.commands.OrderCommand;
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper methods for manipulations involving sequence mappings.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public final class MappingUtils
+{
+
+  /**
+   * Helper method to map a CUT or PASTE command.
+   * 
+   * @param edit
+   *          the original command
+   * @param undo
+   *          if true, the command is to be undone
+   * @param targetSeqs
+   *          the mapped sequences to apply the mapped command to
+   * @param result
+   *          the mapped EditCommand to add to
+   * @param mappings
+   */
+  protected static void mapCutOrPaste(Edit edit, boolean undo,
+          List<SequenceI> targetSeqs, EditCommand result,
+          Set<AlignedCodonFrame> mappings)
+  {
+    Action action = edit.getAction();
+    if (undo)
+    {
+      action = action.getUndoAction();
+    }
+    // TODO write this
+    System.err.println("MappingUtils.mapCutOrPaste not yet implemented");
+  }
+
+  /**
+   * Returns a new EditCommand representing the given command as mapped to the
+   * given sequences. If there is no mapping, returns null.
+   * 
+   * @param command
+   * @param undo
+   * @param mapTo
+   * @param gapChar
+   * @param mappings
+   * @return
+   */
+  public static EditCommand mapEditCommand(EditCommand command,
+          boolean undo, final AlignmentI mapTo, char gapChar,
+          Set<AlignedCodonFrame> mappings)
+  {
+    /*
+     * For now, only support mapping from protein edits to cDna
+     */
+    if (!mapTo.isNucleotide())
+    {
+      return null;
+    }
+
+    /*
+     * Cache a copy of the target sequences so we can mimic successive edits on
+     * them. This lets us compute mappings for all edits in the set.
+     */
+    Map<SequenceI, SequenceI> targetCopies = new HashMap<SequenceI, SequenceI>();
+    for (SequenceI seq : mapTo.getSequences())
+    {
+      SequenceI ds = seq.getDatasetSequence();
+      if (ds != null)
+      {
+        final SequenceI copy = new Sequence(seq);
+        copy.setDatasetSequence(ds);
+        targetCopies.put(ds, copy);
+      }
+    }
+
+    /*
+     * Compute 'source' sequences as they were before applying edits:
+     */
+    Map<SequenceI, SequenceI> originalSequences = command.priorState(undo);
+
+    EditCommand result = new EditCommand();
+    Iterator<Edit> edits = command.getEditIterator(!undo);
+    while (edits.hasNext())
+    {
+      Edit edit = edits.next();
+      if (edit.getAction() == Action.CUT
+              || edit.getAction() == Action.PASTE)
+      {
+        mapCutOrPaste(edit, undo, mapTo.getSequences(), result, mappings);
+      }
+      else if (edit.getAction() == Action.INSERT_GAP
+              || edit.getAction() == Action.DELETE_GAP)
+      {
+        mapInsertOrDelete(edit, undo, originalSequences,
+                mapTo.getSequences(), targetCopies, gapChar, result,
+                mappings);
+      }
+    }
+    return result.getSize() > 0 ? result : null;
+  }
+
+  /**
+   * Helper method to map an edit command to insert or delete gaps.
+   * 
+   * @param edit
+   *          the original command
+   * @param undo
+   *          if true, the action is to undo the command
+   * @param originalSequences
+   *          the sequences the command acted on
+   * @param targetSeqs
+   * @param targetCopies
+   * @param gapChar
+   * @param result
+   *          the new EditCommand to add mapped commands to
+   * @param mappings
+   */
+  protected static void mapInsertOrDelete(Edit edit, boolean undo,
+          Map<SequenceI, SequenceI> originalSequences,
+          final List<SequenceI> targetSeqs,
+          Map<SequenceI, SequenceI> targetCopies, char gapChar,
+          EditCommand result, Set<AlignedCodonFrame> mappings)
+  {
+    Action action = edit.getAction();
+
+    /*
+     * Invert sense of action if an Undo.
+     */
+    if (undo)
+    {
+      action = action.getUndoAction();
+    }
+    final int count = edit.getNumber();
+    final int editPos = edit.getPosition();
+    for (SequenceI seq : edit.getSequences())
+    {
+      /*
+       * Get residue position at (or to right of) edit location. Note we use our
+       * 'copy' of the sequence before editing for this.
+       */
+      SequenceI ds = seq.getDatasetSequence();
+      if (ds == null)
+      {
+        continue;
+      }
+      final SequenceI actedOn = originalSequences.get(ds);
+      final int seqpos = actedOn.findPosition(editPos);
+
+      /*
+       * Determine all mappings from this position to mapped sequences.
+       */
+      SearchResults sr = buildSearchResults(seq, seqpos, mappings);
+
+      if (!sr.isEmpty())
+      {
+        for (SequenceI targetSeq : targetSeqs)
+        {
+          ds = targetSeq.getDatasetSequence();
+          if (ds == null)
+          {
+            continue;
+          }
+          SequenceI copyTarget = targetCopies.get(ds);
+          final int[] match = sr.getResults(copyTarget, 0,
+                  copyTarget.getLength());
+          if (match != null)
+          {
+            final int ratio = 3; // TODO: compute this - how?
+            final int mappedCount = count * ratio;
+
+            /*
+             * Shift Delete start position left, as it acts on positions to its
+             * right.
+             */
+            int mappedEditPos = action == Action.DELETE_GAP ? match[0]
+                    - mappedCount : match[0];
+            Edit e = result.new Edit(action, new SequenceI[]
+            { targetSeq }, mappedEditPos, mappedCount, gapChar);
+            result.addEdit(e);
+
+            /*
+             * and 'apply' the edit to our copy of its target sequence
+             */
+            if (action == Action.INSERT_GAP)
+            {
+              copyTarget.setSequence(new String(StringUtils.insertCharAt(
+                      copyTarget.getSequence(), mappedEditPos, mappedCount,
+                      gapChar)));
+            }
+            else if (action == Action.DELETE_GAP)
+            {
+              copyTarget.setSequence(new String(StringUtils.deleteChars(
+                      copyTarget.getSequence(), mappedEditPos,
+                      mappedEditPos + mappedCount)));
+            }
+          }
+        }
+      }
+      /*
+       * and 'apply' the edit to our copy of its source sequence
+       */
+      if (action == Action.INSERT_GAP)
+      {
+        actedOn.setSequence(new String(StringUtils.insertCharAt(
+                actedOn.getSequence(), editPos, count, gapChar)));
+      }
+      else if (action == Action.DELETE_GAP)
+      {
+        actedOn.setSequence(new String(StringUtils.deleteChars(
+                actedOn.getSequence(), editPos, editPos + count)));
+      }
+    }
+  }
+
+  /**
+   * Returns a SearchResults object describing the mapped region corresponding
+   * to the specified sequence position.
+   * 
+   * @param seq
+   * @param index
+   * @param seqmappings
+   * @return
+   */
+  public static SearchResults buildSearchResults(SequenceI seq, int index,
+          Set<AlignedCodonFrame> seqmappings)
+  {
+    SearchResults results;
+    results = new SearchResults();
+    if (index >= seq.getStart() && index <= seq.getEnd())
+    {
+      for (AlignedCodonFrame acf : seqmappings)
+      {
+        acf.markMappedRegion(seq, index, results);
+      }
+    }
+    return results;
+  }
+
+  /**
+   * Returns a (possibly empty) SequenceGroup containing any sequences in the
+   * mapped viewport corresponding to the given group in the source viewport.
+   * 
+   * @param sg
+   * @param mapFrom
+   * @param mapTo
+   * @return
+   */
+  public static SequenceGroup mapSequenceGroup(SequenceGroup sg,
+          AlignViewportI mapFrom, AlignViewportI mapTo)
+  {
+    /*
+     * Note the SequenceGroup holds aligned sequences, the mappings hold dataset
+     * sequences.
+     */
+    boolean targetIsNucleotide = mapTo.isNucleotide();
+    AlignViewportI protein = targetIsNucleotide ? mapFrom : mapTo;
+    Set<AlignedCodonFrame> codonFrames = protein.getAlignment()
+            .getCodonFrames();
+
+    /*
+     * Copy group name, name colours, but not sequences or sequence colour
+     * scheme
+     */
+    SequenceGroup mappedGroup = new SequenceGroup(sg);
+    mappedGroup.cs = mapTo.getGlobalColourScheme();
+    mappedGroup.clear();
+    // TODO set width of mapped group
+
+    for (SequenceI selected : sg.getSequences())
+    {
+      for (AlignedCodonFrame acf : codonFrames)
+      {
+        SequenceI mappedSequence = targetIsNucleotide ? acf
+                .getDnaForAaSeq(selected) : acf.getAaForDnaSeq(selected);
+        if (mappedSequence != null)
+        {
+          for (SequenceI seq : mapTo.getAlignment().getSequences())
+          {
+            if (seq.getDatasetSequence() == mappedSequence)
+            {
+              mappedGroup.addSequence(seq, false);
+              break;
+            }
+          }
+        }
+      }
+    }
+    return mappedGroup;
+  }
+
+  /**
+   * Returns an OrderCommand equivalent to the given one, but acting on mapped
+   * sequences as described by the mappings, or null if no mapping can be made.
+   * 
+   * @param command
+   *          the original order command
+   * @param undo
+   *          if true, the action is to undo the sort
+   * @param mapTo
+   *          the alignment we are mapping to
+   * @param mappings
+   *          the mappings available
+   * @return
+   */
+  public static CommandI mapOrderCommand(OrderCommand command,
+          boolean undo, AlignmentI mapTo, Set<AlignedCodonFrame> mappings)
+  {
+    SequenceI[] sortOrder = command.getSequenceOrder(undo);
+    List<SequenceI> mappedOrder = new ArrayList<SequenceI>();
+    int j = 0;
+    for (SequenceI seq : sortOrder)
+    {
+      for (AlignedCodonFrame acf : mappings)
+      {
+        /*
+         * Try protein-to-Dna, failing that try dna-to-protein
+         */
+        SequenceI mappedSeq = acf.getDnaForAaSeq(seq);
+        if (mappedSeq == null)
+        {
+          mappedSeq = acf.getAaForDnaSeq(seq);
+        }
+        if (mappedSeq != null)
+        {
+          for (SequenceI seq2 : mapTo.getSequences())
+          {
+            if (seq2.getDatasetSequence() == mappedSeq)
+            {
+              mappedOrder.add(seq2);
+              j++;
+              break;
+            }
+          }
+        }
+      }
+    }
+
+    /*
+     * Return null if no mappings made.
+     */
+    if (j == 0)
+    {
+      return null;
+    }
+
+    /*
+     * Add any unmapped sequences on the end of the sort in their original
+     * ordering.
+     */
+    if (j < mapTo.getHeight())
+    {
+      for (SequenceI seq : mapTo.getSequences())
+      {
+        if (!mappedOrder.contains(seq))
+        {
+          mappedOrder.add(seq);
+        }
+      }
+    }
+
+    /*
+     * Have to sort the sequences before constructing the OrderCommand - which
+     * then resorts them?!?
+     */
+    final SequenceI[] mappedOrderArray = mappedOrder
+            .toArray(new SequenceI[mappedOrder.size()]);
+    SequenceI[] oldOrder = mapTo.getSequencesArray();
+    AlignmentSorter.sortBy(mapTo, new AlignmentOrder(mappedOrderArray));
+    final OrderCommand result = new OrderCommand(command.getDescription(),
+            oldOrder, mapTo);
+    return result;
+  }
+
+  /**
+   * Returns a ColumnSelection in the 'mapTo' view which corresponds to the
+   * given selection in the 'mapFrom' view. We assume one is nucleotide, the
+   * other is protein (and holds the mappings from codons to protein residues).
+   * 
+   * @param colsel
+   * @param mapFrom
+   * @param mapTo
+   * @return
+   */
+  public static ColumnSelection mapColumnSelection(ColumnSelection colsel,
+          AlignViewportI mapFrom, AlignViewportI mapTo)
+  {
+    boolean targetIsNucleotide = mapTo.isNucleotide();
+    AlignViewportI protein = targetIsNucleotide ? mapFrom : mapTo;
+    Set<AlignedCodonFrame> codonFrames = protein.getAlignment()
+            .getCodonFrames();
+    ColumnSelection mappedColumns = new ColumnSelection();
+    char fromGapChar = mapFrom.getAlignment().getGapCharacter();
+
+    // FIXME allow for hidden columns
+
+    /*
+     * For each mapped column, find the range of columns that residues in that
+     * column map to.
+     */
+    for (Object obj : colsel.getSelected())
+    {
+      int col = ((Integer) obj).intValue();
+      int mappedToMin = Integer.MAX_VALUE;
+      int mappedToMax = Integer.MIN_VALUE;
+
+      /*
+       * For each sequence in the 'from' alignment
+       */
+      for (SequenceI fromSeq : mapFrom.getAlignment().getSequences())
+      {
+        /*
+         * Ignore gaps (unmapped anyway)
+         */
+        if (fromSeq.getCharAt(col) == fromGapChar)
+        {
+          continue;
+        }
+
+        /*
+         * Get the residue position and find the mapped position.
+         */
+        int residuePos = fromSeq.findPosition(col);
+        SearchResults sr = buildSearchResults(fromSeq, residuePos,
+                codonFrames);
+        for (Match m : sr.getResults())
+        {
+          int mappedStartResidue = m.getStart();
+          int mappedEndResidue = m.getEnd();
+          SequenceI mappedSeq = m.getSequence();
+
+          /*
+           * Locate the aligned sequence whose dataset is mappedSeq. TODO a
+           * datamodel that can do this efficiently.
+           */
+          for (SequenceI toSeq : mapTo.getAlignment().getSequences())
+          {
+            if (toSeq.getDatasetSequence() == mappedSeq)
+            {
+              int mappedStartCol = toSeq.findIndex(mappedStartResidue);
+              int mappedEndCol = toSeq.findIndex(mappedEndResidue);
+              mappedToMin = Math.min(mappedToMin, mappedStartCol);
+              mappedToMax = Math.max(mappedToMax, mappedEndCol);
+              // System.out.println(fromSeq.getName() + " mapped to cols "
+              // + mappedStartCol + ":" + mappedEndCol);
+              break;
+              // note: remove break if we ever want to map one to many sequences
+            }
+          }
+        }
+      }
+      /*
+       * Add the range of mapped columns to the mapped selection (converting
+       * base 1 to base 0). Note that this may include intron-only regions which
+       * lie between the start and end ranges of the selection.
+       */
+      for (int i = mappedToMin; i <= mappedToMax; i++)
+      {
+        mappedColumns.addElement(i - 1);
+      }
+    }
+    return mappedColumns;
+  }
+
+  /**
+   * Returns the mapped codon for a given aligned sequence column position (base
+   * 0).
+   * 
+   * @param seq
+   *          an aligned peptide sequence
+   * @param col
+   *          an aligned column position (base 0)
+   * @param mappings
+   *          a set of codon mappings
+   * @return the bases of the mapped codon in the cDNA dataset sequence, or null
+   *         if not found
+   */
+  public static char[] findCodonFor(SequenceI seq, int col,
+          Set<AlignedCodonFrame> mappings)
+  {
+    int dsPos = seq.findPosition(col);
+    for (AlignedCodonFrame mapping : mappings)
+    {
+      if (mapping.involvesSequence(seq))
+      {
+        return mapping.getMappedCodon(seq.getDatasetSequence(), dsPos);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Converts a series of [start, end] ranges into an array of individual
+   * positions.
+   * 
+   * @param ranges
+   * @return
+   */
+  public static int[] flattenRanges(int[] ranges)
+  {
+    /*
+     * Count how many positions altogether
+     */
+    int count = 0;
+    for (int i = 0; i < ranges.length - 1; i += 2)
+    {
+      count += ranges[i + 1] - ranges[i] + 1;
+    }
+
+    int[] result = new int[count];
+    int k = 0;
+    for (int i = 0; i < ranges.length - 1; i += 2)
+    {
+      for (int j = ranges[i]; j <= ranges[i + 1]; j++)
+      {
+        result[k++] = j;
+      }
+    }
+    return result;
+  }
+}
index a92b29a..4001cb2 100644 (file)
@@ -92,6 +92,11 @@ public class MessageManager
 
   public static String formatMessage(String key, Object... params)
   {
+    return MessageFormat.format(rb.getString(key), params);
+  }
+
+  public static String formatMessage(String key, String[] params)
+  {
     return MessageFormat.format(rb.getString(key), (Object[]) params);
   }
 
index 19726b9..7aec22d 100644 (file)
@@ -32,6 +32,8 @@ import java.util.regex.Pattern;
  */
 public class ParseHtmlBodyAndLinks
 {
+  private static final Pattern LEFT_ANGLE_BRACKET_PATTERN = Pattern.compile("<");
+
   String orig = null;
 
   public String getOrig()
@@ -153,7 +155,7 @@ public class ParseHtmlBodyAndLinks
     {
       // instead of parsing the html into plaintext
       // clean the description ready for embedding in html
-      sb = new StringBuffer(Pattern.compile("<").matcher(description)
+      sb = new StringBuffer(LEFT_ANGLE_BRACKET_PATTERN.matcher(description)
               .replaceAll("&lt;"));
 
     }
index c630d96..4826bc3 100755 (executable)
  */
 package jalview.util;
 
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * A class to perform efficient sorting of arrays of objects based on arrays of
+ * scores or other attributes. For example, residues by percentage frequency.
+ * 
+ * @author gmcarstairs
+ *
+ */
 public class QuickSort
 {
+  static class FloatComparator implements Comparator<Integer>
+  {
+
+    private final float[] values;
+
+    FloatComparator(float[] v)
+    {
+      values = v;
+    }
+
+    @Override
+    public int compare(Integer o1, Integer o2)
+    {
+      return Float.compare(values[o1], values[o2]);
+    }
+    
+  }
+
+  static class IntComparator implements Comparator<Integer>
+  {
+
+    private final int[] values;
+
+    IntComparator(int[] v)
+    {
+      values = v;
+    }
+
+    @Override
+    public int compare(Integer o1, Integer o2)
+    {
+      return Integer.compare(values[o1], values[o2]);
+    }
+
+  }
+
+  /**
+   * Sorts both arrays with respect to ascending order of the items in the first
+   * array.
+   * 
+   * @param arr
+   * @param s
+   */
   public static void sort(int[] arr, Object[] s)
   {
     sort(arr, 0, arr.length - 1, s);
   }
 
+  /**
+   * Sorts both arrays with respect to ascending order of the items in the first
+   * array.
+   * 
+   * @param arr
+   * @param s
+   */
   public static void sort(float[] arr, Object[] s)
   {
     sort(arr, 0, arr.length - 1, s);
   }
 
+  /**
+   * Sorts both arrays with respect to ascending order of the items in the first
+   * array.
+   * 
+   * @param arr
+   * @param s
+   */
   public static void sort(double[] arr, Object[] s)
   {
     sort(arr, 0, arr.length - 1, s);
   }
 
+  /**
+   * Sorts both arrays with respect to descending order of the items in the
+   * first array.
+   * 
+   * @param arr
+   * @param s
+   */
   public static void sort(String[] arr, Object[] s)
   {
     stringSort(arr, 0, arr.length - 1, s);
   }
 
-  public static void stringSort(String[] arr, int p, int r, Object[] s)
+  static void stringSort(String[] arr, int p, int r, Object[] s)
   {
     int q;
 
@@ -54,7 +128,7 @@ public class QuickSort
     }
   }
 
-  public static void sort(float[] arr, int p, int r, Object[] s)
+  static void sort(float[] arr, int p, int r, Object[] s)
   {
     int q;
 
@@ -66,7 +140,7 @@ public class QuickSort
     }
   }
 
-  public static void sort(double[] arr, int p, int r, Object[] s)
+  static void sort(double[] arr, int p, int r, Object[] s)
   {
     int q;
 
@@ -78,7 +152,7 @@ public class QuickSort
     }
   }
 
-  public static void sort(int[] arr, int p, int r, Object[] s)
+  static void sort(int[] arr, int p, int r, Object[] s)
   {
     int q;
 
@@ -90,7 +164,7 @@ public class QuickSort
     }
   }
 
-  private static int partition(float[] arr, int p, int r, Object[] s)
+  static int partition(float[] arr, int p, int r, Object[] s)
   {
     float x = arr[p];
     int i = p - 1;
@@ -125,7 +199,42 @@ public class QuickSort
     }
   }
 
-  private static int partition(int[] arr, int p, int r, Object[] s)
+  static int partition(float[] arr, int p, int r, char[] s)
+  {
+    float x = arr[p];
+    int i = p - 1;
+    int j = r + 1;
+
+    while (true)
+    {
+      do
+      {
+        j = j - 1;
+      } while (arr[j] > x);
+
+      do
+      {
+        i = i + 1;
+      } while (arr[i] < x);
+
+      if (i < j)
+      {
+        float tmp = arr[i];
+        arr[i] = arr[j];
+        arr[j] = tmp;
+
+        char tmp2 = s[i];
+        s[i] = s[j];
+        s[j] = tmp2;
+      }
+      else
+      {
+        return j;
+      }
+    }
+  }
+
+  static int partition(int[] arr, int p, int r, Object[] s)
   {
     int x = arr[p];
     int i = p - 1;
@@ -160,7 +269,7 @@ public class QuickSort
     }
   }
 
-  private static int partition(double[] arr, int p, int r, Object[] s)
+  static int partition(double[] arr, int p, int r, Object[] s)
   {
     double x = arr[p];
     int i = p - 1;
@@ -195,7 +304,7 @@ public class QuickSort
     }
   }
 
-  private static int stringPartition(String[] arr, int p, int r, Object[] s)
+  static int stringPartition(String[] arr, int p, int r, Object[] s)
   {
     String x = arr[p];
     int i = p - 1;
@@ -229,4 +338,218 @@ public class QuickSort
       }
     }
   }
+
+  /**
+   * Sorts both arrays to give ascending order in the first array, by first
+   * partitioning into zero and non-zero values before sorting the latter.
+   * 
+   * @param arr
+   * @param s
+   */
+  public static void sort(float[] arr, char[] s)
+  {
+    /*
+     * Sort all zero values to the front
+     */
+    float[] f1 = new float[arr.length];
+    char[] s1 = new char[s.length];
+    int nextZeroValue = 0;
+    int nextNonZeroValue = arr.length - 1;
+    for (int i = 0; i < arr.length; i++)
+    {
+      float val = arr[i];
+      if (val > 0f)
+      {
+        f1[nextNonZeroValue] = val;
+        s1[nextNonZeroValue] = s[i];
+        nextNonZeroValue--;
+      }
+      else
+      {
+        f1[nextZeroValue] = val;
+        s1[nextZeroValue] = s[i];
+        nextZeroValue++;
+      }
+    }
+
+    /*
+     * Copy zero values back to original arrays
+     */
+    System.arraycopy(f1, 0, arr, 0, nextZeroValue);
+    System.arraycopy(s1, 0, s, 0, nextZeroValue);
+
+    if (nextZeroValue == arr.length)
+    {
+      return; // all zero
+    }
+    /*
+     * Sort the non-zero values
+     */
+    float[] nonZeroFloats = Arrays
+            .copyOfRange(f1, nextZeroValue, f1.length);
+    char[] nonZeroChars = Arrays.copyOfRange(s1, nextZeroValue, s1.length);
+    externalSort(nonZeroFloats, nonZeroChars);
+    // sort(nonZeroFloats, 0, nonZeroFloats.length - 1, nonZeroChars);
+
+    /*
+     * Assemble sorted non-zero results
+     */
+    System.arraycopy(nonZeroFloats, 0, arr, nextZeroValue,
+            nonZeroFloats.length);
+    System.arraycopy(nonZeroChars, 0, s, nextZeroValue, nonZeroChars.length);
+  }
+
+  /**
+   * Sort by making an array of indices, and sorting it using a comparator that
+   * refers to the float values.
+   * 
+   * @see http
+   *      ://stackoverflow.com/questions/4859261/get-the-indices-of-an-array-
+   *      after-sorting
+   * @param arr
+   * @param s
+   */
+  protected static void externalSort(float[] arr, char[] s)
+  {
+    final int length = arr.length;
+    Integer[] indices = makeIndexArray(length);
+    Arrays.sort(indices, new FloatComparator(arr));
+
+    /*
+     * Copy the array values as per the sorted indices
+     */
+    float[] sortedFloats = new float[length];
+    char[] sortedChars = new char[s.length];
+    for (int i = 0; i < length; i++)
+    {
+      sortedFloats[i] = arr[indices[i]];
+      sortedChars[i] = s[indices[i]];
+    }
+
+    /*
+     * And copy the sorted values back into the arrays
+     */
+    System.arraycopy(sortedFloats, 0, arr, 0, length);
+    System.arraycopy(sortedChars, 0, s, 0, s.length);
+  }
+
+  /**
+   * Make an array whose values are 0...length.
+   * 
+   * @param length
+   * @return
+   */
+  protected static Integer[] makeIndexArray(final int length)
+  {
+    Integer[] indices = new Integer[length];
+    for (int i = 0; i < length; i++)
+    {
+      indices[i] = i;
+    }
+    return indices;
+  }
+
+  static void sort(float[] arr, int p, int r, char[] s)
+  {
+    int q;
+    if (p < r)
+    {
+      q = partition(arr, p, r, s);
+      sort(arr, p, q, s);
+      sort(arr, q + 1, r, s);
+    }
+  }
+
+  /**
+   * Sorts both arrays to give ascending order in the first array, by first
+   * partitioning into zero and non-zero values before sorting the latter.
+   * 
+   * @param arr
+   * @param s
+   */
+  public static void sort(int[] arr, char[] s)
+  {
+    /*
+     * Sort all zero values to the front
+     */
+    int[] f1 = new int[arr.length];
+    char[] s1 = new char[s.length];
+    int nextZeroValue = 0;
+    int nextNonZeroValue = arr.length - 1;
+    for (int i = 0; i < arr.length; i++)
+    {
+      int val = arr[i];
+      if (val > 0f)
+      {
+        f1[nextNonZeroValue] = val;
+        s1[nextNonZeroValue] = s[i];
+        nextNonZeroValue--;
+      }
+      else
+      {
+        f1[nextZeroValue] = val;
+        s1[nextZeroValue] = s[i];
+        nextZeroValue++;
+      }
+    }
+  
+    /*
+     * Copy zero values back to original arrays
+     */
+    System.arraycopy(f1, 0, arr, 0, nextZeroValue);
+    System.arraycopy(s1, 0, s, 0, nextZeroValue);
+  
+    if (nextZeroValue == arr.length)
+    {
+      return; // all zero
+    }
+    /*
+     * Sort the non-zero values
+     */
+    int[] nonZeroInts = Arrays
+            .copyOfRange(f1, nextZeroValue, f1.length);
+    char[] nonZeroChars = Arrays.copyOfRange(s1, nextZeroValue, s1.length);
+    externalSort(nonZeroInts, nonZeroChars);
+    // sort(nonZeroFloats, 0, nonZeroFloats.length - 1, nonZeroChars);
+  
+    /*
+     * Assemble sorted non-zero results
+     */
+    System.arraycopy(nonZeroInts, 0, arr, nextZeroValue, nonZeroInts.length);
+    System.arraycopy(nonZeroChars, 0, s, nextZeroValue, nonZeroChars.length);
+  }
+
+  /**
+   * Sort by making an array of indices, and sorting it using a comparator that
+   * refers to the float values.
+   * 
+   * @see http
+   *      ://stackoverflow.com/questions/4859261/get-the-indices-of-an-array-
+   *      after-sorting
+   * @param arr
+   * @param s
+   */
+  protected static void externalSort(int[] arr, char[] s)
+  {
+    final int length = arr.length;
+    Integer[] indices = makeIndexArray(length);
+    Arrays.sort(indices, new IntComparator(arr));
+
+    /*
+     * Copy the array values as per the sorted indices
+     */
+    int[] sortedInts = new int[length];
+    char[] sortedChars = new char[s.length];
+    for (int i = 0; i < length; i++)
+    {
+      sortedInts[i] = arr[indices[i]];
+      sortedChars[i] = s[indices[i]];
+    }
+
+    /*
+     * And copy the sorted values back into the arrays
+     */
+    System.arraycopy(sortedInts, 0, arr, 0, length);
+    System.arraycopy(sortedChars, 0, s, 0, s.length);
+  }
 }
diff --git a/src/jalview/util/ReverseListIterator.java b/src/jalview/util/ReverseListIterator.java
new file mode 100644 (file)
index 0000000..69a7345
--- /dev/null
@@ -0,0 +1,42 @@
+package jalview.util;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * An iterator that traverses a list backwards.
+ * 
+ * @author gmcarstairs (and checked against
+ *         org.codehaus.groovey.runtime.ReverseListIterator)
+ *
+ * @param <E>
+ */
+public class ReverseListIterator<E> implements Iterator<E>
+{
+
+  private ListIterator<E> iterator;
+
+  public ReverseListIterator(List<E> stuff)
+  {
+    this.iterator = stuff.listIterator(stuff.size());
+  }
+  @Override
+  public boolean hasNext()
+  {
+    return iterator.hasPrevious();
+  }
+
+  @Override
+  public E next()
+  {
+    return iterator.previous();
+  }
+
+  @Override
+  public void remove()
+  {
+    iterator.remove();
+  }
+
+}
index 6d3ce1f..38d6a32 100644 (file)
@@ -20,7 +20,8 @@
  */
 package jalview.util;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * ShiftList Simple way of mapping a linear series to a new linear range with
@@ -29,11 +30,11 @@ import java.util.*;
  */
 public class ShiftList
 {
-  public Vector shifts;
+  private List<int[]> shifts;
 
   public ShiftList()
   {
-    shifts = new Vector();
+    shifts = new ArrayList<int[]>();
   }
 
   /**
@@ -46,21 +47,23 @@ public class ShiftList
    */
   public void addShift(int pos, int shift)
   {
-    int sidx = 0;
-    int[] rshift = null;
-    while (sidx < shifts.size()
-            && (rshift = (int[]) shifts.elementAt(sidx))[0] < pos)
-    {
-      sidx++;
-    }
-    if (sidx == shifts.size())
+    synchronized (shifts)
     {
-      shifts.insertElementAt(new int[]
-      { pos, shift }, sidx);
-    }
-    else
-    {
-      rshift[1] += shift;
+      int sidx = 0;
+      int[] rshift = null;
+      while (sidx < shifts.size() && (rshift = shifts.get(sidx))[0] < pos)
+      {
+        sidx++;
+      }
+      if (sidx == shifts.size())
+      {
+        shifts.add(sidx, new int[]
+        { pos, shift });
+      }
+      else
+      {
+        rshift[1] += shift;
+      }
     }
   }
 
@@ -81,7 +84,7 @@ public class ShiftList
     int sidx = 0;
     int rshift[];
     while (sidx < shifts.size()
-            && (rshift = ((int[]) shifts.elementAt(sidx++)))[0] <= pos)
+            && (rshift = (shifts.get(sidx++)))[0] <= pos)
     {
       shifted += rshift[1];
     }
@@ -91,9 +94,9 @@ public class ShiftList
   /**
    * clear all shifts
    */
-  public void clear()
+  public synchronized void clear()
   {
-    shifts.removeAllElements();
+    shifts.clear();
   }
 
   /**
@@ -104,15 +107,17 @@ public class ShiftList
   public ShiftList getInverse()
   {
     ShiftList inverse = new ShiftList();
-    if (shifts != null)
+    synchronized (shifts)
     {
-      for (int i = 0, j = shifts.size(); i < j; i++)
+      if (shifts != null)
       {
-        int[] sh = (int[]) shifts.elementAt(i);
-        if (sh != null)
+        for (int[] sh : shifts)
         {
-          inverse.shifts.addElement(new int[]
-          { sh[0], -sh[1] });
+          if (sh != null)
+          {
+            inverse.shifts.add(new int[]
+            { sh[0], -sh[1] });
+          }
         }
       }
     }
@@ -120,8 +125,8 @@ public class ShiftList
   }
 
   /**
-   * parse a 1d map of position 1<i<n to L<pos[i]<N such as that returned from
-   * SequenceI.gapMap()
+   * parse a 1d map of position 1&lt;i&lt;n to L&lt;pos[i]&lt;N such as that
+   * returned from SequenceI.gapMap()
    * 
    * @param gapMap
    * @return shifts from map index to mapped position
@@ -143,4 +148,9 @@ public class ShiftList
     }
     return shiftList;
   }
+
+  public List<int[]> getShifts()
+  {
+    return shifts;
+  }
 }
diff --git a/src/jalview/util/StringUtils.java b/src/jalview/util/StringUtils.java
new file mode 100644 (file)
index 0000000..1325ce5
--- /dev/null
@@ -0,0 +1,233 @@
+package jalview.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+
+public class StringUtils
+{
+  private static final Pattern DELIMITERS_PATTERN = Pattern.compile(".*='[^']*(?!')");
+
+  private static final boolean DEBUG = false;
+
+  /**
+   * Returns a new character array, after inserting characters into the given
+   * character array.
+   * 
+   * @param in
+   *          the character array to insert into
+   * @param position
+   *          the 0-based position for insertion
+   * @param count
+   *          the number of characters to insert
+   * @param ch
+   *          the character to insert
+   */
+  public static final char[] insertCharAt(char[] in, int position,
+          int count,
+          char ch)
+  {
+    char[] tmp = new char[in.length + count];
+  
+    if (position >= in.length)
+    {
+      System.arraycopy(in, 0, tmp, 0, in.length);
+      position = in.length;
+    }
+    else
+    {
+      System.arraycopy(in, 0, tmp, 0, position);
+    }
+  
+    int index = position;
+    while (count > 0)
+    {
+      tmp[index++] = ch;
+      count--;
+    }
+  
+    if (position < in.length)
+    {
+      System.arraycopy(in, position, tmp, index,
+              in.length - position);
+    }
+  
+    return tmp;
+  }
+
+  /**
+   * Delete
+   * 
+   * @param in
+   * @param from
+   * @param to
+   * @return
+   */
+  public static final char[] deleteChars(char[] in, int from, int to)
+  {
+    if (from >= in.length)
+    {
+      return in;
+    }
+
+    char[] tmp;
+
+    if (to >= in.length)
+    {
+      tmp = new char[from];
+      System.arraycopy(in, 0, tmp, 0, from);
+      to = in.length;
+    }
+    else
+    {
+      tmp = new char[in.length - to + from];
+      System.arraycopy(in, 0, tmp, 0, from);
+      System.arraycopy(in, to, tmp, from, in.length - to);
+    }
+    return tmp;
+  }
+
+  /**
+   * Returns the last part of 'input' after the last occurrence of 'token'. For
+   * example to extract only the filename from a full path or URL.
+   * 
+   * @param input
+   * @param token
+   *          a delimiter which must be in regular expression format
+   * @return
+   */
+  public static String getLastToken(String input, String token)
+  {
+    if (input == null)
+    {
+      return null;
+    }
+    if (token == null)
+    {
+      return input;
+    }
+    String[] st = input.split(token);
+    return st[st.length - 1];
+  }
+
+  /**
+   * Parses the input string into components separated by the delimiter. Unlike
+   * String.split(), this method will ignore occurrences of the delimiter which
+   * are nested within single quotes in name-value pair values, e.g. a='b,c'.
+   * 
+   * @param input
+   * @param delimiter
+   * @return elements separated by separator
+   */
+  public static String[] separatorListToArray(String input, String delimiter)
+  {
+    int seplen = delimiter.length();
+    if (input == null || input.equals("") || input.equals(delimiter))
+    {
+      return null;
+    }
+    List<String> jv = new ArrayList<String>();
+    int cp = 0, pos, escape;
+    boolean wasescaped = false, wasquoted = false;
+    String lstitem = null;
+    while ((pos = input.indexOf(delimiter, cp)) >= cp)
+    {
+      escape = (pos > 0 && input.charAt(pos - 1) == '\\') ? -1 : 0;
+      if (wasescaped || wasquoted)
+      {
+        // append to previous pos
+        jv.set(jv.size() - 1,
+                lstitem = lstitem + delimiter
+                        + input.substring(cp, pos + escape));
+      }
+      else
+      {
+        jv.add(lstitem = input.substring(cp, pos + escape));
+      }
+      cp = pos + seplen;
+      wasescaped = escape == -1;
+      // last separator may be in an unmatched quote
+      wasquoted = DELIMITERS_PATTERN.matcher(lstitem).matches();
+    }
+    if (cp < input.length())
+    {
+      String c = input.substring(cp);
+      if (wasescaped || wasquoted)
+      {
+        // append final separator
+        jv.set(jv.size() - 1, lstitem + delimiter + c);
+      }
+      else
+      {
+        if (!c.equals(delimiter))
+        {
+          jv.add(c);
+        }
+      }
+    }
+    if (jv.size() > 0)
+    {
+      String[] v = jv.toArray(new String[jv.size()]);
+      jv.clear();
+      if (DEBUG)
+      {
+        System.err.println("Array from '" + delimiter
+                + "' separated List:\n" + v.length);
+        for (int i = 0; i < v.length; i++)
+        {
+          System.err.println("item " + i + " '" + v[i] + "'");
+        }
+      }
+      return v;
+    }
+    if (DEBUG)
+    {
+      System.err.println("Empty Array from '" + delimiter
+              + "' separated List");
+    }
+    return null;
+  }
+
+  /**
+   * Returns a string which contains the list elements delimited by the
+   * separator. Null items are ignored. If the input is null or has length zero,
+   * a single delimiter is returned.
+   * 
+   * @param list
+   * @param separator
+   * @return concatenated string
+   */
+  public static String arrayToSeparatorList(String[] list, String separator)
+  {
+    StringBuffer v = new StringBuffer();
+    if (list != null && list.length > 0)
+    {
+      for (int i = 0, iSize = list.length; i < iSize; i++)
+      {
+        if (list[i] != null)
+        {
+          if (v.length() > 0)
+          {
+            v.append(separator);
+          }
+          // TODO - escape any separator values in list[i]
+          v.append(list[i]);
+        }
+      }
+      if (DEBUG)
+      {
+        System.err.println("Returning '" + separator
+                + "' separated List:\n");
+        System.err.println(v);
+      }
+      return v.toString();
+    }
+    if (DEBUG)
+    {
+      System.err.println("Returning empty '" + separator
+              + "' separated List\n");
+    }
+    return "" + separator;
+  }
+}
index 6cec30a..7054ed3 100644 (file)
  */
 package jalview.viewmodel;
 
+import java.awt.Color;
+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 java.util.Set;
+
+import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.Conservation;
 import jalview.api.AlignCalcManagerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeaturesDisplayedI;
+import jalview.api.ViewStyleI;
+import jalview.commands.CommandI;
+import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceCollectionI;
@@ -38,18 +54,15 @@ import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.PIDColourScheme;
 import jalview.schemes.ResidueProperties;
+import jalview.structure.CommandListener;
+import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasSource;
+import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
+import jalview.workers.ComplementConsensusThread;
 import jalview.workers.ConsensusThread;
 import jalview.workers.StrucConsensusThread;
 
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.Vector;
-
 /**
  * base class holding visualization and analysis attributes and common logic for
  * an active alignment view displayed in the GUI
@@ -57,152 +70,496 @@ import java.util.Vector;
  * @author jimp
  * 
  */
-public abstract class AlignmentViewport implements AlignViewportI
+public abstract class AlignmentViewport implements AlignViewportI,
+        ViewStyleI, CommandListener, VamsasSource
 {
+  protected ViewStyleI viewStyle = new ViewStyle();
+
   /**
-   * alignment displayed in the viewport. Please use get/setter
+   * A viewport that hosts the cDna view of this (protein), or vice versa (if
+   * set).
    */
-  protected AlignmentI alignment;
+  AlignViewportI codingComplement = null;
 
-  protected String sequenceSetID;
+  FeaturesDisplayedI featuresDisplayed = null;
+
+  protected Deque<CommandI> historyList = new ArrayDeque<CommandI>();
+
+  protected Deque<CommandI> redoList = new ArrayDeque<CommandI>();
 
   /**
-   * probably unused indicator that view is of a dataset rather than an
-   * alignment
+   * @param name
+   * @see jalview.api.ViewStyleI#setFontName(java.lang.String)
    */
-  protected boolean isDataset = false;
+  public void setFontName(String name)
+  {
+    viewStyle.setFontName(name);
+  }
 
-  private Map<SequenceI, SequenceCollectionI> hiddenRepSequences;
+  /**
+   * @param style
+   * @see jalview.api.ViewStyleI#setFontStyle(int)
+   */
+  public void setFontStyle(int style)
+  {
+    viewStyle.setFontStyle(style);
+  }
 
-  protected ColumnSelection colSel = new ColumnSelection();
+  /**
+   * @param size
+   * @see jalview.api.ViewStyleI#setFontSize(int)
+   */
+  public void setFontSize(int size)
+  {
+    viewStyle.setFontSize(size);
+  }
 
-  public boolean autoCalculateConsensus = true;
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getFontStyle()
+   */
+  public int getFontStyle()
+  {
+    return viewStyle.getFontStyle();
+  }
 
-  protected boolean autoCalculateStrucConsensus = true;
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getFontName()
+   */
+  public String getFontName()
+  {
+    return viewStyle.getFontName();
+  }
 
-  protected boolean ignoreGapsInConsensusCalculation = false;
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getFontSize()
+   */
+  public int getFontSize()
+  {
+    return viewStyle.getFontSize();
+  }
 
-  protected ColourSchemeI globalColourScheme = null;
+  /**
+   * @param upperCasebold
+   * @see jalview.api.ViewStyleI#setUpperCasebold(boolean)
+   */
+  public void setUpperCasebold(boolean upperCasebold)
+  {
+    viewStyle.setUpperCasebold(upperCasebold);
+  }
 
   /**
-   * gui state - changes to colour scheme propagated to all groups
+   * @return
+   * @see jalview.api.ViewStyleI#isUpperCasebold()
    */
-  private boolean colourAppliesToAllGroups;
+  public boolean isUpperCasebold()
+  {
+    return viewStyle.isUpperCasebold();
+  }
 
   /**
-   * @param value
-   *          indicating if subsequent colourscheme changes will be propagated
-   *          to all groups
+   * @return
+   * @see jalview.api.ViewStyleI#isSeqNameItalics()
+   */
+  public boolean isSeqNameItalics()
+  {
+    return viewStyle.isSeqNameItalics();
+  }
+
+  /**
+   * @param colourByReferenceSeq
+   * @see jalview.api.ViewStyleI#setColourByReferenceSeq(boolean)
+   */
+  public void setColourByReferenceSeq(boolean colourByReferenceSeq)
+  {
+    viewStyle.setColourByReferenceSeq(colourByReferenceSeq);
+  }
+
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setColourAppliesToAllGroups(boolean)
    */
   public void setColourAppliesToAllGroups(boolean b)
   {
-    colourAppliesToAllGroups = b;
+    viewStyle.setColourAppliesToAllGroups(b);
   }
 
   /**
-   * 
-   * 
-   * @return flag indicating if colourchanges propagated to all groups
+   * @return
+   * @see jalview.api.ViewStyleI#getColourAppliesToAllGroups()
    */
   public boolean getColourAppliesToAllGroups()
   {
-    return colourAppliesToAllGroups;
+    return viewStyle.getColourAppliesToAllGroups();
   }
 
-  boolean abovePIDThreshold = false;
-
   /**
-   * GUI state
-   * 
-   * @return true if percent identity threshold is applied to shading
+   * @return
+   * @see jalview.api.ViewStyleI#getAbovePIDThreshold()
    */
   public boolean getAbovePIDThreshold()
   {
-    return abovePIDThreshold;
+    return viewStyle.getAbovePIDThreshold();
+  }
+
+  /**
+   * @param inc
+   * @see jalview.api.ViewStyleI#setIncrement(int)
+   */
+  public void setIncrement(int inc)
+  {
+    viewStyle.setIncrement(inc);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getIncrement()
+   */
+  public int getIncrement()
+  {
+    return viewStyle.getIncrement();
   }
 
   /**
-   * GUI state
-   * 
-   * 
    * @param b
-   *          indicate if percent identity threshold is applied to shading
+   * @see jalview.api.ViewStyleI#setConservationSelected(boolean)
    */
-  public void setAbovePIDThreshold(boolean b)
+  public void setConservationSelected(boolean b)
   {
-    abovePIDThreshold = b;
+    viewStyle.setConservationSelected(b);
   }
 
-  int threshold;
+  /**
+   * @param show
+   * @see jalview.api.ViewStyleI#setShowHiddenMarkers(boolean)
+   */
+  public void setShowHiddenMarkers(boolean show)
+  {
+    viewStyle.setShowHiddenMarkers(show);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowHiddenMarkers()
+   */
+  public boolean getShowHiddenMarkers()
+  {
+    return viewStyle.getShowHiddenMarkers();
+  }
+
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setScaleRightWrapped(boolean)
+   */
+  public void setScaleRightWrapped(boolean b)
+  {
+    viewStyle.setScaleRightWrapped(b);
+  }
+
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setScaleLeftWrapped(boolean)
+   */
+  public void setScaleLeftWrapped(boolean b)
+  {
+    viewStyle.setScaleLeftWrapped(b);
+  }
+
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setScaleAboveWrapped(boolean)
+   */
+  public void setScaleAboveWrapped(boolean b)
+  {
+    viewStyle.setScaleAboveWrapped(b);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getScaleLeftWrapped()
+   */
+  public boolean getScaleLeftWrapped()
+  {
+    return viewStyle.getScaleLeftWrapped();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getScaleAboveWrapped()
+   */
+  public boolean getScaleAboveWrapped()
+  {
+    return viewStyle.getScaleAboveWrapped();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getScaleRightWrapped()
+   */
+  public boolean getScaleRightWrapped()
+  {
+    return viewStyle.getScaleRightWrapped();
+  }
+
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setAbovePIDThreshold(boolean)
+   */
+  public void setAbovePIDThreshold(boolean b)
+  {
+    viewStyle.setAbovePIDThreshold(b);
+  }
 
   /**
-   * DOCUMENT ME!
-   * 
    * @param thresh
-   *          DOCUMENT ME!
+   * @see jalview.api.ViewStyleI#setThreshold(int)
    */
   public void setThreshold(int thresh)
   {
-    threshold = thresh;
+    viewStyle.setThreshold(thresh);
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
+   * @return
+   * @see jalview.api.ViewStyleI#getThreshold()
    */
   public int getThreshold()
   {
-    return threshold;
+    return viewStyle.getThreshold();
   }
 
-  int increment;
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowJVSuffix()
+   */
+  public boolean getShowJVSuffix()
+  {
+    return viewStyle.getShowJVSuffix();
+  }
 
   /**
-   * 
-   * @param inc
-   *          set the scalar for bleaching colourschemes according to degree of
-   *          conservation
+   * @param b
+   * @see jalview.api.ViewStyleI#setShowJVSuffix(boolean)
    */
-  public void setIncrement(int inc)
+  public void setShowJVSuffix(boolean b)
   {
-    increment = inc;
+    viewStyle.setShowJVSuffix(b);
   }
 
   /**
-   * GUI State
-   * 
-   * @return get scalar for bleaching colourschemes by conservation
+   * @param state
+   * @see jalview.api.ViewStyleI#setWrapAlignment(boolean)
    */
-  public int getIncrement()
+  public void setWrapAlignment(boolean state)
   {
-    return increment;
+    viewStyle.setWrapAlignment(state);
   }
 
-  boolean conservationColourSelected = false;
+  /**
+   * @param state
+   * @see jalview.api.ViewStyleI#setShowText(boolean)
+   */
+  public void setShowText(boolean state)
+  {
+    viewStyle.setShowText(state);
+  }
 
   /**
-   * GUI state
-   * 
-   * @return true if conservation based shading is enabled
+   * @param state
+   * @see jalview.api.ViewStyleI#setRenderGaps(boolean)
    */
-  public boolean getConservationSelected()
+  public void setRenderGaps(boolean state)
   {
-    return conservationColourSelected;
+    viewStyle.setRenderGaps(state);
   }
 
   /**
-   * GUI state
-   * 
-   * @param b
-   *          enable conservation based shading
+   * @return
+   * @see jalview.api.ViewStyleI#getColourText()
    */
-  public void setConservationSelected(boolean b)
+  public boolean getColourText()
+  {
+    return viewStyle.getColourText();
+  }
+
+  /**
+   * @param state
+   * @see jalview.api.ViewStyleI#setColourText(boolean)
+   */
+  public void setColourText(boolean state)
+  {
+    viewStyle.setColourText(state);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getWrapAlignment()
+   */
+  public boolean getWrapAlignment()
+  {
+    return viewStyle.getWrapAlignment();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowText()
+   */
+  public boolean getShowText()
+  {
+    return viewStyle.getShowText();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getWrappedWidth()
+   */
+  public int getWrappedWidth()
+  {
+    return viewStyle.getWrappedWidth();
+  }
+
+  /**
+   * @param w
+   * @see jalview.api.ViewStyleI#setWrappedWidth(int)
+   */
+  public void setWrappedWidth(int w)
+  {
+    viewStyle.setWrappedWidth(w);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getCharHeight()
+   */
+  public int getCharHeight()
+  {
+    return viewStyle.getCharHeight();
+  }
+
+  /**
+   * @param h
+   * @see jalview.api.ViewStyleI#setCharHeight(int)
+   */
+  public void setCharHeight(int h)
+  {
+    viewStyle.setCharHeight(h);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getCharWidth()
+   */
+  public int getCharWidth()
+  {
+    return viewStyle.getCharWidth();
+  }
+
+  /**
+   * @param w
+   * @see jalview.api.ViewStyleI#setCharWidth(int)
+   */
+  public void setCharWidth(int w)
+  {
+    viewStyle.setCharWidth(w);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowBoxes()
+   */
+  public boolean getShowBoxes()
+  {
+    return viewStyle.getShowBoxes();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowUnconserved()
+   */
+  public boolean getShowUnconserved()
+  {
+    return viewStyle.getShowUnconserved();
+  }
+
+  /**
+   * @param showunconserved
+   * @see jalview.api.ViewStyleI#setShowUnconserved(boolean)
+   */
+  public void setShowUnconserved(boolean showunconserved)
+  {
+    viewStyle.setShowUnconserved(showunconserved);
+  }
+
+  /**
+   * @param default1
+   * @see jalview.api.ViewStyleI#setSeqNameItalics(boolean)
+   */
+  public void setSeqNameItalics(boolean default1)
+  {
+    viewStyle.setSeqNameItalics(default1);
+  }
+
+  /**
+   * @param selected
+   * @see jalview.api.ViewStyleI#setShowSeqFeaturesHeight(boolean)
+   */
+  public void setShowSeqFeaturesHeight(boolean selected)
+  {
+    viewStyle.setShowSeqFeaturesHeight(selected);
+  }
+
+  /**
+   * alignment displayed in the viewport. Please use get/setter
+   */
+  protected AlignmentI alignment;
+
+  @Override
+  public AlignmentI getAlignment()
   {
-    conservationColourSelected = b;
+    return alignment;
   }
 
   @Override
+  public char getGapCharacter()
+  {
+    return alignment.getGapCharacter();
+  }
+
+  protected String sequenceSetID;
+
+  /**
+   * probably unused indicator that view is of a dataset rather than an
+   * alignment
+   */
+  protected boolean isDataset = false;
+
+  public void setDataset(boolean b)
+  {
+    isDataset = b;
+  }
+
+  public boolean isDataset()
+  {
+    return isDataset;
+  }
+
+
+  private Map<SequenceI, SequenceCollectionI> hiddenRepSequences;
+
+  protected ColumnSelection colSel = new ColumnSelection();
+
+  public boolean autoCalculateConsensus = true;
+
+  protected boolean autoCalculateStrucConsensus = true;
+
+  protected boolean ignoreGapsInConsensusCalculation = false;
+
+  protected ColourSchemeI globalColourScheme = null;
+
+
+  @Override
   public void setGlobalColourScheme(ColourSchemeI cs)
   {
     // TODO: logic refactored from AlignFrame changeColour -
@@ -221,7 +578,8 @@ public abstract class AlignmentViewport implements AlignViewportI
               || cs instanceof Blosum62ColourScheme)
       {
         recalc = true;
-        cs.setThreshold(threshold, ignoreGapsInConsensusCalculation);
+        cs.setThreshold(viewStyle.getThreshold(),
+                ignoreGapsInConsensusCalculation);
       }
       else
       {
@@ -248,12 +606,13 @@ public abstract class AlignmentViewport implements AlignViewportI
         if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
                 || cs instanceof Blosum62ColourScheme)
         {
-          sg.cs.setThreshold(threshold, getIgnoreGapsConsensus());
+          sg.cs.setThreshold(viewStyle.getThreshold(),
+                  isIgnoreGapsConsensus());
           recalc = true;
         }
         else
         {
-          sg.cs.setThreshold(0, getIgnoreGapsConsensus());
+          sg.cs.setThreshold(0, isIgnoreGapsConsensus());
         }
 
         if (getConservationSelected())
@@ -287,6 +646,8 @@ public abstract class AlignmentViewport implements AlignViewportI
 
   protected AlignmentAnnotation consensus;
 
+  protected AlignmentAnnotation complementConsensus;
+
   protected AlignmentAnnotation strucConsensus;
 
   protected AlignmentAnnotation conservation;
@@ -303,6 +664,11 @@ public abstract class AlignmentViewport implements AlignViewportI
   protected Hashtable[] hconsensus = null;
 
   /**
+   * results of cDNA complement consensus visible portion of view
+   */
+  protected Hashtable[] hcomplementConsensus = null;
+
+  /**
    * results of secondary structure base pair consensus for visible portion of
    * view
    */
@@ -332,7 +698,12 @@ public abstract class AlignmentViewport implements AlignViewportI
   public void setSequenceConsensusHash(Hashtable[] hconsensus)
   {
     this.hconsensus = hconsensus;
+  }
 
+  @Override
+  public void setComplementConsensusHash(Hashtable[] hconsensus)
+  {
+    this.hcomplementConsensus = hconsensus;
   }
 
   @Override
@@ -342,6 +713,12 @@ public abstract class AlignmentViewport implements AlignViewportI
   }
 
   @Override
+  public Hashtable[] getComplementConsensusHash()
+  {
+    return hcomplementConsensus;
+  }
+
+  @Override
   public Hashtable[] getRnaStructureConsensusHash()
   {
     return hStrucConsensus;
@@ -373,6 +750,12 @@ public abstract class AlignmentViewport implements AlignViewportI
   }
 
   @Override
+  public AlignmentAnnotation getComplementConsensusAnnotation()
+  {
+    return complementConsensus;
+  }
+
+  @Override
   public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
   {
     return strucConsensus;
@@ -413,6 +796,20 @@ public abstract class AlignmentViewport implements AlignViewportI
     {
       calculator.registerWorker(new ConsensusThread(this, ap));
     }
+
+    /*
+     * A separate thread to compute cDNA consensus for a protein alignment
+     */
+    final AlignmentI al = this.getAlignment();
+    if (!al.isNucleotide() && al.getCodonFrames() != null
+            && !al.getCodonFrames().isEmpty())
+    {
+      if (calculator
+              .getRegisteredWorkersOfClass(ComplementConsensusThread.class) == null)
+      {
+        calculator.registerWorker(new ComplementConsensusThread(this, ap));
+      }
+    }
   }
 
   // --------START Structure Conservation
@@ -517,6 +914,7 @@ public abstract class AlignmentViewport implements AlignViewportI
       // annotation update method from alignframe to viewport
       this.showSequenceLogo = showSequenceLogo;
       calculator.updateAnnotationFor(ConsensusThread.class);
+      calculator.updateAnnotationFor(ComplementConsensusThread.class);
       calculator.updateAnnotationFor(StrucConsensusThread.class);
     }
     this.showSequenceLogo = showSequenceLogo;
@@ -576,39 +974,16 @@ public abstract class AlignmentViewport implements AlignViewportI
     return this.showConsensusHistogram;
   }
 
-  /**
-   * show non-conserved residues only
-   */
-  protected boolean showUnconserved = false;
-
-  /**
-   * when set, updateAlignment will always ensure sequences are of equal length
-   */
-  private boolean padGaps = false;
-
-  /**
-   * when set, alignment should be reordered according to a newly opened tree
-   */
-  public boolean sortByTree = false;
-
-  public boolean getShowUnconserved()
-  {
-    return showUnconserved;
-  }
-
-  public void setShowUnconserved(boolean showunconserved)
-  {
-    showUnconserved = showunconserved;
-  }
+  /**
+   * when set, updateAlignment will always ensure sequences are of equal length
+   */
+  private boolean padGaps = false;
 
   /**
-   * @param showNonconserved
-   *          the showUnconserved to set
+   * when set, alignment should be reordered according to a newly opened tree
    */
-  public void setShowunconserved(boolean displayNonconserved)
-  {
-    this.showUnconserved = displayNonconserved;
-  }
+  public boolean sortByTree = false;
+
 
   /**
    * 
@@ -722,6 +1097,7 @@ public abstract class AlignmentViewport implements AlignViewportI
    */
   protected String viewId = null;
 
+  @Override
   public String getViewId()
   {
     if (viewId == null)
@@ -796,7 +1172,7 @@ public abstract class AlignmentViewport implements AlignViewportI
   }
 
   @Override
-  public boolean getIgnoreGapsConsensus()
+  public boolean isIgnoreGapsConsensus()
   {
     return ignoreGapsInConsensusCalculation;
   }
@@ -813,7 +1189,11 @@ public abstract class AlignmentViewport implements AlignViewportI
 
   protected boolean showConsensus = true;
 
-  Hashtable sequenceColours;
+  private Map<SequenceI, Color> sequenceColours = new HashMap<SequenceI, Color>();
+
+  protected SequenceAnnotationOrder sortAnnotationsBy = null;
+
+  protected boolean showAutocalculatedAbove;
 
   /**
    * Property change listener for changes in alignment
@@ -1050,9 +1430,6 @@ public abstract class AlignmentViewport implements AlignViewportI
   }
 
   @Override
-  public abstract void sendSelection();
-
-  @Override
   public void invertColumnSelection()
   {
     colSel.invertColumnSelection(0, alignment.getWidth());
@@ -1074,10 +1451,8 @@ public abstract class AlignmentViewport implements AlignViewportI
       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
       for (int i = 0; i < sequences.length; i++)
       {
-        sequences[i] = new Sequence(sequences[i], annots); // construct new
-        // sequence with
-        // subset of visible
-        // annotation
+        // construct new sequence with subset of visible annotation
+        sequences[i] = new Sequence(sequences[i], annots);
       }
     }
     else
@@ -1106,10 +1481,10 @@ public abstract class AlignmentViewport implements AlignViewportI
 
 
   @Override
-  public jalview.datamodel.CigarArray getViewAsCigars(
+  public CigarArray getViewAsCigars(
           boolean selectedRegionOnly)
   {
-    return new jalview.datamodel.CigarArray(alignment, colSel,
+    return new CigarArray(alignment, colSel,
             (selectedRegionOnly ? selectionGroup : null));
   }
 
@@ -1171,9 +1546,9 @@ public abstract class AlignmentViewport implements AlignViewportI
 
 
   @Override
-  public int[][] getVisibleRegionBoundaries(int min, int max)
+  public List<int[]> getVisibleRegionBoundaries(int min, int max)
   {
-    Vector regions = new Vector();
+    ArrayList<int[]> regions = new ArrayList<int[]>();
     int start = min;
     int end = max;
 
@@ -1197,7 +1572,7 @@ public abstract class AlignmentViewport implements AlignViewportI
         }
       }
 
-      regions.addElement(new int[]
+      regions.add(new int[]
       { start, end });
 
       if (colSel != null && colSel.hasHiddenColumns())
@@ -1209,10 +1584,7 @@ public abstract class AlignmentViewport implements AlignViewportI
 
     int[][] startEnd = new int[regions.size()][2];
 
-    regions.copyInto(startEnd);
-
-    return startEnd;
-
+    return regions;
   }
 
   @Override
@@ -1347,21 +1719,42 @@ public abstract class AlignmentViewport implements AlignViewportI
       {
         initRNAStructure();
       }
-      initConsensus();
+      consensus = new AlignmentAnnotation("Consensus", "PID",
+              new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
+      initConsensus(consensus);
+
+      initComplementConsensus();
     }
   }
 
-  private void initConsensus()
+  /**
+   * If this is a protein alignment and there are mappings to cDNA, add the cDNA
+   * consensus annotation.
+   */
+  protected void initComplementConsensus()
   {
+    if (!alignment.isNucleotide())
+    {
+      final Set<AlignedCodonFrame> codonMappings = alignment
+              .getCodonFrames();
+      if (codonMappings != null && !codonMappings.isEmpty())
+      {
+        complementConsensus = new AlignmentAnnotation("cDNA Consensus",
+                "PID for cDNA", new Annotation[1], 0f, 100f,
+                AlignmentAnnotation.BAR_GRAPH);
+        initConsensus(complementConsensus);
+      }
+    }
+  }
 
-    consensus = new AlignmentAnnotation("Consensus", "PID",
-            new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
-    consensus.hasText = true;
-    consensus.autoCalculated = true;
+  private void initConsensus(AlignmentAnnotation aa)
+  {
+    aa.hasText = true;
+    aa.autoCalculated = true;
 
     if (showConsensus)
     {
-      alignment.addAnnotation(consensus);
+      alignment.addAnnotation(aa);
     }
   }
 
@@ -1423,57 +1816,57 @@ public abstract class AlignmentViewport implements AlignViewportI
   public int calcPanelHeight()
   {
     // setHeight of panels
-    AlignmentAnnotation[] aa = getAlignment().getAlignmentAnnotation();
+    AlignmentAnnotation[] anns = getAlignment().getAlignmentAnnotation();
     int height = 0;
     int charHeight = getCharHeight();
-    if (aa != null)
+    if (anns != null)
     {
       BitSet graphgrp = new BitSet();
-      for (int i = 0; i < aa.length; i++)
+      for (AlignmentAnnotation aa : anns)
       {
-        if (aa[i] == null)
+        if (aa == null)
         {
           System.err.println("Null annotation row: ignoring.");
           continue;
         }
-        if (!aa[i].visible)
+        if (!aa.visible)
         {
           continue;
         }
-        if (aa[i].graphGroup > -1)
+        if (aa.graphGroup > -1)
         {
-          if (graphgrp.get(aa[i].graphGroup))
+          if (graphgrp.get(aa.graphGroup))
           {
             continue;
           }
           else
           {
-            graphgrp.set(aa[i].graphGroup);
+            graphgrp.set(aa.graphGroup);
           }
         }
-        aa[i].height = 0;
+        aa.height = 0;
 
-        if (aa[i].hasText)
+        if (aa.hasText)
         {
-          aa[i].height += charHeight;
+          aa.height += charHeight;
         }
 
-        if (aa[i].hasIcons)
+        if (aa.hasIcons)
         {
-          aa[i].height += 16;
+          aa.height += 16;
         }
 
-        if (aa[i].graph > 0)
+        if (aa.graph > 0)
         {
-          aa[i].height += aa[i].graphHeight;
+          aa.height += aa.graphHeight;
         }
 
-        if (aa[i].height == 0)
+        if (aa.height == 0)
         {
-          aa[i].height = 20;
+          aa.height = 20;
         }
 
-        height += aa[i].height;
+        height += aa.height;
       }
     }
     if (height == 0)
@@ -1551,60 +1944,34 @@ public abstract class AlignmentViewport implements AlignViewportI
     }
     oldrfs.clear();
   }
-  /**
-   * show the reference sequence in the alignment view
-   */
-  private boolean displayReferenceSeq=false;
-  /**
-   * colour according to the reference sequence defined on the alignment
-   */
-  private boolean colourByReferenceSeq=false;
-
   @Override
   public boolean isDisplayReferenceSeq()
   {
-    return alignment.hasSeqrep() && displayReferenceSeq;
+    return alignment.hasSeqrep() && viewStyle.isDisplayReferenceSeq();
   }
 
   @Override
   public void setDisplayReferenceSeq(boolean displayReferenceSeq)
   {
-    this.displayReferenceSeq = displayReferenceSeq;
+    viewStyle.setDisplayReferenceSeq(displayReferenceSeq);
   }
 
+  @Override
   public boolean isColourByReferenceSeq()
   {
-    return alignment.hasSeqrep() && colourByReferenceSeq;
-  }
-
-  public void setColourByReferenceSeq(boolean colourByReferenceSeq)
-  {
-    this.colourByReferenceSeq = colourByReferenceSeq;
+    return alignment.hasSeqrep() && viewStyle.isColourByReferenceSeq();
   }
 
   @Override
   public Color getSequenceColour(SequenceI seq)
   {
-    Color sqc = Color.white;
-    if (sequenceColours != null)
-    {
-      sqc = (Color) sequenceColours.get(seq);
-      if (sqc == null)
-      {
-        sqc = Color.white;
-      }
-    }
-    return sqc;
+    Color sqc = sequenceColours.get(seq);
+    return (sqc == null ? Color.white : sqc);
   }
 
   @Override
   public void setSequenceColour(SequenceI seq, Color col)
   {
-    if (sequenceColours == null)
-    {
-      sequenceColours = new Hashtable();
-    }
-
     if (col == null)
     {
       sequenceColours.remove(seq);
@@ -1618,10 +1985,6 @@ public abstract class AlignmentViewport implements AlignViewportI
   @Override
   public void updateSequenceIdColours()
   {
-    if (sequenceColours == null)
-    {
-      sequenceColours = new Hashtable();
-    }
     for (SequenceGroup sg : alignment.getGroups())
     {
       if (sg.idColour != null)
@@ -1637,10 +2000,42 @@ public abstract class AlignmentViewport implements AlignViewportI
   @Override
   public void clearSequenceColours()
   {
-    sequenceColours = null;
+    sequenceColours.clear();
   };
 
-  FeaturesDisplayedI featuresDisplayed = null;
+  @Override
+  public AlignViewportI getCodingComplement()
+  {
+    return this.codingComplement;
+  }
+
+  /**
+   * Set this as the (cDna/protein) complement of the given viewport. Also
+   * ensures the reverse relationship is set on the given viewport.
+   */
+  @Override
+  public void setCodingComplement(AlignViewportI av)
+  {
+    if (this == av)
+    {
+      System.err.println("Ignoring recursive setCodingComplement request");
+    }
+    else
+    {
+      this.codingComplement = av;
+      // avoid infinite recursion!
+      if (av.getCodingComplement() != this)
+      {
+        av.setCodingComplement(this);
+      }
+    }
+  }
+
+  @Override
+  public boolean isNucleotide()
+  {
+    return getAlignment() == null ? false : getAlignment().isNucleotide();
+  }
 
   @Override
   public FeaturesDisplayedI getFeaturesDisplayed()
@@ -1661,11 +2056,6 @@ public abstract class AlignmentViewport implements AlignViewportI
   }
 
   /**
-   * display setting for showing/hiding sequence features on alignment view
-   */
-  boolean showSequenceFeatures = false;
-
-  /**
    * set the flag
    * 
    * @param b
@@ -1674,55 +2064,351 @@ public abstract class AlignmentViewport implements AlignViewportI
   @Override
   public void setShowSequenceFeatures(boolean b)
   {
-    showSequenceFeatures = b;
+    viewStyle.setShowSequenceFeatures(b);
   }
   @Override
   public boolean isShowSequenceFeatures()
   {
-    return showSequenceFeatures;
+    return viewStyle.isShowSequenceFeatures();
   }
 
-  boolean showSeqFeaturesHeight;
-
   @Override
   public void setShowSequenceFeaturesHeight(boolean selected)
   {
-    showSeqFeaturesHeight = selected;
+    viewStyle.setShowSeqFeaturesHeight(selected);
   }
 
   @Override
   public boolean isShowSequenceFeaturesHeight()
   {
-    return showSeqFeaturesHeight;
+    return viewStyle.isShowSequenceFeaturesHeight();
   }
 
-  private boolean showAnnotation = true;
-
-  private boolean rightAlignIds = false;
 
 
   @Override
   public void setShowAnnotation(boolean b)
   {
-    showAnnotation = b;
+    viewStyle.setShowAnnotation(b);
   }
 
   @Override
   public boolean isShowAnnotation()
   {
-    return showAnnotation;
+    return viewStyle.isShowAnnotation();
   }
 
   @Override
   public boolean isRightAlignIds()
   {
-    return rightAlignIds;
+    return viewStyle.isRightAlignIds();
   }
 
   @Override
   public void setRightAlignIds(boolean rightAlignIds)
   {
-    this.rightAlignIds = rightAlignIds;
+    viewStyle.setRightAlignIds(rightAlignIds);
+  }
+
+  @Override
+  public boolean getConservationSelected()
+  {
+    return viewStyle.getConservationSelected();
+  }
+
+  @Override
+  public void setShowBoxes(boolean state)
+  {
+    viewStyle.setShowBoxes(state);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getTextColour()
+   */
+  public Color getTextColour()
+  {
+    return viewStyle.getTextColour();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getTextColour2()
+   */
+  public Color getTextColour2()
+  {
+    return viewStyle.getTextColour2();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getThresholdTextColour()
+   */
+  public int getThresholdTextColour()
+  {
+    return viewStyle.getThresholdTextColour();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isConservationColourSelected()
+   */
+  public boolean isConservationColourSelected()
+  {
+    return viewStyle.isConservationColourSelected();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isRenderGaps()
+   */
+  public boolean isRenderGaps()
+  {
+    return viewStyle.isRenderGaps();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isShowColourText()
+   */
+  public boolean isShowColourText()
+  {
+    return viewStyle.isShowColourText();
+  }
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isShowSeqFeaturesHeight()
+   */
+  public boolean isShowSeqFeaturesHeight()
+  {
+    return viewStyle.isShowSeqFeaturesHeight();
+  }
+
+  /**
+   * @param conservationColourSelected
+   * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
+   */
+  public void setConservationColourSelected(
+          boolean conservationColourSelected)
+  {
+    viewStyle.setConservationColourSelected(conservationColourSelected);
+  }
+
+  /**
+   * @param showColourText
+   * @see jalview.api.ViewStyleI#setShowColourText(boolean)
+   */
+  public void setShowColourText(boolean showColourText)
+  {
+    viewStyle.setShowColourText(showColourText);
+  }
+
+  /**
+   * @param textColour
+   * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
+   */
+  public void setTextColour(Color textColour)
+  {
+    viewStyle.setTextColour(textColour);
+  }
+
+  /**
+   * @param thresholdTextColour
+   * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
+   */
+  public void setThresholdTextColour(int thresholdTextColour)
+  {
+    viewStyle.setThresholdTextColour(thresholdTextColour);
+  }
+
+  /**
+   * @param textColour2
+   * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
+   */
+  public void setTextColour2(Color textColour2)
+  {
+    viewStyle.setTextColour2(textColour2);
+  }
+
+  @Override
+  public ViewStyleI getViewStyle()
+  {
+    return new ViewStyle(viewStyle);
+  }
+
+  @Override
+  public void setViewStyle(ViewStyleI settingsForView)
+  {
+    viewStyle = new ViewStyle(settingsForView);
+  }
+
+  @Override
+  public boolean sameStyle(ViewStyleI them)
+  {
+    return viewStyle.sameStyle(them);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getIdWidth()
+   */
+  public int getIdWidth()
+  {
+    return viewStyle.getIdWidth();
+  }
+
+  /**
+   * @param i
+   * @see jalview.api.ViewStyleI#setIdWidth(int)
+   */
+  public void setIdWidth(int i)
+  {
+    viewStyle.setIdWidth(i);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isCentreColumnLabels()
+   */
+  public boolean isCentreColumnLabels()
+  {
+    return viewStyle.isCentreColumnLabels();
+  }
+
+  /**
+   * @param centreColumnLabels
+   * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
+   */
+  public void setCentreColumnLabels(boolean centreColumnLabels)
+  {
+    viewStyle.setCentreColumnLabels(centreColumnLabels);
+  }
+
+  /**
+   * @param showdbrefs
+   * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
+   */
+  public void setShowDBRefs(boolean showdbrefs)
+  {
+    viewStyle.setShowDBRefs(showdbrefs);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isShowDBRefs()
+   */
+  public boolean isShowDBRefs()
+  {
+    return viewStyle.isShowDBRefs();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isShowNPFeats()
+   */
+  public boolean isShowNPFeats()
+  {
+    return viewStyle.isShowNPFeats();
+  }
+
+  /**
+   * @param shownpfeats
+   * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
+   */
+  public void setShowNPFeats(boolean shownpfeats)
+  {
+    viewStyle.setShowNPFeats(shownpfeats);
+  }
+
+  public abstract StructureSelectionManager getStructureSelectionManager();
+
+  /**
+   * Add one command to the command history list.
+   * 
+   * @param command
+   */
+  public void addToHistoryList(CommandI command)
+  {
+    if (this.historyList != null)
+    {
+      this.historyList.push(command);
+      broadcastCommand(command, false);
+    }
+  }
+
+  protected void broadcastCommand(CommandI command, boolean undo)
+  {
+    getStructureSelectionManager().commandPerformed(command, undo, getVamsasSource());
+  }
+
+  /**
+   * Add one command to the command redo list.
+   * 
+   * @param command
+   */
+  public void addToRedoList(CommandI command)
+  {
+    if (this.redoList != null)
+    {
+      this.redoList.push(command);
+    }
+    broadcastCommand(command, true);
+  }
+
+  /**
+   * Clear the command redo list.
+   */
+  public void clearRedoList()
+  {
+    if (this.redoList != null)
+    {
+      this.redoList.clear();
+    }
+  }
+
+  public void setHistoryList(Deque<CommandI> list)
+  {
+    this.historyList = list;
+  }
+
+  public Deque<CommandI> getHistoryList()
+  {
+    return this.historyList;
+  }
+
+  public void setRedoList(Deque<CommandI> list)
+  {
+    this.redoList = list;
+  }
+
+  public Deque<CommandI> getRedoList()
+  {
+    return this.redoList;
+  }
+
+  @Override
+  public VamsasSource getVamsasSource()
+  {
+    return this;
+  }
+
+  public SequenceAnnotationOrder getSortAnnotationsBy()
+  {
+    return sortAnnotationsBy;
   }
 
+  public void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
+  {
+    this.sortAnnotationsBy = sortAnnotationsBy;
+  }
+
+  public boolean isShowAutocalculatedAbove()
+  {
+    return showAutocalculatedAbove;
+  }
+
+  public void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
+  {
+    this.showAutocalculatedAbove = showAutocalculatedAbove;
+  }
 }
index c7cee04..674f3d1 100644 (file)
@@ -247,12 +247,6 @@ public abstract class FeatureRendererModel implements
     ArrayList<SequenceFeature> tmp = new ArrayList<SequenceFeature>();
     SequenceFeature[] features = sequence.getSequenceFeatures();
 
-    while (features == null && sequence.getDatasetSequence() != null)
-    {
-      sequence = sequence.getDatasetSequence();
-      features = sequence.getSequenceFeatures();
-    }
-
     if (features != null)
     {
       for (int i = 0; i < features.length; i++)
@@ -329,9 +323,7 @@ public abstract class FeatureRendererModel implements
     for (int i = 0; i < alignment.getHeight(); i++)
     {
       SequenceI asq = alignment.getSequenceAt(i);
-      SequenceI dasq = asq.getDatasetSequence();
-      SequenceFeature[] features = dasq != null ? dasq
-              .getSequenceFeatures() : asq.getSequenceFeatures();
+      SequenceFeature[] features = asq.getSequenceFeatures();
 
       if (features == null)
       {
diff --git a/src/jalview/viewmodel/styles/ViewStyle.java b/src/jalview/viewmodel/styles/ViewStyle.java
new file mode 100644 (file)
index 0000000..1e97af4
--- /dev/null
@@ -0,0 +1,1007 @@
+package jalview.viewmodel.styles;
+
+import jalview.api.ViewStyleI;
+
+import java.awt.Color;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * A container for holding alignment view properties. View properties are
+ * data-independent, which means they can be safely copied between views
+ * involving different alignment data without causing exceptions in the
+ * rendering system.
+ * 
+ * @author jprocter
+ *
+ */
+public class ViewStyle implements ViewStyleI
+{
+
+  private boolean abovePIDThreshold = false;
+
+  int charHeight;
+
+  int charWidth;
+
+  int idWidth = -1;
+
+  /**
+   * gui state - changes to colour scheme propagated to all groups
+   */
+  private boolean colourAppliesToAllGroups;
+
+  /**
+   * centre columnar annotation labels in displayed alignment annotation
+   */
+  boolean centreColumnLabels = false;
+
+  private boolean showdbrefs;
+
+  private boolean shownpfeats;
+
+  // --------END Structure Conservation
+
+  /**
+   * colour according to the reference sequence defined on the alignment
+   */
+  private boolean colourByReferenceSeq = false;
+
+  boolean conservationColourSelected = false;
+
+  /**
+   * show the reference sequence in the alignment view
+   */
+  private boolean displayReferenceSeq = false;
+
+  private int increment;
+
+  /**
+   * display gap characters
+   */
+  boolean renderGaps = true;
+
+  private boolean rightAlignIds = false;
+
+  boolean scaleAboveWrapped = false;
+
+  boolean scaleLeftWrapped = true;
+
+  boolean scaleRightWrapped = true;
+
+  boolean seqNameItalics;
+
+  /**
+   * show annotation tracks on the alignment
+   */
+  private boolean showAnnotation = true;
+
+  /**
+   * render each residue in a coloured box
+   */
+  boolean showBoxes = true;
+
+  /**
+   * Colour sequence text
+   */
+  boolean showColourText = false;
+
+  /**
+   * show blue triangles
+   */
+  boolean showHiddenMarkers = true;
+
+  /**
+   * show /start-end in ID panel
+   */
+  boolean showJVSuffix = true;
+
+  /**
+   * scale features height according to score
+   */
+  boolean showSeqFeaturesHeight;
+
+  /**
+   * display setting for showing/hiding sequence features on alignment view
+   */
+  boolean showSequenceFeatures = false;
+
+  /**
+   * display sequence symbols
+   */
+  boolean showText = true;
+
+  /**
+   * show non-conserved residues only
+   */
+  protected boolean showUnconserved = false;
+
+  Color textColour = Color.black;
+
+  Color textColour2 = Color.white;
+
+  /**
+   * PID or consensus threshold
+   */
+  int threshold;
+
+  /**
+   * threshold for switching between textColour & textColour2
+   */
+  int thresholdTextColour = 0;
+
+  /**
+   * upper case characters in sequence are shown in bold
+   */
+  boolean upperCasebold = false;
+
+  /**
+   * name of base font for view
+   */
+  private String fontName;
+  /**
+   * size for base font
+   */
+  private int fontSize;
+  
+  public ViewStyle(ViewStyleI viewStyle)
+  {
+    ViewStyle.configureFrom(this, viewStyle);
+  }
+
+  public ViewStyle()
+  {
+  }
+
+  private static HashMap<String, Method> getters, isErs, setters;
+  static
+  {
+    getters = new HashMap<String, Method>();
+    isErs = new HashMap<String, Method>();
+    setters = new HashMap<String, Method>();
+    // Match Getters and Setters
+    for (Method m : ViewStyleI.class.getMethods())
+    {
+      if (m.getDeclaringClass() == ViewStyleI.class)
+      {
+        if (m.getName().startsWith("get"))
+        {
+          getters.put(m.getName().substring(3), m);
+        }
+        if (m.getName().startsWith("is"))
+        {
+          isErs.put(m.getName().substring(2), m);
+        }
+        if (m.getName().startsWith("set"))
+        {
+          setters.put(m.getName().substring(3), m);
+        }
+      }
+    }
+  }
+
+  private static void configureFrom(ViewStyle us, ViewStyleI viewStyle)
+  {
+      // try and do the set thing
+      for (String prop : setters.keySet())
+      {
+        Method getter = getters.get(prop);
+        Method setter = setters.get(prop);
+        if (getter == null)
+        {
+          getter = isErs.get(prop);
+        }
+      if (getter != null && setter != null)
+      {
+        try
+        {
+          setter.invoke(us, getter.invoke(viewStyle));
+        } catch (Exception q)
+        {
+          System.err.println("Unexpected exception setting view property "
+                  + prop + " by reflection");
+          q.printStackTrace();
+        }
+
+      }
+      }
+  }
+
+  private static boolean equivalent(ViewStyle us, ViewStyleI them)
+  {
+    // look for properties we can set
+    for (String prop : setters.keySet())
+    {
+      Method getter = getters.get(prop);
+      if (getter == null)
+      {
+        getter = isErs.get(prop);
+      }
+      if (getter != null)
+      {
+        try
+        {
+          if (!getter.invoke(them).equals(getter.invoke(us)))
+          {
+            return false;
+          }
+        } catch (Exception q)
+        {
+          System.err.println("Unexpected exception testing equivalence of property "
+                  + prop + " by reflection");
+          q.printStackTrace();
+        }
+      }
+    }
+
+    return true;
+  }
+
+  public boolean equals(ViewStyleI other)
+  {
+    return other == null ? false : equivalent(this, other);
+  }
+
+  /**
+   * @return the upperCasebold
+   */
+  @Override
+  public boolean isUpperCasebold()
+  {
+    return upperCasebold;
+  }
+
+  /**
+   * @param upperCasebold
+   *          the upperCasebold to set
+   */
+  @Override
+  public void setUpperCasebold(boolean upperCasebold)
+  {
+    this.upperCasebold = upperCasebold;
+  }
+
+  /**
+   * flag for wrapping
+   */
+  boolean wrapAlignment = false;
+
+  /**
+   * number columns in wrapped alignment
+   */
+  int wrappedWidth;
+
+  private int fontStyle;
+
+  /**
+   * GUI state
+   * 
+   * @return true if percent identity threshold is applied to shading
+   */
+  @Override
+  public boolean getAbovePIDThreshold()
+  {
+    return abovePIDThreshold;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public int getCharHeight()
+  {
+    return charHeight;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public int getCharWidth()
+  {
+    return charWidth;
+  }
+
+  /**
+   * 
+   * 
+   * @return flag indicating if colourchanges propagated to all groups
+   */
+  @Override
+  public boolean getColourAppliesToAllGroups()
+  {
+    return colourAppliesToAllGroups;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public boolean getColourText()
+  {
+    return showColourText;
+  }
+
+  /**
+   * GUI state
+   * 
+   * @return true if conservation based shading is enabled
+   */
+  @Override
+  public boolean getConservationSelected()
+  {
+    return conservationColourSelected;
+  }
+
+  /**
+   * GUI State
+   * 
+   * @return get scalar for bleaching colourschemes by conservation
+   */
+  @Override
+  public int getIncrement()
+  {
+    return increment;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public boolean getScaleAboveWrapped()
+  {
+    return scaleAboveWrapped;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public boolean getScaleLeftWrapped()
+  {
+    return scaleLeftWrapped;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public boolean getScaleRightWrapped()
+  {
+    return scaleRightWrapped;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public boolean getShowBoxes()
+  {
+    return showBoxes;
+  }
+
+  @Override
+  public boolean getShowHiddenMarkers()
+  {
+    return showHiddenMarkers;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public boolean getShowJVSuffix()
+  {
+    return showJVSuffix;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public boolean getShowText()
+  {
+    return showText;
+  }
+
+  @Override
+  public boolean getShowUnconserved()
+  {
+    return showUnconserved;
+  }
+
+  /**
+   * @return the textColour
+   */
+  @Override
+  public Color getTextColour()
+  {
+    return textColour;
+  }
+
+  /**
+   * @return the textColour2
+   */
+  @Override
+  public Color getTextColour2()
+  {
+    return textColour2;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public int getThreshold()
+  {
+    return threshold;
+  }
+
+  /**
+   * @return the thresholdTextColour
+   */
+  @Override
+  public int getThresholdTextColour()
+  {
+    return thresholdTextColour;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public boolean getWrapAlignment()
+  {
+    return wrapAlignment;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  @Override
+  public int getWrappedWidth()
+  {
+    return wrappedWidth;
+  }
+
+  @Override
+  public boolean isColourByReferenceSeq()
+  {
+    return colourByReferenceSeq;
+  }
+
+  /**
+   * @return the conservationColourSelected
+   */
+  @Override
+  public boolean isConservationColourSelected()
+  {
+    return conservationColourSelected;
+  }
+
+  @Override
+  public boolean isDisplayReferenceSeq()
+  {
+    return displayReferenceSeq;
+  }
+
+  /**
+   * @return the renderGaps
+   */
+  @Override
+  public boolean isRenderGaps()
+  {
+    return renderGaps;
+  }
+
+  @Override
+  public boolean isRightAlignIds()
+  {
+    return rightAlignIds;
+  }
+
+  /**
+   * @return the seqNameItalics
+   */
+  @Override
+  public boolean isSeqNameItalics()
+  {
+    return seqNameItalics;
+  }
+
+  @Override
+  public boolean isShowAnnotation()
+  {
+    return showAnnotation;
+  }
+
+  /**
+   * @return the showColourText
+   */
+  @Override
+  public boolean isShowColourText()
+  {
+    return showColourText;
+  }
+
+  /**
+   * @return the showSeqFeaturesHeight
+   */
+  @Override
+  public boolean isShowSeqFeaturesHeight()
+  {
+    return showSeqFeaturesHeight;
+  }
+
+  @Override
+  public boolean isShowSequenceFeatures()
+  {
+    return showSequenceFeatures;
+  }
+
+  @Override
+  public boolean isShowSequenceFeaturesHeight()
+  {
+
+    return showSeqFeaturesHeight;
+  }
+
+  /**
+   * GUI state
+   * 
+   * 
+   * @param b
+   *          indicate if percent identity threshold is applied to shading
+   */
+  @Override
+  public void setAbovePIDThreshold(boolean b)
+  {
+    abovePIDThreshold = b;
+  }
+
+  
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param h
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setCharHeight(int h)
+  {
+    this.charHeight = h;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param w
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setCharWidth(int w)
+  {
+    this.charWidth = w;
+  }
+
+
+  /**
+   * @param value
+   *          indicating if subsequent colourscheme changes will be propagated
+   *          to all groups
+   */
+  @Override
+  public void setColourAppliesToAllGroups(boolean b)
+  {
+    colourAppliesToAllGroups = b;
+  }
+
+  @Override
+  public void setColourByReferenceSeq(boolean colourByReferenceSeq)
+  {
+    this.colourByReferenceSeq = colourByReferenceSeq;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param state
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setColourText(boolean state)
+  {
+    showColourText = state;
+  }
+
+  /**
+   * @param conservationColourSelected
+   *          the conservationColourSelected to set
+   */
+  @Override
+  public void setConservationColourSelected(
+          boolean conservationColourSelected)
+  {
+    this.conservationColourSelected = conservationColourSelected;
+  }
+
+  /**
+   * GUI state
+   * 
+   * @param b
+   *          enable conservation based shading
+   */
+  @Override
+  public void setConservationSelected(boolean b)
+  {
+    conservationColourSelected = b;
+  }
+
+  @Override
+  public void setDisplayReferenceSeq(boolean displayReferenceSeq)
+  {
+    this.displayReferenceSeq = displayReferenceSeq;
+  }
+
+  /**
+   * 
+   * @param inc
+   *          set the scalar for bleaching colourschemes according to degree of
+   *          conservation
+   */
+  @Override
+  public void setIncrement(int inc)
+  {
+    increment = inc;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param state
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setRenderGaps(boolean state)
+  {
+    renderGaps = state;
+  }
+
+  @Override
+  public void setRightAlignIds(boolean rightAlignIds)
+  {
+    this.rightAlignIds = rightAlignIds;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param b
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setScaleAboveWrapped(boolean b)
+  {
+    scaleAboveWrapped = b;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param b
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setScaleLeftWrapped(boolean b)
+  {
+    scaleLeftWrapped = b;
+  }
+
+  /**
+   * 
+   * 
+   * @param scaleRightWrapped
+   *          - true or false
+   */
+
+  @Override
+  public void setScaleRightWrapped(boolean b)
+  {
+    scaleRightWrapped = b;
+  }
+
+  @Override
+  public void setSeqNameItalics(boolean italics)
+  {
+    seqNameItalics = italics;
+  }
+
+  @Override
+  public void setShowAnnotation(boolean b)
+  {
+    showAnnotation = b;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param state
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setShowBoxes(boolean state)
+  {
+    showBoxes = state;
+  }
+
+  /**
+   * @param showColourText
+   *          the showColourText to set
+   */
+  @Override
+  public void setShowColourText(boolean showColourText)
+  {
+    this.showColourText = showColourText;
+  }
+
+  @Override
+  public void setShowHiddenMarkers(boolean show)
+  {
+    showHiddenMarkers = show;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param b
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setShowJVSuffix(boolean b)
+  {
+    showJVSuffix = b;
+  }
+
+  @Override
+  public void setShowSeqFeaturesHeight(boolean selected)
+  {
+    showSeqFeaturesHeight = selected;
+
+  }
+
+  /**
+   * set the flag
+   * 
+   * @param b
+   *          features are displayed if true
+   */
+  @Override
+  public void setShowSequenceFeatures(boolean b)
+  {
+    showSequenceFeatures = b;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param state
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setShowText(boolean state)
+  {
+    showText = state;
+  }
+
+  @Override
+  public void setShowUnconserved(boolean showunconserved)
+  {
+    showUnconserved = showunconserved;
+  }
+
+  /**
+   * @param textColour
+   *          the textColour to set
+   */
+  @Override
+  public void setTextColour(Color textColour)
+  {
+    this.textColour = textColour;
+  }
+
+  /**
+   * @param textColour2
+   *          the textColour2 to set
+   */
+  @Override
+  public void setTextColour2(Color textColour2)
+  {
+    this.textColour2 = textColour2;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param thresh
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setThreshold(int thresh)
+  {
+    threshold = thresh;
+  }
+
+  /**
+   * @param thresholdTextColour
+   *          the thresholdTextColour to set
+   */
+  @Override
+  public void setThresholdTextColour(int thresholdTextColour)
+  {
+    this.thresholdTextColour = thresholdTextColour;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param state
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setWrapAlignment(boolean state)
+  {
+    wrapAlignment = state;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param w
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void setWrappedWidth(int w)
+  {
+    this.wrappedWidth = w;
+  }
+
+  @Override
+  public boolean sameStyle(ViewStyleI them)
+  {
+    return equivalent(this, them);
+  }
+
+  @Override
+  public String getFontName()
+  {
+    return fontName;
+  }
+
+  @Override
+  public int getFontSize()
+  {
+    return fontSize;
+  }
+
+  @Override
+  public int getFontStyle()
+  {
+    return fontStyle;
+  }
+
+  @Override
+  public void setFontName(String name)
+  {
+    fontName = name;
+  }
+
+  @Override
+  public void setFontSize(int size)
+  {
+    fontSize = size;
+
+  }
+
+  @Override
+  public void setFontStyle(int style)
+  {
+    fontStyle = style;
+  }
+
+  @Override
+  public int getIdWidth()
+  {
+    return idWidth;
+  }
+
+  /**
+   * @param idWidth
+   *          the idWidth to set
+   */
+  @Override
+  public void setIdWidth(int idWidth)
+  {
+    this.idWidth = idWidth;
+  }
+
+  /**
+   * @return the centreColumnLabels
+   */
+  @Override
+  public boolean isCentreColumnLabels()
+  {
+    return centreColumnLabels;
+  }
+
+  /**
+   * @param centreColumnLabels
+   *          the centreColumnLabels to set
+   */
+  @Override
+  public void setCentreColumnLabels(boolean centreColumnLabels)
+  {
+    this.centreColumnLabels = centreColumnLabels;
+  }
+
+  /**
+   * @return the showdbrefs
+   */
+  @Override
+  public boolean isShowDBRefs()
+  {
+    return showdbrefs;
+  }
+
+  /**
+   * @param showdbrefs
+   *          the showdbrefs to set
+   */
+  @Override
+  public void setShowDBRefs(boolean showdbrefs)
+  {
+    this.showdbrefs = showdbrefs;
+  }
+
+  /**
+   * @return the shownpfeats
+   */
+  @Override
+  public boolean isShowNPFeats()
+  {
+    return shownpfeats;
+  }
+
+  /**
+   * @param shownpfeats
+   *          the shownpfeats to set
+   */
+  @Override
+  public void setShowNPFeats(boolean shownpfeats)
+  {
+    this.shownpfeats = shownpfeats;
+  }
+}
index dd25593..1063706 100644 (file)
  */
 package jalview.workers;
 
+import jalview.api.AlignCalcManagerI;
+import jalview.api.AlignCalcWorkerI;
+import jalview.datamodel.AlignmentAnnotation;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
@@ -28,10 +32,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import jalview.api.AlignCalcManagerI;
-import jalview.api.AlignCalcWorkerI;
-import jalview.datamodel.AlignmentAnnotation;
-
 public class AlignCalcManager implements AlignCalcManagerI
 {
   private volatile List<AlignCalcWorkerI> restartable = Collections
@@ -256,10 +256,12 @@ public class AlignCalcManager implements AlignCalcManagerI
       for (List<AlignCalcWorkerI> workers : updating.values())
       {
         for (AlignCalcWorkerI worker : workers)
+        {
           if (worker.involves(alignmentAnnotation))
           {
             return true;
           }
+        }
       }
     }
     return false;
@@ -291,7 +293,7 @@ public class AlignCalcManager implements AlignCalcManagerI
     AlignCalcWorkerI[] workers;
     synchronized (canUpdate)
     {
-      workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
+      workers = canUpdate.toArray(new AlignCalcWorkerI[canUpdate.size()]);
     }
     for (AlignCalcWorkerI worker : workers)
     {
diff --git a/src/jalview/workers/ComplementConsensusThread.java b/src/jalview/workers/ComplementConsensusThread.java
new file mode 100644 (file)
index 0000000..2e4424e
--- /dev/null
@@ -0,0 +1,68 @@
+package jalview.workers;
+
+import java.util.Hashtable;
+
+import jalview.analysis.AAFrequency;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+
+/**
+ * A thread to recompute the consensus of the cDNA complement for a linked
+ * protein alignment.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class ComplementConsensusThread extends ConsensusThread
+{
+
+  public ComplementConsensusThread(AlignViewportI alignViewport,
+          AlignmentViewPanel alignPanel)
+  {
+    super(alignViewport, alignPanel);
+  }
+
+  @Override
+  protected AlignmentAnnotation getConsensusAnnotation()
+  {
+    return alignViewport.getComplementConsensusAnnotation();
+  }
+
+  @Override
+  protected Hashtable[] getViewportConsensus()
+  {
+    return alignViewport.getComplementConsensusHash();
+  }
+
+  @Override
+  protected void computeConsensus(AlignmentI alignment)
+  {
+    Hashtable[] hconsensus = new Hashtable[alignment.getWidth()];
+
+    SequenceI[] aseqs = getSequences();
+    AAFrequency.calculateCdna(alignment, hconsensus);
+
+    alignViewport.setComplementConsensusHash(hconsensus);
+  }
+
+  /**
+   * Convert the computed consensus data into the desired annotation for
+   * display.
+   * 
+   * @param consensusAnnotation
+   *          the annotation to be populated
+   * @param consensusData
+   *          the computed consensus data
+   */
+  @Override
+  protected void deriveConsensus(AlignmentAnnotation consensusAnnotation,
+          Hashtable[] consensusData)
+  {
+    AAFrequency.completeCdnaConsensus(consensusAnnotation, consensusData,
+            alignViewport.isShowSequenceLogo(), getSequences().length);
+  }
+
+}
index 8cde493..f244242 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.workers;
 
+import java.util.Hashtable;
+
 import jalview.analysis.AAFrequency;
 import jalview.api.AlignCalcWorkerI;
 import jalview.api.AlignViewportI;
@@ -30,13 +32,9 @@ import jalview.datamodel.Annotation;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
 
-import java.util.Hashtable;
-
 public class ConsensusThread extends AlignCalcWorker implements
         AlignCalcWorkerI
 {
-  private long nseq = -1;
-
   public ConsensusThread(AlignViewportI alignViewport,
           AlignmentViewPanel alignPanel)
   {
@@ -54,8 +52,7 @@ public class ConsensusThread extends AlignCalcWorker implements
     long started = System.currentTimeMillis();
     try
     {
-      AlignmentAnnotation consensus = alignViewport
-              .getAlignmentConsensusAnnotation();
+      AlignmentAnnotation consensus = getConsensusAnnotation();
       if (consensus == null || calcMan.isPending(this))
       {
         calcMan.workerComplete(this);
@@ -88,58 +85,90 @@ public class ConsensusThread extends AlignCalcWorker implements
       if (alignment == null || (aWidth = alignment.getWidth()) < 0)
       {
         calcMan.workerComplete(this);
-        // .updatingConservation = false;
-        // AlignViewport.UPDATING_CONSERVATION = false;
-
         return;
       }
-      consensus = alignViewport.getAlignmentConsensusAnnotation();
 
-      consensus.annotations = null;
-      consensus.annotations = new Annotation[aWidth];
-      Hashtable[] hconsensus = alignViewport.getSequenceConsensusHash();
-      hconsensus = new Hashtable[aWidth];
-      try
-      {
-        SequenceI aseqs[] = alignment.getSequencesArray();
-        nseq = aseqs.length;
-        AAFrequency.calculate(aseqs, 0, alignment.getWidth(), hconsensus,
-                true);
-      } catch (ArrayIndexOutOfBoundsException x)
-      {
-        // this happens due to a race condition -
-        // alignment was edited at same time as calculation was running
-        //
-        // calcMan.workerCannotRun(this);
-        calcMan.workerComplete(this);
-        return;
-      }
-      alignViewport.setSequenceConsensusHash(hconsensus);
+      eraseConsensus(aWidth);
+      computeConsensus(alignment);
       updateResultAnnotation(true);
-      ColourSchemeI globalColourScheme = alignViewport
-              .getGlobalColourScheme();
-      if (globalColourScheme != null)
+
+      if (ap != null)
       {
-        globalColourScheme.setConsensus(hconsensus);
+        ap.paintAlignment(true);
       }
-
     } catch (OutOfMemoryError error)
     {
       calcMan.workerCannotRun(this);
-
-      // consensus = null;
-      // hconsensus = null;
       ap.raiseOOMWarning("calculating consensus", error);
+    } finally
+    {
+      /*
+       * e.g. ArrayIndexOutOfBoundsException can happen due to a race condition
+       * - alignment was edited at same time as calculation was running
+       */
+      calcMan.workerComplete(this);
     }
+  }
+
+  /**
+   * Clear out any existing consensus annotations
+   * 
+   * @param aWidth
+   *          the width (number of columns) of the annotated alignment
+   */
+  protected void eraseConsensus(int aWidth)
+  {
+    AlignmentAnnotation consensus = getConsensusAnnotation();
+    consensus.annotations = new Annotation[aWidth];
+  }
+
+  /**
+   * @param alignment
+   */
+  protected void computeConsensus(AlignmentI alignment)
+  {
+    Hashtable[] hconsensus = new Hashtable[alignment.getWidth()];
+
+    SequenceI[] aseqs = getSequences();
+    AAFrequency.calculate(aseqs, 0, alignment.getWidth(), hconsensus,
+            true);
 
-    calcMan.workerComplete(this);
-    if (ap != null)
+    alignViewport.setSequenceConsensusHash(hconsensus);
+    setColourSchemeConsensus(hconsensus);
+  }
+
+  /**
+   * @return
+   */
+  protected SequenceI[] getSequences()
+  {
+    return alignViewport.getAlignment().getSequencesArray();
+  }
+
+  /**
+   * @param hconsensus
+   */
+  protected void setColourSchemeConsensus(Hashtable[] hconsensus)
+  {
+    ColourSchemeI globalColourScheme = alignViewport
+            .getGlobalColourScheme();
+    if (globalColourScheme != null)
     {
-      ap.paintAlignment(true);
+      globalColourScheme.setConsensus(hconsensus);
     }
   }
 
   /**
+   * Get the Consensus annotation for the alignment
+   * 
+   * @return
+   */
+  protected AlignmentAnnotation getConsensusAnnotation()
+  {
+    return alignViewport.getAlignmentConsensusAnnotation();
+  }
+
+  /**
    * update the consensus annotation from the sequence profile data using
    * current visualization settings.
    */
@@ -151,15 +180,40 @@ public class ConsensusThread extends AlignCalcWorker implements
 
   public void updateResultAnnotation(boolean immediate)
   {
-    AlignmentAnnotation consensus = alignViewport
-            .getAlignmentConsensusAnnotation();
-    Hashtable[] hconsensus = alignViewport.getSequenceConsensusHash();
+    AlignmentAnnotation consensus = getConsensusAnnotation();
+    Hashtable[] hconsensus = getViewportConsensus();
     if (immediate || !calcMan.isWorking(this) && consensus != null
             && hconsensus != null)
     {
-      AAFrequency.completeConsensus(consensus, hconsensus, 0,
-              hconsensus.length, alignViewport.getIgnoreGapsConsensus(),
-              alignViewport.isShowSequenceLogo(), nseq);
+      deriveConsensus(consensus, hconsensus);
     }
   }
+
+  /**
+   * Convert the computed consensus data into the desired annotation for
+   * display.
+   * 
+   * @param consensusAnnotation
+   *          the annotation to be populated
+   * @param consensusData
+   *          the computed consensus data
+   */
+  protected void deriveConsensus(AlignmentAnnotation consensusAnnotation,
+          Hashtable[] consensusData)
+  {
+    long nseq = getSequences().length;
+    AAFrequency.completeConsensus(consensusAnnotation, consensusData, 0,
+            consensusData.length, alignViewport.isIgnoreGapsConsensus(),
+            alignViewport.isShowSequenceLogo(), nseq);
+  }
+
+  /**
+   * Get the consensus data stored on the viewport.
+   * 
+   * @return
+   */
+  protected Hashtable[] getViewportConsensus()
+  {
+    return alignViewport.getSequenceConsensusHash();
+  }
 }
index 02d584a..4471b6e 100644 (file)
@@ -166,7 +166,7 @@ public class StrucConsensusThread extends AlignCalcWorker implements
     {
       StructureFrequency.completeConsensus(strucConsensus, hStrucConsensus,
               0, hStrucConsensus.length,
-              alignViewport.getIgnoreGapsConsensus(),
+              alignViewport.isIgnoreGapsConsensus(),
               alignViewport.isShowSequenceLogo(), nseq);
     }
   }
index 74b118c..e557614 100644 (file)
@@ -28,8 +28,11 @@ import jalview.datamodel.AlignmentView;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.WebserviceInfo;
-import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 import jalview.util.MessageManager;
+import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
 
 public abstract class AWSThread extends Thread
 {
@@ -57,7 +60,7 @@ public abstract class AWSThread extends Thread
   /**
    * dataset sequence relationships to be propagated onto new results
    */
-  protected AlignedCodonFrame[] codonframe = null;
+  protected Set<AlignedCodonFrame> codonframe = null;
 
   /**
    * are there jobs still running in this thread.
@@ -87,6 +90,11 @@ public abstract class AWSThread extends Thread
    */
   protected String WsUrl = null;
 
+  /*
+   * The AlignFrame from which the service was requested.
+   */
+  private AlignFrame alignFrame;
+
   /**
    * generic web service job/subjob poll loop
    */
@@ -124,8 +132,9 @@ public abstract class AWSThread extends Thread
           } catch (Exception ex)
           {
             // Deal with Transaction exceptions
-            wsInfo.appendProgressText(jobs[j].jobnum, 
-                       MessageManager.formatMessage("info.server_exception", new String[]{WebServiceName,ex.getMessage()}));
+            wsInfo.appendProgressText(jobs[j].jobnum, MessageManager
+                    .formatMessage("info.server_exception", new Object[]
+                    { WebServiceName, ex.getMessage() }));
             // always output the exception's stack trace to the log
             Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
                     + ") Server exception.");
@@ -189,7 +198,7 @@ public abstract class AWSThread extends Thread
     {
       Cache.log
               .debug("WebServiceJob poll loop finished with no jobs created.");
-      wsInfo.setStatus(wsInfo.STATE_STOPPED_ERROR);
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
       wsInfo.appendProgressText(MessageManager.getString("info.no_jobs_ran"));
       wsInfo.setFinishedNoResults();
     }
@@ -295,13 +304,12 @@ public abstract class AWSThread extends Thread
       SequenceI[] alignment = al.getSequencesArray();
       for (int sq = 0; sq < alignment.length; sq++)
       {
-        for (int i = 0; i < codonframe.length; i++)
+        for (AlignedCodonFrame acf : codonframe)
         {
-          if (codonframe[i] != null
-                  && codonframe[i].involvesSequence(alignment[sq]))
+          final SequenceI seq = alignment[sq];
+          if (acf != null && acf.involvesSequence(seq))
           {
-            al.addCodonFrame(codonframe[i]);
-            codonframe[i] = null;
+            al.addCodonFrame(acf);
             break;
           }
         }
@@ -360,7 +368,7 @@ public abstract class AWSThread extends Thread
           AlignmentView alview, String wsurl2)
   {
     super();
-    // this.alignFrame = alframe;
+    this.alignFrame = alframe;
     currentView = alframe.getCurrentView().getAlignment();
     featureSettings = alframe.getFeatureRenderer().getSettings();
     defGapChar = alframe.getViewport().getGapCharacter();
@@ -369,13 +377,18 @@ public abstract class AWSThread extends Thread
     WsUrl = wsurl2;
     if (alframe != null)
     {
-      AlignedCodonFrame[] cf = alframe.getViewport().getAlignment()
+      Set<AlignedCodonFrame> cf = alframe.getViewport().getAlignment()
               .getCodonFrames();
       if (cf != null)
       {
-        codonframe = new AlignedCodonFrame[cf.length];
-        System.arraycopy(cf, 0, codonframe, 0, cf.length);
+        codonframe = new LinkedHashSet<AlignedCodonFrame>();
+        codonframe.addAll(cf);
       }
     }
   }
+
+  protected AlignFrame getRequestingAlignFrame()
+  {
+    return this.alignFrame;
+  }
 }
index c416aa5..c6d6629 100644 (file)
@@ -43,6 +43,7 @@ import org.apache.http.entity.mime.content.StringBody;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
 
 /**
@@ -65,12 +66,23 @@ public class HttpClientUtils
    * @throws Exception
    */
   public static BufferedReader doHttpUrlPost(String postUrl,
-          List<NameValuePair> vals) throws ClientProtocolException,
+          List<NameValuePair> vals, int connectionTimeoutMs,
+          int readTimeoutMs) throws ClientProtocolException,
           IOException
   {
+    // todo use HttpClient 4.3 or later and class RequestConfig
     HttpParams params = new BasicHttpParams();
     params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
             HttpVersion.HTTP_1_1);
+    if (connectionTimeoutMs > 0)
+    {
+      HttpConnectionParams
+              .setConnectionTimeout(params, connectionTimeoutMs);
+    }
+    if (readTimeoutMs > 0)
+    {
+      HttpConnectionParams.setSoTimeout(params, readTimeoutMs);
+    }
     HttpClient httpclient = new DefaultHttpClient(params);
     HttpPost httppost = new HttpPost(postUrl);
     UrlEncodedFormEntity ue = new UrlEncodedFormEntity(vals, "UTF-8");
index 33afd10..9c42f0a 100644 (file)
  */
 package jalview.ws;
 
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Vector;
+
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
@@ -28,11 +33,6 @@ import jalview.ws.dbsources.das.api.jalviewSourceI;
 import jalview.ws.seqfetcher.ASequenceFetcher;
 import jalview.ws.seqfetcher.DbSourceProxy;
 
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Vector;
-
 /**
  * This is the the concrete implementation of the sequence retrieval interface
  * and abstract class in jalview.ws.seqfetcher. This implements the run-time
@@ -61,10 +61,8 @@ public class SequenceFetcher extends ASequenceFetcher
     addDBRefSourceImpl(jalview.ws.dbsources.UnprotName.class);
     addDBRefSourceImpl(jalview.ws.dbsources.Pdb.class);
     addDBRefSourceImpl(jalview.ws.dbsources.PfamFull.class);
-    addDBRefSourceImpl(jalview.ws.dbsources.PfamSeed.class); // ensures Seed
-    // alignment is
-    // 'default' for
-    // PFAM
+    addDBRefSourceImpl(jalview.ws.dbsources.PfamSeed.class);
+    // ensures Seed alignment is 'default' for PFAM
     addDBRefSourceImpl(jalview.ws.dbsources.RfamFull.class);
     addDBRefSourceImpl(jalview.ws.dbsources.RfamSeed.class);
     if (addDas)
@@ -285,7 +283,9 @@ public class SequenceFetcher extends ASequenceFetcher
       String db = dbSources[dbsource];
       // skip me
       if (db.equals(DBRefSource.PDB))
+      {
         continue;
+      }
       for (DbSourceProxy sp : sfetcher.getSourceProxy(db))
       {
         System.out.println("Source: " + sp.getDbName() + " (" + db
@@ -348,9 +348,13 @@ public class SequenceFetcher extends ASequenceFetcher
           System.out.println("ERROR:No alignment retrieved.");
           StringBuffer raw = sp.getRawRecords();
           if (raw != null)
+          {
             System.out.println(raw.toString());
+          }
           else
+          {
             System.out.println("ERROR:No Raw results.");
+          }
         }
         else
         {
diff --git a/src/jalview/ws/dbsources/PDBRestClient.java b/src/jalview/ws/dbsources/PDBRestClient.java
new file mode 100644 (file)
index 0000000..1ab6125
--- /dev/null
@@ -0,0 +1,338 @@
+package jalview.ws.dbsources;
+
+import jalview.ws.uimodel.PDBRestRequest;
+import jalview.ws.uimodel.PDBRestResponse;
+import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.api.json.JSONConfiguration;
+
+/**
+ * A rest client for querying the Search endpoing of the PDB REST API
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class PDBRestClient
+{
+  private static String PDB_SEARCH_ENDPOINT = "http://wwwdev.ebi.ac.uk/pdbe/search/pdb/select?";
+
+  private static int DEFAULT_RESPONSE_SIZE = 200;
+
+  /**
+   * Takes a PDBRestRequest object and returns a response upon execution
+   * 
+   * @param pdbRestRequest
+   *          the PDBRestRequest instance to be processed
+   * @return the pdbResponse object for the given request
+   */
+  public PDBRestResponse executeRequest(PDBRestRequest pdbRestRequest)
+  {
+    ClientConfig clientConfig = new DefaultClientConfig();
+    clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING,
+            Boolean.TRUE);
+    Client client = Client.create(clientConfig);
+
+    String wantedFields = getPDBDocFieldsAsCommaDelimitedString(pdbRestRequest
+            .getWantedFields());
+    int responseSize = (pdbRestRequest.getResponseSize() == 0) ? DEFAULT_RESPONSE_SIZE
+            : pdbRestRequest.getResponseSize();
+    String sortParam = (pdbRestRequest.getFieldToSortBy() == null || pdbRestRequest
+            .getFieldToSortBy().trim().isEmpty()) ? "" : (pdbRestRequest
+            .getFieldToSortBy() + (pdbRestRequest.isAscending() ? " asc"
+            : " desc"));
+
+    // Build request parameters for the REST Request
+    WebResource webResource = client.resource(PDB_SEARCH_ENDPOINT)
+            .queryParam("wt", "json").queryParam("fl", wantedFields)
+            .queryParam("rows", String.valueOf(responseSize))
+            .queryParam("q", pdbRestRequest.getQuery())
+            .queryParam("sort", sortParam);
+
+    // Execute the REST request
+    ClientResponse clientResponse = webResource.accept(
+            MediaType.APPLICATION_JSON).get(ClientResponse.class);
+
+    // Get the JSON string from the response object
+    String responseString = clientResponse.getEntity(String.class);
+
+    // Check the response status and report exception if one occurs
+    if (clientResponse.getStatus() != 200)
+    {
+      String errorMessage = "";
+      if (clientResponse.getStatus() == 400)
+      {
+        errorMessage = parseJsonExceptionString(responseString);
+        throw new RuntimeException(errorMessage);
+      }
+      else
+      {
+        errorMessage = "Failed : HTTP error code : "
+                + clientResponse.getStatus();
+        throw new RuntimeException(errorMessage);
+      }
+    }
+
+    // Make redundant objects eligible for garbage collection to conserve
+    // memory
+    clientResponse = null;
+    client = null;
+
+    // Process the response and return the result to the caller.
+    return parsePDBJsonResponse(responseString, pdbRestRequest);
+  }
+
+  /**
+   * Process error response from PDB server if/when one occurs.
+   * 
+   * @param jsonResponse
+   *          the JSON string containing error message from the server
+   * @return the processed error message from the JSON string
+   */
+  public static String parseJsonExceptionString(String jsonErrorResponse)
+  {
+    String errorMessage = "RunTime error";
+    try
+    {
+      JSONParser jsonParser = new JSONParser();
+      JSONObject jsonObj = (JSONObject) jsonParser.parse(jsonErrorResponse);
+      JSONObject errorResponse = (JSONObject) jsonObj.get("error");
+      errorMessage = errorResponse.get("msg").toString();
+
+      JSONObject responseHeader = (JSONObject) jsonObj
+              .get("responseHeader");
+      errorMessage += responseHeader.get("params").toString();
+    } catch (ParseException e)
+    {
+      e.printStackTrace();
+    }
+    return errorMessage;
+  }
+
+  /**
+   * Parses the JSON response string from PDB REST API. The response is dynamic
+   * hence, only fields specifically requested for in the 'wantedFields'
+   * parameter is fetched/processed
+   * 
+   * @param pdbJsonResponseString
+   *          the JSON string to be parsed
+   * @param pdbRestRequest
+   *          the request object which contains parameters used to process the
+   *          JSON string
+   * @return
+   */
+  @SuppressWarnings("unchecked")
+  public static PDBRestResponse parsePDBJsonResponse(
+          String pdbJsonResponseString, PDBRestRequest pdbRestRequest)
+  {
+    PDBRestResponse searchResult = new PDBRestResponse();
+    List<PDBResponseSummary> result = null;
+    try
+    {
+      JSONParser jsonParser = new JSONParser();
+      JSONObject jsonObj = (JSONObject) jsonParser
+              .parse(pdbJsonResponseString);
+
+      JSONObject pdbResponse = (JSONObject) jsonObj.get("response");
+      String queryTime = ((JSONObject) jsonObj.get("responseHeader")).get(
+              "QTime").toString();
+      int numFound = Integer
+              .valueOf(pdbResponse.get("numFound").toString());
+      if (numFound > 0)
+      {
+        result = new ArrayList<PDBResponseSummary>();
+        JSONArray docs = (JSONArray) pdbResponse.get("docs");
+        for (Iterator<JSONObject> docIter = docs.iterator(); docIter
+                .hasNext();)
+        {
+          JSONObject doc = docIter.next();
+          result.add(searchResult.new PDBResponseSummary(doc,
+                  pdbRestRequest));
+        }
+        searchResult.setNumberOfItemsFound(numFound);
+        searchResult.setResponseTime(queryTime);
+        searchResult.setSearchSummary(result);
+      }
+    } catch (ParseException e)
+    {
+      e.printStackTrace();
+    }
+    return searchResult;
+  }
+
+  /**
+   * Takes a collection of PDBDocField and converts its 'code' Field values into
+   * a comma delimited string.
+   * 
+   * @param pdbDocfields
+   *          the collection of PDBDocField to process
+   * @return the comma delimited string from the pdbDocFields collection
+   */
+  public static String getPDBDocFieldsAsCommaDelimitedString(
+          Collection<PDBDocField> pdbDocfields)
+  {
+    String result = "";
+    if (pdbDocfields != null && !pdbDocfields.isEmpty())
+    {
+      StringBuilder returnedFields = new StringBuilder();
+      for (PDBDocField field : pdbDocfields)
+      {
+        returnedFields.append(",").append(field.getCode());
+      }
+      returnedFields.deleteCharAt(0);
+      result = returnedFields.toString();
+    }
+    return result;
+  }
+
+  /**
+   * Determines the column index for 'PDB Id' Fields in the dynamic summary
+   * table. The PDB Id serves as a unique identifier for a given row in the
+   * summary table
+   * 
+   * @param wantedFeilds
+   *          the available table columns in no particular order
+   * @return the pdb id field column index
+   */
+  public static int getPDBIdColumIndex(
+          Collection<PDBDocField> wantedFeilds, boolean hasRefSeq)
+  {
+
+    // If a reference sequence is attached then start counting from 1 else
+    // start from zero
+    int pdbFeildIndexCounter = hasRefSeq ? 1 : 0;
+
+    for (PDBDocField feild : wantedFeilds)
+    {
+      if (feild.equals(PDBDocField.PDB_ID))
+      {
+        break; // Once PDB Id index is determined exit iteration
+      }
+      ++pdbFeildIndexCounter;
+    }
+    return pdbFeildIndexCounter;
+  }
+
+  /**
+   * This enum represents the fields available in the PDB JSON response
+   *
+   */
+  public enum PDBDocField
+  {
+    PDB_ID("PDB Id", "pdb_id"), TITLE("Title", "title"), MOLECULE_NAME(
+            "Molecule", "molecule_name"), MOLECULE_TYPE("Molecule Type",
+            "molecule_type"), MOLECULE_SEQUENCE("Sequence",
+            "molecule_sequence"), PFAM_ACCESSION("PFAM Accession",
+            "pfam_accession"), PFAM_NAME("PFAM Name", "pfam_name"), INTERPRO_NAME(
+            "InterPro Name", "interpro_name"), INTERPRO_ACCESSION(
+            "InterPro Accession", "interpro_accession"), UNIPROT_ID(
+            "UniProt Id", "uniprot_id"), UNIPROT_ACCESSION(
+            "UniProt Accession", "uniprot_accession"), UNIPROT_COVERAGE(
+            "UniProt Coverage", "uniprot_coverage"), UNIPROT_FEATURES(
+            "Uniprot Features", "uniprot_features"), R_FACTOR("R Factor",
+            "r_factor"), RESOLUTION("Resolution", "resolution"), DATA_QUALITY(
+            "Data Quality", "data_quality"), OVERALL_QUALITY(
+            "Overall Quality", "overall_quality"), POLYMER_COUNT(
+            "Number of Polymers", "number_of_polymers"), PROTEIN_CHAIN_COUNT(
+            "Number of Protein Chains", "number_of_protein_chains"), BOUND_MOLECULE_COUNT(
+            "Number of Bound Molecule", "number_of_bound_molecules"), POLYMER_RESIDUE_COUNT(
+            "Number of Polymer Residue", "number_of_polymer_residues"), GENUS(
+            "GENUS", "genus"), GENE_NAME("Gene Name", "gene_name"), EXPERIMENTAL_METHOD(
+            "Experimental Method", "experimental_method"), GO_ID("GO Id",
+            "go_id"), ASSEMBLY_ID("Assembly Id", "assembly_form"), ASSEMBLY_FORM(
+            "Assembly Form", "assembly_id"), ASSEMBLY_TYPE("Assembly Type",
+            "assembly_type"), SPACE_GROUP("Space Group", "spacegroup"), CATH_CODE(
+            "Cath Code", "cath_code"), TAX_ID("Tax Id", "tax_id"), TAX_QUERY(
+            "Tax Query", "tax_query"), INTERACTING_ENTRY_ID(
+            "Interacting Entry Id", "interacting_entry_id"), INTERACTING_ENTITY_ID(
+            "Interacting Entity Id", "interacting_entity_id"), INTERACTING_MOLECULES(
+            "Interacting Molecules", "interacting_molecules"), PUBMED_ID(
+            "Pubmed Id", "pubmed_id"), STATUS("Status", "status"), MODEL_QUALITY(
+            "Model Quality", "model_quality"), PIVOT_RESOLUTION(
+            "Pivot Resolution", "pivot_resolution"), DATA_REDUCTION_SOFTWARE(
+            "Data reduction software", "data_reduction_software"), MAX_OBSERVED_RES(
+            "Max observed residues", "max_observed_residues"), ORG_SCI_NAME(
+            "Organism scientific name", "organism_scientific_name"), SUPER_KINGDOM(
+            "Super kingdom", "superkingdom"), RANK("Rank", "rank"), CRYSTALLISATION_PH(
+            "Crystallisation Ph", "crystallisation_ph"), BIOLOGICAL_FUNCTION(
+            "Biological Function", "biological_function"), BIOLOGICAL_PROCESS(
+            "Biological Process", "biological_process"), BIOLOGICAL_CELL_COMPONENT(
+            "Biological Cell Component", "biological_cell_component"), COMPOUND_NAME(
+            "Compound Name", "compound_name"), COMPOUND_ID("Compound Id",
+            "compound_id"), COMPOUND_WEIGHT("Compound Weight",
+            "compound_weight"), COMPOUND_SYSTEMATIC_NAME(
+            "Compound Systematic Name", "compound_systematic_name"), INTERACTING_LIG(
+            "Interacting Ligands", "interacting_ligands"), JOURNAL(
+            "Journal", "journal"), ALL_AUTHORS("All Authors", "all_authors"), EXPERIMENTAL_DATA_AVAILABLE(
+            "Experiment Data Available", "experiment_data_available"), DIFFRACTION_PROTOCOL(
+            "Diffraction Protocol", "diffraction_protocol"), REFINEMENT_SOFTWARE(
+            "Refinement Software", "refinement_software"), STRUCTURE_DETERMINATION_METHOD(
+            "Structure Determination Method",
+            "structure_determination_method"), SYNCHROTON_SITE(
+            "Synchrotron Site", "synchrotron_site"), SAMPLE_PREP_METHOD(
+            "Sample Preparation Method", "sample_preparation_method"), ENTRY_AUTHORS(
+            "Entry Authors", "entry_authors"), CITATION_TITLE(
+            "Citation Title", "citation_title"), STRUCTURE_SOLUTION_SOFTWARE(
+            "Structure Solution Software", "structure_solution_software"), ENTRY_ENTITY(
+            "Entry Entity", "entry_entity"), R_FREE("R Free", "r_free"), NO_OF_POLYMER_ENTITIES(
+            "Number of Polymer Entities", "number_of_polymer_entities"), NO_OF_BOUND_ENTITIES(
+            "Number of Bound Entities", "number_of_bound_entities"), CRYSTALLISATION_RESERVOIR(
+            "Crystallisation Reservoir", "crystallisation_reservoir"), DATA_SCALING_SW(
+            "Data Scalling Software", "data_scaling_software"), DETECTOR(
+            "Detector", "detector"), DETECTOR_TYPE("Detector Type",
+            "detector_type"), MODIFIED_RESIDUE_FLAG(
+            "Modified Residue Flag", "modified_residue_flag"), NUMBER_OF_COPIES(
+            "Number of Copies", "number_of_copies"), STRUCT_ASYM_ID(
+            "Struc Asym Id", "struct_asym_id"), HOMOLOGUS_PDB_ENTITY_ID(
+            "Homologus PDB Entity Id", "homologus_pdb_entity_id"), MOLECULE_SYNONYM(
+            "Molecule Synonym", "molecule_synonym"), DEPOSITION_SITE(
+            "Deposition Site", "deposition_site"), SYNCHROTRON_BEAMLINE(
+            "Synchrotron Beamline", "synchrotron_beamline"), ENTITY_ID(
+            "Entity Id", "entity_id"), BEAM_SOURCE_NAME("Beam Source Name",
+            "beam_source_name"), PROCESSING_SITE("Processing Site",
+            "processing_site"), ENTITY_WEIGHT("Entity Weight",
+            "entity_weight"), VERSION("Version", "_version_"), ALL("ALL",
+            "text");
+
+    private String name;
+
+    private String code;
+
+    PDBDocField(String name, String code)
+    {
+      this.name = name;
+      this.code = code;
+    }
+
+    public String getName()
+    {
+      return name;
+    }
+
+    public String getCode()
+    {
+      return code;
+    }
+
+    public String toString()
+    {
+      return name;
+    }
+  }
+}
\ No newline at end of file
index b086159..db70fa0 100644 (file)
@@ -35,6 +35,8 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import compbio.data.sequence.FastaSequence;
 import compbio.data.sequence.JpredAlignment;
@@ -90,15 +92,18 @@ public class JPred301Client extends JabawsMsaInterfaceAlignCalcWorker
     return "calculating consensus secondary structure prediction using JPred service";
   }
 
-  private static HashMap<String, String[]> jpredRowLabels = new HashMap<String, String[]>();
+  private static Map<String, String[]> jpredRowLabels = new HashMap<String, String[]>();
 
-  private static HashSet<String> jpredRes_graph, jpredRes_ssonly;
+  private static final Set<String> jpredRes_graph;
+
+  private static final Set<String> jpredRes_ssonly;
+  static
   {
-    jpredRes_ssonly = new HashSet();
+    jpredRes_ssonly = new HashSet<String>();
     jpredRes_ssonly.add("jnetpred".toLowerCase());
     jpredRes_ssonly.add("jnetpssm".toLowerCase());
     jpredRes_ssonly.add("jnethmm".toLowerCase());
-    jpredRes_graph = new HashSet();
+    jpredRes_graph = new HashSet<String>();
     jpredRes_graph.add("jnetconf".toLowerCase());
     jpredRes_graph.add("jnet burial".toLowerCase());
   }
index 3a16248..d0d0a6e 100644 (file)
@@ -23,6 +23,7 @@ package jalview.ws.jws2;
 import jalview.analysis.AlignSeq;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.ColumnSelection;
@@ -30,6 +31,7 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
+import jalview.gui.SplitFrame;
 import jalview.gui.WebserviceInfo;
 import jalview.util.MessageManager;
 import jalview.ws.AWsJob;
@@ -44,6 +46,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Vector;
 
+import javax.swing.JInternalFrame;
+
 import compbio.data.msa.MsaWS;
 import compbio.metadata.Argument;
 import compbio.metadata.ChunkHolder;
@@ -447,7 +451,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
    * @param presorder
    *          boolean
    */
-  MsaWSThread(MsaWS server, String wsUrl, WebserviceInfo wsinfo,
+  private MsaWSThread(MsaWS server, String wsUrl, WebserviceInfo wsinfo,
           jalview.gui.AlignFrame alFrame, AlignmentView alview,
           String wsname, boolean subgaps, boolean presorder)
   {
@@ -892,10 +896,16 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     wsInfo.setProgressBar(null, progbar);
   }
 
+  /**
+   * Display alignment results in a new frame (or - not currently supported -
+   * added to an existing alignment).
+   * 
+   * @param newFrame
+   */
   void displayResults(boolean newFrame)
   {
     // view input or result data for each block
-    Vector alorders = new Vector();
+    List<AlignmentOrder> alorders = new ArrayList<AlignmentOrder>();
     SequenceI[][] results = new SequenceI[jobs.length][];
     AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
     String lastProgram = null;
@@ -907,7 +917,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
         msjob = (MsaWSJob) jobs[j];
         Object[] res = msjob.getAlignment();
         lastProgram = msjob.getAlignmentProgram();
-        alorders.add(res[1]);
+        alorders.add((AlignmentOrder) res[1]);
         results[j] = (SequenceI[]) res[0];
         orders[j] = (AlignmentOrder) res[1];
 
@@ -944,71 +954,126 @@ class MsaWSThread extends AWS2Thread implements WSClientI
 
     if (newFrame)
     {
-      AlignFrame af = new AlignFrame(al, columnselection,
-              AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+      displayInNewFrame(al, alorders, columnselection);
 
-      // initialise with same renderer settings as in parent alignframe.
-      af.getFeatureRenderer().transferSettings(this.featureSettings);
-      // update orders
-      if (alorders.size() > 0)
-      {
-        if (alorders.size() == 1)
-        {
-          af.addSortByOrderMenuItem(WebServiceName + " Ordering",
-                  (AlignmentOrder) alorders.get(0));
-        }
-        else
-        {
-          // construct a non-redundant ordering set
-          Vector names = new Vector();
-          for (int i = 0, l = alorders.size(); i < l; i++)
-          {
-            String orderName = new String(" Region " + i);
-            int j = i + 1;
+    }
+    else
+    {
+      System.out.println("MERGE WITH OLD FRAME");
+      // TODO: modify alignment in original frame, replacing old for new
+      // alignment using the commands.EditCommand model to ensure the update can
+      // be undone
+    }
+  }
 
-            while (j < l)
-            {
-              if (((AlignmentOrder) alorders.get(i))
-                      .equals(((AlignmentOrder) alorders.get(j))))
-              {
-                alorders.remove(j);
-                l--;
-                orderName += "," + j;
-              }
-              else
-              {
-                j++;
-              }
-            }
+  /**
+   * Display the alignment result in a new frame.
+   * 
+   * @param al
+   * @param alorders
+   * @param columnselection
+   */
+  protected void displayInNewFrame(AlignmentI al,
+          List<AlignmentOrder> alorders, ColumnSelection columnselection)
+  {
+    AlignFrame af = new AlignFrame(al, columnselection,
+            AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
 
-            if (i == 0 && j == 1)
-            {
-              names.add(new String(""));
-            }
-            else
-            {
-              names.add(orderName);
-            }
-          }
-          for (int i = 0, l = alorders.size(); i < l; i++)
-          {
-            af.addSortByOrderMenuItem(
-                    WebServiceName + ((String) names.get(i)) + " Ordering",
-                    (AlignmentOrder) alorders.get(i));
-          }
-        }
+    // initialise with same renderer settings as in parent alignframe.
+    af.getFeatureRenderer().transferSettings(this.featureSettings);
+
+    if (alorders.size() > 0)
+    {
+      addSortByMenuItems(af, alorders);
+    }
+
+    /*
+     * If alignment was requested from one half of a SplitFrame, show in a
+     * SplitFrame with the other pane similarly aligned.
+     */
+    AlignFrame requestedBy = getRequestingAlignFrame();
+    if (requestedBy != null && requestedBy.getSplitViewContainer() != null)
+    {
+      AlignmentI complement = requestedBy.getSplitViewContainer()
+              .getComplement(requestedBy);
+      String complementTitle = requestedBy.getSplitViewContainer()
+              .getComplementTitle(requestedBy);
+      AlignmentI copyComplement = new Alignment(complement);
+      copyComplement.alignAs(al);
+      if (copyComplement.getHeight() > 0)
+      {
+        af.setTitle(alTitle);
+        AlignFrame af2 = new AlignFrame(copyComplement,
+                AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+        af2.setTitle(complementTitle);
+        String linkedTitle = MessageManager
+                .getString("label.linked_view_title");
+        JInternalFrame splitFrame = new SplitFrame(al.isNucleotide() ? af
+                : af2, al.isNucleotide() ? af2 : af);
+        Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
+        return;
       }
+    }
 
-      Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
-              AlignFrame.DEFAULT_HEIGHT);
+    /*
+     * Not from SplitFrame, or failed to created a complementary alignment
+     */
+    Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
+  }
 
+  /**
+   * Add sort order options to the AlignFrame menus.
+   * 
+   * @param af
+   * @param alorders
+   */
+  protected void addSortByMenuItems(AlignFrame af,
+          List<AlignmentOrder> alorders)
+  {
+    // update orders
+    if (alorders.size() == 1)
+    {
+      af.addSortByOrderMenuItem(WebServiceName + " Ordering",
+              alorders.get(0));
     }
     else
     {
-      System.out.println("MERGE WITH OLD FRAME");
-      // TODO: modify alignment in original frame, replacing old for new
-      // alignment using the commands.EditCommand model to ensure the update can
-      // be undone
+      // construct a non-redundant ordering set
+      List<String> names = new ArrayList<String>();
+      for (int i = 0, l = alorders.size(); i < l; i++)
+      {
+        String orderName = " Region " + i;
+        int j = i + 1;
+
+        while (j < l)
+        {
+          if (alorders.get(i).equals(alorders.get(j)))
+          {
+            alorders.remove(j);
+            l--;
+            orderName += "," + j;
+          }
+          else
+          {
+            j++;
+          }
+        }
+
+        if (i == 0 && j == 1)
+        {
+          names.add("");
+        }
+        else
+        {
+          names.add(orderName);
+        }
+      }
+      for (int i = 0, l = alorders.size(); i < l; i++)
+      {
+        af.addSortByOrderMenuItem(WebServiceName + (names.get(i))
+                + " Ordering", alorders.get(i));
+      }
     }
   }
 
index 91fce18..8741ddc 100644 (file)
@@ -46,6 +46,8 @@ import org.apache.http.entity.mime.content.StringBody;
  */
 public abstract class InputType
 {
+  private static final Pattern URL_PATTERN = Pattern.compile("^([^=]+)=?'?([^']*)?'?");
+
   /**
    * not used yet
    */
@@ -209,7 +211,7 @@ public abstract class InputType
     boolean valid = true;
     for (String tok : tokenstring)
     {
-      Matcher mtch = Pattern.compile("^([^=]+)=?'?([^']*)?'?").matcher(tok);
+      Matcher mtch = URL_PATTERN.matcher(tok);
       if (mtch.find())
       {
         try
diff --git a/src/jalview/ws/uimodel/PDBRestRequest.java b/src/jalview/ws/uimodel/PDBRestRequest.java
new file mode 100644 (file)
index 0000000..58d7aeb
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 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.uimodel;
+
+
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+
+import java.util.Collection;
+
+/**
+ * Represents the PDB request to be consumed by the PDBRestClient
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class PDBRestRequest
+{
+  private String fieldToSearchBy;
+
+  private String searchTerm;
+
+  private String fieldToSortBy;
+
+  private String associatedSequence;
+
+  private boolean allowEmptySequence;
+
+  private int responseSize;
+
+  private boolean isSortAscending;
+
+  private Collection<PDBDocField> wantedFields;// = new
+                                               // Collection<PDBDocField>();
+
+  public String getFieldToSearchBy()
+  {
+    return fieldToSearchBy;
+  }
+
+  public void setFieldToSearchBy(String fieldToSearchBy)
+  {
+    this.fieldToSearchBy = fieldToSearchBy;
+  }
+
+  public String getSearchTerm()
+  {
+    return searchTerm;
+  }
+
+  public void setSearchTerm(String searchTerm)
+  {
+    this.searchTerm = searchTerm;
+  }
+
+  public boolean isAllowEmptySeq()
+  {
+    return allowEmptySequence;
+  }
+
+  public void setAllowEmptySeq(boolean allowEmptySeq)
+  {
+    this.allowEmptySequence = allowEmptySeq;
+  }
+
+  public int getResponseSize()
+  {
+    return responseSize;
+  }
+
+  public void setResponseSize(int responseSize)
+  {
+    this.responseSize = responseSize;
+  }
+
+  public Collection<PDBDocField> getWantedFields()
+  {
+    return wantedFields;
+  }
+
+  public void setWantedFields(Collection<PDBDocField> wantedFields)
+  {
+    this.wantedFields = wantedFields;
+  }
+
+  public String getFieldToSortBy()
+  {
+    return fieldToSortBy;
+  }
+
+  public void setFieldToSortBy(String fieldToSortBy, boolean isSortAscending)
+  {
+    this.fieldToSortBy = fieldToSortBy;
+    this.isSortAscending = isSortAscending;
+  }
+
+  public boolean isAscending()
+  {
+    return isSortAscending;
+  }
+
+  public String getAssociatedSequence()
+  {
+    return associatedSequence;
+  }
+
+  public void setAssociatedSequence(String associatedSequence)
+  {
+    this.associatedSequence = associatedSequence;
+  }
+
+  public String getQuery()
+  {
+    return fieldToSearchBy + searchTerm
+            + (isAllowEmptySeq() ? "" : " AND molecule_sequence:['' TO *]");
+  }
+
+  public String toString()
+  {
+    return "Query : " + getQuery() + " sort field: " + fieldToSortBy
+            + " isAsc: " + isAscending() + " Associated Seq : "
+            + associatedSequence;
+  }
+}
diff --git a/src/jalview/ws/uimodel/PDBRestResponse.java b/src/jalview/ws/uimodel/PDBRestResponse.java
new file mode 100644 (file)
index 0000000..3b5b05c
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 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.uimodel;
+
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+
+import java.util.Collection;
+import java.util.Objects;
+
+import javax.swing.table.DefaultTableModel;
+
+import org.json.simple.JSONObject;
+
+/**
+ * Represents the response model produced by the PDBRestClient upon successful
+ * execution of a given request
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class PDBRestResponse
+{
+  private int numberOfItemsFound;
+
+  private String responseTime;
+
+  private Collection<PDBResponseSummary> searchSummary;
+
+  public int getNumberOfItemsFound()
+  {
+    return numberOfItemsFound;
+  }
+
+  public void setNumberOfItemsFound(int itemFound)
+  {
+    this.numberOfItemsFound = itemFound;
+  }
+
+  public String getResponseTime()
+  {
+    return responseTime;
+  }
+
+  public void setResponseTime(String responseTime)
+  {
+    this.responseTime = responseTime;
+  }
+
+  public Collection<PDBResponseSummary> getSearchSummary()
+  {
+    return searchSummary;
+  }
+
+  public void setSearchSummary(Collection<PDBResponseSummary> searchSummary)
+  {
+    this.searchSummary = searchSummary;
+  }
+
+
+  /**
+   * Convenience method to obtain a Table model for a given summary List based
+   * on the request parameters
+   * 
+   * @param request
+   *          the PDBRestRequest object which holds useful information for
+   *          creating a table model
+   * @param summariesList
+   *          the summary list which contains the data for populating the
+   *          table's rows
+   * @return the table model which was dynamically generated
+   */
+  public static DefaultTableModel getTableModel(PDBRestRequest request,
+          Collection<PDBResponseSummary> summariesList)
+  {
+    DefaultTableModel tableModel = new DefaultTableModel();
+
+    if (request.getAssociatedSequence() != null)
+    {
+      tableModel.addColumn("Sequence"); // Create sequence column header if
+                                        // exists in the request
+    }
+    for (PDBDocField field : request.getWantedFields())
+    {
+      tableModel.addColumn(field.getName()); // Create sequence column header if
+                                             // exists in the request
+    }
+
+    for (PDBResponseSummary res : summariesList)
+    {
+      tableModel.addRow(res.getSummaryData()); // Populate table rows with
+                                               // summary list
+    }
+
+    return tableModel;
+  }
+
+  /**
+   * Model for a unique response summary
+   * 
+   */
+  public class PDBResponseSummary
+  {
+    private String pdbId;
+
+    private String[] summaryRowData;
+
+    private String associatedSequence;
+
+    public PDBResponseSummary(JSONObject pdbJsonDoc, PDBRestRequest request)
+    {
+      Collection<PDBDocField> diplayFields = request.getWantedFields();
+      String associatedSeq = request.getAssociatedSequence();
+      int colCounter = 0;
+      summaryRowData = new String[(associatedSeq != null) ? diplayFields
+              .size() + 1 : diplayFields.size()];
+      if (associatedSeq != null)
+      {
+        this.associatedSequence = (associatedSeq.length() > 18) ? associatedSeq
+                .substring(0, 18) : associatedSeq;
+        summaryRowData[0] = associatedSequence;
+        colCounter = 1;
+      }
+
+      for (PDBDocField field : diplayFields)
+      {
+        String fieldData = (pdbJsonDoc.get(field.getCode()) == null) ? ""
+                : pdbJsonDoc
+                .get(field.getCode()).toString();
+        if (field.equals(PDBDocField.PDB_ID))
+        {
+          this.pdbId = fieldData;
+          summaryRowData[colCounter++] = this.pdbId;
+        }
+        else
+        {
+          summaryRowData[colCounter++] = fieldData;
+        }
+      }
+    }
+
+    public String getPdbId()
+    {
+      return pdbId;
+    }
+
+    public void setPdbId(String pdbId)
+    {
+      this.pdbId = pdbId;
+    }
+
+    public String[] getSummaryData()
+    {
+      return summaryRowData;
+    }
+
+    public void setSummaryData(String[] summaryData)
+    {
+      this.summaryRowData = summaryData;
+    }
+
+    /**
+     * Returns a string representation of this object;
+     */
+    @Override
+    public String toString()
+    {
+      StringBuilder summaryFieldValues = new StringBuilder();
+      for (String summaryField : summaryRowData)
+      {
+        summaryFieldValues.append(summaryField).append("\t");
+      }
+      return summaryFieldValues.toString();
+    }
+
+    /**
+     * Returns hash code value for this object
+     */
+    @Override
+    public int hashCode()
+    {
+      return Objects.hash(this.pdbId, this.toString());
+    }
+
+    /**
+     * Indicates whether some object is equal to this one
+     */
+    @Override
+    public boolean equals(Object that)
+    {
+      if (!(that instanceof PDBResponseSummary))
+      {
+        return false;
+      }
+      PDBResponseSummary another = (PDBResponseSummary) that;
+      return this.toString().equals(another.toString());
+    }
+
+  }
+
+}
+
index aef2ca9..498a47b 100644 (file)
@@ -244,15 +244,22 @@ public class CrossReference implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
+    if (obj == null)
+    {
+      return false;
+    }
     if (!(obj instanceof CrossReference))
+    {
       return false;
+    }
     CrossReference other = (CrossReference) obj;
-    if (obj == null)
-      return false;
     if (this == obj)
+    {
       return true;
+    }
     if (__equalsCalc != null)
     {
       return (__equalsCalc == obj);
@@ -285,6 +292,11 @@ public class CrossReference implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  /**
+   * hashCode designed to ensure that if two instances satisfy o1.equals(o2)
+   * then they have the same hashcode.
+   */
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 0390d48..c94bf15 100644 (file)
@@ -198,15 +198,22 @@ public class UPEntry implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
+    if (obj == null)
+    {
+      return false;
+    }
     if (!(obj instanceof UPEntry))
+    {
       return false;
+    }
     UPEntry other = (UPEntry) obj;
-    if (obj == null)
-      return false;
     if (this == obj)
+    {
       return true;
+    }
     if (__equalsCalc != null)
     {
       return (__equalsCalc == obj);
@@ -236,6 +243,7 @@ public class UPEntry implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index b12de70..2020fc9 100755 (executable)
@@ -52,17 +52,18 @@ public class Data implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
-    if (!(obj instanceof Data))
+    if (obj == null)
     {
       return false;
     }
-    Data other = (Data) obj;
-    if (obj == null)
+    if (!(obj instanceof Data))
     {
       return false;
     }
+    Data other = (Data) obj;
     if (this == obj)
     {
       return true;
@@ -84,6 +85,7 @@ public class Data implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 189c7f6..fe1c70c 100755 (executable)
@@ -220,17 +220,18 @@ public class InputParams implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
-    if (!(obj instanceof InputParams))
+    if (obj == null)
     {
       return false;
     }
-    InputParams other = (InputParams) obj;
-    if (obj == null)
+    if (!(obj instanceof InputParams))
     {
       return false;
     }
+    InputParams other = (InputParams) obj;
     if (this == obj)
     {
       return true;
@@ -274,6 +275,7 @@ public class InputParams implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 9199353..210de76 100755 (executable)
@@ -52,17 +52,18 @@ public class WSFile implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
-    if (!(obj instanceof WSFile))
+    if (obj == null)
     {
       return false;
     }
-    WSFile other = (WSFile) obj;
-    if (obj == null)
+    if (!(obj instanceof WSFile))
     {
       return false;
     }
+    WSFile other = (WSFile) obj;
     if (this == obj)
     {
       return true;
@@ -84,6 +85,7 @@ public class WSFile implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 9bff9cf..9d47b6a 100755 (executable)
@@ -103,15 +103,22 @@ public class Alignment extends vamsas.objects.simple.Object implements
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
+    if (obj == null)
+    {
+      return false;
+    }
     if (!(obj instanceof Alignment))
+    {
       return false;
+    }
     Alignment other = (Alignment) obj;
-    if (obj == null)
-      return false;
     if (this == obj)
+    {
       return true;
+    }
     if (__equalsCalc != null)
     {
       return (__equalsCalc == obj);
@@ -131,6 +138,7 @@ public class Alignment extends vamsas.objects.simple.Object implements
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 9db8cfe..cc7cc67 100755 (executable)
@@ -79,17 +79,18 @@ public class JpredResult extends vamsas.objects.simple.Result implements
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
-    if (!(obj instanceof JpredResult))
+    if (obj == null)
     {
       return false;
     }
-    JpredResult other = (JpredResult) obj;
-    if (obj == null)
+    if (!(obj instanceof JpredResult))
     {
       return false;
     }
+    JpredResult other = (JpredResult) obj;
     if (this == obj)
     {
       return true;
@@ -111,6 +112,7 @@ public class JpredResult extends vamsas.objects.simple.Result implements
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 30a5414..49fa4a7 100755 (executable)
@@ -58,20 +58,20 @@ public class MsaResult extends vamsas.objects.simple.Result implements
     this.msa = msa;
   }
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
-    if (!(obj instanceof MsaResult))
+    if (obj == null)
     {
       return false;
     }
-
-    MsaResult other = (MsaResult) obj;
-
-    if (obj == null)
+    if (!(obj instanceof MsaResult))
     {
       return false;
     }
 
+    MsaResult other = (MsaResult) obj;
+
     if (this == obj)
     {
       return true;
@@ -93,6 +93,7 @@ public class MsaResult extends vamsas.objects.simple.Result implements
     return _equals;
   }
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 573eb96..90370f0 100755 (executable)
@@ -78,17 +78,18 @@ public class Msfalignment implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
-    if (!(obj instanceof Msfalignment))
+    if (obj == null)
     {
       return false;
     }
-    Msfalignment other = (Msfalignment) obj;
-    if (obj == null)
+    if (!(obj instanceof Msfalignment))
     {
       return false;
     }
+    Msfalignment other = (Msfalignment) obj;
     if (this == obj)
     {
       return true;
@@ -110,6 +111,7 @@ public class Msfalignment implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 23eedec..7e79e6a 100755 (executable)
@@ -29,15 +29,21 @@ public abstract class Object implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
-    if (!(obj instanceof Object))
-      return false;
-    Object other = (Object) obj;
     if (obj == null)
+    {
+      return false;
+    }
+    if (!(obj instanceof Object))
+    {
       return false;
+    }
     if (this == obj)
+    {
       return true;
+    }
     if (__equalsCalc != null)
     {
       return (__equalsCalc == obj);
@@ -51,6 +57,7 @@ public abstract class Object implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 18293f5..87f0a48 100755 (executable)
@@ -288,15 +288,22 @@ public class Result implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
+    if (obj == null)
+    {
+      return false;
+    }
     if (!(obj instanceof Result))
+    {
       return false;
+    }
     Result other = (Result) obj;
-    if (obj == null)
-      return false;
     if (this == obj)
+    {
       return true;
+    }
     if (__equalsCalc != null)
     {
       return (__equalsCalc == obj);
@@ -322,6 +329,7 @@ public class Result implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 77fdd34..349c21e 100755 (executable)
@@ -57,15 +57,15 @@ public class Secstructpred implements java.io.Serializable
 
   public synchronized boolean equals(java.lang.Object obj)
   {
-    if (!(obj instanceof Secstructpred))
+    if (obj == null)
     {
       return false;
     }
-    Secstructpred other = (Secstructpred) obj;
-    if (obj == null)
+    if (!(obj instanceof Secstructpred))
     {
       return false;
     }
+    Secstructpred other = (Secstructpred) obj;
     if (this == obj)
     {
       return true;
index 98d1d21..8ef8f7c 100644 (file)
@@ -127,15 +127,22 @@ public class SeqSearchResult extends vamsas.objects.simple.Result implements
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
+    if (obj == null)
+    {
+      return false;
+    }
     if (!(obj instanceof SeqSearchResult))
+    {
       return false;
+    }
     SeqSearchResult other = (SeqSearchResult) obj;
-    if (obj == null)
-      return false;
     if (this == obj)
+    {
       return true;
+    }
     if (__equalsCalc != null)
     {
       return (__equalsCalc == obj);
@@ -157,6 +164,7 @@ public class SeqSearchResult extends vamsas.objects.simple.Result implements
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 28dc9eb..8eb7208 100755 (executable)
@@ -78,15 +78,22 @@ public class Sequence implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
+    if (obj == null)
+    {
+      return false;
+    }
     if (!(obj instanceof Sequence))
+    {
       return false;
+    }
     Sequence other = (Sequence) obj;
-    if (obj == null)
-      return false;
     if (this == obj)
+    {
       return true;
+    }
     if (__equalsCalc != null)
     {
       return (__equalsCalc == obj);
@@ -104,6 +111,7 @@ public class Sequence implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 2c43f96..d5a269b 100755 (executable)
@@ -55,15 +55,22 @@ public class SequenceSet implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
+    if (obj == null)
+    {
+      return false;
+    }
     if (!(obj instanceof SequenceSet))
+    {
       return false;
+    }
     SequenceSet other = (SequenceSet) obj;
-    if (obj == null)
-      return false;
     if (this == obj)
+    {
       return true;
+    }
     if (__equalsCalc != null)
     {
       return (__equalsCalc == obj);
@@ -78,6 +85,7 @@ public class SequenceSet implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
index 3419899..eb04dcf 100755 (executable)
@@ -78,15 +78,22 @@ public class WsJobId implements java.io.Serializable
 
   private java.lang.Object __equalsCalc = null;
 
+  @Override
   public synchronized boolean equals(java.lang.Object obj)
   {
+    if (obj == null)
+    {
+      return false;
+    }
     if (!(obj instanceof WsJobId))
+    {
       return false;
+    }
     WsJobId other = (WsJobId) obj;
-    if (obj == null)
-      return false;
     if (this == obj)
+    {
       return true;
+    }
     if (__equalsCalc != null)
     {
       return (__equalsCalc == obj);
@@ -103,6 +110,7 @@ public class WsJobId implements java.io.Serializable
 
   private boolean __hashCodeCalc = false;
 
+  @Override
   public synchronized int hashCode()
   {
     if (__hashCodeCalc)
diff --git a/test/jalview/analysis/AAFrequencyTest.java b/test/jalview/analysis/AAFrequencyTest.java
new file mode 100644 (file)
index 0000000..788e742
--- /dev/null
@@ -0,0 +1,134 @@
+
+package jalview.analysis;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import java.util.Hashtable;
+
+import org.junit.Test;
+
+public class AAFrequencyTest
+{
+  private static final String C = AAFrequency.MAXCOUNT;
+
+  private static final String R = AAFrequency.MAXRESIDUE;
+
+  private static final String G = AAFrequency.PID_GAPS;
+
+  private static final String N = AAFrequency.PID_NOGAPS;
+
+  private static final String P = AAFrequency.PROFILE;
+
+  @Test
+  public void testCalculate_noProfile()
+  {
+    SequenceI seq1 = new Sequence("Seq1", "CAGT");
+    SequenceI seq2 = new Sequence("Seq2", "CACT");
+    SequenceI seq3 = new Sequence("Seq3", "C--G");
+    SequenceI seq4 = new Sequence("Seq4", "CA-t");
+    SequenceI[] seqs = new SequenceI[]
+    { seq1, seq2, seq3, seq4 };
+    Hashtable[] result = new Hashtable[seq1.getLength()];
+    
+    AAFrequency.calculate(seqs, 0, seq1.getLength(), result, false);
+
+    // col 0 is 100% C
+    Hashtable col = result[0];
+    assertEquals(100f, (Float) col.get(G), 0.0001f);
+    assertEquals(100f, (Float) col.get(N), 0.0001f);
+    assertEquals(4, col.get(C));
+    assertEquals("C", col.get(R));
+    assertNull(col.get(P));
+
+    // col 1 is 75% A
+    col = result[1];
+    assertEquals(75f, (Float) col.get(G), 0.0001f);
+    assertEquals(100f, (Float) col.get(N), 0.0001f);
+    assertEquals(3, col.get(C));
+    assertEquals("A", col.get(R));
+
+    // col 2 is 50% G 50% C or 25/25 counting gaps
+    col = result[2];
+    assertEquals(25f, (Float) col.get(G), 0.0001f);
+    assertEquals(50f, (Float) col.get(N), 0.0001f);
+    assertEquals(1, col.get(C));
+    assertEquals("CG", col.get(R));
+
+    // col 3 is 75% T 25% G
+    col = result[3];
+    assertEquals(75f, (Float) col.get(G), 0.0001f);
+    assertEquals(75f, (Float) col.get(N), 0.0001f);
+    assertEquals(3, col.get(C));
+    assertEquals("T", col.get(R));
+  }
+
+  @Test
+  public void testCalculate_withProfile()
+  {
+    SequenceI seq1 = new Sequence("Seq1", "CAGT");
+    SequenceI seq2 = new Sequence("Seq2", "CACT");
+    SequenceI seq3 = new Sequence("Seq3", "C--G");
+    SequenceI seq4 = new Sequence("Seq4", "CA-t");
+    SequenceI[] seqs = new SequenceI[]
+    { seq1, seq2, seq3, seq4 };
+    Hashtable[] result = new Hashtable[seq1.getLength()];
+
+    AAFrequency.calculate(seqs, 0, seq1.getLength(), result, true);
+    int[][] profile = (int[][]) result[0].get(P);
+    assertEquals(4, profile[0]['C']);
+    assertEquals(4, profile[1][0]); // no of seqs
+    assertEquals(4, profile[1][1]); // nongapped in column
+
+    profile = (int[][]) result[1].get(P);
+    assertEquals(3, profile[0]['A']);
+    assertEquals(4, profile[1][0]);
+    assertEquals(3, profile[1][1]);
+
+    profile = (int[][]) result[2].get(P);
+    assertEquals(1, profile[0]['G']);
+    assertEquals(1, profile[0]['C']);
+    assertEquals(4, profile[1][0]);
+    assertEquals(2, profile[1][1]);
+
+    profile = (int[][]) result[3].get(P);
+    assertEquals(3, profile[0]['T']);
+    assertEquals(1, profile[0]['G']);
+    assertEquals(4, profile[1][0]);
+    assertEquals(4, profile[1][1]);
+  }
+
+  @Test
+  public void testCalculate_withProfileTiming()
+  {
+    SequenceI seq1 = new Sequence("Seq1", "CAGT");
+    SequenceI seq2 = new Sequence("Seq2", "CACT");
+    SequenceI seq3 = new Sequence("Seq3", "C--G");
+    SequenceI seq4 = new Sequence("Seq4", "CA-t");
+    SequenceI[] seqs = new SequenceI[]
+    { seq1, seq2, seq3, seq4 };
+    Hashtable[] result = new Hashtable[seq1.getLength()];
+
+    // ensure class loaded and initialized
+    AAFrequency.calculate(seqs, 0, seq1.getLength(), result, true);
+    int reps = 100000;
+    long start = System.currentTimeMillis();
+    for (int i = 0; i < reps; i++)
+    {
+      AAFrequency.calculate(seqs, 0, seq1.getLength(), result, true);
+    }
+    System.out.println(System.currentTimeMillis() - start);
+  }
+
+  @Test
+  public void testGetPercentageFormat()
+  {
+    assertNull(AAFrequency.getPercentageFormat(0));
+    assertNull(AAFrequency.getPercentageFormat(99));
+    assertEquals("%3.1f", AAFrequency.getPercentageFormat(100).toString());
+    assertEquals("%3.1f", AAFrequency.getPercentageFormat(999).toString());
+    assertEquals("%3.2f", AAFrequency.getPercentageFormat(1000).toString());
+    assertEquals("%3.2f", AAFrequency.getPercentageFormat(9999).toString());
+  }
+}
index bba21c3..bc6a137 100644 (file)
  */
 package jalview.analysis;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
 import org.junit.Test;
 
+import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.Mapping;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.io.AppletFormatAdapter;
+import jalview.io.FormatAdapter;
+import jalview.util.MapList;
 
 public class AlignmentUtilsTests 
 {
+  // @formatter:off
+  private static final String TEST_DATA = 
+          "# STOCKHOLM 1.0\n" +
+          "#=GS D.melanogaster.1 AC AY119185.1/838-902\n" +
+          "#=GS D.melanogaster.2 AC AC092237.1/57223-57161\n" +
+          "#=GS D.melanogaster.3 AC AY060611.1/560-627\n" +
+          "D.melanogaster.1          G.AGCC.CU...AUGAUCGA\n" +
+          "#=GR D.melanogaster.1 SS  ................((((\n" +
+          "D.melanogaster.2          C.AUUCAACU.UAUGAGGAU\n" +
+          "#=GR D.melanogaster.2 SS  ................((((\n" +
+          "D.melanogaster.3          G.UGGCGCU..UAUGACGCA\n" +
+          "#=GR D.melanogaster.3 SS  (.(((...(....(((((((\n" +
+          "//";
+
+  private static final String AA_SEQS_1 = 
+          ">Seq1Name\n" +
+          "K-QY--L\n" +
+          ">Seq2Name\n" +
+          "-R-FP-W-\n";
+
+  private static final String CDNA_SEQS_1 = 
+          ">Seq1Name\n" +
+          "AC-GG--CUC-CAA-CT\n" +
+          ">Seq2Name\n" +
+          "-CG-TTA--ACG---AAGT\n";
+
+  private static final String CDNA_SEQS_2 = 
+          ">Seq1Name\n" +
+          "GCTCGUCGTACT\n" +
+          ">Seq2Name\n" +
+          "GGGTCAGGCAGT\n";
+  // @formatter:on
+
   public static Sequence ts=new Sequence("short","ASDASDASDASDASDASDASDASDASDASDASDASDASD");
+
   @Test
   public void testExpandFlanks()
   {
@@ -55,6 +106,708 @@ public class AlignmentUtilsTests
           assertTrue("Flanking sequence not the same as original dataset sequence.\n"+ung+"\n"+sq.getDatasetSequence().getSequenceAsString(),ung.equalsIgnoreCase(sq.getDatasetSequence().getSequenceAsString()));
       }
       }
+    }
     }    
+
+  /**
+   * Test method that returns a map of lists of sequences by sequence name.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testGetSequencesByName() throws IOException
+  {
+    final String data = ">Seq1Name\nKQYL\n" + ">Seq2Name\nRFPW\n"
+            + ">Seq1Name\nABCD\n";
+    AlignmentI al = loadAlignment(data, "FASTA");
+    Map<String, List<SequenceI>> map = AlignmentUtils
+            .getSequencesByName(al);
+    assertEquals(2, map.keySet().size());
+    assertEquals(2, map.get("Seq1Name").size());
+    assertEquals("KQYL", map.get("Seq1Name").get(0).getSequenceAsString());
+    assertEquals("ABCD", map.get("Seq1Name").get(1).getSequenceAsString());
+    assertEquals(1, map.get("Seq2Name").size());
+    assertEquals("RFPW", map.get("Seq2Name").get(0).getSequenceAsString());
+  }
+  /**
+   * Helper method to load an alignment and ensure dataset sequences are set up.
+   * 
+   * @param data
+   * @param format TODO
+   * @return
+   * @throws IOException
+   */
+  protected AlignmentI loadAlignment(final String data, String format) throws IOException
+  {
+    Alignment a = new FormatAdapter().readFile(data,
+            AppletFormatAdapter.PASTE, format);
+    a.setDataset(null);
+    return a;
+  }
+  
+  /**
+   * Test mapping of protein to cDNA, for the case where we have no sequence
+   * cross-references, so mappings are made first-served 1-1 where sequences
+   * translate.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapProteinToCdna_noXrefs() throws IOException
+  {
+    List<SequenceI> protseqs = new ArrayList<SequenceI>();
+    protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
+    protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
+    protseqs.add(new Sequence("UNIPROT|V12347", "SAR"));
+    AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3]));
+    protein.setDataset(null);
+
+    List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
+    dnaseqs.add(new Sequence("EMBL|A11111", "TCAGCACGC")); // = SAR
+    dnaseqs.add(new Sequence("EMBL|A22222", "GAGATACAA")); // = EIQ
+    dnaseqs.add(new Sequence("EMBL|A33333", "GAAATCCAG")); // = EIQ
+    dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG")); // = EIQ
+    AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[4]));
+    cdna.setDataset(null);
+
+    assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
+
+    // 3 mappings made, each from 1 to 1 sequence
+    assertEquals(3, protein.getCodonFrames().size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size());
+
+    // V12345 mapped to A22222
+    AlignedCodonFrame acf = protein.getCodonFrame(
+            protein.getSequenceAt(0)).get(0);
+    assertEquals(1, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+    Mapping[] protMappings = acf.getProtMappings();
+    assertEquals(1, protMappings.length);
+    MapList mapList = protMappings[0].getMap();
+    assertEquals(3, mapList.getFromRatio());
+    assertEquals(1, mapList.getToRatio());
+    assertTrue(Arrays.equals(new int[]
+    { 1, 9 }, mapList.getFromRanges().get(0)));
+    assertEquals(1, mapList.getFromRanges().size());
+    assertTrue(Arrays.equals(new int[]
+    { 1, 3 }, mapList.getToRanges().get(0)));
+    assertEquals(1, mapList.getToRanges().size());
+
+    // V12346 mapped to A33333
+    acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
+    assertEquals(1, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(2).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+
+    // V12347 mapped to A11111
+    acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0);
+    assertEquals(1, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+
+    // no mapping involving the 'extra' A44444
+    assertTrue(protein.getCodonFrame(cdna.getSequenceAt(3)).isEmpty());
+  }
+
+  /**
+   * Test for the alignSequenceAs method that takes two sequences and a mapping.
+   */
+  @Test
+  public void testAlignSequenceAs_withMapping_noIntrons()
+  {
+    MapList map = new MapList(new int[]
+    { 1, 6 }, new int[]
+    { 1, 2 }, 3, 1);
+
+    /*
+     * No existing gaps in dna:
+     */
+    checkAlignSequenceAs("GGGAAA", "-A-L-", false, false, map,
+            "---GGG---AAA");
+
+    /*
+     * Now introduce gaps in dna but ignore them when realigning.
+     */
+    checkAlignSequenceAs("-G-G-G-A-A-A-", "-A-L-", false, false, map,
+            "---GGG---AAA");
+
+    /*
+     * Now include gaps in dna when realigning. First retaining 'mapped' gaps
+     * only, i.e. those within the exon region.
+     */
+    checkAlignSequenceAs("-G-G--G-A--A-A-", "-A-L-", true, false, map,
+            "---G-G--G---A--A-A");
+
+    /*
+     * Include all gaps in dna when realigning (within and without the exon
+     * region). The leading gap, and the gaps between codons, are subsumed by
+     * the protein alignment gap.
+     */
+    checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", true, true, map,
+            "---G-GG---AA-A-");
+
+    /*
+     * Include only unmapped gaps in dna when realigning (outside the exon
+     * region). The leading gap, and the gaps between codons, are subsumed by
+     * the protein alignment gap.
+     */
+    checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", false, true, map,
+            "---GGG---AAA-");
+  }
+
+  /**
+   * Test for the alignSequenceAs method that takes two sequences and a mapping.
+   */
+  @Test
+  public void testAlignSequenceAs_withMapping_withIntrons()
+  {
+    /*
+     * Exons at codon 2 (AAA) and 4 (TTT)
+     */
+    MapList map = new MapList(new int[]
+    { 4, 6, 10, 12 }, new int[]
+    { 1, 2 }, 3, 1);
+
+    /*
+     * Simple case: no gaps in dna
+     */
+    checkAlignSequenceAs("GGGAAACCCTTTGGG", "--A-L-", false, false, map,
+            "GGG---AAACCCTTTGGG");
+
+    /*
+     * Add gaps to dna - but ignore when realigning.
+     */
+    checkAlignSequenceAs("-G-G-G--A--A---AC-CC-T-TT-GG-G-", "--A-L-",
+            false, false, map, "GGG---AAACCCTTTGGG");
+
+    /*
+     * Add gaps to dna - include within exons only when realigning.
+     */
+    checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
+            true, false, map, "GGG---A--A---ACCCT-TTGGG");
+
+    /*
+     * Include gaps outside exons only when realigning.
+     */
+    checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
+            false, true, map, "-G-G-GAAAC-CCTTT-GG-G-");
+
+    /*
+     * Include gaps following first intron if we are 'preserving mapped gaps'
+     */
+    checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
+            true, true, map, "-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
+
+    /*
+     * Include all gaps in dna when realigning.
+     */
+    checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
+            true, true, map, "-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
+  }
+
+  /**
+   * Test for the case where not all of the protein sequence is mapped to cDNA.
+   */
+  @Test
+  public void testAlignSequenceAs_withMapping_withUnmappedProtein()
+  {
+    
+    /*
+     * Exons at codon 2 (AAA) and 4 (TTT) mapped to A and P
+     */
+    final MapList map = new MapList(new int[]
+    { 4, 6, 10, 12 }, new int[]
+    { 1, 1, 3, 3 }, 3, 1);
+    
+
+    /*
+     * Expect alignment does nothing (aborts realignment). Change this test
+     * first if different behaviour wanted.
+     */
+    checkAlignSequenceAs("GGGAAACCCTTTGGG", "-A-L-P-", false,
+            false, map, "GGGAAACCCTTTGGG");
+  }
+
+  /**
+   * Helper method that performs and verifies the method under test.
+   * 
+   * @param dnaSeq
+   * @param proteinSeq
+   * @param preserveMappedGaps
+   * @param preserveUnmappedGaps
+   * @param map
+   * @param expected
+   */
+  protected void checkAlignSequenceAs(final String dnaSeq,
+          final String proteinSeq, final boolean preserveMappedGaps,
+          final boolean preserveUnmappedGaps, MapList map,
+          final String expected)
+  {
+    SequenceI dna = new Sequence("Seq1", dnaSeq);
+    dna.createDatasetSequence();
+    SequenceI protein = new Sequence("Seq1", proteinSeq);
+    protein.createDatasetSequence();
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
+
+    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-',
+            preserveMappedGaps, preserveUnmappedGaps);
+    assertEquals(expected, dna.getSequenceAsString());
+  }
+
+  /**
+   * Test for the alignSequenceAs method where we preserve gaps in introns only.
+   */
+  @Test
+  public void testAlignSequenceAs_keepIntronGapsOnly()
+  {
+
+    /*
+     * Intron GGGAAA followed by exon CCCTTT
+     */
+    MapList map = new MapList(new int[]
+    { 7, 12 }, new int[]
+    { 1, 2 }, 3, 1);
+    
+    checkAlignSequenceAs("GG-G-AA-A-C-CC-T-TT", "AL",
+            false, true, map, "GG-G-AA-ACCCTTT");
+  }
+
+  /**
+   * Test for the method that generates an aligned translated sequence from one
+   * mapping.
+   */
+  @Test
+  public void testGetAlignedTranslation_dnaLikeProtein()
+  {
+    // dna alignment will be replaced
+    SequenceI dna = new Sequence("Seq1", "T-G-CC-A--T-TAC-CAG-");
+    dna.createDatasetSequence();
+    // protein alignment will be 'applied' to dna
+    SequenceI protein = new Sequence("Seq1", "-CH-Y--Q-");
+    protein.createDatasetSequence();
+    MapList map = new MapList(new int[]
+    { 1, 12 }, new int[]
+    { 1, 4 }, 3, 1);
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
+
+    final SequenceI aligned = AlignmentUtils
+                .getAlignedTranslation(protein, '-', acf);
+    assertEquals("---TGCCAT---TAC------CAG---", aligned.getSequenceAsString());
+    assertSame(aligned.getDatasetSequence(), dna.getDatasetSequence());
+  }
+
+  /**
+   * Test the method that realigns protein to match mapped codon alignment.
+   */
+  @Test
+  public void testAlignProteinAsDna()
+  {
+    // seq1 codons are [1,2,3] [4,5,6] [7,8,9] [10,11,12]
+    SequenceI dna1 = new Sequence("Seq1", "TGCCATTACCAG-");
+    // seq2 codons are [1,3,4] [5,6,7] [8,9,10] [11,12,13]
+    SequenceI dna2 = new Sequence("Seq2", "T-GCCATTACCAG");
+    // seq3 codons are [1,2,3] [4,5,7] [8,9,10] [11,12,13]
+    SequenceI dna3 = new Sequence("Seq3", "TGCCA-TTACCAG");
+    AlignmentI dna = new Alignment(new SequenceI[]
+    { dna1, dna2, dna3 });
+    dna.setDataset(null);
+
+    // protein alignment will be realigned like dna
+    SequenceI prot1 = new Sequence("Seq1", "CHYQ");
+    SequenceI prot2 = new Sequence("Seq2", "CHYQ");
+    SequenceI prot3 = new Sequence("Seq3", "CHYQ");
+    AlignmentI protein = new Alignment(new SequenceI[]
+    { prot1, prot2, prot3 });
+    protein.setDataset(null);
+
+    MapList map = new MapList(new int[]
+    { 1, 12 }, new int[]
+    { 1, 4 }, 3, 1);
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    acf.addMap(dna1.getDatasetSequence(), prot1.getDatasetSequence(), map);
+    acf.addMap(dna2.getDatasetSequence(), prot2.getDatasetSequence(), map);
+    acf.addMap(dna3.getDatasetSequence(), prot3.getDatasetSequence(), map);
+    protein.setCodonFrames(Collections.singleton(acf));
+
+    /*
+     * Translated codon order is [1,2,3] [1,3,4] [4,5,6] [4,5,7] [5,6,7] [7,8,9]
+     * [8,9,10] [10,11,12] [11,12,13]
+     */
+    AlignmentUtils.alignProteinAsDna(protein, dna);
+    assertEquals("C-H--Y-Q-", prot1.getSequenceAsString());
+    assertEquals("-C--H-Y-Q", prot2.getSequenceAsString());
+    assertEquals("C--H--Y-Q", prot3.getSequenceAsString());
+  }
+
+  /**
+   * Test the method that tests whether a CDNA sequence translates to a protein
+   * sequence
+   */
+  @Test
+  public void testTranslatesAs()
+  {
+    assertTrue(AlignmentUtils.translatesAs("tttcccaaaggg".toCharArray(), 0,
+            "FPKG".toCharArray()));
+    // with start codon
+    assertTrue(AlignmentUtils.translatesAs("atgtttcccaaaggg".toCharArray(),
+            3, "FPKG".toCharArray()));
+    // with stop codon1
+    assertTrue(AlignmentUtils.translatesAs("tttcccaaagggtaa".toCharArray(),
+            0, "FPKG".toCharArray()));
+    // with stop codon2
+    assertTrue(AlignmentUtils.translatesAs("tttcccaaagggtag".toCharArray(),
+            0, "FPKG".toCharArray()));
+    // with stop codon3
+    assertTrue(AlignmentUtils.translatesAs("tttcccaaagggtga".toCharArray(),
+            0, "FPKG".toCharArray()));
+    // with start and stop codon1
+    assertTrue(AlignmentUtils.translatesAs(
+            "atgtttcccaaaggtaa".toCharArray(), 3, "FPKG".toCharArray()));
+    // with start and stop codon2
+    assertTrue(AlignmentUtils.translatesAs(
+            "atgtttcccaaaggtag".toCharArray(), 3, "FPKG".toCharArray()));
+    // with start and stop codon3
+    assertTrue(AlignmentUtils.translatesAs(
+            "atgtttcccaaaggtga".toCharArray(), 3, "FPKG".toCharArray()));
+
+    // wrong protein
+    assertFalse(AlignmentUtils.translatesAs("tttcccaaaggg".toCharArray(),
+            0,
+            "FPMG".toCharArray()));
+  }
+
+  /**
+   * Test mapping of protein to cDNA, for cases where the cDNA has start and/or
+   * stop codons in addition to the protein coding sequence.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapProteinToCdna_withStartAndStopCodons()
+          throws IOException
+  {
+    List<SequenceI> protseqs = new ArrayList<SequenceI>();
+    protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
+    protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
+    protseqs.add(new Sequence("UNIPROT|V12347", "SAR"));
+    AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3]));
+    protein.setDataset(null);
+  
+    List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
+    // start + SAR:
+    dnaseqs.add(new Sequence("EMBL|A11111", "ATGTCAGCACGC"));
+    // = EIQ + stop
+    dnaseqs.add(new Sequence("EMBL|A22222", "GAGATACAATAA"));
+    // = start +EIQ + stop
+    dnaseqs.add(new Sequence("EMBL|A33333", "ATGGAAATCCAGTAG"));
+    dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG"));
+    AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[4]));
+    cdna.setDataset(null);
+  
+    assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
+
+    // 3 mappings made, each from 1 to 1 sequence
+    assertEquals(3, protein.getCodonFrames().size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size());
+  
+    // V12345 mapped from A22222
+    AlignedCodonFrame acf = protein.getCodonFrame(
+            protein.getSequenceAt(0)).get(0);
+    assertEquals(1, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+    Mapping[] protMappings = acf.getProtMappings();
+    assertEquals(1, protMappings.length);
+    MapList mapList = protMappings[0].getMap();
+    assertEquals(3, mapList.getFromRatio());
+    assertEquals(1, mapList.getToRatio());
+    assertTrue(Arrays.equals(new int[]
+    { 1, 9 }, mapList.getFromRanges().get(0)));
+    assertEquals(1, mapList.getFromRanges().size());
+    assertTrue(Arrays.equals(new int[]
+    { 1, 3 }, mapList.getToRanges().get(0)));
+    assertEquals(1, mapList.getToRanges().size());
+
+    // V12346 mapped from A33333 starting position 4
+    acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
+    assertEquals(1, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(2).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+    protMappings = acf.getProtMappings();
+    assertEquals(1, protMappings.length);
+    mapList = protMappings[0].getMap();
+    assertEquals(3, mapList.getFromRatio());
+    assertEquals(1, mapList.getToRatio());
+    assertTrue(Arrays.equals(new int[]
+    { 4, 12 }, mapList.getFromRanges().get(0)));
+    assertEquals(1, mapList.getFromRanges().size());
+    assertTrue(Arrays.equals(new int[]
+    { 1, 3 }, mapList.getToRanges().get(0)));
+    assertEquals(1, mapList.getToRanges().size());
+  
+    // V12347 mapped to A11111 starting position 4
+    acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0);
+    assertEquals(1, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+    protMappings = acf.getProtMappings();
+    assertEquals(1, protMappings.length);
+    mapList = protMappings[0].getMap();
+    assertEquals(3, mapList.getFromRatio());
+    assertEquals(1, mapList.getToRatio());
+    assertTrue(Arrays.equals(new int[]
+    { 4, 12 }, mapList.getFromRanges().get(0)));
+    assertEquals(1, mapList.getFromRanges().size());
+    assertTrue(Arrays.equals(new int[]
+    { 1, 3 }, mapList.getToRanges().get(0)));
+    assertEquals(1, mapList.getToRanges().size());
+  
+    // no mapping involving the 'extra' A44444
+    assertTrue(protein.getCodonFrame(cdna.getSequenceAt(3)).isEmpty());
+  }
+
+  /**
+   * Test mapping of protein to cDNA, for the case where we have some sequence
+   * cross-references. Verify that 1-to-many mappings are made where
+   * cross-references exist and sequences are mappable.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapProteinToCdna_withXrefs() throws IOException
+  {
+    List<SequenceI> protseqs = new ArrayList<SequenceI>();
+    protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
+    protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
+    protseqs.add(new Sequence("UNIPROT|V12347", "SAR"));
+    AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3]));
+    protein.setDataset(null);
+  
+    List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
+    dnaseqs.add(new Sequence("EMBL|A11111", "TCAGCACGC")); // = SAR
+    dnaseqs.add(new Sequence("EMBL|A22222", "ATGGAGATACAA")); // = start + EIQ
+    dnaseqs.add(new Sequence("EMBL|A33333", "GAAATCCAG")); // = EIQ
+    dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG")); // = EIQ
+    dnaseqs.add(new Sequence("EMBL|A55555", "GAGATTCAG")); // = EIQ
+    AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[5]));
+    cdna.setDataset(null);
+  
+    // Xref A22222 to V12345 (should get mapped)
+    dnaseqs.get(1).addDBRef(new DBRefEntry("UNIPROT", "1", "V12345"));
+    // Xref V12345 to A44444 (should get mapped)
+    protseqs.get(0).addDBRef(new DBRefEntry("EMBL", "1", "A44444"));
+    // Xref A33333 to V12347 (sequence mismatch - should not get mapped)
+    dnaseqs.get(2).addDBRef(new DBRefEntry("UNIPROT", "1", "V12347"));
+    // as V12345 is mapped to A22222 and A44444, this leaves V12346 unmapped.
+    // it should get paired up with the unmapped A33333
+    // A11111 should be mapped to V12347
+    // A55555 is spare and has no xref so is not mapped
+
+    assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
+
+    // 4 protein mappings made for 3 proteins, 2 to V12345, 1 each to V12346/7
+    assertEquals(3, protein.getCodonFrames().size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size());
+
+    // one mapping for each of the first 4 cDNA sequences
+    assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(0)).size());
+    assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(1)).size());
+    assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(2)).size());
+    assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(3)).size());
+  
+    // V12345 mapped to A22222 and A44444
+    AlignedCodonFrame acf = protein.getCodonFrame(
+            protein.getSequenceAt(0)).get(0);
+    assertEquals(2, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+    assertEquals(cdna.getSequenceAt(3).getDatasetSequence(),
+            acf.getdnaSeqs()[1]);
+  
+    // V12346 mapped to A33333
+    acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
+    assertEquals(1, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(2).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+  
+    // V12347 mapped to A11111
+    acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0);
+    assertEquals(1, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+  
+    // no mapping involving the 'extra' A55555
+    assertTrue(protein.getCodonFrame(cdna.getSequenceAt(4)).isEmpty());
+  }
+
+  /**
+   * Test mapping of protein to cDNA, for the case where we have some sequence
+   * cross-references. Verify that once we have made an xref mapping we don't
+   * also map un-xrefd sequeces.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapProteinToCdna_prioritiseXrefs() throws IOException
+  {
+    List<SequenceI> protseqs = new ArrayList<SequenceI>();
+    protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
+    protseqs.add(new Sequence("UNIPROT|V12346", "EIQ"));
+    AlignmentI protein = new Alignment(
+            protseqs.toArray(new SequenceI[protseqs.size()]));
+    protein.setDataset(null);
+  
+    List<SequenceI> dnaseqs = new ArrayList<SequenceI>();
+    dnaseqs.add(new Sequence("EMBL|A11111", "GAAATCCAG")); // = EIQ
+    dnaseqs.add(new Sequence("EMBL|A22222", "GAAATTCAG")); // = EIQ
+    AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[dnaseqs
+            .size()]));
+    cdna.setDataset(null);
+  
+    // Xref A22222 to V12345 (should get mapped)
+    // A11111 should then be mapped to the unmapped V12346
+    dnaseqs.get(1).addDBRef(new DBRefEntry("UNIPROT", "1", "V12345"));
+  
+    assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
+  
+    // 2 protein mappings made
+    assertEquals(2, protein.getCodonFrames().size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size());
+    assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size());
+  
+    // one mapping for each of the cDNA sequences
+    assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(0)).size());
+    assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(1)).size());
+  
+    // V12345 mapped to A22222
+    AlignedCodonFrame acf = protein.getCodonFrame(protein.getSequenceAt(0))
+            .get(0);
+    assertEquals(1, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(1).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+  
+    // V12346 mapped to A11111
+    acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0);
+    assertEquals(1, acf.getdnaSeqs().length);
+    assertEquals(cdna.getSequenceAt(0).getDatasetSequence(),
+            acf.getdnaSeqs()[0]);
+  }
+
+  /**
+   * Test the method that shows or hides sequence annotations by type(s) and
+   * selection group.
+   */
+  @Test
+  public void testShowOrHideSequenceAnnotations()
+  {
+    SequenceI seq1 = new Sequence("Seq1", "AAA");
+    SequenceI seq2 = new Sequence("Seq2", "BBB");
+    SequenceI seq3 = new Sequence("Seq3", "CCC");
+    Annotation[] anns = new Annotation[]
+    { new Annotation(2f) };
+    AlignmentAnnotation ann1 = new AlignmentAnnotation("Structure", "ann1",
+            anns);
+    ann1.setSequenceRef(seq1);
+    AlignmentAnnotation ann2 = new AlignmentAnnotation("Structure", "ann2",
+            anns);
+    ann2.setSequenceRef(seq2);
+    AlignmentAnnotation ann3 = new AlignmentAnnotation("Structure", "ann3",
+            anns);
+    AlignmentAnnotation ann4 = new AlignmentAnnotation("Temp", "ann4", anns);
+    ann4.setSequenceRef(seq1);
+    AlignmentAnnotation ann5 = new AlignmentAnnotation("Temp", "ann5", anns);
+    ann5.setSequenceRef(seq2);
+    AlignmentAnnotation ann6 = new AlignmentAnnotation("Temp", "ann6", anns);
+    AlignmentI al = new Alignment(new SequenceI[] {seq1, seq2, seq3});
+    al.addAnnotation(ann1); // Structure for Seq1
+    al.addAnnotation(ann2); // Structure for Seq2
+    al.addAnnotation(ann3); // Structure for no sequence
+    al.addAnnotation(ann4); // Temp for seq1
+    al.addAnnotation(ann5); // Temp for seq2
+    al.addAnnotation(ann6); // Temp for no sequence
+    List<String> types = new ArrayList<String>();
+    List<SequenceI> scope = new ArrayList<SequenceI>();
+
+    /*
+     * Set all sequence related Structure to hidden (ann1, ann2)
+     */
+    types.add("Structure");
+    AlignmentUtils.showOrHideSequenceAnnotations(al, types, null, false,
+            false);
+    assertFalse(ann1.visible);
+    assertFalse(ann2.visible);
+    assertTrue(ann3.visible); // not sequence-related, not affected
+    assertTrue(ann4.visible); // not Structure, not affected
+    assertTrue(ann5.visible); // "
+    assertTrue(ann6.visible); // not sequence-related, not affected
+
+    /*
+     * Set Temp in {seq1, seq3} to hidden
+     */
+    types.clear();
+    types.add("Temp");
+    scope.add(seq1);
+    scope.add(seq3);
+    AlignmentUtils.showOrHideSequenceAnnotations(al, types, scope, false,
+            false);
+    assertFalse(ann1.visible); // unchanged
+    assertFalse(ann2.visible); // unchanged
+    assertTrue(ann3.visible); // not sequence-related, not affected
+    assertFalse(ann4.visible); // Temp for seq1 hidden
+    assertTrue(ann5.visible); // not in scope, not affected
+    assertTrue(ann6.visible); // not sequence-related, not affected
+
+    /*
+     * Set Temp in all sequences to hidden
+     */
+    types.clear();
+    types.add("Temp");
+    scope.add(seq1);
+    scope.add(seq3);
+    AlignmentUtils.showOrHideSequenceAnnotations(al, types, null, false,
+            false);
+    assertFalse(ann1.visible); // unchanged
+    assertFalse(ann2.visible); // unchanged
+    assertTrue(ann3.visible); // not sequence-related, not affected
+    assertFalse(ann4.visible); // Temp for seq1 hidden
+    assertFalse(ann5.visible); // Temp for seq2 hidden
+    assertTrue(ann6.visible); // not sequence-related, not affected
+
+    /*
+     * Set all types in {seq1, seq3} to visible
+     */
+    types.clear();
+    scope.clear();
+    scope.add(seq1);
+    scope.add(seq3);
+    AlignmentUtils.showOrHideSequenceAnnotations(al, types, scope, true,
+            true);
+    assertTrue(ann1.visible); // Structure for seq1 set visible
+    assertFalse(ann2.visible); // not in scope, unchanged
+    assertTrue(ann3.visible); // not sequence-related, not affected
+    assertTrue(ann4.visible); // Temp for seq1 set visible
+    assertFalse(ann5.visible); // not in scope, unchanged
+    assertTrue(ann6.visible); // not sequence-related, not affected
+
+    /*
+     * Set all types in all scope to hidden
+     */
+    AlignmentUtils.showOrHideSequenceAnnotations(al, types, null, true,
+            false);
+    assertFalse(ann1.visible);
+    assertFalse(ann2.visible);
+    assertTrue(ann3.visible); // not sequence-related, not affected
+    assertFalse(ann4.visible);
+    assertFalse(ann5.visible);
+    assertTrue(ann6.visible); // not sequence-related, not affected
   }
 }
diff --git a/test/jalview/analysis/CodingUtilsTest.java b/test/jalview/analysis/CodingUtilsTest.java
new file mode 100644 (file)
index 0000000..0f235fb
--- /dev/null
@@ -0,0 +1,78 @@
+package jalview.analysis;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+public class CodingUtilsTest
+{
+
+  @Test
+  public void testDecodeCodon()
+  {
+    assertTrue(Arrays.equals(new char[]
+    { 'A', 'A', 'A' }, CodingUtils.decodeCodon(0)));
+    assertTrue(Arrays.equals(new char[]
+    { 'A', 'A', 'C' }, CodingUtils.decodeCodon(1)));
+    assertTrue(Arrays.equals(new char[]
+    { 'A', 'A', 'G' }, CodingUtils.decodeCodon(2)));
+    assertTrue(Arrays.equals(new char[]
+    { 'A', 'A', 'T' }, CodingUtils.decodeCodon(3)));
+    assertTrue(Arrays.equals(new char[]
+    { 'A', 'C', 'A' }, CodingUtils.decodeCodon(4)));
+    assertTrue(Arrays.equals(new char[]
+    { 'C', 'A', 'A' }, CodingUtils.decodeCodon(16)));
+    assertTrue(Arrays.equals(new char[]
+    { 'G', 'G', 'G' }, CodingUtils.decodeCodon(42)));
+    assertTrue(Arrays.equals(new char[]
+    { 'T', 'T', 'T' }, CodingUtils.decodeCodon(63)));
+  }
+
+  @Test
+  public void testDecodeNucleotide()
+  {
+    assertEquals('A', CodingUtils.decodeNucleotide(0));
+    assertEquals('C', CodingUtils.decodeNucleotide(1));
+    assertEquals('G', CodingUtils.decodeNucleotide(2));
+    assertEquals('T', CodingUtils.decodeNucleotide(3));
+    assertEquals('0', CodingUtils.decodeNucleotide(4));
+  }
+
+  @Test
+  public void testEncodeCodon()
+  {
+    assertTrue(CodingUtils.encodeCodon('Z') < 0);
+    assertEquals(0, CodingUtils.encodeCodon('a'));
+    assertEquals(0, CodingUtils.encodeCodon('A'));
+    assertEquals(1, CodingUtils.encodeCodon('c'));
+    assertEquals(1, CodingUtils.encodeCodon('C'));
+    assertEquals(2, CodingUtils.encodeCodon('g'));
+    assertEquals(2, CodingUtils.encodeCodon('G'));
+    assertEquals(3, CodingUtils.encodeCodon('t'));
+    assertEquals(3, CodingUtils.encodeCodon('T'));
+    assertEquals(3, CodingUtils.encodeCodon('u'));
+    assertEquals(3, CodingUtils.encodeCodon('U'));
+    
+    assertEquals(-1, CodingUtils.encodeCodon(null));
+    assertEquals(0, CodingUtils.encodeCodon(new char[]
+    { 'A', 'A', 'A' }));
+    assertEquals(1, CodingUtils.encodeCodon(new char[]
+    { 'A', 'A', 'C' }));
+    assertEquals(2, CodingUtils.encodeCodon(new char[]
+    { 'A', 'A', 'G' }));
+    assertEquals(3, CodingUtils.encodeCodon(new char[]
+    { 'A', 'A', 'T' }));
+    assertEquals(4, CodingUtils.encodeCodon(new char[]
+    { 'A', 'C', 'A' }));
+    assertEquals(16, CodingUtils.encodeCodon(new char[]
+    { 'C', 'A', 'A' }));
+    assertEquals(42, CodingUtils.encodeCodon(new char[]
+    { 'G', 'G', 'G' }));
+    assertEquals(63, CodingUtils.encodeCodon(new char[]
+    { 'T', 'T', 'T' }));
+  }
+
+}
diff --git a/test/jalview/analysis/CrossRefTest.java b/test/jalview/analysis/CrossRefTest.java
new file mode 100644 (file)
index 0000000..2ae4afc
--- /dev/null
@@ -0,0 +1,72 @@
+package jalview.analysis;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import org.junit.Test;
+
+public class CrossRefTest
+{
+
+  /**
+   * Tests for the method that checks if one sequence cross-references another
+   */
+  @Test
+  public void testHasCrossRef()
+  {
+    assertFalse(CrossRef.hasCrossRef(null, null));
+    SequenceI seq1 = new Sequence("EMBL|A12345", "ABCDEF");
+    assertFalse(CrossRef.hasCrossRef(seq1, null));
+    assertFalse(CrossRef.hasCrossRef(null, seq1));
+    SequenceI seq2 = new Sequence("UNIPROT|V20192", "ABCDEF");
+    assertFalse(CrossRef.hasCrossRef(seq1, seq2));
+
+    // different ref
+    seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "v20193"));
+    assertFalse(CrossRef.hasCrossRef(seq1, seq2));
+
+    // case-insensitive; version number is ignored
+    seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "v20192"));
+    assertTrue(CrossRef.hasCrossRef(seq1, seq2));
+
+    // right case!
+    seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "V20192"));
+    assertTrue(CrossRef.hasCrossRef(seq1, seq2));
+    // test is one-way only
+    assertFalse(CrossRef.hasCrossRef(seq2, seq1));
+  }
+
+  /**
+   * Tests for the method that checks if either sequence cross-references the
+   * other
+   */
+  @Test
+  public void testHaveCrossRef()
+  {
+    assertFalse(CrossRef.hasCrossRef(null, null));
+    SequenceI seq1 = new Sequence("EMBL|A12345", "ABCDEF");
+    assertFalse(CrossRef.haveCrossRef(seq1, null));
+    assertFalse(CrossRef.haveCrossRef(null, seq1));
+    SequenceI seq2 = new Sequence("UNIPROT|V20192", "ABCDEF");
+    assertFalse(CrossRef.haveCrossRef(seq1, seq2));
+
+    seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "V20192"));
+    assertTrue(CrossRef.haveCrossRef(seq1, seq2));
+    // next is true for haveCrossRef, false for hasCrossRef
+    assertTrue(CrossRef.haveCrossRef(seq2, seq1));
+
+    // now the other way round
+    seq1.setDBRef(null);
+    seq2.addDBRef(new DBRefEntry("EMBL", "1", "A12345"));
+    assertTrue(CrossRef.haveCrossRef(seq1, seq2));
+    assertTrue(CrossRef.haveCrossRef(seq2, seq1));
+
+    // now both ways
+    seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "V20192"));
+    assertTrue(CrossRef.haveCrossRef(seq1, seq2));
+    assertTrue(CrossRef.haveCrossRef(seq2, seq1));
+  }
+}
diff --git a/test/jalview/analysis/DnaAlignmentGenerator.java b/test/jalview/analysis/DnaAlignmentGenerator.java
new file mode 100644 (file)
index 0000000..1b8964f
--- /dev/null
@@ -0,0 +1,253 @@
+package jalview.analysis;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.io.FastaFile;
+
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Generates, and outputs in Fasta format, a random DNA alignment for given
+ * sequence length and count. Will regenerate the same alignment each time if
+ * the same random seed is used (so may be used for reproducible unit tests).
+ * Not guaranteed to reproduce the same results between versions, as the rules
+ * may get tweaked to produce more 'realistic' results.
+ * 
+ * Arguments:
+ * <ul>
+ * <li>length (number of bases in each sequence)</li>
+ * <li>height (number of sequences)</li>
+ * <li>a whole number random seed</li>
+ * <li>percentage of gaps to include (0-100)</li>
+ * <li>percentage chance of variation of each position (0-100)</li>
+ * </ul>
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class DnaAlignmentGenerator
+{
+  private static final char GAP = '-';
+
+  private static final char ZERO = '0';
+
+  private static final char[] BASES = new char[]
+  { 'G', 'T', 'C', 'A' };
+
+  private Random random;
+  
+  /**
+   * Outputs a DNA 'alignment' where each position is a random choice from
+   * 'GTCA-'.
+   * 
+   * @param args
+   */
+  public static void main(String[] args)
+  {
+    if (args.length != 5)
+    {
+      usage();
+      return;
+    }
+    int width = Integer.parseInt(args[0]);
+    int height = Integer.parseInt(args[1]);
+    long randomSeed = Long.valueOf(args[2]);
+    int gapPercentage = Integer.valueOf(args[3]);
+    int changePercentage = Integer.valueOf(args[4]);
+    AlignmentI al = new DnaAlignmentGenerator().generate(width, height,
+            randomSeed, gapPercentage, changePercentage);
+
+    System.out.println("; " + height + " sequences of " + width
+            + " bases with " + gapPercentage + "% gaps and "
+            + changePercentage + "% mutations (random seed = " + randomSeed
+            + ")");
+    System.out.println(new FastaFile().print(al.getSequencesArray()));
+  }
+
+  /**
+   * Print parameter help.
+   */
+  private static void usage()
+  {
+    System.out.println("Usage:");
+    System.out.println("arg0: number of (non-gap) bases per sequence");
+    System.out.println("arg1: number sequences");
+    System.out
+            .println("arg2: an integer as random seed (same seed = same results)");
+    System.out.println("arg3: percentage of gaps to (randomly) generate");
+    System.out
+            .println("arg4: percentage of 'mutations' to (randomly) generate");
+    System.out.println("Example: DnaAlignmentGenerator 12 15 387 10 5");
+    System.out
+            .println("- 15 sequences of 12 bases each, approx 10% gaps and 5% mutations, random seed = 387");
+
+  }
+
+  /**
+   * Default constructor
+   */
+  public DnaAlignmentGenerator()
+  {
+
+  }
+
+  /**
+   * Outputs a DNA 'alignment' of given width and height, where each position is
+   * a random choice from 'GTCA-'.
+   * 
+   * @param width
+   * @param height
+   * @param randomSeed
+   * @param changePercentage
+   * @param gapPercentage
+   */
+  public AlignmentI generate(int width, int height, long randomSeed,
+          int gapPercentage, int changePercentage)
+  {
+    SequenceI[] seqs = new SequenceI[height];
+    random = new Random(randomSeed);
+    seqs[0] = generateSequence(1, width, gapPercentage);
+    for (int seqno = 1; seqno < height; seqno++)
+    {
+      seqs[seqno] = generateAnotherSequence(seqs[0].getSequence(),
+              seqno + 1, width, changePercentage);
+    }
+    AlignmentI al = new Alignment(seqs);
+    return al;
+  }
+
+  /**
+   * Outputs a DNA 'sequence' of given length, with some random gaps included.
+   * 
+   * @param seqno
+   * @param length
+   * @param gapPercentage
+   */
+  private SequenceI generateSequence(int seqno, int length,
+          int gapPercentage)
+  {
+    StringBuilder seq = new StringBuilder(length);
+
+    /*
+     * Loop till we've added 'length' bases (excluding gaps)
+     */
+    for (int count = 0; count < length;)
+    {
+      boolean addGap = random.nextInt(100) < gapPercentage;
+      char c = addGap ? GAP : BASES[random.nextInt(Integer.MAX_VALUE) % 4];
+      seq.append(c);
+      if (!addGap)
+      {
+        count++;
+      }
+    }
+    final String seqName = "SEQ" + seqno;
+    final String seqString = seq.toString();
+    SequenceI sq = new Sequence(seqName, seqString);
+    sq.createDatasetSequence();
+    return sq;
+  }
+
+  /**
+   * Generate a sequence approximately aligned to the first one.
+   * 
+   * @param ds
+   * @param seqno
+   * @param width
+   *          number of bases
+   * @param changePercentage
+   * @return
+   */
+  private SequenceI generateAnotherSequence(char[] ds, int seqno,
+          int width, int changePercentage)
+  {
+    int length = ds.length;
+    char[] seq = new char[length];
+    Arrays.fill(seq, ZERO);
+    int gapsWanted = length - width;
+    int gapsAdded = 0;
+
+    /*
+     * First 'randomly' mimic gaps in model sequence.
+     */
+    for (int pos = 0; pos < length; pos++)
+    {
+      if (ds[pos] == GAP)
+      {
+        /*
+         * Add a gap at the same position with changePercentage likelihood
+         */
+        seq[pos] = randomCharacter(GAP, changePercentage);
+        if (seq[pos] == GAP)
+        {
+          gapsAdded++;
+        }
+      }
+    }
+
+    /*
+     * Next scatter any remaining gaps (if any) at random. This gives an even
+     * distribution.
+     */
+    while (gapsAdded < gapsWanted)
+    {
+      boolean added = false;
+      while (!added)
+      {
+        int pos = random.nextInt(length);
+        if (seq[pos] != GAP)
+        {
+          seq[pos] = GAP;
+          added = true;
+          gapsAdded++;
+        }
+      }
+    }
+
+    /*
+     * Finally fill in the rest with randomly mutated bases.
+     */
+    for (int pos = 0; pos < length; pos++)
+    {
+      if (seq[pos] == ZERO)
+      {
+        char c = randomCharacter(ds[pos], changePercentage);
+        seq[pos] = c;
+      }
+    }
+    final String seqName = "SEQ" + seqno;
+    final String seqString = new String(seq);
+    SequenceI sq = new Sequence(seqName, seqString);
+    sq.createDatasetSequence();
+    return sq;
+  }
+
+  /**
+   * Returns a random character that is changePercentage% likely to match the
+   * given type (as base or gap).
+   * 
+   * @param changePercentage
+   * 
+   * @param c
+   * @return
+   */
+  private char randomCharacter(char c, int changePercentage)
+  {
+    final boolean mutation = random.nextInt(100) < changePercentage;
+
+    if (!mutation)
+    {
+      return c;
+    }
+
+    char newchar = c;
+    while (newchar == c)
+    {
+      newchar = BASES[random.nextInt(Integer.MAX_VALUE) % 4];
+    }
+    return newchar;
+  }
+}
diff --git a/test/jalview/analysis/DnaTest.java b/test/jalview/analysis/DnaTest.java
new file mode 100644 (file)
index 0000000..01ed183
--- /dev/null
@@ -0,0 +1,445 @@
+package jalview.analysis;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import jalview.api.AlignViewportI;
+import jalview.datamodel.AlignedCodon;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignViewport;
+import jalview.io.FormatAdapter;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+public class DnaTest
+{
+  // @formatter:off
+  // AA encoding codons as ordered on the Jalview help page Amino Acid Table
+  private static String fasta = ">B\n" + "GCT" + "GCC" + "GCA" + "GCG"
+          + "TGT" + "TGC" + "GAT" + "GAC" + "GAA" + "GAG" + "TTT" + "TTC"
+          + "GGT" + "GGC" + "GGA" + "GGG" + "CAT" + "CAC" + "ATT" + "ATC"
+          + "ATA" + "AAA" + "AAG" + "TTG" + "TTA" + "CTT" + "CTC" + "CTA"
+          + "CTG" + "ATG" + "AAT" + "AAC" + "CCT" + "CCC" + "CCA" + "CCG"
+          + "CAA" + "CAG" + "CGT" + "CGC" + "CGA" + "CGG" + "AGA" + "AGG"
+          + "TCT" + "TCC" + "TCA" + "TCG" + "AGT" + "AGC" + "ACT" + "ACC"
+          + "ACA" + "ACG" + "GTT" + "GTC" + "GTA" + "GTG" + "TGG" + "TAT"
+          + "TAC" + "TAA" + "TAG" + "TGA";
+
+  private static String JAL_1312_example_align_fasta = ">B.FR.83.HXB2_LAI_IIIB_BRU_K03455/45-306\n"
+          + "ATGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAATTAAAACATATAGTATGGGCAAGCAG\n"
+          + "GGAGCTAGAACGATTCGCAGTTAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATACTGGGACA\n"
+          + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAGATCATTATATAATACAGTAGCAACCCTCTATTG\n"
+          + "TGTGCATCAAAGGATAGAGATAAAAGACACCAAGGAAGCTTTAGAC\n"
+          + ">gi|27804621|gb|AY178912.1|/1-259\n"
+          + "-TGGGAGAA-ATTCGGTT-CGGCCAGGGGGAAAGAAAAAATATCAGTTAAAACATATAGTATGGGCAAGCAG\n"
+          + "AGAGCTAGAACGATTCGCAGTTAACCCTGGCCTTTTAGAGACATCACAAGGCTGTAGACAAATACTGGGACA\n"
+          + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
+          + "TGTTCATCAAAGGATAGATATAAAAGACACCAAGGAAGCTTTAGAT\n"
+          + ">gi|27804623|gb|AY178913.1|/1-259\n"
+          + "-TGGGAGAA-ATTCGGTT-CGGCCAGGGGGAAAGAAAAAATATCAGTTAAAACATATAGTATGGGCAAGCAG\n"
+          + "AGAGCTAGAACGATTCGCAGTTAACCCTGGCCTTTTAGAGACATCACAAGGCTGTAGACAAATACTGGAACA\n"
+          + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
+          + "TGTTCATCAAAGGATAGATGTAAAAGACACCAAGGAAGCTTTAGAT\n"
+          + ">gi|27804627|gb|AY178915.1|/1-260\n"
+          + "-TGGGAAAA-ATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n"
+          + "GGAGCTAGAACGATTCGCAGTTAACCCTGGCCTGTTAGAAACATCAGAAGGTTGTAGACAAATATTGGGACA\n"
+          + "GCTACAACCATCCCTTGAGACAGGATCAGAAGAACTTAAATCATTATWTAATACCATAGCAGTCCTCTATTG\n"
+          + "TGTACATCAAAGGATAGATATAAAAGACACCAAGGAAGCTTTAGAG\n"
+          + ">gi|27804631|gb|AY178917.1|/1-261\n"
+          + "-TGGGAAAAAATTCGGTTGAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n"
+          + "GGAGCTAGAACGATTCGCAGTCAACCCTGGCCTGTTAGAAACACCAGAAGGCTGTAGACAAATACTGGGACA\n"
+          + "GCTACAACCGTCCCTTCAGACAGGATCGGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
+          + "TGTGCATCAAAGGATAGATGTAAAAGACACCAAGGAGGCTTTAGAC\n"
+          + ">gi|27804635|gb|AY178919.1|/1-261\n"
+          + "-TGGGAGAGAATTCGGTTACGGCCAGGAGGAAAGAAAAAATATAAATTGAAACATATAGTATGGGCAGGCAG\n"
+          + "AGAGCTAGATCGATTCGCAGTCAATCCTGGCCTGTTAGAAACATCAGAAGGCTGCAGACAGATATTGGGACA\n"
+          + "GCTACAACCGTCCCTTAAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
+          + "TGTACATCAAAGGATAGATGTAAAAGACACCAAGGAAGCTTTAGAT\n"
+          + ">gi|27804641|gb|AY178922.1|/1-261\n"
+          + "-TGGGAGAAAATTCGGTTACGGCCAGGGGGAAAGAAAAGATATAAGTTAAAACATATAGTATGGGCAAGCAG\n"
+          + "GGAGCTAGAACGATTCGCAGTCAACCCTGGCCTGTTAGAAACATCAGAAGGCTGCAGACAAATACTGGGACA\n"
+          + "GTTACACCCATCCCTTCATACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
+          + "TGTGCATCAAAGGATAGAAGTAAAAGACACCAAGGAAGCTTTAGAC\n"
+          + ">gi|27804647|gb|AY178925.1|/1-261\n"
+          + "-TGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATCAATTAAAACATGTAGTATGGGCAAGCAG\n"
+          + "GGAACTAGAACGATTCGCAGTTAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATATTGGGACA\n"
+          + "GCTACAACCATCCCTTCAGACAGGATCAGAGGAACTTAAATCATTATTTAATACAGTAGCAGTCCTCTATTG\n"
+          + "TGTACATCAAAGAATAGATGTAAAAGACACCAAGGAAGCTCTAGAA\n"
+          + ">gi|27804649|gb|AY178926.1|/1-261\n"
+          + "-TGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n"
+          + "GGAGCTAGAACGATTCGCGGTCAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAACTACTGGGACA\n"
+          + "GTTACAACCATCCCTTCAGACAGGATCAGAAGAACTCAAATCATTATATAATACAATAGCAACCCTCTATTG\n"
+          + "TGTGCATCAAAGGATAGAGATAAAAGACACCAAGGAAGCCTTAGAT\n"
+          + ">gi|27804653|gb|AY178928.1|/1-261\n"
+          + "-TGGGAAAGAATTCGGTTAAGGCCAGGGGGAAAGAAACAATATAAATTAAAACATATAGTATGGGCAAGCAG\n"
+          + "GGAGCTAGACCGATTCGCACTTAACCCCGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATATTGGGACA\n"
+          + "GCTACAATCGTCCCTTCAGACAGGATCAGAAGAACTTAGATCACTATATAATACAGTAGCAGTCCTCTATTG\n"
+          + "TGTGCATCAAAAGATAGATGTAAAAGACACCAAGGAAGCCTTAGAC\n"
+          + ">gi|27804659|gb|AY178931.1|/1-261\n"
+          + "-TGGGAAAAAATTCGGTTACGGCCAGGAGGAAAGAAAAGATATAAATTAAAACATATAGTATGGGCAAGCAG\n"
+          + "GGAGCTAGAACGATTYGCAGTTAATCCTGGCCTTTTAGAAACAGCAGAAGGCTGTAGACAAATACTGGGACA\n"
+          + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
+          + "TGTACATCAAAGGATAGAGATAAAAGACACCAAGGAAGCTTTAGAA\n";
+  // @formatter:on
+
+  /**
+   * Corner case for this test is the presence of codons after codons that were
+   * not translated.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testTranslateCdna_withUntranslatableCodons()
+          throws IOException
+  {
+    AlignmentI alf = new FormatAdapter().readFile(
+            JAL_1312_example_align_fasta, jalview.io.FormatAdapter.PASTE,
+            "FASTA");
+    ColumnSelection cs = new ColumnSelection();
+    AlignViewportI av = new AlignViewport(alf, cs);
+    Dna dna = new Dna(av, new int[]
+    { 0, alf.getWidth() - 1 });
+    AlignmentI translated = dna.translateCdna();
+    assertNotNull("Couldn't do a full width translation of test data.",
+            translated);
+  }
+
+  /**
+   * Test variant in which 15 column blocks at a time are translated (the rest
+   * hidden).
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testTranslateCdna_withUntranslatableCodonsAndHiddenColumns()
+          throws IOException
+  {
+    AlignmentI alf = new FormatAdapter().readFile(
+            JAL_1312_example_align_fasta, jalview.io.FormatAdapter.PASTE,
+            "FASTA");
+    int vwidth = 15;
+    for (int ipos = 0; ipos + vwidth < alf.getWidth(); ipos += vwidth)
+    {
+      ColumnSelection cs = new ColumnSelection();
+      if (ipos > 0)
+      {
+        cs.hideColumns(0, ipos - 1);
+      }
+      cs.hideColumns(ipos + vwidth, alf.getWidth());
+      int[] vcontigs = cs.getVisibleContigs(0, alf.getWidth());
+      AlignViewportI av = new AlignViewport(alf, cs);
+      Dna dna = new Dna(av, vcontigs);
+      AlignmentI transAlf = dna.translateCdna();
+
+      assertTrue("Translation failed (ipos=" + ipos
+              + ") No alignment data.", transAlf != null);
+      assertTrue("Translation failed (ipos=" + ipos + ") Empty alignment.",
+              transAlf.getHeight() > 0);
+      assertTrue("Translation failed (ipos=" + ipos + ") Translated "
+              + transAlf.getHeight() + " sequences from " + alf.getHeight()
+              + " sequences", alf.getHeight() == transAlf.getHeight());
+    }
+  }
+
+  /**
+   * Test simple translation to Amino Acids (with STOP codons translated to X).
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testTranslateCdna_simple() throws IOException
+  {
+    AlignmentI alf = new FormatAdapter().readFile(fasta,
+            FormatAdapter.PASTE, "FASTA");
+    ColumnSelection cs = new ColumnSelection();
+    AlignViewportI av = new AlignViewport(alf, cs);
+    Dna dna = new Dna(av, new int[]
+    { 0, alf.getWidth() - 1 });
+    AlignmentI translated = dna.translateCdna();
+    String aa = translated.getSequenceAt(0).getSequenceAsString();
+    assertEquals(
+            "AAAACCDDEEFFGGGGHHIIIKKLLLLLLMNNPPPPQQRRRRRRSSSSSSTTTTVVVVWYYXXX",
+            aa);
+  }
+
+  /**
+   * Test translation excluding hidden columns.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testTranslateCdna_hiddenColumns() throws IOException
+  {
+    AlignmentI alf = new FormatAdapter().readFile(fasta,
+            FormatAdapter.PASTE, "FASTA");
+    ColumnSelection cs = new jalview.datamodel.ColumnSelection();
+    cs.hideColumns(6, 14); // hide codons 3/4/5
+    cs.hideColumns(24, 35); // hide codons 9-12
+    cs.hideColumns(177, 191); // hide codons 60-64
+    AlignViewportI av = new AlignViewport(alf, cs);
+    Dna dna = new Dna(av, new int[]
+    { 0, alf.getWidth() - 1 });
+    AlignmentI translated = dna.translateCdna();
+    String aa = translated.getSequenceAt(0).getSequenceAsString();
+    assertEquals("AACDDGGGGHHIIIKKLLLLLLMNNPPPPQQRRRRRRSSSSSSTTTTVVVVW", aa);
+  }
+
+  /**
+   * Use this test to help debug into any cases of interest.
+   */
+  @Test
+  public void testCompareCodonPos_oneOnly()
+  {
+    assertFollows("-AA--A", "G--GG"); // 2 shifted seq2, 3 shifted seq1
+  }
+
+  /**
+   * Tests for method that compares 'alignment' of two codon position triplets.
+   */
+  @Test
+  public void testCompareCodonPos()
+  {
+    /*
+     * Returns 0 for any null argument
+     */
+    assertEquals(0, Dna.compareCodonPos(new AlignedCodon(1, 2, 3), null));
+    assertEquals(0, Dna.compareCodonPos(null, new AlignedCodon(1, 2, 3)));
+
+    /*
+     * Work through 27 combinations. First 9 cases where first position matches.
+     */
+    assertMatches("AAA", "GGG"); // 2 and 3 match
+    assertFollows("AA-A", "GGG"); // 2 matches, 3 shifted seq1
+    assertPrecedes("AAA", "GG-G"); // 2 matches, 3 shifted seq2
+    assertFollows("A-AA", "GG-G"); // 2 shifted seq1, 3 matches
+    assertFollows("A-A-A", "GG-G"); // 2 shifted seq1, 3 shifted seq1
+    assertPrecedes("A-AA", "GG--G"); // 2 shifted seq1, 3 shifted seq2
+    assertPrecedes("AA-A", "G-GG"); // 2 shifted seq2, 3 matches
+    assertFollows("AA--A", "G-GG"); // 2 shifted seq2, 3 shifted seq1
+    assertPrecedes("AAA", "G-GG"); // 2 shifted seq2, 3 shifted seq2
+
+    /*
+     * 9 cases where first position is shifted in first sequence.
+     */
+    assertFollows("-AAA", "G-GG"); // 2 and 3 match
+    assertFollows("-AA-A", "G-GG"); // 2 matches, 3 shifted seq1
+    // 'enclosing' case: pick first to start precedes
+    assertFollows("-AAA", "G-G-G"); // 2 matches, 3 shifted seq2
+    assertFollows("-A-AA", "G-G-G"); // 2 shifted seq1, 3 matches
+    assertFollows("-A-A-A", "G-G-G"); // 2 shifted seq1, 3 shifted seq1
+    // 'enclosing' case: pick first to start precedes
+    assertFollows("-A-AA", "G-G--G"); // 2 shifted seq1, 3 shifted seq2
+    assertFollows("-AA-A", "G--GG"); // 2 shifted seq2, 3 matches
+    assertFollows("-AA--A", "G--GG"); // 2 shifted seq2, 3 shifted seq1
+    assertPrecedes("-AAA", "G--GG"); // 2 shifted seq2, 3 shifted seq2
+
+    /*
+     * 9 cases where first position is shifted in second sequence.
+     */
+    assertPrecedes("A-AA", "-GGG"); // 2 and 3 match
+    assertPrecedes("A-A-A", "-GGG"); // 2 matches, 3 shifted seq1
+    assertPrecedes("A-AA", "-GG-G"); // 2 matches, 3 shifted seq2
+    assertPrecedes("A--AA", "-GG-G"); // 2 shifted seq1, 3 matches
+    // 'enclosing' case with middle base deciding:
+    assertFollows("A--AA", "-GGG"); // 2 shifted seq1, 3 shifted seq1
+    assertPrecedes("A--AA", "-GG--G"); // 2 shifted seq1, 3 shifted seq2
+    assertPrecedes("AA-A", "-GGG"); // 2 shifted seq2, 3 matches
+    assertPrecedes("AA--A", "-GGG"); // 2 shifted seq2, 3 shifted seq1
+    assertPrecedes("AAA", "-GGG"); // 2 shifted seq2, 3 shifted seq2
+  }
+
+  /**
+   * This test generates a random cDNA alignment and its translation, then
+   * reorders the cDNA and retranslates, and verifies that the translations are
+   * the same (apart from ordering).
+   */
+  @Test
+  public void testTranslateCdna_sequenceOrderIndependent()
+  {
+    /*
+     * Generate cDNA - 8 sequences of 12 bases each.
+     */
+    AlignmentI cdna = new DnaAlignmentGenerator().generate(12, 8, 97, 5, 5);
+    ColumnSelection cs = new ColumnSelection();
+    AlignViewportI av = new AlignViewport(cdna, cs);
+    Dna dna = new Dna(av, new int[]
+    { 0, cdna.getWidth() - 1 });
+    AlignmentI translated = dna.translateCdna();
+
+    /*
+     * Jumble the cDNA sequences and translate.
+     */
+    SequenceI[] sorted = new SequenceI[cdna.getHeight()];
+    final int[] jumbler = new int[]
+    { 6, 7, 3, 4, 2, 0, 1, 5 };
+    int seqNo = 0;
+    for (int i : jumbler)
+    {
+      sorted[seqNo++] = cdna.getSequenceAt(i);
+    }
+    AlignmentI cdnaReordered = new Alignment(sorted);
+    av = new AlignViewport(cdnaReordered, cs);
+    dna = new Dna(av, new int[]
+    { 0, cdna.getWidth() - 1 });
+    AlignmentI translated2 = dna.translateCdna();
+
+    /*
+     * Check translated sequences are the same in both alignments.
+     */
+    System.out.println("Original");
+    System.out.println(translated.toString());
+    System.out.println("Sorted");
+    System.out.println(translated2.toString());
+
+    int sortedSequenceIndex = 0;
+    for (int originalSequenceIndex : jumbler)
+    {
+      final String translation1 = translated.getSequenceAt(
+              originalSequenceIndex).getSequenceAsString();
+      final String translation2 = translated2.getSequenceAt(sortedSequenceIndex)
+              .getSequenceAsString();
+      assertEquals(translation2, translation1);
+      sortedSequenceIndex++;
+    }
+  }
+
+  /**
+   * Test that all the cases in testCompareCodonPos have a 'symmetric'
+   * comparison (without checking the actual comparison result).
+   */
+  @Test
+  public void testCompareCodonPos_isSymmetric()
+  {
+    assertSymmetric("AAA", "GGG");
+    assertSymmetric("AA-A", "GGG");
+    assertSymmetric("AAA", "GG-G");
+    assertSymmetric("A-AA", "GG-G");
+    assertSymmetric("A-A-A", "GG-G");
+    assertSymmetric("A-AA", "GG--G");
+    assertSymmetric("AA-A", "G-GG");
+    assertSymmetric("AA--A", "G-GG");
+    assertSymmetric("AAA", "G-GG");
+    assertSymmetric("-AAA", "G-GG");
+    assertSymmetric("-AA-A", "G-GG");
+    assertSymmetric("-AAA", "G-G-G");
+    assertSymmetric("-A-AA", "G-G-G");
+    assertSymmetric("-A-A-A", "G-G-G");
+    assertSymmetric("-A-AA", "G-G--G");
+    assertSymmetric("-AA-A", "G--GG");
+    assertSymmetric("-AA--A", "G--GG");
+    assertSymmetric("-AAA", "G--GG");
+    assertSymmetric("A-AA", "-GGG");
+    assertSymmetric("A-A-A", "-GGG");
+    assertSymmetric("A-AA", "-GG-G");
+    assertSymmetric("A--AA", "-GG-G");
+    assertSymmetric("A--AA", "-GGG");
+    assertSymmetric("A--AA", "-GG--G");
+    assertSymmetric("AA-A", "-GGG");
+    assertSymmetric("AA--A", "-GGG");
+    assertSymmetric("AAA", "-GGG");
+  }
+
+  private void assertSymmetric(String codon1, String codon2)
+  {
+    assertEquals("Comparison of '" + codon1 + "' and '" + codon2
+            + " not symmetric", Integer.signum(compare(codon1, codon2)),
+            -Integer.signum(compare(codon2, codon1)));
+  }
+
+  /**
+   * Assert that the first sequence should map to the same position as the
+   * second in a translated alignment. Also checks that this is true if the
+   * order of the codons is reversed.
+   * 
+   * @param codon1
+   * @param codon2
+   */
+  private void assertMatches(String codon1, String codon2)
+  {
+    assertEquals("Expected '" + codon1 + "' matches '" + codon2 + "'", 0,
+            compare(codon1, codon2));
+    assertEquals("Expected '" + codon2 + "' matches '" + codon1 + "'", 0,
+            compare(codon2, codon1));
+  }
+
+  /**
+   * Assert that the first sequence should precede the second in a translated
+   * alignment
+   * 
+   * @param codon1
+   * @param codon2
+   */
+  private void assertPrecedes(String codon1, String codon2)
+  {
+    assertEquals("Expected '" + codon1 + "'  precedes '" + codon2 + "'",
+            -1, compare(codon1, codon2));
+  }
+
+  /**
+   * Assert that the first sequence should follow the second in a translated
+   * alignment
+   * 
+   * @param codon1
+   * @param codon2
+   */
+  private void assertFollows(String codon1, String codon2)
+  {
+    assertEquals("Expected '" + codon1 + "'  follows '" + codon2 + "'", 1,
+            compare(codon1, codon2));
+  }
+
+  /**
+   * Convert two nucleotide strings to base positions and pass to
+   * Dna.compareCodonPos, return the result.
+   * 
+   * @param s1
+   * @param s2
+   * @return
+   */
+  private int compare(String s1, String s2)
+  {
+    final AlignedCodon cd1 = convertCodon(s1);
+    final AlignedCodon cd2 = convertCodon(s2);
+    System.out.println("K: " + s1 + "  " + cd1.toString());
+    System.out.println("G: " + s2 + "  " + cd2.toString());
+    System.out.println();
+    return Dna.compareCodonPos(cd1, cd2);
+  }
+
+  /**
+   * Convert a string e.g. "-GC-T" to base positions e.g. [1, 2, 4]. The string
+   * should have exactly 3 non-gap characters, and use '-' for gaps.
+   * 
+   * @param s
+   * @return
+   */
+  private AlignedCodon convertCodon(String s)
+  {
+    int[] codon = new int[3];
+    int i = 0;
+    for (int j = 0; j < s.length(); j++)
+    {
+      if (s.charAt(j) != '-')
+      {
+        codon[i++] = j;
+      }
+    }
+    return new AlignedCodon(codon[0], codon[1], codon[2]);
+  }
+
+  /**
+   * Weirdly, maybe worth a test to prove the helper method of this test class.
+   */
+  @Test
+  public void testConvertCodon()
+  {
+    assertEquals("[0, 1, 2]", convertCodon("AAA").toString());
+    assertEquals("[0, 2, 5]", convertCodon("A-A--A").toString());
+    assertEquals("[1, 3, 4]", convertCodon("-A-AA-").toString());
+  }
+}
diff --git a/test/jalview/analysis/DnaTranslation.java b/test/jalview/analysis/DnaTranslation.java
deleted file mode 100644 (file)
index 9cedbb0..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * 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.analysis;
-
-import static org.junit.Assert.*;
-import jalview.datamodel.ColumnSelection;
-
-import java.io.IOException;
-
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-public class DnaTranslation
-{
-
-  private static String JAL_1312_example_align_fasta = ">B.FR.83.HXB2_LAI_IIIB_BRU_K03455/45-306\n"
-          + "ATGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAATTAAAACATATAGTATGGGCAAGCAG\n"
-          + "GGAGCTAGAACGATTCGCAGTTAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATACTGGGACA\n"
-          + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAGATCATTATATAATACAGTAGCAACCCTCTATTG\n"
-          + "TGTGCATCAAAGGATAGAGATAAAAGACACCAAGGAAGCTTTAGAC\n"
-          + ">gi|27804621|gb|AY178912.1|/1-259\n"
-          + "-TGGGAGAA-ATTCGGTT-CGGCCAGGGGGAAAGAAAAAATATCAGTTAAAACATATAGTATGGGCAAGCAG\n"
-          + "AGAGCTAGAACGATTCGCAGTTAACCCTGGCCTTTTAGAGACATCACAAGGCTGTAGACAAATACTGGGACA\n"
-          + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
-          + "TGTTCATCAAAGGATAGATATAAAAGACACCAAGGAAGCTTTAGAT\n"
-          + ">gi|27804623|gb|AY178913.1|/1-259\n"
-          + "-TGGGAGAA-ATTCGGTT-CGGCCAGGGGGAAAGAAAAAATATCAGTTAAAACATATAGTATGGGCAAGCAG\n"
-          + "AGAGCTAGAACGATTCGCAGTTAACCCTGGCCTTTTAGAGACATCACAAGGCTGTAGACAAATACTGGAACA\n"
-          + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
-          + "TGTTCATCAAAGGATAGATGTAAAAGACACCAAGGAAGCTTTAGAT\n"
-          + ">gi|27804627|gb|AY178915.1|/1-260\n"
-          + "-TGGGAAAA-ATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n"
-          + "GGAGCTAGAACGATTCGCAGTTAACCCTGGCCTGTTAGAAACATCAGAAGGTTGTAGACAAATATTGGGACA\n"
-          + "GCTACAACCATCCCTTGAGACAGGATCAGAAGAACTTAAATCATTATWTAATACCATAGCAGTCCTCTATTG\n"
-          + "TGTACATCAAAGGATAGATATAAAAGACACCAAGGAAGCTTTAGAG\n"
-          + ">gi|27804631|gb|AY178917.1|/1-261\n"
-          + "-TGGGAAAAAATTCGGTTGAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n"
-          + "GGAGCTAGAACGATTCGCAGTCAACCCTGGCCTGTTAGAAACACCAGAAGGCTGTAGACAAATACTGGGACA\n"
-          + "GCTACAACCGTCCCTTCAGACAGGATCGGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
-          + "TGTGCATCAAAGGATAGATGTAAAAGACACCAAGGAGGCTTTAGAC\n"
-          + ">gi|27804635|gb|AY178919.1|/1-261\n"
-          + "-TGGGAGAGAATTCGGTTACGGCCAGGAGGAAAGAAAAAATATAAATTGAAACATATAGTATGGGCAGGCAG\n"
-          + "AGAGCTAGATCGATTCGCAGTCAATCCTGGCCTGTTAGAAACATCAGAAGGCTGCAGACAGATATTGGGACA\n"
-          + "GCTACAACCGTCCCTTAAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
-          + "TGTACATCAAAGGATAGATGTAAAAGACACCAAGGAAGCTTTAGAT\n"
-          + ">gi|27804641|gb|AY178922.1|/1-261\n"
-          + "-TGGGAGAAAATTCGGTTACGGCCAGGGGGAAAGAAAAGATATAAGTTAAAACATATAGTATGGGCAAGCAG\n"
-          + "GGAGCTAGAACGATTCGCAGTCAACCCTGGCCTGTTAGAAACATCAGAAGGCTGCAGACAAATACTGGGACA\n"
-          + "GTTACACCCATCCCTTCATACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
-          + "TGTGCATCAAAGGATAGAAGTAAAAGACACCAAGGAAGCTTTAGAC\n"
-          + ">gi|27804647|gb|AY178925.1|/1-261\n"
-          + "-TGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATCAATTAAAACATGTAGTATGGGCAAGCAG\n"
-          + "GGAACTAGAACGATTCGCAGTTAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATATTGGGACA\n"
-          + "GCTACAACCATCCCTTCAGACAGGATCAGAGGAACTTAAATCATTATTTAATACAGTAGCAGTCCTCTATTG\n"
-          + "TGTACATCAAAGAATAGATGTAAAAGACACCAAGGAAGCTCTAGAA\n"
-          + ">gi|27804649|gb|AY178926.1|/1-261\n"
-          + "-TGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n"
-          + "GGAGCTAGAACGATTCGCGGTCAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAACTACTGGGACA\n"
-          + "GTTACAACCATCCCTTCAGACAGGATCAGAAGAACTCAAATCATTATATAATACAATAGCAACCCTCTATTG\n"
-          + "TGTGCATCAAAGGATAGAGATAAAAGACACCAAGGAAGCCTTAGAT\n"
-          + ">gi|27804653|gb|AY178928.1|/1-261\n"
-          + "-TGGGAAAGAATTCGGTTAAGGCCAGGGGGAAAGAAACAATATAAATTAAAACATATAGTATGGGCAAGCAG\n"
-          + "GGAGCTAGACCGATTCGCACTTAACCCCGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATATTGGGACA\n"
-          + "GCTACAATCGTCCCTTCAGACAGGATCAGAAGAACTTAGATCACTATATAATACAGTAGCAGTCCTCTATTG\n"
-          + "TGTGCATCAAAAGATAGATGTAAAAGACACCAAGGAAGCCTTAGAC\n"
-          + ">gi|27804659|gb|AY178931.1|/1-261\n"
-          + "-TGGGAAAAAATTCGGTTACGGCCAGGAGGAAAGAAAAGATATAAATTAAAACATATAGTATGGGCAAGCAG\n"
-          + "GGAGCTAGAACGATTYGCAGTTAATCCTGGCCTTTTAGAAACAGCAGAAGGCTGTAGACAAATACTGGGACA\n"
-          + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n"
-          + "TGTACATCAAAGGATAGAGATAAAAGACACCAAGGAAGCTTTAGAA\n";
-
-  @Test
-  public void translationWithUntranslatableCodonsTest()
-  {
-    // Corner case for this test is the presence of codons after codons that
-    // were not translated.
-    jalview.datamodel.AlignmentI alf = null;
-    try
-    {
-      alf = new jalview.io.FormatAdapter().readFile(
-              JAL_1312_example_align_fasta, jalview.io.FormatAdapter.PASTE,
-              "FASTA");
-    } catch (IOException x)
-    {
-      x.printStackTrace();
-      fail("Unexpected IOException (" + x.getMessage()
-              + ") - check test environment");
-    }
-    {
-      // full translation
-      ColumnSelection cs = new jalview.datamodel.ColumnSelection();
-      assertNotNull("Couldn't do a full width translation of test data.",
-              jalview.analysis.Dna.CdnaTranslate(
-                      alf.getSequencesArray(),
-                      cs.getVisibleSequenceStrings(0, alf.getWidth(),
-                              alf.getSequencesArray()), new int[]
-                      { 0, alf.getWidth() - 1 }, alf.getGapCharacter(),
-                      null, alf.getWidth(), null));
-    }
-    int vwidth = 15; // translate in 15 base stretches
-    for (int ipos = 0; ipos + vwidth < alf.getWidth(); ipos += vwidth)
-    {
-      ColumnSelection cs = new jalview.datamodel.ColumnSelection();
-      if (ipos > 0)
-      {
-        cs.hideColumns(0, ipos - 1);
-      }
-      cs.hideColumns(ipos + vwidth, alf.getWidth());
-      int[] vcontigs = cs.getVisibleContigs(0, alf.getWidth());
-      String[] sel = cs.getVisibleSequenceStrings(0, alf.getWidth(),
-              alf.getSequencesArray());
-      jalview.datamodel.AlignmentI transAlf = jalview.analysis.Dna
-              .CdnaTranslate(alf.getSequencesArray(), sel, vcontigs,
-                      alf.getGapCharacter(), null, alf.getWidth(), null);
-
-      assertTrue("Translation failed (ipos=" + ipos
-              + ") No alignment data.", transAlf != null);
-      assertTrue("Translation failed (ipos=" + ipos + ") Empty algnment.",
-              transAlf.getHeight() > 0);
-      assertTrue("Translation failed (ipos=" + ipos + ") Translated "
-              + transAlf.getHeight() + " sequences from " + alf.getHeight()
-              + " sequences", alf.getHeight() == transAlf.getHeight());
-    }
-
-  }
-}
index 65213e2..7352b94 100644 (file)
@@ -20,7 +20,9 @@
  */
 package jalview.analysis;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import jalview.datamodel.Mapping;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
@@ -74,4 +76,13 @@ public class TestAlignSeq
     }
   }
 
+  @Test
+  public void testExtractGaps()
+  {
+    assertNull(AlignSeq.extractGaps(null, null));
+    assertNull(AlignSeq.extractGaps(". -", null));
+    assertNull(AlignSeq.extractGaps(null, "AB-C"));
+
+    assertEquals("ABCD", AlignSeq.extractGaps(" .-", ". -A-B.C D."));
+  }
 }
index fc821b9..6ea05e6 100644 (file)
@@ -1,6 +1,7 @@
 package jalview.commands;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
 import jalview.commands.EditCommand.Action;
 import jalview.commands.EditCommand.Edit;
 import jalview.datamodel.Alignment;
@@ -8,6 +9,8 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 
+import java.util.Map;
+
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -33,9 +36,13 @@ public class EditCommandTest
     testee = new EditCommand();
     seqs = new SequenceI[4];
     seqs[0] = new Sequence("seq0", "abcdefghjk");
+    seqs[0].setDatasetSequence(new Sequence("seq0ds", "abcdefghjk"));
     seqs[1] = new Sequence("seq1", "fghjklmnopq");
+    seqs[1].setDatasetSequence(new Sequence("seq1ds", "fghjklmnopq"));
     seqs[2] = new Sequence("seq2", "qrstuvwxyz");
+    seqs[2].setDatasetSequence(new Sequence("seq2ds", "qrstuvwxyz"));
     seqs[3] = new Sequence("seq3", "1234567890");
+    seqs[3].setDatasetSequence(new Sequence("seq3ds", "1234567890"));
     al = new Alignment(seqs);
     al.setGapCharacter('?');
   }
@@ -227,6 +234,373 @@ public class EditCommandTest
     assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
     assertEquals("1234567890", seqs[3].getSequenceAsString());
     seqs[1] = new Sequence("seq1", "fghjZXYnopq");
+  }
+
+  /**
+   * Test that the addEdit command correctly merges insert gap commands when
+   * possible.
+   */
+  @Test
+  public void testAddEdit_multipleInsertGap()
+  {
+    /*
+     * 3 insert gap in a row (aka mouse drag right):
+     */
+    Edit e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+    { seqs[0] }, 1, 1, al);
+    testee.addEdit(e);
+    SequenceI edited = new Sequence("seq0", "a?bcdefghjk");
+    edited.setDatasetSequence(seqs[0].getDatasetSequence());
+    e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+    { edited }, 2, 1, al);
+    testee.addEdit(e);
+    edited = new Sequence("seq0", "a??bcdefghjk");
+    edited.setDatasetSequence(seqs[0].getDatasetSequence());
+    e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+    { edited }, 3, 1, al);
+    testee.addEdit(e);
+    assertEquals(1, testee.getSize());
+    assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
+    assertEquals(1, testee.getEdit(0).getPosition());
+    assertEquals(3, testee.getEdit(0).getNumber());
+
+    /*
+     * Add a non-contiguous edit - should not be merged.
+     */
+    e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+    { edited }, 5, 2, al);
+    testee.addEdit(e);
+    assertEquals(2, testee.getSize());
+    assertEquals(5, testee.getEdit(1).getPosition());
+    assertEquals(2, testee.getEdit(1).getNumber());
+
+    /*
+     * Add a Delete after the Insert - should not be merged.
+     */
+    e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+    { edited }, 6, 2, al);
+    testee.addEdit(e);
+    assertEquals(3, testee.getSize());
+    assertEquals(Action.DELETE_GAP, testee.getEdit(2).getAction());
+    assertEquals(6, testee.getEdit(2).getPosition());
+    assertEquals(2, testee.getEdit(2).getNumber());
+  }
+
+  /**
+   * Test that the addEdit command correctly merges delete gap commands when
+   * possible.
+   */
+  @Test
+  public void testAddEdit_multipleDeleteGap()
+  {
+    /*
+     * 3 delete gap in a row (aka mouse drag left):
+     */
+    seqs[0].setSequence("a???bcdefghjk");
+    Edit e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+    { seqs[0] }, 4, 1, al);
+    testee.addEdit(e);
+    assertEquals(1, testee.getSize());
+
+    SequenceI edited = new Sequence("seq0", "a??bcdefghjk");
+    edited.setDatasetSequence(seqs[0].getDatasetSequence());
+    e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+    { edited }, 3, 1, al);
+    testee.addEdit(e);
+    assertEquals(1, testee.getSize());
+
+    edited = new Sequence("seq0", "a?bcdefghjk");
+    edited.setDatasetSequence(seqs[0].getDatasetSequence());
+    e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+    { edited }, 2, 1, al);
+    testee.addEdit(e);
+    assertEquals(1, testee.getSize());
+    assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
+    assertEquals(2, testee.getEdit(0).getPosition());
+    assertEquals(3, testee.getEdit(0).getNumber());
+
+    /*
+     * Add a non-contiguous edit - should not be merged.
+     */
+    e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+    { edited }, 2, 1, al);
+    testee.addEdit(e);
+    assertEquals(2, testee.getSize());
+    assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
+    assertEquals(2, testee.getEdit(1).getPosition());
+    assertEquals(1, testee.getEdit(1).getNumber());
+
+    /*
+     * Add an Insert after the Delete - should not be merged.
+     */
+    e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+    { edited }, 1, 1, al);
+    testee.addEdit(e);
+    assertEquals(3, testee.getSize());
+    assertEquals(Action.INSERT_GAP, testee.getEdit(2).getAction());
+    assertEquals(1, testee.getEdit(2).getPosition());
+    assertEquals(1, testee.getEdit(2).getNumber());
+  }
+
+  /**
+   * Test that the addEdit command correctly handles 'remove gaps' edits for the
+   * case when they appear contiguous but are acting on different sequences.
+   * They should not be merged.
+   */
+  @Test
+  public void testAddEdit_removeAllGaps()
+  {
+    seqs[0].setSequence("a???bcdefghjk");
+    Edit e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+    { seqs[0] }, 4, 1, al);
+    testee.addEdit(e);
+
+    seqs[1].setSequence("f??ghjklmnopq");
+    Edit e2 = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+    { seqs[1] }, 3, 1, al);
+    testee.addEdit(e2);
+    assertEquals(2, testee.getSize());
+    assertSame(e, testee.getEdit(0));
+    assertSame(e2, testee.getEdit(1));
+  }
+
+  /**
+   * Test that the addEdit command correctly merges insert gap commands acting
+   * on a multi-sequence selection.
+   */
+  @Test
+  public void testAddEdit_groupInsertGaps()
+  {
+    /*
+     * 2 insert gap in a row (aka mouse drag right), on two sequences:
+     */
+    Edit e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+    { seqs[0], seqs[1] }, 1, 1, al);
+    testee.addEdit(e);
+    SequenceI seq1edited = new Sequence("seq0", "a?bcdefghjk");
+    seq1edited.setDatasetSequence(seqs[0].getDatasetSequence());
+    SequenceI seq2edited = new Sequence("seq1", "f?ghjklmnopq");
+    seq2edited.setDatasetSequence(seqs[1].getDatasetSequence());
+    e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+    { seq1edited, seq2edited }, 2, 1, al);
+    testee.addEdit(e);
+
+    assertEquals(1, testee.getSize());
+    assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
+    assertEquals(1, testee.getEdit(0).getPosition());
+    assertEquals(2, testee.getEdit(0).getNumber());
+    assertEquals(seqs[0].getDatasetSequence(), testee.getEdit(0)
+            .getSequences()[0].getDatasetSequence());
+    assertEquals(seqs[1].getDatasetSequence(), testee.getEdit(0)
+            .getSequences()[1].getDatasetSequence());
+  }
+
+  /**
+   * Test for 'undoing' a series of gap insertions.
+   * <ul>
+   * <li>Start: ABCDEF insert 2 at pos 1</li>
+   * <li>next: A--BCDEF insert 1 at pos 4</li>
+   * <li>next: A--B-CDEF insert 2 at pos 0</li>
+   * <li>last: --A--B-CDEF</li>
+   * </ul>
+   */
+  @Test
+  public void testPriorState_multipleInserts()
+  {
+    EditCommand command = new EditCommand();
+    SequenceI seq = new Sequence("", "--A--B-CDEF");
+    SequenceI ds = new Sequence("", "ABCDEF");
+    seq.setDatasetSequence(ds);
+    SequenceI[] seqs = new SequenceI[]
+    { seq };
+    Edit e = command.new Edit(Action.INSERT_GAP, seqs, 1, 2, '-');
+    command.addEdit(e);
+    e = command.new Edit(Action.INSERT_GAP, seqs, 4, 1, '-');
+    command.addEdit(e);
+    e = command.new Edit(Action.INSERT_GAP, seqs, 0, 2, '-');
+    command.addEdit(e);
+
+    Map<SequenceI, SequenceI> unwound = command.priorState(false);
+    assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString());
+  }
+
+  /**
+   * Test for 'undoing' a series of gap deletions.
+   * <ul>
+   * <li>Start: A-B-C delete 1 at pos 1</li>
+   * <li>Next: AB-C delete 1 at pos 2</li>
+   * <li>End: ABC</li>
+   * </ul>
+   */
+  @Test
+  public void testPriorState_removeAllGaps()
+  {
+    EditCommand command = new EditCommand();
+    SequenceI seq = new Sequence("", "ABC");
+    SequenceI ds = new Sequence("", "ABC");
+    seq.setDatasetSequence(ds);
+    SequenceI[] seqs = new SequenceI[]
+    { seq };
+    Edit e = command.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
+    command.addEdit(e);
+    e = command.new Edit(Action.DELETE_GAP, seqs, 2, 1, '-');
+    command.addEdit(e);
+
+    Map<SequenceI, SequenceI> unwound = command.priorState(false);
+    assertEquals("A-B-C", unwound.get(ds).getSequenceAsString());
+  }
+
+  /**
+   * Test for 'undoing' a single delete edit.
+   */
+  @Test
+  public void testPriorState_singleDelete()
+  {
+    EditCommand command = new EditCommand();
+    SequenceI seq = new Sequence("", "ABCDEF");
+    SequenceI ds = new Sequence("", "ABCDEF");
+    seq.setDatasetSequence(ds);
+    SequenceI[] seqs = new SequenceI[]
+    { seq };
+    Edit e = command.new Edit(Action.DELETE_GAP, seqs, 2, 2, '-');
+    command.addEdit(e);
+  
+    Map<SequenceI, SequenceI> unwound = command.priorState(false);
+    assertEquals("AB--CDEF", unwound.get(ds).getSequenceAsString());
+  }
+
+  /**
+   * Test 'undoing' a single gap insertion edit command.
+   */
+  @Test
+  public void testPriorState_singleInsert()
+  {
+    EditCommand command = new EditCommand();
+    SequenceI seq = new Sequence("", "AB---CDEF");
+    SequenceI ds = new Sequence("", "ABCDEF");
+    seq.setDatasetSequence(ds);
+    SequenceI[] seqs = new SequenceI[]
+    { seq };
+    Edit e = command.new Edit(Action.INSERT_GAP, seqs, 2, 3, '-');
+    command.addEdit(e);
+  
+    Map<SequenceI, SequenceI> unwound = command.priorState(false);
+    assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString());
+  }
+
+  /**
+   * Test that mimics 'remove all gaps' action. This generates delete gap edits
+   * for contiguous gaps in each sequence separately.
+   */
+  @Test
+  public void testPriorState_removeGapsMultipleSeqs()
+  {
+    EditCommand command = new EditCommand();
+    String original1 = "--ABC-DEF";
+    String original2 = "FG-HI--J";
+    String original3 = "M-NOPQ";
+
+    /*
+     * Two edits for the first sequence
+     */
+    SequenceI seq = new Sequence("", "ABC-DEF");
+    SequenceI ds1 = new Sequence("", "ABCDEF");
+    seq.setDatasetSequence(ds1);
+    SequenceI[] seqs = new SequenceI[]
+    { seq };
+    Edit e = command.new Edit(Action.DELETE_GAP, seqs, 0, 2, '-');
+    command.addEdit(e);
+    seq = new Sequence("", "ABCDEF");
+    seq.setDatasetSequence(ds1);
+    seqs = new SequenceI[]
+    { seq };
+    e = command.new Edit(Action.DELETE_GAP, seqs, 3, 1, '-');
+    command.addEdit(e);
+  
+    /*
+     * Two edits for the second sequence
+     */
+    seq = new Sequence("", "FGHI--J");
+    SequenceI ds2 = new Sequence("", "FGHIJ");
+    seq.setDatasetSequence(ds2);
+    seqs = new SequenceI[]
+    { seq };
+    e = command.new Edit(Action.DELETE_GAP, seqs, 2, 1, '-');
+    command.addEdit(e);
+    seq = new Sequence("", "FGHIJ");
+    seq.setDatasetSequence(ds2);
+    seqs = new SequenceI[]
+    { seq };
+    e = command.new Edit(Action.DELETE_GAP, seqs, 4, 2, '-');
+    command.addEdit(e);
+
+    /*
+     * One edit for the third sequence.
+     */
+    seq = new Sequence("", "MNOPQ");
+    SequenceI ds3 = new Sequence("", "MNOPQ");
+    seq.setDatasetSequence(ds3);
+    seqs = new SequenceI[]
+    { seq };
+    e = command.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
+    command.addEdit(e);
+
+    Map<SequenceI, SequenceI> unwound = command.priorState(false);
+    assertEquals(original1, unwound.get(ds1).getSequenceAsString());
+    assertEquals(original2, unwound.get(ds2).getSequenceAsString());
+    assertEquals(original3, unwound.get(ds3).getSequenceAsString());
+  }
+
+  /**
+   * Test that mimics 'remove all gapped columns' action. This generates a
+   * series Delete Gap edits that each act on all sequences that share a gapped
+   * column region.
+   */
+  @Test
+  public void testPriorState_removeGappedCols()
+  {
+    EditCommand command = new EditCommand();
+    String original1 = "--ABC--DEF";
+    String original2 = "-G-HI--J";
+    String original3 = "-M-NO--PQ";
+
+    /*
+     * First edit deletes the first column.
+     */
+    SequenceI seq1 = new Sequence("", "-ABC--DEF");
+    SequenceI ds1 = new Sequence("", "ABCDEF");
+    seq1.setDatasetSequence(ds1);
+    SequenceI seq2 = new Sequence("", "G-HI--J");
+    SequenceI ds2 = new Sequence("", "GHIJ");
+    seq2.setDatasetSequence(ds2);
+    SequenceI seq3 = new Sequence("", "M-NO--PQ");
+    SequenceI ds3 = new Sequence("", "MNOPQ");
+    seq3.setDatasetSequence(ds3);
+    SequenceI[] seqs = new SequenceI[]
+    { seq1, seq2, seq3 };
+    Edit e = command.new Edit(Action.DELETE_GAP, seqs, 0, 1, '-');
+    command.addEdit(e);
+
+    /*
+     * Second edit deletes what is now columns 4 and 5.
+     */
+    seq1 = new Sequence("", "-ABCDEF");
+    seq1.setDatasetSequence(ds1);
+    seq2 = new Sequence("", "G-HIJ");
+    seq2.setDatasetSequence(ds2);
+    seq3 = new Sequence("", "M-NOPQ");
+    seq3.setDatasetSequence(ds3);
+    seqs = new SequenceI[]
+    { seq1, seq2, seq3 };
+    e = command.new Edit(Action.DELETE_GAP, seqs, 4, 2, '-');
+    command.addEdit(e);
 
+    Map<SequenceI, SequenceI> unwound = command.priorState(false);
+    assertEquals(original1, unwound.get(ds1).getSequenceAsString());
+    assertEquals(original2, unwound.get(ds2).getSequenceAsString());
+    assertEquals(original3, unwound.get(ds3).getSequenceAsString());
+    assertEquals(ds1, unwound.get(ds1).getDatasetSequence());
+    assertEquals(ds2, unwound.get(ds2).getDatasetSequence());
+    assertEquals(ds3, unwound.get(ds3).getDatasetSequence());
   }
 }
diff --git a/test/jalview/datamodel/AlignedCodonFrameTest.java b/test/jalview/datamodel/AlignedCodonFrameTest.java
new file mode 100644 (file)
index 0000000..0e24bf6
--- /dev/null
@@ -0,0 +1,141 @@
+package jalview.datamodel;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import jalview.util.MapList;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+public class AlignedCodonFrameTest
+{
+
+  /**
+   * Test the method that locates the first aligned sequence that has a mapping.
+   */
+  @Test
+  public void testFindAlignedSequence()
+  {
+    AlignmentI cdna = new Alignment(new SequenceI[]
+    {});
+    final Sequence seq1 = new Sequence("Seq1", "C-G-TA-GC");
+    seq1.createDatasetSequence();
+    cdna.addSequence(seq1);
+    final Sequence seq2 = new Sequence("Seq2", "-TA-GG-GG");
+    seq2.createDatasetSequence();
+    cdna.addSequence(seq2);
+
+    AlignmentI aa = new Alignment(new SequenceI[]
+    {});
+    final Sequence aseq1 = new Sequence("Seq1", "-P-R");
+    aseq1.createDatasetSequence();
+    aa.addSequence(aseq1);
+    final Sequence aseq2 = new Sequence("Seq2", "-LY-");
+    aseq2.createDatasetSequence();
+    aa.addSequence(aseq2);
+
+    /*
+     * Mapping from first DNA sequence to second AA sequence.
+     */
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+
+    assertNull(acf.findAlignedSequence(seq1, aa));
+
+    MapList map = new MapList(new int[]
+    { 1, 6 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(seq1.getDatasetSequence(), aseq2.getDatasetSequence(), map);
+
+    /*
+     * DNA seq1 maps to AA seq2
+     */
+    assertEquals(aa.getSequenceAt(1),
+ acf.findAlignedSequence(cdna
+            .getSequenceAt(0).getDatasetSequence(), aa));
+
+    assertEquals(cdna.getSequenceAt(0),
+ acf.findAlignedSequence(aa
+            .getSequenceAt(1).getDatasetSequence(), cdna));
+  }
+
+  /**
+   * Test the method that locates the mapped codon for a protein position.
+   */
+    @Test
+  public void testGetMappedRegion()
+  {
+    // introns lower case, exons upper case
+    final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T");
+    seq1.createDatasetSequence();
+    final Sequence seq2 = new Sequence("Seq2", "-TA-gG-Gg-CG-a");
+    seq2.createDatasetSequence();
+
+    final Sequence aseq1 = new Sequence("Seq1", "-P-R");
+    aseq1.createDatasetSequence();
+    final Sequence aseq2 = new Sequence("Seq2", "-LY-");
+    aseq2.createDatasetSequence();
+
+    /*
+     * First with no mappings
+     */
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+
+    assertNull(acf.getMappedRegion(seq1, aseq1, 1));
+
+    /*
+     * Set up the mappings for the exons (upper-case bases)
+     */
+    MapList map = new MapList(new int[]
+    { 2, 4, 6, 6, 8, 9 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
+    map = new MapList(new int[]
+    { 1, 2, 4, 5, 7, 8 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(seq2.getDatasetSequence(), aseq2.getDatasetSequence(), map);
+
+    assertEquals("[2, 4]",
+            Arrays.toString(acf.getMappedRegion(seq1, aseq1, 1)));
+    assertEquals("[6, 6, 8, 9]",
+            Arrays.toString(acf.getMappedRegion(seq1, aseq1, 2)));
+    assertEquals("[1, 2, 4, 4]",
+            Arrays.toString(acf.getMappedRegion(seq2, aseq2, 1)));
+    assertEquals("[5, 5, 7, 8]",
+            Arrays.toString(acf.getMappedRegion(seq2, aseq2, 2)));
+
+    /*
+     * No mapping from sequence 1 to sequence 2
+     */
+    assertNull(acf.getMappedRegion(seq1, aseq2, 1));
+  }
+
+  @Test
+  public void testGetMappedCodon()
+  {
+    final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T");
+    seq1.createDatasetSequence();
+    final Sequence aseq1 = new Sequence("Seq1", "-P-R");
+    aseq1.createDatasetSequence();
+
+    /*
+     * First with no mappings
+     */
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+
+    assertNull(acf.getMappedCodon(seq1.getDatasetSequence(), 0));
+
+    /*
+     * Set up the mappings for the exons (upper-case bases)
+     */
+    MapList map = new MapList(new int[]
+    { 2, 4, 6, 6, 8, 9 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
+    
+    assertEquals("[G, T, A]", Arrays.toString(acf.getMappedCodon(
+            aseq1.getDatasetSequence(), 1)));
+    assertEquals("[C, T, T]", Arrays.toString(acf.getMappedCodon(
+            aseq1.getDatasetSequence(), 2)));
+  }
+}
diff --git a/test/jalview/datamodel/AlignedCodonIteratorTest.java b/test/jalview/datamodel/AlignedCodonIteratorTest.java
new file mode 100644 (file)
index 0000000..671c51d
--- /dev/null
@@ -0,0 +1,135 @@
+package jalview.datamodel;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import jalview.util.MapList;
+
+import java.util.Iterator;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for Mapping$AlignedCodonIterator
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class AlignedCodonIteratorTest
+{
+  /**
+   * Test normal case for iterating over aligned codons.
+   */
+  @Test
+  public void testNext()
+  {
+    SequenceI from = new Sequence("Seq1", "-CgC-C-cCtAG-AtG-Gc");
+    from.createDatasetSequence();
+    SequenceI to = new Sequence("Seq1", "-PQ-R-");
+    to.createDatasetSequence();
+    MapList map = new MapList(new int[]
+    { 1, 1, 3, 4, 6, 6, 8, 10, 12, 13 }, new int[]
+    { 1, 3 }, 3, 1);
+    Mapping m = new Mapping(to.getDatasetSequence(), map);
+
+    Iterator<AlignedCodon> codons = m.getCodonIterator(from, '-');
+    AlignedCodon codon = codons.next();
+    assertEquals("[1, 3, 5]", codon.toString());
+    assertEquals("P", codon.product);
+    codon = codons.next();
+    assertEquals("[8, 10, 11]", codon.toString());
+    assertEquals("Q", codon.product);
+    codon = codons.next();
+    assertEquals("[13, 15, 17]", codon.toString());
+    assertEquals("R", codon.product);
+    assertFalse(codons.hasNext());
+  }
+
+  /**
+   * Test weird case where the mapping skips over a peptide.
+   */
+  @Test
+  public void testNext_unmappedPeptide()
+  {
+    SequenceI from = new Sequence("Seq1", "-CgC-C-cCtAG-AtG-Gc");
+    from.createDatasetSequence();
+    SequenceI to = new Sequence("Seq1", "-PQ-TR-");
+    to.createDatasetSequence();
+    MapList map = new MapList(new int[]
+    { 1, 1, 3, 4, 6, 6, 8, 10, 12, 13 }, new int[]
+    { 1, 2, 4, 4 }, 3, 1);
+    Mapping m = new Mapping(to.getDatasetSequence(), map);
+
+    Iterator<AlignedCodon> codons = m.getCodonIterator(from, '-');
+    AlignedCodon codon = codons.next();
+    assertEquals("[1, 3, 5]", codon.toString());
+    assertEquals("P", codon.product);
+    codon = codons.next();
+    assertEquals("[8, 10, 11]", codon.toString());
+    assertEquals("Q", codon.product);
+    codon = codons.next();
+    assertEquals("[13, 15, 17]", codon.toString());
+    assertEquals("R", codon.product);
+    assertFalse(codons.hasNext());
+  }
+  
+  /**
+   * Test for exception thrown for an incomplete codon.
+   */
+  @Test
+  public void testNext_incompleteCodon()
+  {
+    SequenceI from = new Sequence("Seq1", "-CgC-C-cCgTt");
+    from.createDatasetSequence();
+    SequenceI to = new Sequence("Seq1", "-PQ-R-");
+    to.createDatasetSequence();
+    MapList map = new MapList(new int[]
+    { 1, 1, 3, 4, 6, 6, 8, 8 }, new int[]
+    { 1, 3 }, 3, 1);
+    Mapping m = new Mapping(to.getDatasetSequence(), map);
+
+    Iterator<AlignedCodon> codons = m.getCodonIterator(from, '-');
+    AlignedCodon codon = codons.next();
+    assertEquals("[1, 3, 5]", codon.toString());
+    assertEquals("P", codon.product);
+    try
+    {
+      codon = codons.next();
+      fail("expected exception");
+    } catch (IncompleteCodonException e)
+    {
+      // expected
+    }
+  }
+
+  /**
+   * Test normal case for iterating over aligned codons.
+   */
+  @Test
+  public void testAnother()
+  {
+    SequenceI from = new Sequence("Seq1", "TGCCATTACCAG-");
+    from.createDatasetSequence();
+    SequenceI to = new Sequence("Seq1", "CHYQ");
+    to.createDatasetSequence();
+    MapList map = new MapList(new int[]
+    { 1, 12 }, new int[]
+    { 1, 4 }, 3, 1);
+    Mapping m = new Mapping(to.getDatasetSequence(), map);
+  
+    Iterator<AlignedCodon> codons = m.getCodonIterator(from, '-');
+    AlignedCodon codon = codons.next();
+    assertEquals("[0, 1, 2]", codon.toString());
+    assertEquals("C", codon.product);
+    codon = codons.next();
+    assertEquals("[3, 4, 5]", codon.toString());
+    assertEquals("H", codon.product);
+    codon = codons.next();
+    assertEquals("[6, 7, 8]", codon.toString());
+    assertEquals("Y", codon.product);
+    codon = codons.next();
+    assertEquals("[9, 10, 11]", codon.toString());
+    assertEquals("Q", codon.product);
+    assertFalse(codons.hasNext());
+  }
+}
diff --git a/test/jalview/datamodel/AlignedCodonTest.java b/test/jalview/datamodel/AlignedCodonTest.java
new file mode 100644 (file)
index 0000000..60368b1
--- /dev/null
@@ -0,0 +1,28 @@
+package jalview.datamodel;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class AlignedCodonTest
+{
+
+  @Test
+  public void testEquals()
+  {
+    AlignedCodon ac = new AlignedCodon(1, 3, 4);
+    assertTrue(ac.equals(null));
+    assertFalse(ac.equals("hello"));
+    assertFalse(ac.equals(new AlignedCodon(1, 3, 5)));
+    assertTrue(ac.equals(new AlignedCodon(1, 3, 4)));
+    assertTrue(ac.equals(ac));
+  }
+
+  @Test
+  public void testToString() {
+    AlignedCodon ac = new AlignedCodon(1, 3, 4);
+    assertEquals("[1, 3, 4]", ac.toString());
+  }
+}
index 93170b7..df98af9 100644 (file)
@@ -4,6 +4,8 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import jalview.io.AppletFormatAdapter;
+import jalview.io.FormatAdapter;
+import jalview.util.MapList;
 
 import java.io.IOException;
 import java.util.Iterator;
@@ -32,10 +34,45 @@ public class AlignmentTest
           "D.melanogaster.3          G.UGGCGCU..UAUGACGCA\n" +
           "#=GR D.melanogaster.3 SS  (.(((...(....(((((((\n" +
           "//";
+
+  private static final String AA_SEQS_1 = 
+          ">Seq1Name\n" +
+          "K-QY--L\n" +
+          ">Seq2Name\n" +
+          "-R-FP-W-\n";
+
+  private static final String CDNA_SEQS_1 = 
+          ">Seq1Name\n" +
+          "AC-GG--CUC-CAA-CT\n" +
+          ">Seq2Name\n" +
+          "-CG-TTA--ACG---AAGT\n";
+
+  private static final String CDNA_SEQS_2 = 
+          ">Seq1Name\n" +
+          "GCTCGUCGTACT\n" +
+          ">Seq2Name\n" +
+          "GGGTCAGGCAGT\n";
   // @formatter:on
 
+  private AlignmentI al;
 
-  private Alignment al;
+  /**
+   * Helper method to load an alignment and ensure dataset sequences are set up.
+   * 
+   * @param data
+   * @param format
+   *          TODO
+   * @return
+   * @throws IOException
+   */
+  protected AlignmentI loadAlignment(final String data, String format)
+          throws IOException
+  {
+    Alignment a = new FormatAdapter().readFile(data,
+            AppletFormatAdapter.PASTE, format);
+    a.setDataset(null);
+    return a;
+  }
 
   /*
    * Read in Stockholm format test data including secondary structure
@@ -44,13 +81,12 @@ public class AlignmentTest
   @Before
   public void setUp() throws IOException
   {
-    al = new jalview.io.FormatAdapter().readFile(TEST_DATA,
-            AppletFormatAdapter.PASTE, "STH");
-    for (int i = 0; i < al.getSequencesArray().length; ++i)
+    al = loadAlignment(TEST_DATA, "STH");
+    int i = 0;
+    for (AlignmentAnnotation ann : al.getAlignmentAnnotation())
     {
-      al.addAnnotation(al.getSequenceAt(i).getAnnotation()[0]);
-      al.getSequenceAt(i).getAnnotation()[0].setCalcId("CalcIdFor"
-              + al.getSequenceAt(i).getName());
+      ann.setCalcId("CalcIdFor" + al.getSequenceAt(i).getName());
+      i++;
     }
   }
 
@@ -68,4 +104,180 @@ public class AlignmentTest
     assertEquals("D.melanogaster.2", ann.sequenceRef.getName());
     assertFalse(iter.hasNext());
   }
+
+  @Test
+  public void testDeleteAllAnnotations_includingAutocalculated()
+  {
+    AlignmentAnnotation aa = new AlignmentAnnotation("Consensus",
+            "Consensus", 0.5);
+    aa.autoCalculated = true;
+    al.addAnnotation(aa);
+    AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
+    assertEquals("Wrong number of annotations before deleting", 4,
+            anns.length);
+    al.deleteAllAnnotations(true);
+    assertEquals("Not all deleted", 0, al.getAlignmentAnnotation().length);
+  }
+
+  @Test
+  public void testDeleteAllAnnotations_excludingAutocalculated()
+  {
+    AlignmentAnnotation aa = new AlignmentAnnotation("Consensus",
+            "Consensus", 0.5);
+    aa.autoCalculated = true;
+    al.addAnnotation(aa);
+    AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
+    assertEquals("Wrong number of annotations before deleting", 4,
+            anns.length);
+    al.deleteAllAnnotations(false);
+    assertEquals("Not just one annotation left", 1,
+            al.getAlignmentAnnotation().length);
+  }
+
+  /**
+   * Tests for realigning as per a supplied alignment: Dna as Dna.
+   * 
+   * Note: AlignedCodonFrame's state variables are named for protein-to-cDNA
+   * mapping, but can be exploited for a general 'sequence-to-sequence' mapping
+   * as here.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testAlignAs_dnaAsDna() throws IOException
+  {
+    // aligned cDNA:
+    AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA");
+    // unaligned cDNA:
+    AlignmentI al2 = loadAlignment(CDNA_SEQS_2, "FASTA");
+
+    /*
+     * Make mappings between sequences. The 'aligned cDNA' is playing the role
+     * of what would normally be protein here.
+     */
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList ml = new MapList(new int[]
+    { 1, 12 }, new int[]
+    { 1, 12 }, 1, 1);
+    acf.addMap(al2.getSequenceAt(0), al1.getSequenceAt(0), ml);
+    acf.addMap(al2.getSequenceAt(1), al1.getSequenceAt(1), ml);
+    al1.addCodonFrame(acf);
+
+    ((Alignment) al2).alignAs(al1, false, true);
+    assertEquals("GC-TC--GUC-GTA-CT", al2.getSequenceAt(0)
+            .getSequenceAsString());
+    assertEquals("-GG-GTC--AGG---CAGT", al2.getSequenceAt(1)
+            .getSequenceAsString());
+  }
+
+  /**
+   * Aligning protein from cDNA.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testAlignAs_proteinAsCdna() throws IOException
+  {
+    // see also AlignmentUtilsTests
+    AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA");
+    AlignmentI al2 = loadAlignment(AA_SEQS_1, "FASTA");
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList ml = new MapList(new int[]
+    { 1, 12 }, new int[]
+    { 1, 4 }, 3, 1);
+    acf.addMap(al1.getSequenceAt(0), al2.getSequenceAt(0), ml);
+    acf.addMap(al1.getSequenceAt(1), al2.getSequenceAt(1), ml);
+    al2.addCodonFrame(acf);
+
+    ((Alignment) al2).alignAs(al1, false, true);
+    assertEquals("K-Q-Y-L-", al2.getSequenceAt(0).getSequenceAsString());
+    assertEquals("-R-F-P-W", al2.getSequenceAt(1).getSequenceAsString());
+  }
+
+  /**
+   * Test aligning cdna as per protein alignment.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testAlignAs_cdnaAsProtein() throws IOException
+  {
+    /*
+     * Load alignments and add mappings for cDNA to protein
+     */
+    AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA");
+    AlignmentI al2 = loadAlignment(AA_SEQS_1, "FASTA");
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList ml = new MapList(new int[]
+    { 1, 12 }, new int[]
+    { 1, 4 }, 3, 1);
+    acf.addMap(al1.getSequenceAt(0), al2.getSequenceAt(0), ml);
+    acf.addMap(al1.getSequenceAt(1), al2.getSequenceAt(1), ml);
+    al2.addCodonFrame(acf);
+
+    /*
+     * Realign DNA; currently keeping existing gaps in introns only
+     */
+    ((Alignment) al1).alignAs(al2, false, true);
+    assertEquals("ACG---GCUCCA------ACT", al1.getSequenceAt(0)
+            .getSequenceAsString());
+    assertEquals("---CGT---TAACGA---AGT", al1.getSequenceAt(1)
+            .getSequenceAsString());
+  }
+
+  /**
+   * Test aligning dna as per protein alignment, for the case where there are
+   * introns (i.e. some dna sites have no mapping from a peptide).
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testAlignAs_dnaAsProtein_withIntrons() throws IOException
+  {
+    /*
+     * Load alignments and add mappings for cDNA to protein
+     */
+    String dna1 = "A-Aa-gG-GCC-cT-TT";
+    String dna2 = "c--CCGgg-TT--T-AA-A";
+    AlignmentI al1 = loadAlignment(">Seq1\n" + dna1 + "\n>Seq2\n" + dna2
+            + "\n", "FASTA");
+    AlignmentI al2 = loadAlignment(">Seq1\n-P--YK\n>Seq2\nG-T--F\n",
+            "FASTA");
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    // Seq1 has intron at dna positions 3,4,9 so splice is AAG GCC TTT
+    // Seq2 has intron at dna positions 1,5,6 so splice is CCG TTT AAA
+    MapList ml1 = new MapList(new int[]
+    { 1, 2, 5, 8, 10, 12 }, new int[]
+    { 1, 3 }, 3, 1);
+    acf.addMap(al1.getSequenceAt(0), al2.getSequenceAt(0), ml1);
+    MapList ml2 = new MapList(new int[]
+    { 2, 4, 7, 12 }, new int[]
+    { 1, 3 }, 3, 1);
+    acf.addMap(al1.getSequenceAt(1), al2.getSequenceAt(1), ml2);
+    al2.addCodonFrame(acf);
+
+    /*
+     * Align ignoring gaps in dna introns and exons
+     */
+    ((Alignment) al1).alignAs(al2, false, false);
+    assertEquals("---AAagG------GCCcTTT", al1.getSequenceAt(0)
+            .getSequenceAsString());
+    // note 1 gap in protein corresponds to 'gg-' in DNA (3 positions)
+    assertEquals("cCCGgg-TTT------AAA", al1.getSequenceAt(1)
+            .getSequenceAsString());
+
+    /*
+     * Reset and realign, preserving gaps in dna introns and exons
+     */
+    al1.getSequenceAt(0).setSequence(dna1);
+    al1.getSequenceAt(1).setSequence(dna2);
+    ((Alignment) al1).alignAs(al2, true, true);
+    // String dna1 = "A-Aa-gG-GCC-cT-TT";
+    // String dna2 = "c--CCGgg-TT--T-AA-A";
+    // assumption: we include 'the greater of' protein/dna gap lengths, not both
+    assertEquals("---A-Aa-gG------GCC-cT-TT", al1.getSequenceAt(0)
+            .getSequenceAsString());
+    assertEquals("c--CCGgg-TT--T------AA-A", al1.getSequenceAt(1)
+            .getSequenceAsString());
+  }
 }
diff --git a/test/jalview/datamodel/ColumnSelectionTest.java b/test/jalview/datamodel/ColumnSelectionTest.java
new file mode 100644 (file)
index 0000000..228156a
--- /dev/null
@@ -0,0 +1,48 @@
+package jalview.datamodel;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.junit.Test;
+
+public class ColumnSelectionTest
+{
+
+  @Test
+  public void testAddElement()
+  {
+    ColumnSelection cs = new ColumnSelection();
+    cs.addElement(2);
+    cs.addElement(5);
+    List<Integer> sel = cs.getSelected();
+    assertEquals(2, sel.size());
+    assertEquals(new Integer(2), sel.get(0));
+    assertEquals(new Integer(5), sel.get(1));
+  }
+
+  /**
+   * Test the remove method - in particular to verify that remove(int i) removes
+   * the element whose value is i, _NOT_ the i'th element.
+   */
+  @Test
+  public void testRemoveElement()
+  {
+    ColumnSelection cs = new ColumnSelection();
+    cs.addElement(2);
+    cs.addElement(5);
+
+    // removing elements not in the list has no effect
+    cs.removeElement(0);
+    cs.removeElement(1);
+    List<Integer> sel = cs.getSelected();
+    assertEquals(2, sel.size());
+    assertEquals(new Integer(2), sel.get(0));
+    assertEquals(new Integer(5), sel.get(1));
+
+    // removing an element in the list removes it
+    cs.removeElement(2);
+    assertEquals(1, sel.size());
+    assertEquals(new Integer(5), sel.get(0));
+  }
+}
diff --git a/test/jalview/datamodel/DBRefEntryTest.java b/test/jalview/datamodel/DBRefEntryTest.java
new file mode 100644 (file)
index 0000000..38c7f51
--- /dev/null
@@ -0,0 +1,44 @@
+package jalview.datamodel;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import jalview.util.MapList;
+
+import org.junit.Test;
+
+public class DBRefEntryTest
+{
+
+  /**
+   * Tests for the method that compares equality of reference (but not mapping)
+   */
+  @Test
+  public void testEqualRef()
+  {
+    DBRefEntry ref1 = new DBRefEntry("UNIPROT", "1", "V71633");
+    assertTrue(ref1.equalRef(ref1));
+    assertFalse(ref1.equalRef(null));
+
+    // comparison is not case sensitive
+    DBRefEntry ref2 = new DBRefEntry("uniprot", "1", "v71633");
+    assertTrue(ref1.equalRef(ref2));
+    assertTrue(ref2.equalRef(ref1));
+
+    // source, version and accessionid must match
+    assertFalse(ref1.equalRef(new DBRefEntry("UNIPRO", "1", "V71633")));
+    assertFalse(ref1.equalRef(new DBRefEntry("UNIPROT", "2", "V71633")));
+    assertFalse(ref1.equalRef(new DBRefEntry("UNIPROT", "1", "V71632")));
+
+    // presence of or differences in mappings are ignored
+    ref1.setMap(new Mapping(new MapList(new int[]
+    { 1, 3 }, new int[]
+    { 1, 1 }, 3, 1)));
+    assertTrue(ref1.equalRef(ref2));
+    assertTrue(ref2.equalRef(ref1));
+    ref1.setMap(new Mapping(new MapList(new int[]
+    { 1, 6 }, new int[]
+    { 1, 2 }, 3, 1)));
+    assertTrue(ref1.equalRef(ref2));
+    assertTrue(ref2.equalRef(ref1));
+  }
+}
diff --git a/test/jalview/datamodel/SearchResultsTest.java b/test/jalview/datamodel/SearchResultsTest.java
new file mode 100644 (file)
index 0000000..15b0314
--- /dev/null
@@ -0,0 +1,24 @@
+package jalview.datamodel;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class SearchResultsTest
+{
+
+  @Test
+  public void testToString()
+  {
+    SequenceI seq = new Sequence("", "abcdefghijklm");
+    SearchResults sr = new SearchResults();
+    sr.addResult(seq, 1, 1);
+    assertEquals("a", sr.toString());
+    sr.addResult(seq, 3, 5);
+    assertEquals("acde", sr.toString());
+
+    seq = new Sequence("", "pqrstuvwxy");
+    sr.addResult(seq, 6, 7);
+    assertEquals("acdeuv", sr.toString());
+  }
+}
index 40476a0..857ee84 100644 (file)
@@ -5,6 +5,7 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import java.util.Arrays;
 import java.util.List;
 
 import org.junit.Before;
@@ -135,4 +136,177 @@ public class SequenceTest
     assertSame(annotation2, anns[1]);
 
   }
+
+  @Test
+  public void testGetStartGetEnd()
+  {
+    SequenceI seq = new Sequence("test", "ABCDEF");
+    assertEquals(1, seq.getStart());
+    assertEquals(6, seq.getEnd());
+
+    seq = new Sequence("test", "--AB-C-DEF--");
+    assertEquals(1, seq.getStart());
+    assertEquals(6, seq.getEnd());
+
+    seq = new Sequence("test", "----");
+    assertEquals(1, seq.getStart());
+    assertEquals(0, seq.getEnd()); // ??
+  }
+
+  /**
+   * Tests for the method that returns an alignment column position (base 1) for
+   * a given sequence position (base 1).
+   */
+  @Test
+  public void testFindIndex()
+  {
+    SequenceI seq = new Sequence("test", "ABCDEF");
+    assertEquals(0, seq.findIndex(0));
+    assertEquals(1, seq.findIndex(1));
+    assertEquals(5, seq.findIndex(5));
+    assertEquals(6, seq.findIndex(6));
+    assertEquals(6, seq.findIndex(9));
+
+    seq = new Sequence("test", "-A--B-C-D-E-F--");
+    assertEquals(2, seq.findIndex(1));
+    assertEquals(5, seq.findIndex(2));
+    assertEquals(7, seq.findIndex(3));
+
+    // before start returns 0
+    assertEquals(0, seq.findIndex(0));
+    assertEquals(0, seq.findIndex(-1));
+
+    // beyond end returns last residue column
+    assertEquals(13, seq.findIndex(99));
+
+  }
+
+  /**
+   * Tests for the method that returns a dataset sequence position (base 1) for
+   * an aligned column position (base 0).
+   */
+  @Test
+  public void testFindPosition()
+  {
+    SequenceI seq = new Sequence("test", "ABCDEF");
+    assertEquals(1, seq.findPosition(0));
+    assertEquals(6, seq.findPosition(5));
+    // assertEquals(-1, seq.findPosition(6)); // fails
+
+    seq = new Sequence("test", "AB-C-D--");
+    assertEquals(1, seq.findPosition(0));
+    assertEquals(2, seq.findPosition(1));
+    // gap position 'finds' residue to the right (not the left as per javadoc)
+    assertEquals(3, seq.findPosition(2));
+    assertEquals(3, seq.findPosition(3));
+    assertEquals(4, seq.findPosition(4));
+    assertEquals(4, seq.findPosition(5));
+    // returns 1 more than sequence length if off the end ?!?
+    assertEquals(5, seq.findPosition(6));
+    assertEquals(5, seq.findPosition(7));
+
+    seq = new Sequence("test", "--AB-C-DEF--");
+    assertEquals(1, seq.findPosition(0));
+    assertEquals(1, seq.findPosition(1));
+    assertEquals(1, seq.findPosition(2));
+    assertEquals(2, seq.findPosition(3));
+    assertEquals(3, seq.findPosition(4));
+    assertEquals(3, seq.findPosition(5));
+    assertEquals(4, seq.findPosition(6));
+    assertEquals(4, seq.findPosition(7));
+    assertEquals(5, seq.findPosition(8));
+    assertEquals(6, seq.findPosition(9));
+    assertEquals(7, seq.findPosition(10));
+    assertEquals(7, seq.findPosition(11));
+  }
+
+  @Test
+  public void testDeleteChars()
+  {
+    SequenceI seq = new Sequence("test", "ABCDEF");
+    assertEquals(1, seq.getStart());
+    assertEquals(6, seq.getEnd());
+    seq.deleteChars(2, 3);
+    assertEquals("ABDEF", seq.getSequenceAsString());
+    assertEquals(1, seq.getStart());
+    assertEquals(5, seq.getEnd());
+
+    seq = new Sequence("test", "ABCDEF");
+    seq.deleteChars(0, 2);
+    assertEquals("CDEF", seq.getSequenceAsString());
+    assertEquals(3, seq.getStart());
+    assertEquals(6, seq.getEnd());
+  }
+
+  @Test
+  public void testInsertCharAt()
+  {
+    // non-static methods:
+    SequenceI seq = new Sequence("test", "ABCDEF");
+    seq.insertCharAt(0, 'z');
+    assertEquals("zABCDEF", seq.getSequenceAsString());
+    seq.insertCharAt(2, 2, 'x');
+    assertEquals("zAxxBCDEF", seq.getSequenceAsString());
+    // for static method see StringUtilsTest
+  }
+
+  /**
+   * Test the method that returns an array of aligned sequence positions where
+   * the array index is the data sequence position (both base 0).
+   */
+  @Test
+  public void testGapMap()
+  {
+    SequenceI seq = new Sequence("test", "-A--B-CD-E--F-");
+    seq.createDatasetSequence();
+    assertEquals("[1, 4, 6, 7, 9, 12]", Arrays.toString(seq.gapMap()));
+  }
+
+  /**
+   * Test the method that gets sequence features, either from the sequence or
+   * its dataset.
+   */
+  @Test
+  public void testGetSequenceFeatures()
+  {
+    SequenceI seq = new Sequence("test", "GATCAT");
+    seq.createDatasetSequence();
+
+    assertNull(seq.getSequenceFeatures());
+
+    /*
+     * SequenceFeature on sequence
+     */
+    SequenceFeature sf = new SequenceFeature();
+    seq.addSequenceFeature(sf);
+    SequenceFeature[] sfs = seq.getSequenceFeatures();
+    assertEquals(1, sfs.length);
+    assertSame(sf, sfs[0]);
+
+    /*
+     * SequenceFeature on sequence and dataset sequence; returns that on
+     * sequence
+     */
+    SequenceFeature sf2 = new SequenceFeature();
+    seq.getDatasetSequence().addSequenceFeature(sf2);
+    sfs = seq.getSequenceFeatures();
+    assertEquals(1, sfs.length);
+    assertSame(sf, sfs[0]);
+
+    /*
+     * SequenceFeature on dataset sequence only
+     */
+    seq.setSequenceFeatures(null);
+    sfs = seq.getSequenceFeatures();
+    assertEquals(1, sfs.length);
+    assertSame(sf2, sfs[0]);
+
+    /*
+     * Corrupt case - no SequenceFeature, dataset's dataset is the original
+     * sequence. Test shows no infinite loop results.
+     */
+    seq.getDatasetSequence().setSequenceFeatures(null);
+    seq.getDatasetSequence().setDatasetSequence(seq); // loop!
+    assertNull(seq.getSequenceFeatures());
+  }
 }
index 6085e03..3365c52 100644 (file)
 package jalview.ext.paradise;
 
 import static org.junit.Assert.assertTrue;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SequenceI;
-import jalview.io.FastaFile;
-import jalview.io.FormatAdapter;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -35,9 +31,13 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import MCview.PDBfile;
-
 import compbio.util.FileUtil;
 
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.io.FastaFile;
+import jalview.io.FormatAdapter;
+
 public class TestAnnotate3D
 {
 
@@ -124,11 +124,12 @@ public class TestAnnotate3D
         sb.append(line + "\n");
       }
       assertTrue("No data returned by Annotate3D", sb.length() > 0);
-      AlignmentI al = new FormatAdapter().readFile(sb.toString(),
+      final String lines = sb.toString();
+      AlignmentI al = new FormatAdapter().readFile(lines,
               FormatAdapter.PASTE, "RNAML");
       if (al == null || al.getHeight() == 0)
       {
-        System.out.println(sb.toString());
+        System.out.println(lines);
       }
       assertTrue("No alignment returned.", al != null);
       assertTrue("No sequences in returned alignment.", al.getHeight() > 0);
@@ -141,7 +142,8 @@ public class TestAnnotate3D
             String sq_ = new String(sq.getSequence()).toLowerCase();
             for (SequenceI _struseq : pdbf.getSeqsAsArray())
             {
-              if (new String(_struseq.getSequence()).toLowerCase().equals(
+              final String lowerCase = new String(_struseq.getSequence()).toLowerCase();
+              if (lowerCase.equals(
                       sq_))
               {
                 struseq = _struseq;
index be4e5ea..ad4f997 100644 (file)
@@ -1,13 +1,14 @@
 package jalview.ext.rbvi.chimera;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertTrue;
 
-import java.util.Arrays;
 import java.util.Collection;
 
 import org.junit.Test;
 
-import ext.edu.ucsf.rbvi.strucviz2.*;
+import ext.edu.ucsf.rbvi.strucviz2.ChimeraManager;
+import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel;
+import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 
 public class ChimeraConnect
 {
@@ -15,9 +16,10 @@ public class ChimeraConnect
   @Test
   public void test()
   {
-    StructureManager csm; 
+    StructureManager csm;
             ext.edu.ucsf.rbvi.strucviz2.ChimeraManager cm = new ChimeraManager(csm = new ext.edu.ucsf.rbvi.strucviz2.StructureManager(true));
-    assertTrue("Couldn't launch chimera",cm.launchChimera(csm.getChimeraPaths()));
+    assertTrue("Couldn't launch chimera",
+            cm.launchChimera(StructureManager.getChimeraPaths()));
     int n=0;
     while (n++<100)
     {
index 858806b..adbf230 100644 (file)
@@ -1,6 +1,11 @@
 package jalview.ext.rbvi.chimera;
 
 import static org.junit.Assert.assertTrue;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
@@ -8,10 +13,6 @@ import jalview.gui.StructureViewer;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.FormatAdapter;
 
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
 public class JalviewChimeraView
 {
 
@@ -31,7 +32,13 @@ public class JalviewChimeraView
   @AfterClass
   public static void tearDownAfterClass() throws Exception
   {
+    try
+    {
     jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    } catch (Exception e)
+    {
+      // ignore NullPointerException thrown by JMol
+    }
 
   }
 
index 944ab9c..2b890d2 100644 (file)
@@ -3,6 +3,8 @@ package jalview.gui;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
+import jalview.bin.Cache;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
@@ -52,6 +54,12 @@ public class AnnotationChooserTest
   @Before
   public void setUp() throws IOException
   {
+    // pin down annotation sort order for test
+    Cache.applicationProperties.setProperty(Preferences.SORT_ANNOTATIONS,
+            SequenceAnnotationOrder.NONE.name());
+    Cache.applicationProperties.setProperty(
+            Preferences.SHOW_AUTOCALC_ABOVE, Boolean.TRUE.toString());
+
     AlignmentI al = new jalview.io.FormatAdapter().readFile(TEST_DATA,
             AppletFormatAdapter.PASTE, "FASTA");
     af = new AlignFrame(al, 700, 500);
@@ -490,6 +498,7 @@ public class AnnotationChooserTest
   @Test
   public void testSelectType_showForSelected()
   {
+    // sequences 1 and 2 have annotations IUPred and Jmol
     selectSequences(1, 2);
     testee = new AnnotationChooser(parentPanel);
     final Checkbox showCheckbox = (Checkbox) getComponent(testee, 1, 0, 0);
@@ -505,6 +514,7 @@ public class AnnotationChooserTest
     setSelected(selectedSequencesCheckbox, true);
     setSelected(hideCheckbox, true);
     setSelected(getTypeCheckbox("JMol"), true);
+
     assertTrue(anns[5].visible); // JMol for seq3
     assertFalse(anns[7].visible); // JMol for seq1
     // ...now show them...
@@ -738,8 +748,8 @@ 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)
+    assertTrue(anns[3].visible); // Beauty (not seq-related)
+    assertFalse(anns[4].visible); // IUPRED
     assertFalse(anns[5].visible); // JMol
     assertFalse(anns[6].visible); // IUPRED
     assertFalse(anns[7].visible); // JMol
diff --git a/test/jalview/gui/JvSwingUtilsTest.java b/test/jalview/gui/JvSwingUtilsTest.java
new file mode 100644 (file)
index 0000000..11e6ea5
--- /dev/null
@@ -0,0 +1,41 @@
+package jalview.gui;
+
+import static org.junit.Assert.assertEquals;
+
+import javax.swing.JScrollBar;
+
+import org.junit.Test;
+
+public class JvSwingUtilsTest
+{
+
+  @Test
+  public void testGetScrollBarProportion()
+  {
+    /*
+     * orientation, value, extent (width), min, max
+     */
+    JScrollBar sb = new JScrollBar(0, 125, 50, 0, 450);
+
+    /*
+     * operating range is 25 - 425 (400 wide) so value 125 is 100/400ths of this
+     * range
+     */
+    assertEquals(0.25f, JvSwingUtils.getScrollBarProportion(sb), 0.001f);
+  }
+
+  @Test
+  public void testGetScrollValueForProportion()
+  {
+    /*
+     * orientation, value, extent (width), min, max
+     */
+    JScrollBar sb = new JScrollBar(0, 125, 50, 0, 450);
+
+    /*
+     * operating range is 25 - 425 (400 wide) so value 125 is a quarter of this
+     * range
+     */
+    assertEquals(125, JvSwingUtils.getScrollValueForProportion(sb, 0.25f));
+  }
+}
diff --git a/test/jalview/gui/PDBSearchPanelTest.java b/test/jalview/gui/PDBSearchPanelTest.java
new file mode 100644 (file)
index 0000000..17ba85a
--- /dev/null
@@ -0,0 +1,50 @@
+package jalview.gui;
+
+import static org.junit.Assert.assertTrue;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JTextField;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PDBSearchPanelTest
+{
+
+  @Before
+  public void setUp() throws Exception
+  {
+  }
+
+  @After
+  public void tearDown() throws Exception
+  {
+  }
+
+  @Test
+  public void populateCmbSearchTargetOptionsTest()
+  {
+    PDBSearchPanel searchPanel = new PDBSearchPanel(null);
+    assertTrue(searchPanel.getCmbSearchTarget().getItemCount() > 0);
+    searchPanel.populateCmbSearchTargetOptions();
+  }
+
+  @Test
+  public void txt_search_ActionPerformedTest()
+  {
+    PDBSearchPanel searchPanel = new PDBSearchPanel(null);
+    JInternalFrame mainFrame = searchPanel.getMainFrame();
+    JTextField txt_search = searchPanel.getTxtSearch();
+
+    assertTrue(mainFrame.getTitle().length() == 20);
+    assertTrue(mainFrame.getTitle()
+            .equalsIgnoreCase("PDB Sequence Fetcher"));
+
+    txt_search.setText("ABC");
+
+    assertTrue(mainFrame.getTitle().length() > 20);
+    assertTrue(!mainFrame.getTitle().equalsIgnoreCase(
+            "PDB Sequence Fetcher"));
+  }
+}
diff --git a/test/jalview/gui/PaintRefresherTest.java b/test/jalview/gui/PaintRefresherTest.java
new file mode 100644 (file)
index 0000000..1da7c8c
--- /dev/null
@@ -0,0 +1,116 @@
+package jalview.gui;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.viewmodel.AlignmentViewport;
+
+import java.awt.Component;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JPanel;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PaintRefresherTest
+{
+  // TODO would prefer PaintRefresher to be a single rather than static
+  @Before
+  public void setUp()
+  {
+    PaintRefresher.components.clear();
+  }
+
+  @After
+  public void tearDown()
+  {
+    PaintRefresher.components.clear();
+  }
+
+  @Test
+  public void testRegister()
+  {
+    JPanel jp = new JPanel();
+    JPanel jp2 = new JPanel();
+    JPanel jp3 = new JPanel();
+    JPanel jp4 = new JPanel();
+    PaintRefresher.Register(jp, "22");
+    PaintRefresher.Register(jp, "22");
+    PaintRefresher.Register(jp2, "22");
+    PaintRefresher.Register(jp3, "33");
+    PaintRefresher.Register(jp3, "44");
+    PaintRefresher.Register(jp4, "44");
+
+    Map<String, List<Component>> registered = PaintRefresher.components;
+    assertEquals(3, registered.size());
+    assertEquals(2, registered.get("22").size());
+    assertEquals(1, registered.get("33").size());
+    assertEquals(2, registered.get("44").size());
+    assertTrue(registered.get("22").contains(jp));
+    assertTrue(registered.get("22").contains(jp2));
+    assertTrue(registered.get("33").contains(jp3));
+    assertTrue(registered.get("44").contains(jp3));
+    assertTrue(registered.get("44").contains(jp4));
+  }
+
+  @Test
+  public void testRemoveComponent()
+  {
+    Map<String, List<Component>> registered = PaintRefresher.components;
+    
+    // no error with an empty PaintRefresher
+    JPanel jp = new JPanel();
+    JPanel jp2 = new JPanel();
+    PaintRefresher.RemoveComponent(jp);
+    assertTrue(registered.isEmpty());
+
+    /*
+     * Add then remove one item
+     */
+    PaintRefresher.Register(jp, "11");
+    PaintRefresher.RemoveComponent(jp);
+    assertTrue(registered.isEmpty());
+
+    /*
+     * Add one item under two ids, then remove it. It is removed from both ids,
+     * and the now empty id is removed.
+     */
+    PaintRefresher.Register(jp, "11");
+    PaintRefresher.Register(jp, "22");
+    PaintRefresher.Register(jp2, "22");
+    PaintRefresher.RemoveComponent(jp);
+    // "11" is removed as now empty, only 22/jp2 left
+    assertEquals(1, registered.size());
+    assertEquals(1, registered.get("22").size());
+    assertTrue(registered.get("22").contains(jp2));
+  }
+
+  @Test
+  public void testGetAssociatedPanels()
+  {
+    SequenceI [] seqs = new SequenceI[]{new Sequence("", "ABC")};
+    Alignment al = new Alignment(seqs);
+
+    /*
+     * AlignFrame constructor has side-effects: AlignmentPanel is constructed,
+     * and SeqCanvas, IdPanel, AlignmentPanel are all registered under the
+     * sequence set id of the viewport.
+     */
+    AlignmentViewport av = new AlignViewport(al);
+    AlignFrame af = new AlignFrame(al, 4, 1);
+    AlignmentPanel ap1 = af.alignPanel;
+    AlignmentPanel[] panels = PaintRefresher.getAssociatedPanels(av
+            .getSequenceSetId());
+    assertEquals(1, panels.length);
+    assertSame(ap1, panels[0]);
+    
+    panels = PaintRefresher.getAssociatedPanels(av.getSequenceSetId() + 1);
+    assertEquals(0, panels.length);
+  }
+}
index f7b1482..f2248a8 100644 (file)
@@ -3,12 +3,6 @@ package jalview.gui;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SequenceI;
-import jalview.io.AppletFormatAdapter;
-import jalview.io.FormatAdapter;
-import jalview.util.MessageManager;
 
 import java.awt.Component;
 import java.io.IOException;
@@ -23,6 +17,12 @@ import javax.swing.JSeparator;
 import org.junit.Before;
 import org.junit.Test;
 
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.io.AppletFormatAdapter;
+import jalview.io.FormatAdapter;
+
 public class PopupMenuTest
 {
   // 4 sequences x 13 positions
@@ -66,12 +66,9 @@ public class PopupMenuTest
     List<SequenceI> seqs = new ArrayList<SequenceI>();
     testee.configureReferenceAnnotationsMenu(menu, seqs);
     assertFalse(menu.isEnabled());
-    assertEquals(
-            MessageManager.getString("label.add_reference_annotations"),
-            menu.getText());
     // now try null list
     menu.setEnabled(true);
-    testee.configureReferenceAnnotationsMenu(menu, seqs);
+    testee.configureReferenceAnnotationsMenu(menu, null);
     assertFalse(menu.isEnabled());
   }
 
diff --git a/test/jalview/gui/StructureChooserTest.java b/test/jalview/gui/StructureChooserTest.java
new file mode 100644 (file)
index 0000000..24e8bc9
--- /dev/null
@@ -0,0 +1,87 @@
+package jalview.gui;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import java.util.Vector;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class StructureChooserTest
+{
+  Sequence seq;
+
+  @Before
+  public void setUp() throws Exception
+  {
+    seq = new Sequence("Test_Seq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1, 26);
+    seq.setDatasetSequence(seq);
+    for (int x = 1; x < 5; x++)
+    {
+      DBRefEntry dbRef = new DBRefEntry();
+      dbRef.setAccessionId("XYZ_" + x);
+      seq.addDBRef(dbRef);
+    }
+
+    PDBEntry dbRef = new PDBEntry();
+    dbRef.setId("1tim");
+
+    Vector<PDBEntry> pdbIds = new Vector<PDBEntry>();
+    pdbIds.add(dbRef);
+
+    seq.setPDBId(pdbIds);
+  }
+
+  @After
+  public void tearDown() throws Exception
+  {
+    seq = null;
+  }
+
+
+
+  @Test
+  public void buildQueryTest()
+  {
+    assertEquals(
+            "1tim OR text:XYZ_1 OR text:XYZ_2 OR text:XYZ_3 OR text:XYZ_4",
+            StructureChooser.buildQuery(seq));
+  }
+
+  @Test
+  public void populateFilterComboBoxTest()
+  {
+    SequenceI[] selectedSeqs = new SequenceI[]
+    { seq };
+    StructureChooser sc = new StructureChooser(selectedSeqs, seq,
+            null);
+    sc.populateFilterComboBox();
+    int optionsSize = sc.getCmbFilterOption().getItemCount();
+    assertEquals(2, optionsSize); // if structures are not discovered then don't
+                                  // populate filter options
+
+    sc.setStructuresDiscovered(true);
+    sc.populateFilterComboBox();
+    optionsSize = sc.getCmbFilterOption().getItemCount();
+    assertTrue(optionsSize > 2); // if structures are found, filter options
+                                 // should be populated
+  }
+
+  @Test
+  public void fetchStructuresInfoTest()
+  {
+    SequenceI[] selectedSeqs = new SequenceI[]
+    { seq };
+    StructureChooser sc = new StructureChooser(selectedSeqs, seq, null);
+    sc.fetchStructuresMetaData();
+    assertTrue(sc.getDiscoveredStructuresSet() != null);
+    assertTrue(sc.getDiscoveredStructuresSet().size() > 0);
+
+  }
+}
index 30b6d55..29d5549 100644 (file)
@@ -7,8 +7,12 @@ import static org.junit.Assert.assertTrue;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
 
 import java.io.File;
 import java.util.Vector;
@@ -73,6 +77,48 @@ public class AnnotatedPDBFileInputTest
     }
   }
 
+  /**
+   * Check sequence features have been added
+   */
+  @Test
+  public void checkPDBSequenceFeatures()
+  {
+    StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
+    StructureMapping[] mappings = ssm.getMapping("1gaq");
+    // suspect we really want to make assertions on sequence features
+    // in these mappings' sequencess
+    /*
+     * 1GAQ/A
+     */
+    SequenceFeature[] sf = al.getSequenceAt(0).getSequenceFeatures();
+    assertEquals(296, sf.length);
+    assertEquals("RESNUM", sf[0].getType());
+    assertEquals("GLU:19 1gaqA", sf[0].getDescription());
+    assertEquals("RESNUM", sf[295].getType());
+    assertEquals("TYR:314 1gaqA", sf[295].getDescription());
+
+    /*
+     * 1GAQ/B
+     */
+    sf = al.getSequenceAt(1).getSequenceFeatures();
+    assertEquals(98, sf.length);
+    assertEquals("RESNUM", sf[0].getType());
+    assertEquals("ALA:1 1gaqB", sf[0].getDescription());
+    assertEquals("RESNUM", sf[97].getType());
+    assertEquals("ALA:98 1gaqB", sf[97].getDescription());
+
+    /*
+     * 1GAQ/C
+     */
+    sf = al.getSequenceAt(2).getSequenceFeatures();
+    assertEquals(296, sf.length);
+    assertEquals("RESNUM", sf[0].getType());
+    assertEquals("GLU:19 1gaqC", sf[0].getDescription());
+    assertEquals("RESNUM", sf[295].getType());
+    assertEquals("TYR:314 1gaqC", sf[295].getDescription());
+  }
+
   @Test
   public void checkAnnotationWiring()
   {
index be228b8..59a5b68 100644 (file)
@@ -1,13 +1,15 @@
 package jalview.io;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.fail;
 
+import org.junit.Ignore;
 import org.junit.Test;
 
 public class HtmlFileTest
 {
 
   @Test
+  @Ignore
   public void test()
   {
     fail("Not yet implemented");
index 0e61959..8b83934 100644 (file)
@@ -23,6 +23,7 @@ package jalview.io;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.ViewStyleI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -34,6 +35,7 @@ import jalview.schemes.ColourSchemeI;
 import java.io.File;
 
 import org.junit.AfterClass;
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -239,13 +241,13 @@ public class Jalview2xmlTests
   @Test
   public void gatherViewsHere() throws Exception
   {
-    int origCount = Desktop.getAlignframes() == null ? 0 : Desktop
-            .getAlignframes().length;
+    int origCount = Desktop.getAlignFrames() == null ? 0 : Desktop
+            .getAlignFrames().length;
     AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
             "examples/exampleFile_2_7.jar", FormatAdapter.FILE);
     assertTrue("Didn't read in the example file correctly.", af != null);
     assertTrue("Didn't gather the views in the example file.",
-            Desktop.getAlignframes().length == 1 + origCount);
+            Desktop.getAlignFrames().length == 1 + origCount);
 
   }
 
@@ -306,4 +308,37 @@ public class Jalview2xmlTests
     }
     
   }
+
+  @Test
+  public void testCopyViewSettings() throws Exception
+  {
+    AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
+            "examples/exampleFile_2_7.jar", FormatAdapter.FILE);
+    Assert.assertTrue("Didn't read in the example file correctly.", af != null);
+    AlignmentViewPanel sps = null, groups = null;
+    for (AlignmentViewPanel ap : af.alignPanel.alignFrame.getAlignPanels())
+    {
+      if ("Spinach Feredoxin Structure".equals(ap.getViewName()))
+      {
+        sps = ap;
+      }
+      if (ap.getViewName().contains("MAFFT"))
+      {
+        groups = ap;
+      }
+    }
+    assertTrue("Couldn't find the structure view", sps != null);
+    assertTrue("Couldn't find the MAFFT view", groups != null);
+
+    ViewStyleI structureStyle = sps.getAlignViewport().getViewStyle();
+    ViewStyleI groupStyle = groups.getAlignViewport().getViewStyle();
+    Assert.assertFalse(structureStyle.sameStyle(groupStyle));
+
+    groups.getAlignViewport().setViewStyle(structureStyle);
+    Assert.assertFalse(groupStyle.sameStyle(groups.getAlignViewport()
+            .getViewStyle()));
+    Assert.assertTrue(structureStyle.sameStyle(groups.getAlignViewport()
+            .getViewStyle()));
+
+  }
 }
diff --git a/test/jalview/io/pdb_request_json_error.txt b/test/jalview/io/pdb_request_json_error.txt
new file mode 100644 (file)
index 0000000..de14bf1
--- /dev/null
@@ -0,0 +1,17 @@
+{
+    "responseHeader": {
+        "status": 400,
+        "QTime": 0,
+        "params": {
+            "sort": "",
+            "fl": "pdb_id",
+            "q": "text:abc OR text:go:abc AND molecule_sequence:['' TO *]",
+            "wt": "json",
+            "rows": "100"
+        }
+    },
+    "error": {
+        "msg": "org.apache.solr.search.SyntaxError: Cannot parse 'text:abc OR text:go:abc AND molecule_sequence:['' TO *]': Encountered \" \":\" \": \"\" at line 1, column 19.",
+        "code": 400
+    }
+}
\ No newline at end of file
diff --git a/test/jalview/io/pdb_response_json.txt b/test/jalview/io/pdb_response_json.txt
new file mode 100644 (file)
index 0000000..e3f3583
--- /dev/null
@@ -0,0 +1,176 @@
+{
+    "responseHeader": {
+        "status": 0,
+        "QTime": 0,
+        "params": {
+            "sort": "",
+            "fl": "molecule_type,pdb_id,genus,gene_name,title",
+            "q": "text:abc AND molecule_sequence:['' TO *]",
+            "wt": "json",
+            "rows": "100"
+        }
+    },
+    "response": {
+        "numFound": 931,
+        "start": 0,
+        "docs": [
+            {
+                "pdb_id": "3qf4",
+                "title": "Crystal structure of a heterodimeric ABC transporter in its inward-facing conformation",
+                "genus": [
+                    "Thermotoga"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "THEMA_03290",
+                    "Tmari_0285",
+                    "TM_0287"
+                ]
+            },
+            {
+                "pdb_id": "4wbs",
+                "title": "Crystal structure of an ABC transporter related protein from Burkholderia phymatum",
+                "genus": [
+                    "Burkholderia"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "Bphy_0327"
+                ]
+            },
+            {
+                "pdb_id": "1hn0",
+                "title": "CRYSTAL STRUCTURE OF CHONDROITIN ABC LYASE I FROM PROTEUS VULGARIS AT 1.9 ANGSTROMS RESOLUTION",
+                "genus": [
+                    "Proteus"
+                ],
+                "molecule_type": "Protein"
+            },
+            {
+                "pdb_id": "4a82",
+                "title": "Fitted model of staphylococcus aureus sav1866 model ABC transporter in the human cystic fibrosis transmembrane conductance regulator volume map EMD-1966.",
+                "genus": [
+                    "Homo"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "SAV1866"
+                ]
+            },
+            {
+                "pdb_id": "3nh6",
+                "title": "Nucleotide Binding Domain of human ABCB6 (apo structure)",
+                "genus": [
+                    "Homo"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "ABCB6",
+                    "UMAT",
+                    "MTABC3",
+                    "PRP"
+                ]
+            },
+            {
+                "pdb_id": "2nq2",
+                "title": "An inward-facing conformation of a putative metal-chelate type ABC transporter.",
+                "genus": [
+                    "Haemophilus"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "HI_1471"
+                ]
+            },
+            {
+                "pdb_id": "3s4u",
+                "title": "Crystal structure of open, unliganded E. coli PhnD H157A",
+                "genus": [
+                    "Escherichia"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "phnD",
+                    "UTI89_C4699"
+                ]
+            },
+            {
+                "pdb_id": "4q4a",
+                "title": "Improved model of AMP-PNP bound TM287/288",
+                "genus": [
+                    "Thermotoga"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "TM_0288"
+                ]
+            },
+            {
+                "pdb_id": "1oxs",
+                "title": "Crystal structure of GlcV, the ABC-ATPase of the glucose ABC transporter from Sulfolobus solfataricus",
+                "genus": [
+                    "Sulfolobus"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "SSO2850"
+                ]
+            },
+            {
+                "pdb_id": "4q4j",
+                "title": "Structure of crosslinked TM287/288_S498C_S520C mutant",
+                "genus": [
+                    "Thermotoga"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "TM_0288"
+                ]
+            },
+            {
+                "pdb_id": "1oxt",
+                "title": "Crystal structure of GlcV, the ABC-ATPase of the glucose ABC transporter from Sulfolobus solfataricus",
+                "genus": [
+                    "Sulfolobus"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "SSO2850"
+                ]
+            },
+            {
+                "pdb_id": "4q4h",
+                "title": "TM287/288 in its apo state",
+                "genus": [
+                    "Thermotoga"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "TM_0288"
+                ]
+            },
+            {
+                "pdb_id": "3qf4",
+                "title": "Crystal structure of a heterodimeric ABC transporter in its inward-facing conformation",
+                "genus": [
+                    "Thermotoga"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "TM_0288"
+                ]
+            },
+            {
+                "pdb_id": "2fgk",
+                "title": "Crystal structure of the ABC-cassette E631Q mutant of HlyB with bound ATP",
+                "genus": [
+                    "Escherichia"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "hlyB"
+                ]
+            }
+        ]
+    }
+}
\ No newline at end of file
index 6791d7c..42f17fd 100644 (file)
  */
 package jalview.schemes;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertTrue;
 
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.ColumnSelection;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceI;
-
-import java.io.IOException;
 import java.util.Map;
-import java.util.Vector;
 
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class DnaCodonTests
@@ -71,13 +62,10 @@ public class DnaCodonTests
     // note - this test will be removed once the old codon table (including
     // Vectors) is removed
     String additional = "", failtrans = "", differentTr = "";
-    for (Object aa : ResidueProperties.codonHash.keySet())
+    for (String amacid : ResidueProperties.codonHash.keySet())
     {
-      String amacid = (String) aa;
-      for (Object codons : ((Vector) ResidueProperties.codonHash
-              .get(amacid)))
+      for (String codon : ResidueProperties.codonHash.get(amacid))
       {
-        String codon = (String) codons;
         String trans = ResidueProperties.codonTranslate(codon);
         String oldtrans = ResidueProperties._codonTranslate(codon);
         if (trans == null)
diff --git a/test/jalview/schemes/ResiduePropertiesTest.java b/test/jalview/schemes/ResiduePropertiesTest.java
new file mode 100644 (file)
index 0000000..b976e44
--- /dev/null
@@ -0,0 +1,174 @@
+package jalview.schemes;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+
+public class ResiduePropertiesTest
+{
+
+  /**
+   * Test 'standard' codon translations (no ambiguity codes)
+   */
+  @Test
+  public void testCodonTranslate()
+  {
+    // standard translation table order column 1/2/3/4
+    assertEquals("F", ResidueProperties.codonTranslate("TTT"));
+    assertEquals("F", ResidueProperties.codonTranslate("TTC"));
+    assertEquals("L", ResidueProperties.codonTranslate("TTA"));
+    assertEquals("L", ResidueProperties.codonTranslate("TTG"));
+    assertEquals("L", ResidueProperties.codonTranslate("CTT"));
+    assertEquals("L", ResidueProperties.codonTranslate("CTC"));
+    assertEquals("L", ResidueProperties.codonTranslate("CTA"));
+    assertEquals("L", ResidueProperties.codonTranslate("CTG"));
+    assertEquals("I", ResidueProperties.codonTranslate("ATT"));
+    assertEquals("I", ResidueProperties.codonTranslate("ATC"));
+    assertEquals("I", ResidueProperties.codonTranslate("ATA"));
+    assertEquals("M", ResidueProperties.codonTranslate("ATG"));
+    assertEquals("V", ResidueProperties.codonTranslate("GTT"));
+    assertEquals("V", ResidueProperties.codonTranslate("GTC"));
+    assertEquals("V", ResidueProperties.codonTranslate("GTA"));
+    assertEquals("V", ResidueProperties.codonTranslate("GTG"));
+    assertEquals("S", ResidueProperties.codonTranslate("TCT"));
+    assertEquals("S", ResidueProperties.codonTranslate("TCC"));
+    assertEquals("S", ResidueProperties.codonTranslate("TCA"));
+    assertEquals("S", ResidueProperties.codonTranslate("TCG"));
+    assertEquals("P", ResidueProperties.codonTranslate("CCT"));
+    assertEquals("P", ResidueProperties.codonTranslate("CCC"));
+    assertEquals("P", ResidueProperties.codonTranslate("CCA"));
+    assertEquals("P", ResidueProperties.codonTranslate("CCG"));
+    assertEquals("T", ResidueProperties.codonTranslate("ACT"));
+    assertEquals("T", ResidueProperties.codonTranslate("ACC"));
+    assertEquals("T", ResidueProperties.codonTranslate("ACA"));
+    assertEquals("T", ResidueProperties.codonTranslate("ACG"));
+    assertEquals("A", ResidueProperties.codonTranslate("GCT"));
+    assertEquals("A", ResidueProperties.codonTranslate("GCC"));
+    assertEquals("A", ResidueProperties.codonTranslate("GCA"));
+    assertEquals("A", ResidueProperties.codonTranslate("GCG"));
+    assertEquals("Y", ResidueProperties.codonTranslate("TAT"));
+    assertEquals("Y", ResidueProperties.codonTranslate("TAC"));
+    assertEquals("STOP", ResidueProperties.codonTranslate("TAA"));
+    assertEquals("STOP", ResidueProperties.codonTranslate("TAG"));
+    assertEquals("H", ResidueProperties.codonTranslate("CAT"));
+    assertEquals("H", ResidueProperties.codonTranslate("CAC"));
+    assertEquals("Q", ResidueProperties.codonTranslate("CAA"));
+    assertEquals("Q", ResidueProperties.codonTranslate("CAG"));
+    assertEquals("N", ResidueProperties.codonTranslate("AAT"));
+    assertEquals("N", ResidueProperties.codonTranslate("AAC"));
+    assertEquals("K", ResidueProperties.codonTranslate("AAA"));
+    assertEquals("K", ResidueProperties.codonTranslate("AAG"));
+    assertEquals("D", ResidueProperties.codonTranslate("GAT"));
+    assertEquals("D", ResidueProperties.codonTranslate("GAC"));
+    assertEquals("E", ResidueProperties.codonTranslate("GAA"));
+    assertEquals("E", ResidueProperties.codonTranslate("GAG"));
+    assertEquals("C", ResidueProperties.codonTranslate("TGT"));
+    assertEquals("C", ResidueProperties.codonTranslate("TGC"));
+    assertEquals("STOP", ResidueProperties.codonTranslate("TGA"));
+    assertEquals("W", ResidueProperties.codonTranslate("TGG"));
+    assertEquals("R", ResidueProperties.codonTranslate("CGT"));
+    assertEquals("R", ResidueProperties.codonTranslate("CGC"));
+    assertEquals("R", ResidueProperties.codonTranslate("CGA"));
+    assertEquals("R", ResidueProperties.codonTranslate("CGG"));
+    assertEquals("S", ResidueProperties.codonTranslate("AGT"));
+    assertEquals("S", ResidueProperties.codonTranslate("AGC"));
+    assertEquals("R", ResidueProperties.codonTranslate("AGA"));
+    assertEquals("R", ResidueProperties.codonTranslate("AGG"));
+    assertEquals("G", ResidueProperties.codonTranslate("GGT"));
+    assertEquals("G", ResidueProperties.codonTranslate("GGC"));
+    assertEquals("G", ResidueProperties.codonTranslate("GGA"));
+    assertEquals("G", ResidueProperties.codonTranslate("GGG"));
+  }
+
+  /**
+   * Test a sample of codon translations involving ambiguity codes. Should
+   * return a protein value where the ambiguity does not affect the translation.
+   */
+  @Test
+  public void testCodonTranslate_ambiguityCodes()
+  {
+    // Y is C or T
+    assertEquals("C", ResidueProperties.codonTranslate("TGY"));
+    // Phenylalanine first base variation
+    assertEquals("L", ResidueProperties.codonTranslate("YTA"));
+
+    // W is A or T
+    assertEquals("L", ResidueProperties.codonTranslate("CTW"));
+    assertNull(ResidueProperties.codonTranslate("TTW"));
+
+    // S is G or C
+    assertEquals("G", ResidueProperties.codonTranslate("GGS"));
+    assertNull(ResidueProperties.codonTranslate("ATS"));
+
+    // K is T or G
+    assertEquals("S", ResidueProperties.codonTranslate("TCK"));
+    assertNull(ResidueProperties.codonTranslate("ATK"));
+
+    // M is C or A
+    assertEquals("T", ResidueProperties.codonTranslate("ACM"));
+    // Arginine first base variation
+    assertEquals("R", ResidueProperties.codonTranslate("MGA"));
+    assertEquals("R", ResidueProperties.codonTranslate("MGG"));
+    assertNull(ResidueProperties.codonTranslate("TAM"));
+
+    // D is A, G or T
+    assertEquals("P", ResidueProperties.codonTranslate("CCD"));
+    assertNull(ResidueProperties.codonTranslate("AAD"));
+
+    // V is A, C or G
+    assertEquals("V", ResidueProperties.codonTranslate("GTV"));
+    assertNull(ResidueProperties.codonTranslate("TTV"));
+
+    // H is A, C or T
+    assertEquals("A", ResidueProperties.codonTranslate("GCH"));
+    assertEquals("I", ResidueProperties.codonTranslate("ATH"));
+    assertNull(ResidueProperties.codonTranslate("AGH"));
+
+    // B is C, G or T
+    assertEquals("P", ResidueProperties.codonTranslate("CCB"));
+    assertNull(ResidueProperties.codonTranslate("TAB"));
+
+    // R is A or G
+    // additional tests for JAL-1685 (resolved)
+    assertEquals("L", ResidueProperties.codonTranslate("CTR"));
+    assertEquals("V", ResidueProperties.codonTranslate("GTR"));
+    assertEquals("S", ResidueProperties.codonTranslate("TCR"));
+    assertEquals("P", ResidueProperties.codonTranslate("CCR"));
+    assertEquals("T", ResidueProperties.codonTranslate("ACR"));
+    assertEquals("A", ResidueProperties.codonTranslate("GCR"));
+    assertEquals("R", ResidueProperties.codonTranslate("CGR"));
+    assertEquals("G", ResidueProperties.codonTranslate("GGR"));
+    assertEquals("R", ResidueProperties.codonTranslate("AGR"));
+    assertEquals("E", ResidueProperties.codonTranslate("GAR"));
+    assertEquals("K", ResidueProperties.codonTranslate("AAR"));
+    assertEquals("L", ResidueProperties.codonTranslate("TTR"));
+    assertEquals("Q", ResidueProperties.codonTranslate("CAR"));
+    assertEquals("STOP", ResidueProperties.codonTranslate("TAR"));
+    assertEquals("STOP", ResidueProperties.codonTranslate("TRA"));
+    // Arginine first and third base ambiguity
+    assertEquals("R", ResidueProperties.codonTranslate("MGR"));
+    assertNull(ResidueProperties.codonTranslate("ATR"));
+
+    // N is any base; 8 proteins accept any base in 3rd position
+    assertEquals("L", ResidueProperties.codonTranslate("CTN"));
+    assertEquals("V", ResidueProperties.codonTranslate("GTN"));
+    assertEquals("S", ResidueProperties.codonTranslate("TCN"));
+    assertEquals("P", ResidueProperties.codonTranslate("CCN"));
+    assertEquals("T", ResidueProperties.codonTranslate("ACN"));
+    assertEquals("A", ResidueProperties.codonTranslate("GCN"));
+    assertEquals("R", ResidueProperties.codonTranslate("CGN"));
+    assertEquals("G", ResidueProperties.codonTranslate("GGN"));
+    assertNull(ResidueProperties.codonTranslate("ATN"));
+    assertNull(ResidueProperties.codonTranslate("ANT"));
+    assertNull(ResidueProperties.codonTranslate("NAT"));
+    assertNull(ResidueProperties.codonTranslate("ANN"));
+    assertNull(ResidueProperties.codonTranslate("NNA"));
+    assertNull(ResidueProperties.codonTranslate("NNN"));
+
+    // some random stuff
+    assertNull(ResidueProperties.codonTranslate("YWB"));
+    assertNull(ResidueProperties.codonTranslate("VHD"));
+    assertNull(ResidueProperties.codonTranslate("WSK"));
+  }
+}
index 716755b..cc8138c 100644 (file)
@@ -3,6 +3,13 @@ package jalview.structure;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import MCview.PDBfile;
+
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.Sequence;
@@ -11,11 +18,6 @@ import jalview.gui.AlignFrame;
 import jalview.io.FileLoader;
 import jalview.io.FormatAdapter;
 
-import org.junit.Assert;
-import org.junit.Test;
-
-import MCview.PDBfile;
-
 public class Mapping
 {
 
@@ -27,6 +29,7 @@ public class Mapping
    * msd numbering, not pdb res numbering.
    */
   @Test
+  @Ignore
   public void pdbEntryPositionMap() throws Exception
   {
     fail("This test intentionally left to fail");
@@ -109,6 +112,7 @@ public class Mapping
   }
 
   @Test
+  @Ignore
   public void testPDBentryMapping() throws Exception
   {
     fail("This test intentionally left to fail");
diff --git a/test/jalview/structure/StructureSelectionManagerTest.java b/test/jalview/structure/StructureSelectionManagerTest.java
new file mode 100644 (file)
index 0000000..487ef2c
--- /dev/null
@@ -0,0 +1,175 @@
+package jalview.structure;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import jalview.datamodel.AlignedCodonFrame;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class StructureSelectionManagerTest
+{
+  private StructureSelectionManager ssm;
+
+  @Before
+  public void setUp()
+  {
+    ssm = new StructureSelectionManager();
+  }
+
+  @Test
+  public void testAddMapping()
+  {
+    AlignedCodonFrame acf1 = new AlignedCodonFrame();
+    AlignedCodonFrame acf2 = new AlignedCodonFrame();
+
+    /*
+     * One mapping only.
+     */
+    ssm.addMapping(acf1);
+    assertEquals(1, ssm.seqmappings.size());
+    assertTrue(ssm.seqmappings.contains(acf1));
+    assertEquals(1, ssm.seqMappingRefCounts.size());
+    assertEquals(1, ssm.seqMappingRefCounts.get(acf1).intValue());
+
+    /*
+     * A second mapping.
+     */
+    ssm.addMapping(acf2);
+    assertEquals(2, ssm.seqmappings.size());
+    assertTrue(ssm.seqmappings.contains(acf1));
+    assertTrue(ssm.seqmappings.contains(acf2));
+    assertEquals(2, ssm.seqMappingRefCounts.size());
+    assertEquals(1, ssm.seqMappingRefCounts.get(acf1).intValue());
+    assertEquals(1, ssm.seqMappingRefCounts.get(acf2).intValue());
+
+    /*
+     * A second reference to the first mapping.
+     */
+    ssm.addMapping(acf1);
+    assertEquals(2, ssm.seqmappings.size());
+    assertTrue(ssm.seqmappings.contains(acf1));
+    assertTrue(ssm.seqmappings.contains(acf2));
+    assertEquals(2, ssm.seqMappingRefCounts.size());
+    assertEquals(2, ssm.seqMappingRefCounts.get(acf1).intValue());
+    assertEquals(1, ssm.seqMappingRefCounts.get(acf2).intValue());
+  }
+
+  @Test
+  public void testAddMappings()
+  {
+    AlignedCodonFrame acf1 = new AlignedCodonFrame();
+    AlignedCodonFrame acf2 = new AlignedCodonFrame();
+    AlignedCodonFrame acf3 = new AlignedCodonFrame();
+
+    Set<AlignedCodonFrame> set1 = new HashSet<AlignedCodonFrame>();
+    set1.add(acf1);
+    set1.add(acf2);
+    Set<AlignedCodonFrame> set2 = new HashSet<AlignedCodonFrame>();
+    set2.add(acf2);
+    set2.add(acf3);
+
+    /*
+     * Adding both sets adds acf2 twice and acf1 and acf3 once each.
+     */
+    ssm.addMappings(set1);
+    ssm.addMappings(set2);
+
+    assertEquals(3, ssm.seqmappings.size());
+    assertTrue(ssm.seqmappings.contains(acf1));
+    assertTrue(ssm.seqmappings.contains(acf2));
+    assertTrue(ssm.seqmappings.contains(acf3));
+    assertEquals(3, ssm.seqMappingRefCounts.size());
+    assertEquals(1, ssm.seqMappingRefCounts.get(acf1).intValue());
+    assertEquals(2, ssm.seqMappingRefCounts.get(acf2).intValue());
+    assertEquals(1, ssm.seqMappingRefCounts.get(acf3).intValue());
+  }
+
+  @Test
+  public void testRemoveMapping()
+  {
+    AlignedCodonFrame acf1 = new AlignedCodonFrame();
+    AlignedCodonFrame acf2 = new AlignedCodonFrame();
+    ssm.addMapping(acf1);
+
+    /*
+     * Add one and remove it.
+     */
+    ssm.removeMapping(acf1);
+    ssm.removeMapping(acf2);
+    assertEquals(0, ssm.seqmappings.size());
+    assertEquals(0, ssm.seqMappingRefCounts.size());
+
+    /*
+     * Add one twice and remove it once.
+     */
+    ssm.addMapping(acf1);
+    ssm.addMapping(acf2);
+    ssm.addMapping(acf1);
+    ssm.removeMapping(acf1);
+    assertEquals(2, ssm.seqmappings.size());
+    assertTrue(ssm.seqmappings.contains(acf1));
+    assertTrue(ssm.seqmappings.contains(acf2));
+    assertEquals(2, ssm.seqMappingRefCounts.size());
+    assertEquals(1, ssm.seqMappingRefCounts.get(acf1).intValue());
+    assertEquals(1, ssm.seqMappingRefCounts.get(acf2).intValue());
+
+    /*
+     * Remove both once more to clear the set.
+     */
+    ssm.removeMapping(acf1);
+    ssm.removeMapping(acf2);
+    assertEquals(0, ssm.seqmappings.size());
+    assertEquals(0, ssm.seqMappingRefCounts.size());
+  }
+
+  @Test
+  public void testRemoveMappings()
+  {
+    AlignedCodonFrame acf1 = new AlignedCodonFrame();
+    AlignedCodonFrame acf2 = new AlignedCodonFrame();
+    AlignedCodonFrame acf3 = new AlignedCodonFrame();
+
+    /*
+     * Initial ref counts are 3/2/1:
+     */
+    ssm.addMapping(acf1);
+    ssm.addMapping(acf1);
+    ssm.addMapping(acf1);
+    ssm.addMapping(acf2);
+    ssm.addMapping(acf2);
+    ssm.addMapping(acf3);
+
+    Set<AlignedCodonFrame> set1 = new HashSet<AlignedCodonFrame>();
+    set1.add(acf1);
+    set1.add(acf2);
+    Set<AlignedCodonFrame> set2 = new HashSet<AlignedCodonFrame>();
+    set2.add(acf2);
+    set2.add(acf3);
+
+    /*
+     * Remove one ref each to acf1, acf2, counts are now 2/1/1:
+     */
+    ssm.removeMappings(set1);
+    assertEquals(3, ssm.seqmappings.size());
+    assertTrue(ssm.seqmappings.contains(acf1));
+    assertTrue(ssm.seqmappings.contains(acf2));
+    assertTrue(ssm.seqmappings.contains(acf3));
+    assertEquals(3, ssm.seqMappingRefCounts.size());
+    assertEquals(2, ssm.seqMappingRefCounts.get(acf1).intValue());
+    assertEquals(1, ssm.seqMappingRefCounts.get(acf2).intValue());
+    assertEquals(1, ssm.seqMappingRefCounts.get(acf3).intValue());
+
+    /*
+     * Remove one ref each to acf2, acf3 - they are removed
+     */
+    ssm.removeMappings(set2);
+    assertEquals(1, ssm.seqmappings.size());
+    assertTrue(ssm.seqmappings.contains(acf1));
+    assertEquals(1, ssm.seqMappingRefCounts.size());
+    assertEquals(2, ssm.seqMappingRefCounts.get(acf1).intValue());
+  }
+}
diff --git a/test/jalview/util/ComparisonTest.java b/test/jalview/util/ComparisonTest.java
new file mode 100644 (file)
index 0000000..bfc2610
--- /dev/null
@@ -0,0 +1,89 @@
+package jalview.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import org.junit.Test;
+
+public class ComparisonTest
+{
+
+  @Test
+  public void testIsGap()
+  {
+    assertTrue(Comparison.isGap('-'));
+    assertTrue(Comparison.isGap('.'));
+    assertTrue(Comparison.isGap(' '));
+    assertFalse(Comparison.isGap('X'));
+    assertFalse(Comparison.isGap('x'));
+    assertFalse(Comparison.isGap('*'));
+    assertFalse(Comparison.isGap('G'));
+  }
+
+  /**
+   * Test for isNucleotide is that sequences in a dataset are more than 85%
+   * AGCTU. Test is not case-sensitive and ignores gaps.
+   */
+  @Test
+  public void testIsNucleotide() {
+    SequenceI seq = new Sequence("eightypercent", "agctuAGCPV");
+    assertFalse(Comparison.isNucleotide(new SequenceI[]
+    { seq }));
+
+    seq = new Sequence("eightyfivepercent", "agctuAGCPVagctuAGCUV");
+    assertFalse(Comparison.isNucleotide(new SequenceI[]
+    { seq }));
+
+    seq = new Sequence("nineypercent", "agctuAGCgVagctuAGCUV");
+    assertTrue(Comparison.isNucleotide(new SequenceI[]
+    { seq }));
+
+    seq = new Sequence("eightyfivepercentgapped",
+            "--agc--tuA--GCPV-a---gct-uA-GC---UV");
+    assertFalse(Comparison.isNucleotide(new SequenceI[]
+    { seq }));
+
+    seq = new Sequence("nineypercentgapped",
+            "ag--ct-u-A---GC---g----Vag--c---tuAGCUV");
+    assertTrue(Comparison.isNucleotide(new SequenceI[]
+    { seq }));
+
+    seq = new Sequence("allgap", "---------");
+    assertFalse(Comparison.isNucleotide(new SequenceI[]
+    { seq }));
+
+    seq = new Sequence("DNA", "ACTugGCCAG");
+    SequenceI seq2 = new Sequence("Protein", "FLIMVSPTYW");
+    assertTrue(Comparison.isNucleotide(new SequenceI[]
+    { seq, seq, seq, seq, seq, seq, seq, seq, seq, seq2 })); // 90% DNA
+    assertFalse(Comparison.isNucleotide(new SequenceI[]
+    { seq, seq, seq, seq, seq, seq, seq, seq, seq2, seq2 })); // 80% DNA
+
+    seq = new Sequence("ProteinThatLooksLikeDNA", "WYATGCCTGAgtcgt");
+    // 12/14 = 85.7%
+    assertTrue(Comparison.isNucleotide(new SequenceI[]
+    { seq }));
+
+    assertFalse(Comparison.isNucleotide(null));
+  }
+
+  /**
+   * Test percentage identity calculation for two sequences.
+   */
+  @Test
+  public void testPID_matchGaps()
+  {
+    String seq1 = "ABCDEF";
+    String seq2 = "abcdef";
+    assertEquals("identical", 100f, Comparison.PID(seq1, seq2), 0.001f);
+
+    // comparison range defaults to length of first sequence
+    seq2 = "abcdefghijklmnopqrstuvwxyz";
+    assertEquals("identical", 100f, Comparison.PID(seq1, seq2), 0.001f);
+
+    seq2 = "a---bcdef";
+  }
+}
diff --git a/test/jalview/util/MapListTest.java b/test/jalview/util/MapListTest.java
new file mode 100644 (file)
index 0000000..f69fe40
--- /dev/null
@@ -0,0 +1,501 @@
+package jalview.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MapListTest
+{
+
+  @Test
+  public void testSomething()
+  {
+    MapList ml = new MapList(new int[]
+    { 1, 5, 10, 15, 25, 20 }, new int[]
+    { 51, 1 }, 1, 3);
+    MapList ml1 = new MapList(new int[]
+    { 1, 3, 17, 4 }, new int[]
+    { 51, 1 }, 1, 3);
+    MapList ml2 = new MapList(new int[]
+    { 1, 60 }, new int[]
+    { 1, 20 }, 3, 1);
+    // test internal consistency
+    int to[] = new int[51];
+    testMap(ml, 1, 60);
+    MapList mldna = new MapList(new int[]
+    { 2, 2, 6, 8, 12, 16 }, new int[]
+    { 1, 3 }, 3, 1);
+    int[] frm = mldna.locateInFrom(1, 1);
+    testLocateFrom(mldna, 1, 1, new int[]
+    { 2, 2, 6, 7 });
+    testMap(mldna, 1, 3);
+    /*
+     * for (int from=1; from<=51; from++) { int[] too=ml.shiftTo(from); int[]
+     * toofrom=ml.shiftFrom(too[0]);
+     * System.out.println("ShiftFrom("+from+")=="+too[0]+" %
+     * "+too[1]+"\t+-+\tShiftTo("+too[0]+")=="+toofrom[0]+" % "+toofrom[1]); }
+     */
+  }
+
+  private static void testLocateFrom(MapList mldna, int i, int j, int[] ks)
+  {
+    int[] frm = mldna.locateInFrom(i, j);
+    Assert.assertEquals("Failed test locate from " + i + " to " + j,
+            Arrays.toString(frm), Arrays.toString(ks));
+  }
+
+  /**
+   * test routine. not incremental.
+   * 
+   * @param ml
+   * @param fromS
+   * @param fromE
+   */
+  private void testMap(MapList ml, int fromS, int fromE)
+  {
+    // todo convert to JUnit style tests
+    for (int from = 1; from <= 25; from++)
+    {
+      int[] too = ml.shiftFrom(from);
+      System.out.print("ShiftFrom(" + from + ")==");
+      if (too == null)
+      {
+        System.out.print("NaN\n");
+      }
+      else
+      {
+        System.out.print(too[0] + " % " + too[1] + " (" + too[2] + ")");
+        System.out.print("\t+--+\t");
+        int[] toofrom = ml.shiftTo(too[0]);
+        if (toofrom != null)
+        {
+          if (toofrom[0] != from)
+          {
+            System.err.println("Mapping not reflexive:" + from + " "
+                    + too[0] + "->" + toofrom[0]);
+          }
+          System.out.println("ShiftTo(" + too[0] + ")==" + toofrom[0]
+                  + " % " + toofrom[1] + " (" + toofrom[2] + ")");
+        }
+        else
+        {
+          System.out.println("ShiftTo(" + too[0] + ")=="
+                  + "NaN! - not Bijective Mapping!");
+        }
+      }
+    }
+    int mmap[][] = ml.makeFromMap();
+    System.out.println("FromMap : (" + mmap[0][0] + " " + mmap[0][1] + " "
+            + mmap[0][2] + " " + mmap[0][3] + " ");
+    for (int i = 1; i <= mmap[1].length; i++)
+    {
+      if (mmap[1][i - 1] == -1)
+      {
+        System.out.print(i + "=XXX");
+  
+      }
+      else
+      {
+        System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1]));
+      }
+      if (i % 20 == 0)
+      {
+        System.out.print("\n");
+      }
+      else
+      {
+        System.out.print(",");
+      }
+    }
+    // test range function
+    System.out.print("\nTest locateInFrom\n");
+    {
+      int f = mmap[0][2], t = mmap[0][3];
+      while (f <= t)
+      {
+        System.out.println("Range " + f + " to " + t);
+        int rng[] = ml.locateInFrom(f, t);
+        if (rng != null)
+        {
+          for (int i = 0; i < rng.length; i++)
+          {
+            System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
+          }
+        }
+        else
+        {
+          System.out.println("No range!");
+        }
+        System.out.print("\nReversed\n");
+        rng = ml.locateInFrom(t, f);
+        if (rng != null)
+        {
+          for (int i = 0; i < rng.length; i++)
+          {
+            System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
+          }
+        }
+        else
+        {
+          System.out.println("No range!");
+        }
+        System.out.print("\n");
+        f++;
+        t--;
+      }
+    }
+    System.out.print("\n");
+    mmap = ml.makeToMap();
+    System.out.println("ToMap : (" + mmap[0][0] + " " + mmap[0][1] + " "
+            + mmap[0][2] + " " + mmap[0][3] + " ");
+    for (int i = 1; i <= mmap[1].length; i++)
+    {
+      if (mmap[1][i - 1] == -1)
+      {
+        System.out.print(i + "=XXX");
+  
+      }
+      else
+      {
+        System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1]));
+      }
+      if (i % 20 == 0)
+      {
+        System.out.print("\n");
+      }
+      else
+      {
+        System.out.print(",");
+      }
+    }
+    System.out.print("\n");
+    // test range function
+    System.out.print("\nTest locateInTo\n");
+    {
+      int f = mmap[0][2], t = mmap[0][3];
+      while (f <= t)
+      {
+        System.out.println("Range " + f + " to " + t);
+        int rng[] = ml.locateInTo(f, t);
+        if (rng != null)
+        {
+          for (int i = 0; i < rng.length; i++)
+          {
+            System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
+          }
+        }
+        else
+        {
+          System.out.println("No range!");
+        }
+        System.out.print("\nReversed\n");
+        rng = ml.locateInTo(t, f);
+        if (rng != null)
+        {
+          for (int i = 0; i < rng.length; i++)
+          {
+            System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
+          }
+        }
+        else
+        {
+          System.out.println("No range!");
+        }
+        f++;
+        t--;
+        System.out.print("\n");
+      }
+    }
+  }
+
+  /**
+   * Tests for method that locates ranges in the 'from' map for given range in
+   * the 'to' map.
+   */
+  @Test
+  public void testLocateInFrom_noIntrons()
+  {
+    /*
+     * Simple mapping with no introns
+     */
+    int[] codons = new int[]
+    { 1, 12 };
+    int[] protein = new int[]
+    { 1, 4 };
+    MapList ml = new MapList(codons, protein, 3, 1);
+    assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(1, 1)));
+    assertEquals("[4, 6]", Arrays.toString(ml.locateInFrom(2, 2)));
+    assertEquals("[7, 9]", Arrays.toString(ml.locateInFrom(3, 3)));
+    assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
+    assertEquals("[1, 6]", Arrays.toString(ml.locateInFrom(1, 2)));
+    assertEquals("[1, 9]", Arrays.toString(ml.locateInFrom(1, 3)));
+    assertEquals("[1, 12]", Arrays.toString(ml.locateInFrom(1, 4)));
+    assertEquals("[4, 9]", Arrays.toString(ml.locateInFrom(2, 3)));
+    assertEquals("[4, 12]", Arrays.toString(ml.locateInFrom(2, 4)));
+    assertEquals("[7, 12]", Arrays.toString(ml.locateInFrom(3, 4)));
+    assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
+
+    assertNull(ml.locateInFrom(0, 0));
+    assertNull(ml.locateInFrom(1, 5));
+    assertNull(ml.locateInFrom(-1, 1));
+  }
+
+  /**
+   * Tests for method that locates ranges in the 'from' map for given range in
+   * the 'to' map.
+   */
+  @Test
+  public void testLocateInFrom_withIntrons()
+  {
+    /*
+     * Exons at positions [2, 3, 5] [6, 7, 9] [10, 12, 14] [16, 17, 18] i.e.
+     * 2-3, 5-7, 9-10, 12-12, 14-14, 16-18
+     */
+    int[] codons =
+    { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
+    int[] protein =
+    { 1, 4 };
+    MapList ml = new MapList(codons, protein, 3, 1);
+    assertEquals("[2, 3, 5, 5]", Arrays.toString(ml.locateInFrom(1, 1)));
+    assertEquals("[6, 7, 9, 9]", Arrays.toString(ml.locateInFrom(2, 2)));
+    assertEquals("[10, 10, 12, 12, 14, 14]",
+            Arrays.toString(ml.locateInFrom(3, 3)));
+    assertEquals("[16, 18]", Arrays.toString(ml.locateInFrom(4, 4)));
+  }
+
+  /**
+   * Tests for method that locates ranges in the 'to' map for given range in the
+   * 'from' map.
+   */
+  @Test
+  public void testLocateInTo_noIntrons()
+  {
+    /*
+     * Simple mapping with no introns
+     */
+    int[] codons = new int[]
+    { 1, 12 };
+    int[] protein = new int[]
+    { 1, 4 };
+    MapList ml = new MapList(codons, protein, 3, 1);
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 3)));
+    assertEquals("[2, 2]", Arrays.toString(ml.locateInTo(4, 6)));
+    assertEquals("[3, 3]", Arrays.toString(ml.locateInTo(7, 9)));
+    assertEquals("[4, 4]", Arrays.toString(ml.locateInTo(10, 12)));
+    assertEquals("[1, 2]", Arrays.toString(ml.locateInTo(1, 6)));
+    assertEquals("[1, 3]", Arrays.toString(ml.locateInTo(1, 9)));
+    assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(1, 12)));
+    assertEquals("[2, 2]", Arrays.toString(ml.locateInTo(4, 6)));
+    assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(4, 12)));
+
+    /*
+     * A part codon is treated as if a whole one.
+     */
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 1)));
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 2)));
+    assertEquals("[1, 2]", Arrays.toString(ml.locateInTo(1, 4)));
+    assertEquals("[1, 3]", Arrays.toString(ml.locateInTo(2, 8)));
+    assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(3, 11)));
+    assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(5, 11)));
+
+    assertNull(ml.locateInTo(0, 0));
+    assertNull(ml.locateInTo(1, 13));
+    assertNull(ml.locateInTo(-1, 1));
+  }
+
+  /**
+   * Tests for method that locates ranges in the 'to' map for given range in the
+   * 'from' map.
+   */
+  @Test
+  public void testLocateInTo_withIntrons()
+  {
+    /*
+     * Exons at positions [2, 3, 5] [6, 7, 9] [10, 12, 14] [16, 17, 18] i.e.
+     * 2-3, 5-7, 9-10, 12-12, 14-14, 16-18
+     */
+    int[] codons =
+    { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
+    /*
+     * Mapped proteins at positions 1, 3, 4, 6 in the sequence
+     */
+    int[] protein =
+    { 1, 1, 3, 4, 6, 6 };
+    MapList ml = new MapList(codons, protein, 3, 1);
+
+    /*
+     * Can't map from an unmapped position
+     */
+    assertNull(ml.locateInTo(1, 2));
+    assertNull(ml.locateInTo(2, 4));
+    assertNull(ml.locateInTo(4, 4));
+
+    /*
+     * Valid range or subrange of codon1 maps to protein1.
+     */
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 2)));
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(3, 3)));
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(3, 5)));
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 3)));
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 5)));
+
+    // codon position 6 starts the next protein:
+    assertEquals("[1, 1, 3, 3]", Arrays.toString(ml.locateInTo(3, 6)));
+
+    // codon positions 7 to 17 (part) cover proteins 2/3/4 at positions 3/4/6
+    assertEquals("[3, 4, 6, 6]", Arrays.toString(ml.locateInTo(7, 17)));
+
+  }
+
+  /**
+   * Test equals method.
+   */
+  @Test
+  public void testEquals()
+  {
+    int[] codons = new int[]
+    { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
+    int[] protein = new int[]
+    { 1, 4 };
+    MapList ml = new MapList(codons, protein, 3, 1);
+    MapList ml1 = new MapList(codons, protein, 3, 1); // same values
+    MapList ml2 = new MapList(codons, protein, 2, 1); // fromRatio differs
+    MapList ml3 = new MapList(codons, protein, 3, 2); // toRatio differs
+    codons[2] = 4;
+    MapList ml6 = new MapList(codons, protein, 3, 1); // fromShifts differ
+    protein[1] = 3;
+    MapList ml7 = new MapList(codons, protein, 3, 1); // toShifts differ
+
+    assertTrue(ml.equals(ml));
+    assertTrue(ml.equals(ml1));
+    assertTrue(ml1.equals(ml));
+
+    assertFalse(ml.equals(null));
+    assertFalse(ml.equals("hello"));
+    assertFalse(ml.equals(ml2));
+    assertFalse(ml.equals(ml3));
+    assertFalse(ml.equals(ml6));
+    assertFalse(ml.equals(ml7));
+    assertFalse(ml6.equals(ml7));
+
+    try
+    {
+      MapList ml4 = new MapList(codons, null, 3, 1); // toShifts null
+      assertFalse(ml.equals(ml4));
+    } catch (NullPointerException e)
+    {
+      // actually thrown by constructor before equals can be called
+    }
+    try
+    {
+      MapList ml5 = new MapList(null, protein, 3, 1); // fromShifts null
+      assertFalse(ml.equals(ml5));
+    } catch (NullPointerException e)
+    {
+      // actually thrown by constructor before equals can be called
+    }
+  }
+
+  /**
+   * Test for the method that flattens a list of ranges into a single array.
+   */
+  @Test
+  public void testGetRanges()
+  {
+    List<int[]> ranges = new ArrayList<int[]>();
+    ranges.add(new int[]
+    { 2, 3 });
+    ranges.add(new int[]
+    { 5, 6 });
+    assertEquals("[2, 3, 5, 6]", Arrays.toString(MapList.getRanges(ranges)));
+  }
+
+  /**
+   * Check state after construction
+   */
+  @Test
+  public void testConstructor()
+  {
+    int[] codons =
+    { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
+    int[] protein =
+    { 1, 1, 3, 4, 6, 6 };
+    MapList ml = new MapList(codons, protein, 3, 1);
+    assertEquals(3, ml.getFromRatio());
+    assertEquals(2, ml.getFromLowest());
+    assertEquals(18, ml.getFromHighest());
+    assertEquals(1, ml.getToLowest());
+    assertEquals(6, ml.getToHighest());
+    assertEquals("{[2, 3], [5, 7], [9, 10], [12, 12], [14, 14], [16, 18]}",
+            prettyPrint(ml.getFromRanges()));
+    assertEquals("{[1, 1], [3, 4], [6, 6]}", prettyPrint(ml.getToRanges()));
+
+    /*
+     * Also copy constructor
+     */
+    MapList ml2 = new MapList(ml);
+    assertEquals(3, ml2.getFromRatio());
+    assertEquals(2, ml2.getFromLowest());
+    assertEquals(18, ml2.getFromHighest());
+    assertEquals(1, ml2.getToLowest());
+    assertEquals(6, ml2.getToHighest());
+    assertEquals("{[2, 3], [5, 7], [9, 10], [12, 12], [14, 14], [16, 18]}",
+            prettyPrint(ml2.getFromRanges()));
+    assertEquals("{[1, 1], [3, 4], [6, 6]}", prettyPrint(ml2.getToRanges()));
+  }
+
+  /**
+   * Convert a List of {[i, j], [k, l], ...} to "[[i, j], [k, l], ...]"
+   * 
+   * @param ranges
+   * @return
+   */
+  private String prettyPrint(List<int[]> ranges)
+  {
+    StringBuilder sb = new StringBuilder(ranges.size() * 5);
+    boolean first = true;
+    sb.append("{");
+    for (int[] range : ranges)
+    {
+      if (!first)
+      {
+        sb.append(", ");
+      }
+      sb.append(Arrays.toString(range));
+      first = false;
+    }
+    sb.append("}");
+    return sb.toString();
+  }
+
+  /**
+   * Test the method that creates an inverse mapping
+   */
+  @Test
+  public void testGetInverse()
+  {
+    int[] codons =
+    { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
+    int[] protein =
+    { 1, 1, 3, 4, 6, 6 };
+
+    MapList ml = new MapList(codons, protein, 3, 1);
+    MapList ml2 = ml.getInverse();
+    assertEquals(ml.getFromRatio(), ml2.getToRatio());
+    assertEquals(ml.getFromRatio(), ml2.getToRatio());
+    assertEquals(ml.getToHighest(), ml2.getFromHighest());
+    assertEquals(ml.getFromHighest(), ml2.getToHighest());
+    assertEquals(prettyPrint(ml.getFromRanges()),
+            prettyPrint(ml2.getToRanges()));
+    assertEquals(prettyPrint(ml.getToRanges()),
+            prettyPrint(ml2.getFromRanges()));
+  }
+}
diff --git a/test/jalview/util/MappingUtilsTest.java b/test/jalview/util/MappingUtilsTest.java
new file mode 100644 (file)
index 0000000..f32e7ff
--- /dev/null
@@ -0,0 +1,402 @@
+package jalview.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import jalview.api.AlignViewportI;
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.gui.AlignViewport;
+import jalview.io.AppletFormatAdapter;
+import jalview.io.FormatAdapter;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+
+import org.junit.Test;
+
+public class MappingUtilsTest
+{
+  private AlignViewportI dnaView;
+  private AlignViewportI proteinView;
+
+  /**
+   * Simple test of mapping with no intron involved.
+   */
+  @Test
+  public void testBuildSearchResults()
+  {
+    final Sequence seq1 = new Sequence("Seq1", "C-G-TA-GC");
+    seq1.createDatasetSequence();
+
+    final Sequence aseq1 = new Sequence("Seq1", "-P-R");
+    aseq1.createDatasetSequence();
+
+    /*
+     * Map dna bases 1-6 to protein residues 1-2
+     */
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList map = new MapList(new int[]
+    { 1, 6 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
+    Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
+
+    /*
+     * Check protein residue 1 maps to codon 1-3, 2 to codon 4-6
+     */
+    SearchResults sr = MappingUtils.buildSearchResults(aseq1, 1, acfList);
+    assertEquals(1, sr.getResults().size());
+    Match m = sr.getResults().get(0);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(1, m.getStart());
+    assertEquals(3, m.getEnd());
+    sr = MappingUtils.buildSearchResults(aseq1, 2, acfList);
+    assertEquals(1, sr.getResults().size());
+    m = sr.getResults().get(0);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(4, m.getStart());
+    assertEquals(6, m.getEnd());
+
+    /*
+     * Check inverse mappings, from codons 1-3, 4-6 to protein 1, 2
+     */
+    for (int i = 1; i < 7; i++)
+    {
+      sr = MappingUtils.buildSearchResults(seq1, i, acfList);
+      assertEquals(1, sr.getResults().size());
+      m = sr.getResults().get(0);
+      assertEquals(aseq1.getDatasetSequence(), m.getSequence());
+      int residue = i > 3 ? 2 : 1;
+      assertEquals(residue, m.getStart());
+      assertEquals(residue, m.getEnd());
+    }
+  }
+
+  /**
+   * Simple test of mapping with introns involved.
+   */
+  @Test
+  public void testBuildSearchResults_withIntro()
+  {
+    final Sequence seq1 = new Sequence("Seq1", "C-G-TAGA-GCAGCTT");
+    seq1.createDatasetSequence();
+  
+    final Sequence aseq1 = new Sequence("Seq1", "-P-R");
+    aseq1.createDatasetSequence();
+  
+    /*
+     * Map dna bases [2, 4, 5], [7, 9, 11] to protein residues 1 and 2
+     */
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList map = new MapList(new int[]
+    { 2, 2, 4, 5, 7, 7, 9, 9, 11, 11 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
+    Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
+  
+    /*
+     * Check protein residue 1 maps to [2, 4, 5]
+     */
+    SearchResults sr = MappingUtils.buildSearchResults(aseq1, 1, acfList);
+    assertEquals(2, sr.getResults().size());
+    Match m = sr.getResults().get(0);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(2, m.getStart());
+    assertEquals(2, m.getEnd());
+    m = sr.getResults().get(1);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(4, m.getStart());
+    assertEquals(5, m.getEnd());
+
+    /*
+     * Check protein residue 2 maps to [7, 9, 11]
+     */
+    sr = MappingUtils.buildSearchResults(aseq1, 2, acfList);
+    assertEquals(3, sr.getResults().size());
+    m = sr.getResults().get(0);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(7, m.getStart());
+    assertEquals(7, m.getEnd());
+    m = sr.getResults().get(1);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(9, m.getStart());
+    assertEquals(9, m.getEnd());
+    m = sr.getResults().get(2);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(11, m.getStart());
+    assertEquals(11, m.getEnd());
+  
+    /*
+     * Check inverse mappings, from codons to protein
+     */
+    for (int i = 1; i < 14; i++)
+    {
+      sr = MappingUtils.buildSearchResults(seq1, i, acfList);
+      int residue = (i == 2 || i == 4 || i == 5) ? 1 : (i == 7 || i == 9
+              || i == 11 ? 2 : 0);
+      if (residue == 0)
+      {
+        assertEquals(0, sr.getResults().size());
+        continue;
+      }
+      assertEquals(1, sr.getResults().size());
+      m = sr.getResults().get(0);
+      assertEquals(aseq1.getDatasetSequence(), m.getSequence());
+      assertEquals(residue, m.getStart());
+      assertEquals(residue, m.getEnd());
+    }
+  }
+
+  /**
+   * Test mapping a sequence group.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapSequenceGroup() throws IOException
+  {
+    /*
+     * Set up dna and protein Seq1/2/3 with mappings (held on the protein
+     * viewport).
+     */
+    AlignmentI cdna = loadAlignment(">Seq1\nACG\n>Seq2\nTGA\n>Seq3\nTAC\n",
+            "FASTA");
+    cdna.setDataset(null);
+    AlignmentI protein = loadAlignment(">Seq1\nK\n>Seq2\nL\n>Seq3\nQ\n",
+            "FASTA");
+    protein.setDataset(null);
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList map = new MapList(new int[]
+    { 1, 3 }, new int[]
+    { 1, 1 }, 3, 1);
+    for (int seq = 0; seq < 3; seq++)
+    {
+      acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
+              .getSequenceAt(seq).getDatasetSequence(), map);
+    }
+    Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
+
+    AlignViewportI dnaView = new AlignViewport(cdna);
+    AlignViewportI proteinView = new AlignViewport(protein);
+    protein.setCodonFrames(acfList);
+
+    /*
+     * Select Seq1 and Seq3 in the protein
+     */
+    SequenceGroup sg = new SequenceGroup();
+    sg.setColourText(true);
+    sg.setIdColour(Color.GREEN);
+    sg.setOutlineColour(Color.LIGHT_GRAY);
+    sg.addSequence(protein.getSequenceAt(0), false);
+    sg.addSequence(protein.getSequenceAt(2), false);
+
+    /*
+     * Verify the mapped sequence group in dna
+     */
+    SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, proteinView, dnaView);
+    assertTrue(mappedGroup.getColourText());
+    assertSame(sg.getIdColour(), mappedGroup.getIdColour());
+    assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
+    assertEquals(2, mappedGroup.getSequences().size());
+    assertSame(cdna.getSequenceAt(0), mappedGroup.getSequences().get(0));
+    assertSame(cdna.getSequenceAt(2), mappedGroup.getSequences().get(1));
+
+    /*
+     * Verify mapping sequence group from dna to protein
+     */
+    sg.clear();
+    sg.addSequence(cdna.getSequenceAt(1), false);
+    sg.addSequence(cdna.getSequenceAt(0), false);
+    mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
+    assertTrue(mappedGroup.getColourText());
+    assertSame(sg.getIdColour(), mappedGroup.getIdColour());
+    assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
+    assertEquals(2, mappedGroup.getSequences().size());
+    assertSame(protein.getSequenceAt(1), mappedGroup.getSequences().get(0));
+    assertSame(protein.getSequenceAt(0), mappedGroup.getSequences().get(1));
+  }
+
+  /**
+   * Helper method to load an alignment and ensure dataset sequences are set up.
+   * 
+   * @param data
+   * @param format
+   *          TODO
+   * @return
+   * @throws IOException
+   */
+  protected AlignmentI loadAlignment(final String data, String format)
+          throws IOException
+  {
+    Alignment a = new FormatAdapter().readFile(data,
+            AppletFormatAdapter.PASTE, format);
+    a.setDataset(null);
+    return a;
+  }
+
+  /**
+   * Test mapping a column selection in protein to its dna equivalent
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapColumnSelection_proteinToDna() throws IOException
+  {
+    setupMappedAlignments();
+  
+    ColumnSelection colsel = new ColumnSelection();
+
+    /*
+     * Column 0 in protein picks up Seq2/L, Seq3/G which map to cols 0-4 and 0-3
+     * in dna respectively, overall 0-4
+     */
+    colsel.addElement(0);
+    ColumnSelection cs = MappingUtils.mapColumnSelection(colsel,
+            proteinView, dnaView);
+    assertEquals("[0, 1, 2, 3, 4]", cs.getSelected().toString());
+
+    /*
+     * Column 1 in protein picks up Seq1/K which maps to cols 0-3 in dna
+     */
+    colsel.clear();
+    colsel.addElement(1);
+    cs = MappingUtils.mapColumnSelection(colsel, proteinView, dnaView);
+    assertEquals("[0, 1, 2, 3]", cs.getSelected().toString());
+
+    /*
+     * Column 2 in protein picks up gaps only - no mapping
+     */
+    colsel.clear();
+    colsel.addElement(2);
+    cs = MappingUtils.mapColumnSelection(colsel, proteinView, dnaView);
+    assertEquals("[]", cs.getSelected().toString());
+
+    /*
+     * Column 3 in protein picks up Seq1/P, Seq2/Q, Seq3/S which map to columns
+     * 6-9, 6-10, 5-8 respectively, overall to 5-10
+     */
+    colsel.clear();
+    colsel.addElement(3);
+    cs = MappingUtils.mapColumnSelection(colsel, proteinView, dnaView);
+    assertEquals("[5, 6, 7, 8, 9, 10]", cs.getSelected().toString());
+
+    /*
+     * Combine selection of columns 1 and 3 to get a discontiguous mapped
+     * selection
+     */
+    colsel.clear();
+    colsel.addElement(1);
+    colsel.addElement(3);
+    cs = MappingUtils.mapColumnSelection(colsel, proteinView, dnaView);
+    assertEquals("[0, 1, 2, 3, 5, 6, 7, 8, 9, 10]", cs.getSelected()
+            .toString());
+  }
+
+  /**
+   * @throws IOException
+   */
+  protected void setupMappedAlignments() throws IOException
+  {
+    /*
+     * Set up dna and protein Seq1/2/3 with mappings (held on the protein
+     * viewport). Lower case for introns.
+     */
+    AlignmentI cdna = loadAlignment(">Seq1\nAC-GctGtC-T\n"
+            + ">Seq2\nTc-GA-G-T-Tc\n" + ">Seq3\nTtTT-AaCGg-\n",
+            "FASTA");
+    cdna.setDataset(null);
+    AlignmentI protein = loadAlignment(
+            ">Seq1\n-K-P\n>Seq2\nL--Q\n>Seq3\nG--S\n",
+            "FASTA");
+    protein.setDataset(null);
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList map = new MapList(new int[]
+    { 1, 3, 6, 6, 8, 9 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(cdna.getSequenceAt(0).getDatasetSequence(), protein
+            .getSequenceAt(0).getDatasetSequence(), map);
+    map = new MapList(new int[]
+    { 1, 1, 3, 4, 5, 7 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(cdna.getSequenceAt(1).getDatasetSequence(), protein
+            .getSequenceAt(1).getDatasetSequence(), map);
+    map = new MapList(new int[]
+    { 1, 1, 3, 4, 5, 5, 7, 8 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(cdna.getSequenceAt(2).getDatasetSequence(), protein
+            .getSequenceAt(2).getDatasetSequence(), map);
+    Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
+  
+    dnaView = new AlignViewport(cdna);
+    proteinView = new AlignViewport(protein);
+    protein.setCodonFrames(acfList);
+  }
+
+  /**
+   * Test mapping a column selection in dna to its protein equivalent
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapColumnSelection_dnaToProtein() throws IOException
+  {
+    setupMappedAlignments();
+  
+    ColumnSelection colsel = new ColumnSelection();
+  
+    /*
+     * Column 0 in dna picks up first bases which map to residue 1, columns 0-1
+     * in protein.
+     */
+    colsel.addElement(0);
+    ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, dnaView,
+            proteinView);
+    assertEquals("[0, 1]", cs.getSelected().toString());
+
+    /*
+     * Columns 3-5 in dna map to the first residues in protein Seq1, Seq2, and
+     * the first two in Seq3. Overall to columns 0, 1, 3 (col2 is all gaps).
+     */
+    colsel.addElement(3);
+    colsel.addElement(4);
+    colsel.addElement(5);
+    cs = MappingUtils.mapColumnSelection(colsel, dnaView, proteinView);
+    assertEquals("[0, 1, 3]", cs.getSelected().toString());
+  }
+
+  /**
+   * Tests for the method that converts a series of [start, end] ranges to
+   * single positions
+   */
+  @Test
+  public void testFlattenRanges()
+  {
+    assertEquals("[1, 2, 3, 4]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 1, 4 })));
+    assertEquals("[1, 2, 3, 4]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 1, 2, 3, 4 })));
+    assertEquals("[1, 2, 3, 4]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 1, 1, 2, 2, 3, 3, 4, 4 })));
+    assertEquals("[1, 2, 3, 4, 7, 8, 9, 12]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 1, 4, 7, 9, 12, 12 })));
+    // unpaired start position is ignored:
+    assertEquals("[1, 2, 3, 4, 7, 8, 9, 12]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 1, 4, 7, 9, 12, 12, 15 })));
+  }
+}
diff --git a/test/jalview/util/QuickSortTest.java b/test/jalview/util/QuickSortTest.java
new file mode 100644 (file)
index 0000000..2293163
--- /dev/null
@@ -0,0 +1,121 @@
+package jalview.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class QuickSortTest
+{
+  private static final String c1 = "Blue";
+
+  private static final String c2 = "Yellow";
+
+  private static final String c3 = "Orange";
+
+  private static final String c4 = "Green";
+
+  private Object[] things;
+
+  private final Object[] sortedThings = new Object[]
+  { c4, c2, c1, c3 };
+
+  @Before
+  public void setUp()
+  {
+    things = new Object[]
+    { c1, c2, c3, c4 };
+  }
+
+  @Test
+  public void testSort_byIntValues()
+  {
+    int[] values = new int[]
+    { 3, 2, 4, 1 };
+    QuickSort.sort(values, things);
+    assertTrue(Arrays.equals(new int[]
+    { 1, 2, 3, 4 }, values));
+    assertTrue(Arrays.equals(sortedThings, things));
+  }
+
+  @Test
+  public void testSort_byFloatValues()
+  {
+    float[] values = new float[]
+    { 3f, 2f, 4f, 1f };
+    QuickSort.sort(values, things);
+    assertTrue(Arrays.equals(new float[]
+    { 1f, 2f, 3f, 4f }, values));
+    assertTrue(Arrays.equals(sortedThings, things));
+  }
+
+  @Test
+  public void testSort_byDoubleValues()
+  {
+    double[] values = new double[]
+    { 3d, 2d, 4d, 1d };
+    QuickSort.sort(values, things);
+    assertTrue(Arrays.equals(new double[]
+    { 1d, 2d, 3d, 4d }, values));
+    assertTrue(Arrays.equals(sortedThings, things));
+  }
+
+  /**
+   * Sort by String is descending order, case-sensitive
+   */
+  @Test
+  public void testSort_byStringValues()
+  {
+    String[] values = new String[]
+    { "JOHN", "henry", "lucy", "ALISON" };
+    QuickSort.sort(values, things);
+    assertTrue(Arrays.equals(new String[]
+    { "lucy", "henry", "JOHN", "ALISON" }, values));
+    assertTrue(Arrays.equals(new Object[]
+    { c3, c2, c1, c4 }, things));
+  }
+
+  /**
+   * Test whether sort is stable i.e. equal values retain their mutual ordering.
+   */
+  @Test
+  @Ignore
+  public void testSort_withDuplicates()
+  {
+    int[] values = new int[]
+    { 3, 4, 2, 4, 1 };
+    Object [] things = new Object [] {"A", "X", "Y", "B", "Z"};
+    QuickSort.sort(values, things);
+    assertTrue(Arrays.equals(new int[]
+    { 1, 2, 3, 4, 4 }, values));
+    // this fails - do we care?
+    assertTrue(Arrays.equals(new Object[]
+    { "Z", "Y", "A", "X", "B" }, things));
+  }
+
+  /**
+   * Test that exercises sort with a mostly zero-valued sortby array. May be of
+   * interest to check the sort algorithm is efficient.
+   */
+  @Test
+  public void testSort_MostlyZeroValues()
+  {
+    char[] residues = new char[64];
+    for (int i = 0; i < 64; i++)
+    {
+      residues[i] = (char) i;
+    }
+    float[] counts = new float[64];
+    counts[43] = 16;
+    counts[59] = 7;
+    counts[62] = 2;
+    QuickSort.sort(counts, residues);
+    assertEquals(43, residues[63]);
+    assertEquals(59, residues[62]);
+    assertEquals(62, residues[61]);
+  }
+}
diff --git a/test/jalview/util/ShiftListTest.java b/test/jalview/util/ShiftListTest.java
new file mode 100644 (file)
index 0000000..f680d6c
--- /dev/null
@@ -0,0 +1,35 @@
+package jalview.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+public class ShiftListTest
+{
+
+  @Test
+  public void testParseMap()
+  {
+    assertNull(ShiftList.parseMap(null));
+    assertNull(ShiftList.parseMap(new int[]
+    {}));
+    
+    /*
+     * Gap map showing residues in aligned positions 2,3,6,8,9,10,12
+     */
+    int[] gm = new int[]
+    { 2, 3, 6, 8, 9, 10, 12 };
+    List<int[]> shifts = ShiftList.parseMap(gm).getShifts();
+    assertEquals(4, shifts.size());
+
+    // TODO are these results (which pass) correct??
+    assertEquals("[0, 2]", Arrays.toString(shifts.get(0)));
+    assertEquals("[4, 2]", Arrays.toString(shifts.get(1)));
+    assertEquals("[7, 1]", Arrays.toString(shifts.get(2)));
+    assertEquals("[11, 1]", Arrays.toString(shifts.get(3)));
+  }
+}
diff --git a/test/jalview/util/StringUtilsTest.java b/test/jalview/util/StringUtilsTest.java
new file mode 100644 (file)
index 0000000..6930e40
--- /dev/null
@@ -0,0 +1,109 @@
+package jalview.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+public class StringUtilsTest
+{
+
+  @Test
+  public void testInsertCharAt()
+  {
+    char[] c1 = "ABC".toCharArray();
+    char[] expected = new char[]
+    { 'A', 'B', 'C', 'w', 'w' };
+    assertTrue(Arrays.equals(expected,
+            StringUtils.insertCharAt(c1, 3, 2, 'w')));
+    expected = new char[]
+    { 'A', 'B', 'C', 'w', 'w' };
+    assertTrue(Arrays.equals(expected,
+            StringUtils.insertCharAt(c1, 4, 2, 'w')));
+    assertTrue(Arrays.equals(expected,
+            StringUtils.insertCharAt(c1, 5, 2, 'w')));
+    assertTrue(Arrays.equals(expected,
+            StringUtils.insertCharAt(c1, 6, 2, 'w')));
+    assertTrue(Arrays.equals(expected,
+            StringUtils.insertCharAt(c1, 7, 2, 'w')));
+  }
+
+  @Test
+  public void testDeleteChars()
+  {
+    char[] c1 = "ABC".toCharArray();
+
+    // delete second position
+    assertTrue(Arrays.equals(new char[]
+    { 'A', 'C' }, StringUtils.deleteChars(c1, 1, 2)));
+
+    // delete positions 1 and 2
+    assertTrue(Arrays.equals(new char[]
+    { 'C' }, StringUtils.deleteChars(c1, 0, 2)));
+
+    // delete positions 1-3
+    assertTrue(Arrays.equals(new char[]
+    {}, StringUtils.deleteChars(c1, 0, 3)));
+
+    // delete position 3
+    assertTrue(Arrays.equals(new char[]
+    { 'A', 'B' }, StringUtils.deleteChars(c1, 2, 3)));
+
+    // out of range deletion is ignore
+    assertTrue(Arrays.equals(c1, StringUtils.deleteChars(c1, 3, 4)));
+  }
+
+  @Test
+  public void testGetLastToken()
+  {
+    assertNull(StringUtils.getLastToken(null, null));
+    assertNull(StringUtils.getLastToken(null, "/"));
+    assertEquals("a", StringUtils.getLastToken("a", null));
+
+    assertEquals("abc", StringUtils.getLastToken("abc", "/"));
+    assertEquals("c", StringUtils.getLastToken("abc", "b"));
+    assertEquals("file1.dat", StringUtils.getLastToken(
+            "file://localhost:8080/data/examples/file1.dat", "/"));
+  }
+
+  @Test
+  public void testSeparatorListToArray()
+  {
+    String[] result = StringUtils.separatorListToArray(
+            "foo=',',min='foo',max='1,2,3',fa=','", ",");
+    assertEquals("[foo=',', min='foo', max='1,2,3', fa=',']",
+            Arrays.toString(result));
+    /*
+     * Comma nested in '' is not treated as delimiter; tokens are not trimmed
+     */
+    result = StringUtils.separatorListToArray("minsize='2', sep=','", ",");
+    assertEquals("[minsize='2',  sep=',']", Arrays.toString(result));
+    
+    /*
+     * String delimited by | containing a quoted | (should not be treated as
+     * delimiter)
+     */
+    assertEquals("[abc='|'d, ef, g]", Arrays.toString(StringUtils
+            .separatorListToArray("abc='|'d|ef|g", "|")));
+  }
+
+  @Test
+  public void testArrayToSeparatorList()
+  {
+    assertEquals("*", StringUtils.arrayToSeparatorList(null, "*"));
+    assertEquals("*", StringUtils.arrayToSeparatorList(new String[]
+    {}, "*"));
+    assertEquals("a*bc*cde", StringUtils.arrayToSeparatorList(new String[]
+    { "a", "bc", "cde" }, "*"));
+    assertEquals("a*cde", StringUtils.arrayToSeparatorList(new String[]
+    { "a", null, "cde" }, "*"));
+    assertEquals("a**cde", StringUtils.arrayToSeparatorList(new String[]
+    { "a", "", "cde" }, "*"));
+    // delimiter within token is not (yet) escaped
+    assertEquals("a*b*c*cde", StringUtils.arrayToSeparatorList(new String[]
+    { "a", "b*c", "cde" }, "*"));
+  }
+}
diff --git a/test/jalview/viewmodel/styles/TestviewStyle.java b/test/jalview/viewmodel/styles/TestviewStyle.java
new file mode 100644 (file)
index 0000000..88ea82e
--- /dev/null
@@ -0,0 +1,23 @@
+package jalview.viewmodel.styles;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestviewStyle
+{
+
+  @Test
+  public void testSetterGetter()
+  {
+    ViewStyle orig = new ViewStyle();
+    orig.setCharHeight(23);
+    orig.setWrappedWidth(253);
+    orig.setWrapAlignment(true);
+    ViewStyle copy = new ViewStyle(orig);
+    orig.setWrapAlignment(false);
+    Assert.assertEquals(copy.getCharHeight(), orig.getCharHeight());
+    Assert.assertEquals(copy.getWrappedWidth(), orig.getWrappedWidth());
+    Assert.assertEquals(copy.getWrapAlignment(), !orig.getWrapAlignment());
+  }
+
+}
diff --git a/test/jalview/ws/dbsources/PDBRestClientTest.java b/test/jalview/ws/dbsources/PDBRestClientTest.java
new file mode 100644 (file)
index 0000000..ba07562
--- /dev/null
@@ -0,0 +1,181 @@
+package jalview.ws.dbsources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+import jalview.ws.uimodel.PDBRestRequest;
+import jalview.ws.uimodel.PDBRestResponse;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PDBRestClientTest
+{
+
+  @Before
+  public void setUp() throws Exception
+  {
+  }
+
+  @After
+  public void tearDown() throws Exception
+  {
+  }
+
+  @Test
+  public void executeRequestTest()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.MOLECULE_TYPE);
+    wantedFields.add(PDBDocField.PDB_ID);
+    wantedFields.add(PDBDocField.GENUS);
+    wantedFields.add(PDBDocField.GENE_NAME);
+    wantedFields.add(PDBDocField.TITLE);
+
+    PDBRestRequest request = new PDBRestRequest();
+    request.setAllowEmptySeq(false);
+    request.setResponseSize(100);
+    request.setFieldToSearchBy("text:");
+    request.setSearchTerm("abc");
+    request.setWantedFields(wantedFields);
+
+    PDBRestResponse response = new PDBRestClient().executeRequest(request);
+    assertTrue(response.getNumberOfItemsFound() > 99);
+    assertTrue(response.getSearchSummary() != null);
+    assertTrue(response.getSearchSummary().size() > 99);
+  }
+
+  @Test
+  public void getPDBDocFieldsAsCommaDelimitedStringTest()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.MOLECULE_TYPE);
+    wantedFields.add(PDBDocField.PDB_ID);
+    wantedFields.add(PDBDocField.GENUS);
+    wantedFields.add(PDBDocField.GENE_NAME);
+    wantedFields.add(PDBDocField.TITLE);
+    assertEquals("molecule_type,pdb_id,genus,gene_name,title",
+            PDBRestClient
+                    .getPDBDocFieldsAsCommaDelimitedString(wantedFields));
+  }
+
+  @Test
+  public void parsePDBJsonExceptionStringTest()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.MOLECULE_TYPE);
+    wantedFields.add(PDBDocField.PDB_ID);
+    wantedFields.add(PDBDocField.GENUS);
+    wantedFields.add(PDBDocField.GENE_NAME);
+    wantedFields.add(PDBDocField.TITLE);
+
+    PDBRestRequest request = new PDBRestRequest();
+    request.setAllowEmptySeq(false);
+    request.setResponseSize(100);
+    request.setFieldToSearchBy("text:");
+    request.setSearchTerm("abc");
+    request.setWantedFields(wantedFields);
+
+    String jsonErrorResponse = "";
+    try
+    {
+      jsonErrorResponse = readJsonStringFromFile("test/jalview/io/pdb_request_json_error.txt");
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+
+    String parsedErrorResponse = PDBRestClient
+            .parseJsonExceptionString(jsonErrorResponse);
+    String expectedErrorMsg = "org.apache.solr.search.SyntaxError: Cannot parse 'text:abc OR text:go:abc AND molecule_sequence:['' TO *]': Encountered \" \":\" \": \"\" at line 1, column 19.{\"q\":\"text:abc OR text:go:abc AND molecule_sequence:['' TO *]\",\"fl\":\"pdb_id\",\"sort\":\"\",\"rows\":\"100\",\"wt\":\"json\"}";
+
+    assertEquals(expectedErrorMsg, parsedErrorResponse);
+  }
+
+  @Test(expected = RuntimeException.class)
+  public void testForExpectedRuntimeException()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.PDB_ID);
+
+    PDBRestRequest request = new PDBRestRequest();
+    request.setFieldToSearchBy("text:");
+    request.setSearchTerm("abc OR text:go:abc");
+    request.setWantedFields(wantedFields);
+    new PDBRestClient().executeRequest(request);
+  }
+
+  @Test
+  public void parsePDBJsonResponseTest()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.MOLECULE_TYPE);
+    wantedFields.add(PDBDocField.PDB_ID);
+    wantedFields.add(PDBDocField.GENUS);
+    wantedFields.add(PDBDocField.GENE_NAME);
+    wantedFields.add(PDBDocField.TITLE);
+
+    PDBRestRequest request = new PDBRestRequest();
+    request.setAllowEmptySeq(false);
+    request.setWantedFields(wantedFields);
+
+    String jsonString = "";
+    try
+    {
+      jsonString = readJsonStringFromFile("test/jalview/io/pdb_response_json.txt");
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+    PDBRestResponse response = PDBRestClient.parsePDBJsonResponse(
+            jsonString, request);
+    assertTrue(response.getSearchSummary() != null);
+    assertTrue(response.getNumberOfItemsFound() == 931);
+    assertTrue(response.getSearchSummary().size() == 14);
+  }
+
+  @Test
+  public void getPDBIdColumIndexTest()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.MOLECULE_TYPE);
+    wantedFields.add(PDBDocField.GENUS);
+    wantedFields.add(PDBDocField.GENE_NAME);
+    wantedFields.add(PDBDocField.TITLE);
+    wantedFields.add(PDBDocField.PDB_ID);
+    assertEquals(5, PDBRestClient.getPDBIdColumIndex(wantedFields, true));
+    assertEquals(4, PDBRestClient.getPDBIdColumIndex(wantedFields, false));
+  }
+
+  public String readJsonStringFromFile(String filePath) throws IOException
+  {
+    String fileContent;
+    BufferedReader br = new BufferedReader(new FileReader(filePath));
+    try
+    {
+      StringBuilder sb = new StringBuilder();
+      String line = br.readLine();
+
+      while (line != null)
+      {
+        sb.append(line);
+        sb.append(System.lineSeparator());
+        line = br.readLine();
+        }
+      fileContent = sb.toString();
+    } finally
+    {
+      br.close();
+    }
+    return fileContent;
+  }
+
+
+}
index c2b483a..f7b422a 100644 (file)
@@ -22,14 +22,16 @@ package jalview.ws.jabaws;
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import jalview.ws.jws2.Jws2Discoverer;
 
 import java.util.Vector;
 
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 
+import jalview.ws.jws2.Jws2Discoverer;
+
 public class JalviewJabawsTestUtils
 {
 
@@ -50,6 +52,7 @@ public class JalviewJabawsTestUtils
   { "http://localhost:8080/jabaws" };
 
   @Test
+  @Ignore
   public void testAnnotExport()
   {
     fail("Not yet implemented");
index 7046b55..36b3196 100644 (file)
@@ -80,7 +80,7 @@ public class RNAStructExportImport
 
     if (rnaalifoldws == null)
     {
-      fail("rnaalifoldws is null");
+      fail("no web service");
     }
 
     jalview.io.FileLoader fl = new jalview.io.FileLoader(false);
index 5fc215c..c4430ad 100644 (file)
  */
 package jalview.ws.rest;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.List;
 import java.util.Map;
 
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.AlignmentView;
-import jalview.gui.AlignFrame;
-import jalview.io.FileParse;
-import jalview.ws.rest.InputType;
-import jalview.ws.rest.params.SeqGroupIndexVector;
-
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
 import org.junit.Test;
 
+import jalview.gui.AlignFrame;
+import jalview.util.StringUtils;
+
 /**
  * @author jimp
  * 
@@ -48,20 +38,6 @@ public class ShmmrRSBSService
 {
 
   @Test
-  public void testSeparatorListToArrayForRestServiceDescriptions()
-  {
-    assertTrue(
-            "separatorListToArray is faulty.",
-            RestServiceDescription.separatorListToArray(
-                    "foo=',',min='foo',max='1,2,3',fa=','", ",").length == 4);
-    assertTrue("separatorListToArray is faulty.",
-            RestServiceDescription.separatorListToArray(
-                    "minsize='2', sep=','", ",").length != 2); // probably
-                                                               // should come as
-                                                               // 2
-  }
-
-  @Test
   public void testShmmrService()
   {
 
index afaadbb..7540edf 100644 (file)
@@ -23,12 +23,6 @@ package jalview.ws.seqfetcher;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import jalview.analysis.CrossRef;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.util.DBRefUtils;
-import jalview.ws.SequenceFetcher;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -37,6 +31,13 @@ import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import jalview.analysis.CrossRef;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.util.DBRefUtils;
+import jalview.ws.SequenceFetcher;
+
 /**
  * @author jimp
  * 
@@ -50,6 +51,7 @@ public class DbRefFetcherTest
   @BeforeClass
   public static void setUpBeforeClass() throws Exception
   {
+    jalview.bin.Cache.initLogger();
   }
 
   /**
@@ -102,13 +104,33 @@ public class DbRefFetcherTest
   {
     String retrievalId = "CAA23748"; // "V00488";
     DbSourceProxy embl = new SequenceFetcher().getSourceProxy(DBRefSource.EMBL).get(0);
-    assertNotNull("Couldn't find the EMBL retrieval client",embl);
+    assertNotNull("Couldn't find the EMBL retrieval client", embl);
+    verifyProteinNucleotideXref(retrievalId, embl);
+  }
+
+  @Test
+  public void testEmblCDSUniprotProductRecovery() throws Exception
+  {
+    String retrievalId = "AAH29712";
+    DbSourceProxy embl = new SequenceFetcher().getSourceProxy(
+            DBRefSource.EMBLCDS).get(0);
+    assertNotNull("Couldn't find the EMBL retrieval client", embl);
+    verifyProteinNucleotideXref(retrievalId, embl);
+  }
+
+  private void verifyProteinNucleotideXref(String retrievalId,
+          DbSourceProxy embl) throws Exception
+  {
     AlignmentI alsq = embl.getSequenceRecords(retrievalId);
     assertNotNull("Couldn't find the EMBL record " + retrievalId, alsq);
     assertEquals("Didn't retrieve right number of records", 1, alsq.getHeight());
     DBRefEntry[] dr = DBRefUtils.selectRefs(alsq.getSequenceAt(0).getDBRef(), DBRefSource.PROTEINSEQ);
     assertNotNull(dr);
     assertEquals("Expected a single Uniprot cross reference", 1, dr.length);
+    assertEquals("Expected cross refernce map to be one amino acid", dr[0]
+            .getMap().getMappedWidth(), 1);
+    assertEquals("Expected local refernce map to be 3 nucleotides", dr[0]
+            .getMap().getWidth(), 3);
     AlignmentI sprods = CrossRef.findXrefSequences(alsq.getSequencesArray(), true, dr[0].getSource(), alsq.getDataset());
     assertNotNull(
             "Couldn't recover cross reference sequence from dataset. Was it ever added ?",