Merge branch 'releases/Release_2_10_4_Branch'
authorJim Procter <jprocter@issues.jalview.org>
Wed, 9 May 2018 20:20:00 +0000 (21:20 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Wed, 9 May 2018 20:20:00 +0000 (21:20 +0100)
178 files changed:
.ant-targets-build.xml
.classpath
.settings/org.eclipse.jdt.core.prefs
RELEASE
THIRDPARTYLIBS
benchmarking/README
benchmarking/src/main/java/org/jalview/HiddenColsIteratorsBenchmark.java [new file with mode: 0644]
benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java
benchmarking/src/main/java/org/jalview/SeqWidthBenchmark.java [new file with mode: 0644]
build.xml
examples/testdata/4IM2_missing.pdb [new file with mode: 0644]
examples/testdata/4IM2_missing_noid.pdb [new file with mode: 0644]
examples/testdata/4IM2_nterm.pdb [new file with mode: 0644]
help/help.jhm
help/helpTOC.xml
help/html/features/chimera.html
help/html/features/jmol.html
help/html/features/preferences.html
help/html/features/schooser_enter-id.png
help/html/features/schooser_main.png
help/html/features/splitView.html
help/html/features/structurechooser.html
help/html/features/viewingpdbs.html
help/html/menus/desktopMenu.html
help/html/releases.html
help/html/whatsNew.html
lib/VAqua4.jar [new file with mode: 0644]
resources/images/idwidth.gif [deleted file]
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/MCview/PDBChain.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/Dna.java
src/jalview/api/structures/JalviewStructureDisplayI.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/appletgui/AnnotationLabels.java
src/jalview/appletgui/AnnotationPanel.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/IdCanvas.java
src/jalview/appletgui/IdwidthAdjuster.java
src/jalview/appletgui/OverviewCanvas.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/appletgui/ScalePanel.java
src/jalview/appletgui/SeqCanvas.java
src/jalview/appletgui/SeqPanel.java
src/jalview/bin/Cache.java
src/jalview/bin/Jalview.java
src/jalview/bin/JalviewLite.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentAnnotation.java
src/jalview/datamodel/AlignmentI.java
src/jalview/datamodel/CigarArray.java
src/jalview/datamodel/DBRefSource.java
src/jalview/datamodel/HiddenColumns.java
src/jalview/datamodel/HiddenColumnsCursor.java [new file with mode: 0644]
src/jalview/datamodel/HiddenCursorPosition.java [new file with mode: 0644]
src/jalview/datamodel/RangeElementsIterator.java [new file with mode: 0644]
src/jalview/datamodel/RangeIterator.java [new file with mode: 0644]
src/jalview/datamodel/SearchResults.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/datamodel/SequenceI.java
src/jalview/datamodel/StartRegionIterator.java [new file with mode: 0644]
src/jalview/datamodel/VisibleColsCollection.java
src/jalview/datamodel/VisibleColsIterator.java [deleted file]
src/jalview/datamodel/VisibleContigsIterator.java [new file with mode: 0644]
src/jalview/ext/ensembl/EnsemblGenomes.java
src/jalview/ext/ensembl/EnsemblLookup.java
src/jalview/ext/ensembl/EnsemblRestClient.java
src/jalview/ext/ensembl/EnsemblSequenceFetcher.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolCommands.java
src/jalview/ext/jmol/JmolParser.java
src/jalview/fts/core/FTSRestClient.java
src/jalview/fts/service/uniprot/UniProtFTSRestClient.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/AnnotationColourChooser.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/AnnotationRowFilter.java
src/jalview/gui/AppJmol.java
src/jalview/gui/AquaInternalFrameManager.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/IdPanel.java
src/jalview/gui/IdwidthAdjuster.java
src/jalview/gui/JDatabaseTree.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/OverviewCanvas.java
src/jalview/gui/OverviewPanel.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.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/StructureChooser.java
src/jalview/gui/StructureViewer.java
src/jalview/gui/StructureViewerBase.java
src/jalview/gui/VamsasApplication.java
src/jalview/io/AlignFile.java
src/jalview/io/AnnotationFile.java
src/jalview/io/FormatAdapter.java
src/jalview/jbgui/GStructureChooser.java
src/jalview/renderer/AnnotationRenderer.java
src/jalview/renderer/OverviewRenderer.java
src/jalview/renderer/ScaleRenderer.java
src/jalview/structure/StructureMapping.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/util/MapList.java
src/jalview/util/MappingUtils.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/OverviewDimensions.java
src/jalview/viewmodel/OverviewDimensionsHideHidden.java
src/jalview/viewmodel/OverviewDimensionsShowHidden.java
src/jalview/viewmodel/ViewportRanges.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/ws/DBRefFetcher.java
src/jalview/ws/dbsources/Pfam.java
src/jalview/ws/dbsources/PfamFull.java
src/jalview/ws/dbsources/PfamSeed.java
src/jalview/ws/dbsources/Rfam.java
src/jalview/ws/dbsources/RfamFull.java
src/jalview/ws/dbsources/RfamSeed.java
src/jalview/ws/dbsources/Uniprot.java
src/jalview/ws/dbsources/Xfam.java
src/jalview/ws/jws1/JPredThread.java
src/jalview/ws/sifts/SiftsClient.java
test/MCview/PDBChainTest.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/analysis/CrossRefTest.java
test/jalview/analysis/DnaTest.java
test/jalview/datamodel/AlignmentAnnotationTests.java
test/jalview/datamodel/AlignmentTest.java
test/jalview/datamodel/ColumnSelectionTest.java
test/jalview/datamodel/HiddenColumnsCursorTest.java [new file with mode: 0644]
test/jalview/datamodel/HiddenColumnsTest.java
test/jalview/datamodel/RangeElementsIteratorTest.java [moved from test/jalview/datamodel/VisibleColsIteratorTest.java with 84% similarity]
test/jalview/datamodel/SequenceTest.java
test/jalview/datamodel/StartRegionIteratorTest.java [new file with mode: 0644]
test/jalview/datamodel/VisibleContigsIteratorTest.java [new file with mode: 0644]
test/jalview/ext/ensembl/EnsemblGeneTest.java
test/jalview/ext/jmol/JmolCommandsTest.java
test/jalview/ext/jmol/JmolViewerTest.java
test/jalview/gui/AlignFrameTest.java
test/jalview/gui/AnnotationColumnChooserTest.java
test/jalview/gui/PopupMenuTest.java
test/jalview/gui/SeqCanvasTest.java
test/jalview/gui/StructureChooserTest.java
test/jalview/io/AnnotatedPDBFileInputTest.java
test/jalview/io/CrossRef2xmlTests.java
test/jalview/io/JSONFileTest.java
test/jalview/io/Jalview2xmlBase.java
test/jalview/io/Jalview2xmlTests.java
test/jalview/renderer/OverviewRendererTest.java [new file with mode: 0644]
test/jalview/renderer/seqfeatures/FeatureRendererTest.java
test/jalview/structure/Mapping.java
test/jalview/structure/StructureMappingTest.java
test/jalview/structure/StructureSelectionManagerTest.java
test/jalview/util/MapListTest.java
test/jalview/util/MappingUtilsTest.java
test/jalview/ws/PDBSequenceFetcherTest.java
test/jalview/ws/dbsources/PfamFullTest.java [new file with mode: 0644]
test/jalview/ws/dbsources/PfamSeedTest.java [new file with mode: 0644]
test/jalview/ws/dbsources/RfamFullTest.java [new file with mode: 0644]
test/jalview/ws/dbsources/RfamSeedTest.java [new file with mode: 0644]
test/jalview/ws/dbsources/XfamFetcherTest.java
test/jalview/ws/sifts/SiftsClientTest.java
utils/InstallAnywhere/Jalview.iap_xml
utils/InstallAnywhere/jalview_buildinstaller.xml
utils/proguard.jar [deleted file]
utils/proguard_5.3.3.jar [new file with mode: 0755]

index 15432a1..7ef21f1 100644 (file)
@@ -1,31 +1,2 @@
-build
-buildPropertiesFile
-buildTests
-buildextclients
-buildindices
-castorbinding
-clean
-compileApplet
-distclean
 help
-init
-linkcheck
-makeApplet
-makedist
-makefulldist
-obfuscate
-packageApplet
-prepare
-prepareTests
-preparejnlp
-prepubapplet_1
-pubapplet
-runenv
-signApplet
-sourcedist
-sourcedoc
-sourcescrub
-testclean
-testng
 usage
-writejnlpf
index d704f10..3a05b47 100644 (file)
        <classpathentry kind="lib" path="lib/jfreesvg-2.1.jar"/>
        <classpathentry kind="lib" path="lib/quaqua-filechooser-only-8.0.jar"/>
        <classpathentry kind="lib" path="lib/htsjdk-1.133.jar"/>
+       <classpathentry kind="lib" path="lib/VAqua4.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"/>
@@ -69,5 +69,6 @@
        <classpathentry kind="lib" path="lib/biojava-core-4.1.0.jar"/>
        <classpathentry kind="lib" path="lib/biojava-ontology-4.1.0.jar"/>
        <classpathentry kind="lib" path="lib/groovy-all-2.4.12-indy.jar"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
        <classpathentry kind="output" path="classes"/>
 </classpath>
index 8a5e7a7..5908bb2 100644 (file)
@@ -1,15 +1,15 @@
 eclipse.preferences.version=1
 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
 org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.compliance=1.8
 org.eclipse.jdt.core.compiler.debug.lineNumber=generate
 org.eclipse.jdt.core.compiler.debug.localVariable=generate
 org.eclipse.jdt.core.compiler.debug.sourceFile=generate
 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.compiler.source=1.8
 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=52
diff --git a/RELEASE b/RELEASE
index eb9d7cd..5ad87c8 100644 (file)
--- a/RELEASE
+++ b/RELEASE
@@ -1,2 +1,2 @@
-jalview.release=releases/Release_2_10_3_Branch
-jalview.version=2.10.3b1
+jalview.release=releases/Release_2_10_4_Branch
+jalview.version=2.10.4
index e0be904..e2baa85 100644 (file)
@@ -49,6 +49,8 @@ jfreesvg-2.1.jar : GPL v3 licensed library from the JFree suite: http://www.jfre
 
 quaqua: v.8.0 (latest stable) by Randel S Hofer. LGPL and BSD Modified license: downloaded from http://www.randelshofer.ch/quaqua/ 
 
+vaqua: v4 (latest stable) by Alan Snyder et al. GPLv2 with Classpathe xception, also includes contributions from Quaqua: ownloaded from http://violetlib.org/vaqua/overview.html
+
 lib/htsjdk-1.120-SNAPSHOT.jar: (currently not required for 2.10) built from maven master at https://github.com/samtools/htsjdk MIT License to Broad Institute
 
 lib/biojava-core-4.1.0.jar LGPLv2.1 - latest license at https://github.com/biojava/biojava/blob/master/LICENSE
index ccf53c1..d1ec146 100644 (file)
@@ -27,6 +27,9 @@ to install the jalview.jar file in the local maven repository. The pom.xml in th
   To get JSON output instead use:
   java -jar target/benchmarks.jar -rf json
   
+  To run a specific benchmark file use:
+  java -jar target/benchmarks.jar <Benchmark class name> 
+  
   JSON output can be viewed quickly by drag-dropping on http://jmh.morethan.io/
   
   To get help use the standard -h option:
@@ -38,3 +41,4 @@ to install the jalview.jar file in the local maven repository. The pom.xml in th
   
   
  6. If you make changes to the Jalview code everything will need to be refreshed, by performing steps 3-5 again.
+
diff --git a/benchmarking/src/main/java/org/jalview/HiddenColsIteratorsBenchmark.java b/benchmarking/src/main/java/org/jalview/HiddenColsIteratorsBenchmark.java
new file mode 100644 (file)
index 0000000..477bfad
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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 org.jalview;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Param;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
+
+/*
+ * A class to benchmark hidden columns performance
+ */
+@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
+@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
+@Fork(1)
+public class HiddenColsIteratorsBenchmark {
+       /*
+        * State with multiple hidden columns and a start position set
+        */
+       @State(Scope.Thread)
+       public static class HiddenColsAndStartState
+       {
+               @Param({"300", "10000", "100000"})
+               public int maxcols;
+               
+               @Param({"1", "50", "90"})
+               public int startpcnt; // position as percentage of maxcols
+               
+               @Param({"1","15","100"})
+               public int hide;
+               
+               HiddenColumns h = new HiddenColumns();
+               Random rand = new Random();
+               
+               public int hiddenColumn;
+               public int visibleColumn;
+       
+               @Setup
+               public void setup()
+               {
+                       rand.setSeed(1234);
+                       int lastcol = 0;
+               while (lastcol < maxcols)
+               {
+                       int count = rand.nextInt(100); 
+                       lastcol += count;
+                       h.hideColumns(lastcol, lastcol+hide);
+                       lastcol+=hide;
+               }
+               
+               // make sure column at start is hidden
+               hiddenColumn = (int)(maxcols * startpcnt/100.0);
+               h.hideColumns(hiddenColumn, hiddenColumn);
+               
+               // and column <hide> after start is visible
+               ColumnSelection sel = new ColumnSelection();
+               h.revealHiddenColumns(hiddenColumn+hide, sel);
+               visibleColumn = hiddenColumn+hide;
+               
+               System.out.println("Maxcols: " + maxcols + " HiddenCol: " + hiddenColumn + " Hide: " + hide);
+               System.out.println("Number of hidden columns: " + h.getSize());
+               }
+       }
+       
+       /* Convention: functions in alphabetical order */
+       
+       @Benchmark
+       @BenchmarkMode({Mode.Throughput})
+       public int benchStartIterator(HiddenColsAndStartState tstate)
+       {
+               int res = 0;
+               int startx = tstate.visibleColumn;
+               Iterator<Integer> it = tstate.h.getStartRegionIterator(startx,
+                               startx+60);
+        while (it.hasNext())
+        {
+          res = it.next() - startx;
+          Blackhole.consumeCPU(5);
+        }
+        return res;
+       }
+       
+       @Benchmark
+       @BenchmarkMode({Mode.Throughput})
+       public int benchBoundedIterator(HiddenColsAndStartState tstate)
+       {
+               int startx = tstate.visibleColumn;
+               int blockStart = startx;
+               int blockEnd;
+               int screenY = 0;
+               Iterator<int[]> it = tstate.h.getBoundedIterator(startx,
+                               startx+60);
+        while (it.hasNext())
+        {
+               int[] region = it.next();
+          
+               blockEnd = Math.min(region[0] - 1, blockStart + 60 - screenY);
+             
+               screenY += blockEnd - blockStart + 1;
+               blockStart = region[1] + 1;
+               
+               Blackhole.consumeCPU(5);
+        }
+        return blockStart;
+       }
+}
index d3c67d7..07b76f9 100644 (file)
@@ -47,128 +47,111 @@ import jalview.datamodel.HiddenColumns;
 @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
 @Fork(1)
 public class HiddenColumnsBenchmark 
-{
-  /*
-   * State with multiple hidden columns and a start position set
-   */
-  @State(Scope.Thread)
-  public static class HiddenColsAndStartState
-  {
-    @Param({ "300", "10000", "100000" })
-    public int maxcols;
-
-    @Param({ "1", "50", "90" })
-    public int startpcnt; // position as percentage of maxcols
-
-    @Param({ "1", "15", "100" })
-    public int hide;
-
-    HiddenColumns h = new HiddenColumns();
-
-    Random rand = new Random();
-
-    public int hiddenColumn;
-
-    public int visibleColumn;
-
-    @Setup
-    public void setup()
+{      
+       /*
+        * State with multiple hidden columns and a start position set
+        */
+       @State(Scope.Thread)
+       public static class HiddenColsAndStartState
+       {
+               @Param({"100", "1000", "10000", "100000", "1000000"})
+               public int maxcols;
+               
+               @Param({"1", "50", "90"})
+               public int startpcnt; // position as percentage of maxcols
+               
+               @Param({"1","15","100"})
+               public int hide;
+               
+               HiddenColumns h = new HiddenColumns();
+               Random rand = new Random();
+               
+               public int hiddenColumn;
+               public int visibleColumn;
+       
+               @Setup
+               public void setup()
+               {
+                       rand.setSeed(1234);
+                       int lastcol = 0;
+               while (lastcol < maxcols)
+               {
+                       int count = rand.nextInt(100); 
+                       lastcol += count;
+                       h.hideColumns(lastcol, lastcol+hide);
+                       lastcol+=hide;
+               }
+               
+               // make sure column at start is hidden
+               hiddenColumn = (int)(maxcols * startpcnt/100.0);
+               h.hideColumns(hiddenColumn, hiddenColumn);
+               
+               // and column <hide> after start is visible
+               ColumnSelection sel = new ColumnSelection();
+               h.revealHiddenColumns(hiddenColumn+hide, sel);
+               visibleColumn = hiddenColumn+hide;
+               
+               System.out.println("Maxcols: " + maxcols + " HiddenCol: " + hiddenColumn + " Hide: " + hide);
+               System.out.println("Number of hidden columns: " + h.getSize());
+               }
+       }
+       
+       /* Convention: functions in alphabetical order */
+       
+       @Benchmark
+       @BenchmarkMode({Mode.Throughput})
+       public int benchAdjustForHiddenColumns(HiddenColsAndStartState tstate)
+       {
+               return tstate.h.visibleToAbsoluteColumn(tstate.visibleColumn);
+       }
+       
+       @Benchmark
+       @BenchmarkMode({Mode.Throughput})
+       public int benchFindColumnPosition(HiddenColsAndStartState tstate)
+       {
+               return tstate.h.absoluteToVisibleColumn(tstate.visibleColumn);
+       }
+       
+       @Benchmark
+       @BenchmarkMode({Mode.Throughput})
+    public int benchGetSize(HiddenColsAndStartState tstate)
     {
-      rand.setSeed(1234);
-      int lastcol = 0;
-      while (lastcol < maxcols)
-      {
-        int count = rand.nextInt(100);
-        lastcol += count;
-        h.hideColumns(lastcol, lastcol + hide);
-        lastcol += hide;
-      }
-
-      // make sure column at start is hidden
-      hiddenColumn = (int) (maxcols * startpcnt / 100.0);
-      h.hideColumns(hiddenColumn, hiddenColumn);
-
-      // and column <hide> after start is visible
-      ColumnSelection sel = new ColumnSelection();
-      h.revealHiddenColumns(hiddenColumn + hide, sel);
-      visibleColumn = hiddenColumn + hide;
-
-      System.out.println("Maxcols: " + maxcols + " HiddenCol: "
-              + hiddenColumn + " Hide: " + hide);
-      System.out.println("Number of hidden columns: " + h.getSize());
+               return tstate.h.getSize();
     }
-  }
-
-  /* Convention: functions in alphabetical order */
-
-  @Benchmark
-  @BenchmarkMode({ Mode.Throughput })
-  public int benchAdjustForHiddenColumns(HiddenColsAndStartState tstate)
-  {
-    return tstate.h.adjustForHiddenColumns(tstate.visibleColumn);
-  }
-
-  @Benchmark
-  @BenchmarkMode({ Mode.Throughput })
-  public int benchFindColumnPosition(HiddenColsAndStartState tstate)
-  {
-    return tstate.h.findColumnPosition(tstate.visibleColumn);
-  }
-
-  @Benchmark
-  @BenchmarkMode({ Mode.Throughput })
-  public List<Integer> benchFindHiddenRegionPositions(
-          HiddenColsAndStartState tstate)
-  {
-    return tstate.h.findHiddenRegionPositions();
-  }
-
-  @Benchmark
-  @BenchmarkMode({ Mode.Throughput })
-  public ArrayList<int[]> benchGetHiddenColumnsCopy(
-          HiddenColsAndStartState tstate)
-  {
-    return tstate.h.getHiddenColumnsCopy();
-  }
-
-  @Benchmark
-  @BenchmarkMode({ Mode.Throughput })
-  public int benchGetSize(HiddenColsAndStartState tstate)
-  {
-    return tstate.h.getSize();
-  }
-
-  @Benchmark
-  @BenchmarkMode({ Mode.Throughput })
-  public HiddenColumns benchHideCols(HiddenColsAndStartState tstate)
-  {
-    tstate.h.hideColumns(tstate.visibleColumn, tstate.visibleColumn + 2000);
-    return tstate.h;
-  }
-
-  @Benchmark
-  @BenchmarkMode({ Mode.Throughput })
-  public boolean benchIsVisible(HiddenColsAndStartState tstate)
-  {
-    return tstate.h.isVisible(tstate.hiddenColumn);
-  }
-
-  @Benchmark
-  @BenchmarkMode({ Mode.Throughput })
-  public HiddenColumns benchReveal(HiddenColsAndStartState tstate)
-  {
-    ColumnSelection sel = new ColumnSelection();
-    tstate.h.revealHiddenColumns(tstate.hiddenColumn, sel);
-    return tstate.h;
-  }
-
-  @Benchmark
-  @BenchmarkMode({ Mode.Throughput })
-  public HiddenColumns benchRevealAll(HiddenColsAndStartState tstate)
-  {
-    ColumnSelection sel = new ColumnSelection();
-    tstate.h.revealAllHiddenColumns(sel);
-    return tstate.h;
-  }
 
+   @Benchmark
+    @BenchmarkMode({Mode.Throughput})
+    public HiddenColumns benchHideCols(HiddenColsAndStartState tstate) 
+    {
+       tstate.h.hideColumns(tstate.visibleColumn,
+                       tstate.visibleColumn+2000); 
+       return tstate.h;
+    }
+   
+       @Benchmark
+       @BenchmarkMode({Mode.Throughput})
+    public boolean benchIsVisible(HiddenColsAndStartState tstate) 
+    {
+       return tstate.h.isVisible(tstate.hiddenColumn); 
+    }
+       
+       @Benchmark
+       @BenchmarkMode({Mode.Throughput})
+    public HiddenColumns benchReveal(HiddenColsAndStartState tstate) 
+    {
+               ColumnSelection sel = new ColumnSelection();
+       tstate.h.revealHiddenColumns(tstate.hiddenColumn, sel);
+       return tstate.h;
+    }
+       
+       @Benchmark
+       @BenchmarkMode({Mode.Throughput})
+    public HiddenColumns benchRevealAll(HiddenColsAndStartState tstate) 
+    {
+               ColumnSelection sel = new ColumnSelection();
+       tstate.h.revealAllHiddenColumns(sel);
+       return tstate.h;
+    }
+       
+       
 }
\ No newline at end of file
diff --git a/benchmarking/src/main/java/org/jalview/SeqWidthBenchmark.java b/benchmarking/src/main/java/org/jalview/SeqWidthBenchmark.java
new file mode 100644 (file)
index 0000000..a92d4f0
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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 org.jalview;
+
+import org.jalview.HiddenColumnsBenchmark.HiddenColsAndStartState;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Param;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+/*
+ * A class to benchmark hidden columns performance
+ */
+@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
+@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
+@Fork(1)
+public class SeqWidthBenchmark {
+
+       /*
+        * State with multiple hidden columns and a start position set
+        */
+       @State(Scope.Thread)
+       public static class AlignmentState
+       {
+               @Param({"100", "1000", "10000", "100000"})
+               public int numSeqs;
+               
+               Random rand = new Random();
+               
+               AlignmentI al;
+               
+               @Setup
+               public void setup()
+               {
+                       rand.setSeed(1234);
+                       
+                       SequenceI[] seqs = new Sequence[numSeqs];
+                   for (int i = 0; i < numSeqs; i++)
+                   {
+                     int count = rand.nextInt(10000); 
+                     StringBuilder aas = new StringBuilder();
+                     for (int j=0; j<count; j++)
+                     {
+                        aas.append("a");
+                     }
+
+                     seqs[i] = new Sequence("Sequence" + i, aas.toString());
+                   }
+                       al = new Alignment(seqs);
+               }
+       }
+       
+       
+       @Benchmark
+       @BenchmarkMode({Mode.Throughput})
+       public int benchSeqGetWidth(AlignmentState tstate)
+       {
+               return tstate.al.getWidth();
+       }
+       
+}
index a414491..3d26efc 100755 (executable)
--- a/build.xml
+++ b/build.xml
@@ -17,7 +17,9 @@
  * 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.
 -->
-<project name="jalviewX" default="usage" basedir=".">
+<project name="jalviewX" default="usage" basedir="."
+ xmlns:if="ant:if"
+    xmlns:unless="ant:unless">
   <target name="help" depends="usage" />
   <target name="usage" depends="init">
     <echo message="~~~Jalview Ant build.xml Usage~~~~" />
@@ -36,7 +38,7 @@
     <echo message="testng - run jalview's tests via testNG. Default group is '${testng-groups}'" />
     <echo message="      you can specify particular test groups as a list via -Dtestng-groups=" />
     <echo message="See docs/building.html and the comments in build file for other targets." />
-    <echo message="note: compile and makeApplet require the property java118.home to be set to point to a java 1.1.8 jdk." />
+    <echo message="note: compile and makeApplet optionally compile/obfuscate applet against a different Java version by specifying -Djava118.home=PathtoJDK/lib which is the lib directory in the JDK install that contains rt.jar " />
     <echo message="Useful -D flags: -Ddonotobfuscate will prevent applet obfuscation" />
   </target>
 
 
     <!-- default TestNG groups to run -->
     <property name="testng-groups" value="Functional" />
+    <!-- Java 9 JVM args -->
+    <condition property="java9">
+      <equals arg1="${ant.java.version}" arg2="9"/>
+    </condition>
 
     <!-- Don't change anything below here unless you know what you are doing! -->
     <!-- Url path for WebStart in JNLP file -->
     <!-- Anne's version needs 1.7 - should rebuild VARNA to java 1.6 for release -->
     <property name="j2sev" value="1.7+" />
     <!-- Java Compilation settings - source and target javac version -->
-    <property name="javac.source" value="1.7" />
-    <property name="javac.target" value="1.7" />
+    <property name="javac.source" value="1.8" />
+    <property name="javac.target" value="1.8" />
 
     <!-- Permissions for running Java applets and applications. -->
     <!-- Defaults are those suitable for deploying jalview webstart www.jalview.org -->
     </path>
     <property name="source.dist.name" value="${basedir}/jalview-src.tar.gz" />
     <!-- The Location of the java 1.1.8 jdk -->
-    <!--<property name="java118.home" value="C:\Sun\jdk1.1.8" /> -->
     <property name="java118.home" value="${java.home}" />
-    <!-- <property name="applet.jre.tools" value="${java118.home}/lib/classes.zip" />
-               -->
     <!-- jre for 1.4 version -->
-    <property name="applet.jre.tools" value="${java.home}/lib/rt.jar" />
+    <property name="applet.jre.tools" value="${java118.home}/lib/rt.jar" />
 
     <!-- the classpath for building the 1.1 applet -->
     <path id="jalviewlite.deps">
       <fileset dir="${java118.home}">
-        <include name="lib/classes.zip" />
+        <include name="lib/rt.jar" />
       </fileset>
       <fileset dir="${java.home}/lib">
         <include name="plugin.jar" />
         <pathelement location="${testOutputDir}" />
         <path refid="test.classpath" />
       </classpath>
+      <jvmarg value="--add-modules=java.se.ee" if:set="java9"/>
+      <jvmarg value="--illegal-access=warn" if:set="java9"/>
       <classfileset dir="${testOutputDir}" includes="**/*.class" />
     </testng>
   </target>
           <offline_allowed />
         </information>
         <resources>
-          <j2se version="1.7+" />
+          <j2se version="1.8+" />
           <jar main="true" href="jalview.jar"/>
           <fileset dir="${packageDir}">
             <exclude name="jalview.jar" />
 
     <jnlpf toFile="${jnlpFile}" />
     <!-- add the add-modules j2se attribute for java 9 -->
-    <replace file="${jnlpFile}" value="j2se version=&quot;1.7+&quot; initial-heap-size=&quot;${inih}&quot; max-heap-size=&quot;${maxh}&quot; java-vm-args=&quot;--add-modules=java.se.ee --illegal-access=warn&quot;">
-          <replacetoken>j2se version="1.7+"</replacetoken>
+    <replace file="${jnlpFile}" value="j2se version=&quot;1.8+&quot; initial-heap-size=&quot;${inih}&quot; max-heap-size=&quot;${maxh}&quot; java-vm-args=&quot;--add-modules=java.se.ee --illegal-access=warn&quot;">
+          <replacetoken>j2se version="1.8+"</replacetoken>
            
         </replace>
   </target>
 </target>
 
 <target name="packageApplet" depends="compileApplet, buildPropertiesFile">
-  <copy file="${resourceDir}/images/idwidth.gif" toFile="${outputDir}/images/idwidth.gif" />
   <copy file="${resourceDir}/images/link.gif" toFile="${outputDir}/images/link.gif" />
   <copy todir="${outputDir}/lang">
     <fileset dir="${resourceDir}/lang">
       <include name="MCview/**" />
       <include name="jalview/**" />
       <include name=".build_properties" />
-      <include name="images/idwidth.gif" />
       <include name="images/link.gif" />
       <include name="lang/**" />
     </fileset>
       <include name="plugin.jar" />
     </fileset>
   </path>
-  <taskdef resource="proguard/ant/task.properties" classpath="utils/proguard.jar" />
+  <taskdef resource="proguard/ant/task.properties" classpath="utils/proguard_5.3.3.jar" />
 
   <proguard verbose="true" >
     <injar file="in.jar" />
diff --git a/examples/testdata/4IM2_missing.pdb b/examples/testdata/4IM2_missing.pdb
new file mode 100644 (file)
index 0000000..35f94b2
--- /dev/null
@@ -0,0 +1,525 @@
+HEADER    TRANSFERASE/TRANSFERASE INHIBITOR       01-JAN-13   4IM2              
+TITLE     STRUCTURE OF TANK-BINDING KINASE 1  (Truncated test file)
+ATOM   3510  N   LEU A 468      76.337  90.332  -7.628  1.00168.53           N  
+ANISOU 3510  N   LEU A 468    19760  15191  29082   5189   1248  -1702       N  
+ATOM   3511  CA  LEU A 468      75.576  90.788  -6.476  1.00181.60           C  
+ANISOU 3511  CA  LEU A 468    21073  16927  30998   5158   1421  -2051       C  
+ATOM   3512  C   LEU A 468      76.285  91.971  -5.836  1.00196.57           C  
+ANISOU 3512  C   LEU A 468    23029  18636  33021   5053   1667  -2229       C  
+ATOM   3513  O   LEU A 468      75.727  93.067  -5.733  1.00197.97           O  
+ANISOU 3513  O   LEU A 468    23115  18581  33523   5166   1662  -2304       O  
+ATOM   3514  CB  LEU A 468      75.409  89.671  -5.449  1.00173.72           C  
+ANISOU 3514  CB  LEU A 468    19829  16340  29836   4981   1593  -2304       C  
+ATOM   3515  CG  LEU A 468      74.099  88.882  -5.420  1.00170.58           C  
+ANISOU 3515  CG  LEU A 468    19142  16142  29529   5074   1449  -2345       C  
+ATOM   3516  CD1 LEU A 468      74.023  87.940  -6.581  1.00167.98           C  
+ANISOU 3516  CD1 LEU A 468    18951  15858  29015   5180   1156  -2025       C  
+ATOM   3517  CD2 LEU A 468      73.988  88.096  -4.145  1.00165.16           C  
+ANISOU 3517  CD2 LEU A 468    18217  15818  28720   4865   1706  -2656       C  
+ATOM   3518  N   ASP A 469      77.529  91.753  -5.429  1.00208.80           N  
+ANISOU 3518  N   ASP A 469    24731  20282  34322   4836   1875  -2296       N  
+ATOM   3519  CA  ASP A 469      78.267  92.785  -4.720  1.00223.11           C  
+ANISOU 3519  CA  ASP A 469    26577  21960  36233   4701   2123  -2500       C  
+ATOM   3520  C   ASP A 469      78.653  93.973  -5.599  1.00231.96           C  
+ANISOU 3520  C   ASP A 469    27954  22654  37525   4817   2037  -2283       C  
+ATOM   3521  O   ASP A 469      78.718  95.100  -5.118  1.00235.71           O  
+ANISOU 3521  O   ASP A 469    28377  22940  38242   4798   2175  -2449       O  
+ATOM   3522  CB  ASP A 469      79.506  92.197  -4.057  1.00224.50           C  
+ANISOU 3522  CB  ASP A 469    26841  22370  36089   4433   2350  -2633       C  
+ATOM   3523  CG  ASP A 469      79.748  92.767  -2.679  1.00229.51           C  
+ANISOU 3523  CG  ASP A 469    27303  23107  36794   4254   2637  -3018       C  
+ATOM   3524  OD1 ASP A 469      79.958  93.992  -2.564  1.00232.36           O  
+ANISOU 3524  OD1 ASP A 469    27695  23203  37387   4262   2718  -3093       O  
+ATOM   3525  OD2 ASP A 469      79.751  91.979  -1.709  1.00229.48           O  
+ANISOU 3525  OD2 ASP A 469    27145  23450  36598   4098   2779  -3242       O  
+ATOM   3526  N   PHE A 470      78.904  93.735  -6.882  1.00233.88           N  
+ANISOU 3526  N   PHE A 470    28481  22742  37641   4931   1813  -1912       N  
+ATOM   3527  CA  PHE A 470      79.292  94.831  -7.766  1.00237.73           C  
+ANISOU 3527  CA  PHE A 470    29252  22819  38256   5033   1733  -1677       C  
+ATOM   3528  C   PHE A 470      78.103  95.470  -8.467  1.00232.15           C  
+ANISOU 3528  C   PHE A 470    28512  21868  37828   5322   1470  -1512       C  
+ATOM   3529  O   PHE A 470      78.021  96.691  -8.532  1.00230.69           O  
+ANISOU 3529  O   PHE A 470    28371  21372  37909   5402   1493  -1517       O  
+ATOM   3530  CB  PHE A 470      80.344  94.379  -8.784  1.00242.99           C  
+ANISOU 3530  CB  PHE A 470    30297  23410  38616   4976   1661  -1357       C  
+ATOM   3531  CG  PHE A 470      80.663  95.388  -9.839  1.00251.53           C  
+ANISOU 3531  CG  PHE A 470    31709  24074  39788   5089   1550  -1064       C  
+ATOM   3532  CD1 PHE A 470      81.658  96.315  -9.620  1.00252.90           C  
+ANISOU 3532  CD1 PHE A 470    32021  24036  40032   4942   1771  -1133       C  
+ATOM   3533  CD2 PHE A 470      80.005  95.389 -11.059  1.00255.02           C  
+ANISOU 3533  CD2 PHE A 470    32335  24339  40221   5330   1225   -715       C  
+ATOM   3534  CE1 PHE A 470      81.981  97.242 -10.575  1.00254.75           C  
+ANISOU 3534  CE1 PHE A 470    32569  23879  40344   5028   1688   -862       C  
+ATOM   3535  CE2 PHE A 470      80.321  96.320 -12.027  1.00256.76           C  
+ANISOU 3535  CE2 PHE A 470    32894  24173  40491   5427   1126   -433       C  
+ATOM   3536  CZ  PHE A 470      81.313  97.248 -11.784  1.00256.57           C  
+ANISOU 3536  CZ  PHE A 470    33006  23929  40550   5272   1365   -504       C  
+ATOM   3537  N   CYS A 471      77.190  94.665  -8.997  1.00176.88           N  
+ANISOU 3537  N   CYS A 471    20955  21812  24441   2225  -3821  -3711       N  
+ATOM   3538  CA  CYS A 471      76.124  95.233  -9.815  1.00176.41           C  
+ANISOU 3538  CA  CYS A 471    20820  21906  24301   2124  -3576  -3614       C  
+ATOM   3539  C   CYS A 471      74.868  95.631  -9.050  1.00176.82           C  
+ANISOU 3539  C   CYS A 471    21026  22206  23952   2247  -3588  -3734       C  
+ATOM   3540  O   CYS A 471      74.632  96.811  -8.854  1.00178.66           O  
+ANISOU 3540  O   CYS A 471    21175  22393  24314   2353  -3749  -3873       O  
+ATOM   3541  CB  CYS A 471      75.805  94.346 -11.019  1.00173.95           C  
+ANISOU 3541  CB  CYS A 471    20521  21694  23877   1925  -3217  -3359       C  
+ATOM   3542  SG  CYS A 471      76.954  94.571 -12.398  1.00273.04           S  
+ANISOU 3542  SG  CYS A 471    32850  33934  36957   1863  -3075  -3146       S  
+ATOM   3543  N   ILE A 472      74.068  94.670  -8.603  1.00173.50           N  
+ANISOU 3543  N   ILE A 472    20813  22023  23085   2253  -3402  -3662       N  
+ATOM   3544  CA  ILE A 472      72.771  95.033  -8.022  1.00171.09           C  
+ANISOU 3544  CA  ILE A 472    20616  21936  22453   2374  -3307  -3694       C  
+ATOM   3545  C   ILE A 472      72.832  96.022  -6.874  1.00177.82           C  
+ANISOU 3545  C   ILE A 472    21591  22750  23222   2698  -3611  -3931       C  
+ATOM   3546  O   ILE A 472      71.992  96.914  -6.776  1.00182.92           O  
+ANISOU 3546  O   ILE A 472    22220  23491  23792   2778  -3598  -3985       O  
+ATOM   3547  CB  ILE A 472      71.979  93.843  -7.518  1.00159.41           C  
+ANISOU 3547  CB  ILE A 472    19318  20653  20599   2393  -3038  -3546       C  
+ATOM   3548  CG1 ILE A 472      70.708  93.667  -8.341  1.00147.27           C  
+ANISOU 3548  CG1 ILE A 472    17653  19253  19051   2196  -2745  -3384       C  
+ATOM   3549  CG2 ILE A 472      71.526  94.090  -6.105  1.00155.95           C  
+ANISOU 3549  CG2 ILE A 472    19129  20312  19812   2752  -3085  -3637       C  
+ATOM   3550  CD1 ILE A 472      70.927  92.915  -9.606  1.00135.92           C  
+ANISOU 3550  CD1 ILE A 472    16097  17752  17794   1929  -2643  -3254       C  
+ATOM   3551  N   ARG A 473      73.812  95.865  -5.996  1.00180.33           N  
+ANISOU 3551  N   ARG A 473    22055  22917  23544   2920  -3920  -4092       N  
+ATOM   3552  CA  ARG A 473      73.860  96.762  -4.877  1.00189.65           C  
+ANISOU 3552  CA  ARG A 473    23415  24034  24610   3307  -4283  -4357       C  
+ATOM   3553  C   ARG A 473      74.328  98.021  -5.558  1.00185.98           C  
+ANISOU 3553  C   ARG A 473    22628  23344  24692   3195  -4530  -4477       C  
+ATOM   3554  O   ARG A 473      73.519  98.885  -5.866  1.00181.76           O  
+ANISOU 3554  O   ARG A 473    22002  22904  24153   3165  -4446  -4474       O  
+ATOM   3555  CB  ARG A 473      74.803  96.287  -3.768  1.00205.24           C  
+ANISOU 3555  CB  ARG A 473    25661  25862  26460   3635  -4629  -4545       C  
+ATOM   3556  CG  ARG A 473      74.468  96.921  -2.433  1.00220.50           C  
+ANISOU 3556  CG  ARG A 473    27955  27814  28011   4171  -4923  -4787       C  
+ATOM   3557  CD  ARG A 473      75.337  96.433  -1.295  1.00232.58           C  
+ANISOU 3557  CD  ARG A 473    29837  29192  29341   4591  -5300  -5003       C  
+ATOM   3558  NE  ARG A 473      76.763  96.602  -1.549  1.00237.46           N  
+ANISOU 3558  NE  ARG A 473    30220  29440  30564   4489  -5773  -5205       N  
+ATOM   3559  CZ  ARG A 473      77.716  95.991  -0.861  1.00240.32           C  
+ANISOU 3559  CZ  ARG A 473    30786  29623  30903   4725  -6098  -5371       C  
+ATOM   3560  NH1 ARG A 473      77.388  95.172   0.115  1.00241.12           N  
+ANISOU 3560  NH1 ARG A 473    31370  29900  30344   5100  -5978  -5355       N  
+ATOM   3561  NH2 ARG A 473      78.988  96.195  -1.159  1.00241.54           N  
+ANISOU 3561  NH2 ARG A 473    30651  29398  31724   4605  -6518  -5533       N  
+ATOM   3562  N   ASN A 474      75.608  98.073  -5.901  1.00188.74           N  
+ANISOU 3562  N   ASN A 474    22768  23381  25563   3107  -4772  -4534       N  
+ATOM   3563  CA  ASN A 474      76.168  99.264  -6.526  1.00188.61           C  
+ANISOU 3563  CA  ASN A 474    22395  23075  26194   3026  -4977  -4605       C  
+ATOM   3564  C   ASN A 474      75.420  99.892  -7.729  1.00173.98           C  
+ANISOU 3564  C   ASN A 474    20312  21326  24467   2781  -4623  -4414       C  
+ATOM   3565  O   ASN A 474      75.482 101.107  -7.891  1.00176.08           O  
+ANISOU 3565  O   ASN A 474    20374  21426  25104   2827  -4803  -4518       O  
+ATOM   3566  CB  ASN A 474      77.650  99.066  -6.857  1.00199.45           C  
+ANISOU 3566  CB  ASN A 474    23514  24060  28207   2939  -5170  -4595       C  
+ATOM   3567  CG  ASN A 474      78.504  98.809  -5.625  1.00210.23           C  
+ANISOU 3567  CG  ASN A 474    25062  25219  29597   3243  -5689  -4883       C  
+ATOM   3568  OD1 ASN A 474      78.174  99.240  -4.527  1.00215.26           O  
+ANISOU 3568  OD1 ASN A 474    25969  25891  29928   3596  -6043  -5160       O  
+ATOM   3569  ND2 ASN A 474      79.613  98.103  -5.810  1.00212.07           N  
+ANISOU 3569  ND2 ASN A 474    25176  25222  30179   3148  -5748  -4822       N  
+ATOM   3570  N   ILE A 475      74.722  99.114  -8.562  1.00161.86           N  
+ANISOU 3570  N   ILE A 475    18813  20033  22652   2556  -4165  -4158       N  
+ATOM   3571  CA  ILE A 475      74.022  99.727  -9.709  1.00154.84           C  
+ANISOU 3571  CA  ILE A 475    17755  19216  21861   2390  -3885  -4015       C  
+ATOM   3572  C   ILE A 475      72.785 100.520  -9.312  1.00159.27           C  
+ANISOU 3572  C   ILE A 475    18401  19995  22121   2490  -3891  -4125       C  
+ATOM   3573  O   ILE A 475      72.662 101.687  -9.663  1.00162.60           O  
+ANISOU 3573  O   ILE A 475    18651  20327  22804   2505  -3962  -4186       O  
+ATOM   3574  CB  ILE A 475      73.600  98.727 -10.818  1.00214.93           C  
+ANISOU 3574  CB  ILE A 475    25397  26975  29293   2173  -3475  -3753       C  
+ATOM   3575  CG1 ILE A 475      74.808  98.227 -11.613  1.00218.74           C  
+ANISOU 3575  CG1 ILE A 475    25753  27211  30145   2083  -3390  -3585       C  
+ATOM   3576  CG2 ILE A 475      72.618  99.379 -11.789  1.00210.37           C  
+ANISOU 3576  CG2 ILE A 475    24743  26509  28681   2097  -3256  -3677       C  
+ATOM   3577  CD1 ILE A 475      74.446  97.201 -12.664  1.00218.15           C  
+ANISOU 3577  CD1 ILE A 475    25777  27260  29850   1946  -3056  -3359       C  
+ATOM   3578  N   GLU A 476      71.856  99.893  -8.600  1.00156.45           N  
+ANISOU 3578  N   GLU A 476    18293  19907  21245   2571  -3778  -4120       N  
+ATOM   3579  CA  GLU A 476      70.623 100.594  -8.252  1.00142.72           C  
+ANISOU 3579  CA  GLU A 476    16627  18370  19231   2677  -3720  -4176       C  
+ATOM   3580  C   GLU A 476      70.845 101.560  -7.094  1.00144.41           C  
+ANISOU 3580  C   GLU A 476    16959  18500  19412   3012  -4114  -4440       C  
+ATOM   3581  O   GLU A 476      70.012 102.417  -6.811  1.00142.58           O  
+ANISOU 3581  O   GLU A 476    16768  18380  19024   3141  -4136  -4517       O  
+ATOM   3582  CB  GLU A 476      69.486  99.616  -7.958  1.00128.68           C  
+ANISOU 3582  CB  GLU A 476    15020  16860  17012   2665  -3395  -4018       C  
+ATOM   3583  CG  GLU A 476      69.562  98.905  -6.628  1.00128.73           C  
+ANISOU 3583  CG  GLU A 476    15321  16930  16662   2941  -3433  -4041       C  
+ATOM   3584  CD  GLU A 476      68.465  97.866  -6.484  1.00133.35           C  
+ANISOU 3584  CD  GLU A 476    15992  17719  16955   2900  -3026  -3806       C  
+ATOM   3585  OE1 GLU A 476      68.285  97.061  -7.422  1.00132.30           O  
+ANISOU 3585  OE1 GLU A 476    15710  17598  16962   2603  -2816  -3643       O  
+ATOM   3586  OE2 GLU A 476      67.768  97.860  -5.448  1.00136.37           O  
+ANISOU 3586  OE2 GLU A 476    16588  18223  17003   3196  -2912  -3773       O  
+ATOM   3587  N   LYS A 477      71.990 101.424  -6.439  1.00148.46           N  
+ANISOU 3587  N   LYS A 477    17534  18788  20087   3179  -4467  -4596       N  
+ATOM   3588  CA  LYS A 477      72.399 102.365  -5.405  1.00154.25           C  
+ANISOU 3588  CA  LYS A 477    18378  19348  20882   3544  -4976  -4907       C  
+ATOM   3589  C   LYS A 477      72.634 103.732  -5.994  1.00159.76           C  
+ANISOU 3589  C   LYS A 477    18754  19833  22117   3467  -5183  -5014       C  
+ATOM   3590  O   LYS A 477      72.568 104.742  -5.292  1.00158.01           O  
+ANISOU 3590  O   LYS A 477    18598  19516  21921   3749  -5568  -5266       O  
+ATOM   3591  CB  LYS A 477      73.667 101.883  -4.706  1.00154.03           C  
+ANISOU 3591  CB  LYS A 477    18446  19057  21022   3730  -5372  -5076       C  
+ATOM   3592  CG  LYS A 477      73.367 100.787  -3.750  1.00149.76           C  
+ANISOU 3592  CG  LYS A 477    18324  18717  19861   3975  -5258  -5042       C  
+ATOM   3593  CD  LYS A 477      74.437 100.554  -2.740  1.00143.39           C  
+ANISOU 3593  CD  LYS A 477    17737  17664  19082   4327  -5762  -5306       C  
+ATOM   3594  CE  LYS A 477      73.739 100.087  -1.488  1.00145.72           C  
+ANISOU 3594  CE  LYS A 477    18569  18186  18613   4805  -5697  -5337       C  
+ATOM   3595  NZ  LYS A 477      74.661  99.556  -0.469  1.00149.42           N  
+ANISOU 3595  NZ  LYS A 477    19366  18470  18936   5206  -6108  -5561       N  
+ATOM   3596  N   THR A 478      72.908 103.757  -7.293  1.00166.45           N  
+ANISOU 3596  N   THR A 478    19271  20589  23384   3124  -4918  -4810       N  
+ATOM   3597  CA  THR A 478      73.186 105.012  -7.968  1.00170.99           C  
+ANISOU 3597  CA  THR A 478    19509  20929  24531   3052  -5023  -4844       C  
+ATOM   3598  C   THR A 478      71.905 105.733  -8.391  1.00172.33           C  
+ANISOU 3598  C   THR A 478    19686  21358  24435   2999  -4780  -4796       C  
+ATOM   3599  O   THR A 478      71.021 105.170  -9.046  1.00163.10           O  
+ANISOU 3599  O   THR A 478    18581  20461  22928   2823  -4356  -4594       O  
+ATOM   3600  CB  THR A 478      74.217 104.856  -9.128  1.00146.93           C  
+ANISOU 3600  CB  THR A 478    16115  17589  22125   2815  -4843  -4626       C  
+ATOM   3601  OG1 THR A 478      74.873 106.106  -9.369  1.00147.15           O  
+ANISOU 3601  OG1 THR A 478    15790  17239  22881   2857  -5085  -4705       O  
+ATOM   3602  CG2 THR A 478      73.562 104.391 -10.425  1.00144.32           C  
+ANISOU 3602  CG2 THR A 478    15764  17473  21597   2570  -4282  -4318       C  
+ATOM   3603  N   VAL A 479      71.809 106.979  -7.944  1.00177.73           N  
+ANISOU 3603  N   VAL A 479    20309  21929  25291   3181  -5110  -5014       N  
+ATOM   3604  CA  VAL A 479      70.674 107.838  -8.218  1.00175.65           C  
+ANISOU 3604  CA  VAL A 479    20044  21875  24819   3171  -4959  -5013       C  
+ATOM   3605  C   VAL A 479      71.176 109.077  -8.939  1.00177.67           C  
+ANISOU 3605  C   VAL A 479    19928  21830  25749   3101  -5069  -5035       C  
+ATOM   3606  O   VAL A 479      72.353 109.417  -8.840  1.00179.02           O  
+ANISOU 3606  O   VAL A 479    19865  21600  26555   3150  -5387  -5121       O  
+ATOM   3607  CB  VAL A 479      69.983 108.266  -6.909  1.00168.37           C  
+ANISOU 3607  CB  VAL A 479    19442  21113  23418   3524  -5243  -5249       C  
+ATOM   3608  CG1 VAL A 479      69.008 109.403  -7.169  1.00162.23           C  
+ANISOU 3608  CG1 VAL A 479    18603  20470  22565   3531  -5176  -5285       C  
+ATOM   3609  CG2 VAL A 479      69.288 107.080  -6.256  1.00163.89           C  
+ANISOU 3609  CG2 VAL A 479    19236  20856  22180   3624  -4991  -5142       C  
+ATOM   3610  N   MET A 486      57.931 103.433  -6.150  1.00 92.00           N  
+ANISOU 3610  N   MET A 486    10154  13425  11375   3371  -1523  -3283       N  
+ATOM   3611  CA  MET A 486      56.824 104.104  -5.482  1.00110.61           C  
+ANISOU 3611  CA  MET A 486    12558  15882  13585   3644  -1287  -3161       C  
+ATOM   3612  C   MET A 486      56.182 105.150  -6.389  1.00111.38           C  
+ANISOU 3612  C   MET A 486    12428  16016  13877   3424  -1416  -3308       C  
+ATOM   3613  O   MET A 486      56.000 106.305  -6.002  1.00120.35           O  
+ANISOU 3613  O   MET A 486    13691  17243  14794   3639  -1526  -3430       O  
+ATOM   3614  CB  MET A 486      57.288 104.743  -4.170  1.00120.60           C  
+ANISOU 3614  CB  MET A 486    14253  17221  14347   4174  -1395  -3243       C  
+ATOM   3615  CG  MET A 486      58.405 105.768  -4.316  1.00126.87           C  
+ANISOU 3615  CG  MET A 486    15171  17987  15047   4196  -1944  -3629       C  
+ATOM   3616  SD  MET A 486      58.150 107.169  -3.210  1.00146.78           S  
+ANISOU 3616  SD  MET A 486    18044  20597  17128   4757  -2124  -3782       S  
+ATOM   3617  CE  MET A 486      58.003 106.326  -1.640  1.00102.35           C  
+ANISOU 3617  CE  MET A 486    12883  15002  11003   5391  -1797  -3524       C  
+ATOM   3618  N   GLY A 496      57.154  99.412  -2.065  1.00157.62           N  
+ANISOU 3618  N   GLY A 496    19150  21656  19083   4616     82  -2024       N  
+ATOM   3619  CA  GLY A 496      57.031  99.217  -3.498  1.00158.07           C  
+ANISOU 3619  CA  GLY A 496    18749  21636  19677   3991   -116  -2141       C  
+ATOM   3620  C   GLY A 496      57.664  97.914  -3.942  1.00163.71           C  
+ANISOU 3620  C   GLY A 496    19349  22235  20619   3709   -140  -2093       C  
+ATOM   3621  O   GLY A 496      58.040  97.090  -3.111  1.00167.17           O  
+ANISOU 3621  O   GLY A 496    20009  22652  20858   3970     79  -1915       O  
+ATOM   3622  N   GLU A 497      57.778  97.722  -5.254  1.00166.67           N  
+ANISOU 3622  N   GLU A 497    19413  22530  21383   3222   -403  -2250       N  
+ATOM   3623  CA  GLU A 497      58.431  96.534  -5.792  1.00170.73           C  
+ANISOU 3623  CA  GLU A 497    19840  22933  22098   2955   -485  -2239       C  
+ATOM   3624  C   GLU A 497      59.931  96.621  -5.556  1.00172.64           C  
+ANISOU 3624  C   GLU A 497    20403  23228  21965   3043   -809  -2472       C  
+ATOM   3625  O   GLU A 497      60.589  95.609  -5.311  1.00173.01           O  
+ANISOU 3625  O   GLU A 497    20547  23223  21965   3047   -764  -2395       O  
+ATOM   3626  CB  GLU A 497      58.173  96.394  -7.290  1.00169.62           C  
+ANISOU 3626  CB  GLU A 497    19361  22681  22408   2505   -727  -2374       C  
+ATOM   3627  CG  GLU A 497      56.882  97.012  -7.777  1.00173.11           C  
+ANISOU 3627  CG  GLU A 497    19518  23086  23171   2413   -663  -2350       C  
+ATOM   3628  CD  GLU A 497      56.703  96.840  -9.270  1.00173.61           C  
+ANISOU 3628  CD  GLU A 497    19329  23013  23621   2054   -974  -2531       C  
+ATOM   3629  OE1 GLU A 497      57.597  96.241  -9.908  1.00170.78           O  
+ANISOU 3629  OE1 GLU A 497    19040  22600  23250   1901  -1202  -2647       O  
+ATOM   3630  OE2 GLU A 497      55.672  97.300  -9.805  1.00174.73           O  
+ANISOU 3630  OE2 GLU A 497    19233  23093  24064   1967   -999  -2559       O  
+ATOM   3631  N   ILE A 498      60.466  97.836  -5.652  1.00171.44           N  
+ANISOU 3631  N   ILE A 498    20381  23149  21608   3107  -1147  -2757       N  
+ATOM   3632  CA  ILE A 498      61.883  98.078  -5.403  1.00167.93           C  
+ANISOU 3632  CA  ILE A 498    20186  22695  20926   3210  -1503  -2995       C  
+ATOM   3633  C   ILE A 498      62.251  97.620  -4.004  1.00173.82           C  
+ANISOU 3633  C   ILE A 498    21298  23465  21280   3659  -1379  -2903       C  
+ATOM   3634  O   ILE A 498      63.315  97.034  -3.789  1.00168.12           O  
+ANISOU 3634  O   ILE A 498    20732  22684  20462   3692  -1544  -2980       O  
+ATOM   3635  CB  ILE A 498      62.227  99.564  -5.520  1.00165.53           C  
+ANISOU 3635  CB  ILE A 498    19934  22420  20540   3287  -1855  -3283       C  
+ATOM   3636  CG1 ILE A 498      61.848 100.100  -6.901  1.00171.37           C  
+ANISOU 3636  CG1 ILE A 498    20360  23137  21618   2911  -1947  -3365       C  
+ATOM   3637  CG2 ILE A 498      63.706  99.798  -5.239  1.00153.61           C  
+ANISOU 3637  CG2 ILE A 498    18613  20820  18931   3398  -2256  -3525       C  
+ATOM   3638  CD1 ILE A 498      61.891 101.605  -6.996  1.00174.73           C  
+ANISOU 3638  CD1 ILE A 498    20793  23594  22004   2996  -2195  -3587       C  
+ATOM   3639  N   SER A 499      61.362  97.890  -3.054  1.00187.32           N  
+ANISOU 3639  N   SER A 499    23169  25252  22753   4049  -1074  -2727       N  
+ATOM   3640  CA  SER A 499      61.558  97.444  -1.683  1.00198.47           C  
+ANISOU 3640  CA  SER A 499    24999  26682  23729   4594   -880  -2593       C  
+ATOM   3641  C   SER A 499      61.611  95.926  -1.651  1.00206.95           C  
+ANISOU 3641  C   SER A 499    26003  27687  24943   4481   -548  -2315       C  
+ATOM   3642  O   SER A 499      62.302  95.334  -0.823  1.00211.11           O  
+ANISOU 3642  O   SER A 499    26867  28195  25151   4806   -533  -2293       O  
+ATOM   3643  CB  SER A 499      60.435  97.952  -0.780  1.00200.16           C  
+ANISOU 3643  CB  SER A 499    25385  26976  23690   5064   -498  -2368       C  
+ATOM   3644  OG  SER A 499      60.635  97.541   0.556  1.00203.12           O  
+ANISOU 3644  OG  SER A 499    26244  27359  23574   5697   -286  -2224       O  
+ATOM   3645  N   ASP A 500      60.877  95.300  -2.564  1.00209.43           N  
+ANISOU 3645  N   ASP A 500    25883  27938  25751   4040   -320  -2123       N  
+ATOM   3646  CA  ASP A 500      60.862  93.851  -2.655  1.00212.36           C  
+ANISOU 3646  CA  ASP A 500    26120  28207  26361   3883    -39  -1865       C  
+ATOM   3647  C   ASP A 500      62.097  93.307  -3.366  1.00203.21           C  
+ANISOU 3647  C   ASP A 500    24946  26999  25265   3557   -426  -2093       C  
+ATOM   3648  O   ASP A 500      62.511  92.195  -3.092  1.00204.96           O  
+ANISOU 3648  O   ASP A 500    25238  27165  25472   3573   -286  -1954       O  
+ATOM   3649  CB  ASP A 500      59.580  93.355  -3.335  1.00221.23           C  
+ANISOU 3649  CB  ASP A 500    26765  29217  28075   3573    287  -1593       C  
+ATOM   3650  CG  ASP A 500      58.346  93.583  -2.486  1.00233.55           C  
+ANISOU 3650  CG  ASP A 500    28310  30773  29657   3938    816  -1243       C  
+ATOM   3651  OD1 ASP A 500      58.502  93.807  -1.272  1.00239.09           O  
+ANISOU 3651  OD1 ASP A 500    29432  31558  29852   4492   1029  -1137       O  
+ATOM   3652  OD2 ASP A 500      57.226  93.532  -3.034  1.00237.24           O  
+ANISOU 3652  OD2 ASP A 500    28355  31128  30656   3709   1011  -1072       O  
+ATOM   3653  N   ILE A 501      62.692  94.094  -4.258  1.00192.67           N  
+ANISOU 3653  N   ILE A 501    23525  25673  24008   3292   -875  -2413       N  
+ATOM   3654  CA  ILE A 501      63.864  93.633  -5.007  1.00181.71           C  
+ANISOU 3654  CA  ILE A 501    22108  24215  22719   3006  -1195  -2587       C  
+ATOM   3655  C   ILE A 501      65.165  93.699  -4.196  1.00170.68           C  
+ANISOU 3655  C   ILE A 501    21064  22814  20972   3287  -1450  -2764       C  
+ATOM   3656  O   ILE A 501      65.940  92.735  -4.173  1.00164.70           O  
+ANISOU 3656  O   ILE A 501    20376  21999  20202   3222  -1476  -2736       O  
+ATOM   3657  CB  ILE A 501      64.023  94.385  -6.353  1.00138.03           C  
+ANISOU 3657  CB  ILE A 501    16341  18649  17453   2657  -1507  -2802       C  
+ATOM   3658  CG1 ILE A 501      63.385  93.589  -7.491  1.00130.33           C  
+ANISOU 3658  CG1 ILE A 501    15061  17587  16872   2296  -1411  -2683       C  
+ATOM   3659  CG2 ILE A 501      65.491  94.642  -6.682  1.00137.98           C  
+ANISOU 3659  CG2 ILE A 501    16437  18581  17410   2596  -1883  -3037       C  
+ATOM   3660  CD1 ILE A 501      61.877  93.516  -7.426  1.00127.50           C  
+ANISOU 3660  CD1 ILE A 501    14480  17217  16748   2292  -1111  -2482       C  
+ATOM   3661  N   HIS A 502      65.396  94.829  -3.532  1.00164.00           N  
+ANISOU 3661  N   HIS A 502    20439  22007  19869   3613  -1676  -2964       N  
+ATOM   3662  CA  HIS A 502      66.603  95.022  -2.737  1.00157.69           C  
+ANISOU 3662  CA  HIS A 502    19968  21147  18800   3931  -2029  -3195       C  
+ATOM   3663  C   HIS A 502      66.627  94.018  -1.594  1.00162.64           C  
+ANISOU 3663  C   HIS A 502    20933  21795  19069   4322  -1761  -3012       C  
+ATOM   3664  O   HIS A 502      67.677  93.496  -1.229  1.00164.35           O  
+ANISOU 3664  O   HIS A 502    21348  21935  19163   4433  -1971  -3125       O  
+ATOM   3665  CB  HIS A 502      66.654  96.452  -2.195  1.00150.83           C  
+ANISOU 3665  CB  HIS A 502    19272  20282  17755   4264  -2351  -3452       C  
+ATOM   3666  CG  HIS A 502      67.989  97.117  -2.348  1.00143.78           C  
+ANISOU 3666  CG  HIS A 502    18387  19223  17019   4248  -2929  -3804       C  
+ATOM   3667  ND1 HIS A 502      68.672  97.155  -3.544  1.00137.00           N  
+ANISOU 3667  ND1 HIS A 502    17193  18253  16609   3785  -3096  -3872       N  
+ATOM   3668  CD2 HIS A 502      68.759  97.787  -1.457  1.00140.15           C  
+ANISOU 3668  CD2 HIS A 502    18217  18647  16387   4674  -3387  -4099       C  
+ATOM   3669  CE1 HIS A 502      69.807  97.814  -3.383  1.00131.33           C  
+ANISOU 3669  CE1 HIS A 502    16503  17343  16055   3893  -3583  -4155       C  
+ATOM   3670  NE2 HIS A 502      69.883  98.208  -2.125  1.00130.34           N  
+ANISOU 3670  NE2 HIS A 502    16741  17200  15583   4416  -3815  -4326       N  
+ATOM   3671  N   THR A 503      65.450  93.751  -1.039  1.00163.37           N  
+ANISOU 3671  N   THR A 503    21080  21971  19022   4552  -1268  -2705       N  
+ATOM   3672  CA  THR A 503      65.295  92.771   0.033  1.00163.31           C  
+ANISOU 3672  CA  THR A 503    21384  21972  18696   4974   -872  -2437       C  
+ATOM   3673  C   THR A 503      65.393  91.327  -0.475  1.00151.49           C  
+ANISOU 3673  C   THR A 503    19659  20415  17484   4610   -614  -2206       C  
+ATOM   3674  O   THR A 503      65.955  90.464   0.198  1.00146.11           O  
+ANISOU 3674  O   THR A 503    19247  19705  16563   4853   -520  -2130       O  
+ATOM   3675  CB  THR A 503      63.956  92.972   0.783  1.00170.09           C  
+ANISOU 3675  CB  THR A 503    22342  22899  19387   5379   -334  -2106       C  
+ATOM   3676  OG1 THR A 503      64.026  94.162   1.576  1.00176.61           O  
+ANISOU 3676  OG1 THR A 503    23551  23775  19778   5902   -585  -2325       O  
+ATOM   3677  CG2 THR A 503      63.645  91.790   1.693  1.00171.53           C  
+ANISOU 3677  CG2 THR A 503    22747  23054  19374   5760    235  -1707       C  
+ATOM   3678  N   LYS A 504      64.865  91.071  -1.669  1.00141.41           N  
+ANISOU 3678  N   LYS A 504    17911  19106  16711   4058   -541  -2117       N  
+ATOM   3679  CA  LYS A 504      64.918  89.730  -2.251  1.00138.96           C  
+ANISOU 3679  CA  LYS A 504    17368  18711  16719   3706   -369  -1930       C  
+ATOM   3680  C   LYS A 504      66.335  89.382  -2.646  1.00142.04           C  
+ANISOU 3680  C   LYS A 504    17848  19062  17058   3520   -779  -2178       C  
+ATOM   3681  O   LYS A 504      66.762  88.230  -2.559  1.00134.46           O  
+ANISOU 3681  O   LYS A 504    16927  18053  16108   3463   -666  -2057       O  
+ATOM   3682  CB  LYS A 504      64.010  89.614  -3.477  1.00129.82           C  
+ANISOU 3682  CB  LYS A 504    15721  17488  16116   3223   -303  -1839       C  
+ATOM   3683  CG  LYS A 504      62.585  89.222  -3.164  1.00130.75           C  
+ANISOU 3683  CG  LYS A 504    15616  17544  16518   3307    225  -1458       C  
+ATOM   3684  N   LEU A 505      67.053  90.395  -3.100  1.00138.41           N  
+ANISOU 3684  N   LEU A 505    17394  18602  16593   3427  -1237  -2505       N  
+ATOM   3685  CA  LEU A 505      68.435  90.224  -3.458  1.00120.29           C  
+ANISOU 3685  CA  LEU A 505    15154  16230  14322   3285  -1619  -2726       C  
+ATOM   3686  C   LEU A 505      69.260  89.889  -2.223  1.00124.70           C  
+ANISOU 3686  C   LEU A 505    16126  16770  14484   3724  -1710  -2800       C  
+ATOM   3687  O   LEU A 505      70.166  89.052  -2.259  1.00124.02           O  
+ANISOU 3687  O   LEU A 505    16106  16619  14396   3643  -1808  -2823       O  
+ATOM   3688  CB  LEU A 505      68.966  91.499  -4.066  1.00109.42           C  
+ANISOU 3688  CB  LEU A 505    13673  14807  13094   3171  -2036  -3016       C  
+ATOM   3689  CG  LEU A 505      70.386  91.097  -4.404  1.00128.61           C  
+ANISOU 3689  CG  LEU A 505    16120  17111  15635   3034  -2336  -3157       C  
+ATOM   3690  CD1 LEU A 505      70.509  91.048  -5.920  1.00141.22           C  
+ANISOU 3690  CD1 LEU A 505    17393  18648  17616   2580  -2370  -3131       C  
+ATOM   3691  CD2 LEU A 505      71.441  91.946  -3.655  1.00114.58           C  
+ANISOU 3691  CD2 LEU A 505    14554  15224  13758   3350  -2784  -3462       C  
+ATOM   3692  N   LEU A 506      68.951  90.579  -1.132  1.00130.40           N  
+ANISOU 3692  N   LEU A 506    17159  17539  14847   4233  -1705  -2854       N  
+ATOM   3693  CA  LEU A 506      69.612  90.336   0.139  1.00131.63           C  
+ANISOU 3693  CA  LEU A 506    17798  17665  14549   4786  -1814  -2947       C  
+ATOM   3694  C   LEU A 506      69.501  88.865   0.507  1.00144.65           C  
+ANISOU 3694  C   LEU A 506    19548  19330  16080   4841  -1380  -2638       C  
+ATOM   3695  O   LEU A 506      70.448  88.287   1.024  1.00156.36           O  
+ANISOU 3695  O   LEU A 506    21304  20754  17351   5038  -1550  -2741       O  
+ATOM   3696  CB  LEU A 506      69.008  91.206   1.240  1.00120.17           C  
+ANISOU 3696  CB  LEU A 506    16713  16270  12675   5409  -1772  -2980       C  
+ATOM   3697  N   ARG A 507      68.351  88.260   0.214  1.00146.01           N  
+ANISOU 3697  N   ARG A 507    19470  19552  16453   4658   -839  -2263       N  
+ATOM   3698  CA  ARG A 507      68.159  86.831   0.443  1.00152.26           C  
+ANISOU 3698  CA  ARG A 507    20261  20320  17271   4649   -394  -1930       C  
+ATOM   3699  C   ARG A 507      69.164  86.004  -0.360  1.00145.14           C  
+ANISOU 3699  C   ARG A 507    19199  19352  16597   4200   -653  -2042       C  
+ATOM   3700  O   ARG A 507      69.471  84.873   0.007  1.00142.95           O  
+ANISOU 3700  O   ARG A 507    19044  19045  16224   4273   -450  -1882       O  
+ATOM   3701  CB  ARG A 507      66.724  86.401   0.111  1.00161.16           C  
+ANISOU 3701  CB  ARG A 507    21023  21434  18777   4463    165  -1523       C  
+ATOM   3702  CG  ARG A 507      65.661  86.975   1.046  1.00177.63           C  
+ANISOU 3702  CG  ARG A 507    23283  23565  20645   4978    585  -1290       C  
+ATOM   3703  CD  ARG A 507      64.263  86.468   0.696  1.00187.68           C  
+ANISOU 3703  CD  ARG A 507    24111  24757  22442   4769   1148   -857       C  
+ATOM   3704  NE  ARG A 507      63.229  87.082   1.527  1.00198.88           N  
+ANISOU 3704  NE  ARG A 507    25661  26202  23704   5259   1588   -600       N  
+ATOM   3705  CZ  ARG A 507      61.923  86.883   1.368  1.00205.71           C  
+ANISOU 3705  CZ  ARG A 507    26137  26967  25058   5169   2095   -210       C  
+ATOM   3706  NH1 ARG A 507      61.484  86.084   0.406  1.00207.33           N  
+ANISOU 3706  NH1 ARG A 507    25797  27020  25959   4610   2159    -75       N  
+ATOM   3707  NH2 ARG A 507      61.055  87.485   2.171  1.00208.11           N  
+ANISOU 3707  NH2 ARG A 507    26596  27293  25185   5668   2517     41       N  
+ATOM   3708  N   LEU A 508      69.681  86.579  -1.445  1.00134.19           N  
+ANISOU 3708  N   LEU A 508    17555  17933  15496   3776  -1072  -2294       N  
+ATOM   3709  CA  LEU A 508      70.654  85.888  -2.287  1.00125.06           C  
+ANISOU 3709  CA  LEU A 508    16259  16704  14555   3385  -1304  -2382       C  
+ATOM   3710  C   LEU A 508      72.070  85.983  -1.730  1.00133.75           C  
+ANISOU 3710  C   LEU A 508    17660  17739  15419   3609  -1705  -2655       C  
+ATOM   3711  O   LEU A 508      72.775  84.975  -1.650  1.00132.42           O  
+ANISOU 3711  O   LEU A 508    17581  17530  15202   3562  -1700  -2615       O  
+ATOM   3712  CB  LEU A 508      70.606  86.404  -3.726  1.00112.64           C  
+ANISOU 3712  CB  LEU A 508    14312  15095  13392   2907  -1513  -2482       C  
+ATOM   3713  CG  LEU A 508      69.271  86.186  -4.436  1.00113.17           C  
+ANISOU 3713  CG  LEU A 508    14056  15174  13769   2653  -1215  -2260       C  
+ATOM   3714  CD1 LEU A 508      69.459  86.184  -5.945  1.00106.22           C  
+ANISOU 3714  CD1 LEU A 508    12898  14225  13237   2216  -1432  -2343       C  
+ATOM   3715  CD2 LEU A 508      68.613  84.894  -3.968  1.00117.32           C  
+ANISOU 3715  CD2 LEU A 508    14560  15678  14341   2703   -779  -1932       C  
+ATOM   3716  N   SER A 509      72.491  87.188  -1.355  1.00130.72           N  
+ANISOU 3716  N   SER A 509    17414  17317  14936   3856  -2085  -2944       N  
+ATOM   3717  CA  SER A 509      73.767  87.347  -0.663  1.00136.66           C  
+ANISOU 3717  CA  SER A 509    18457  17948  15519   4156  -2531  -3236       C  
+ATOM   3718  C   SER A 509      73.698  86.583   0.646  1.00143.40           C  
+ANISOU 3718  C   SER A 509    19773  18845  15866   4687  -2319  -3138       C  
+ATOM   3719  O   SER A 509      74.680  85.977   1.082  1.00138.35           O  
+ANISOU 3719  O   SER A 509    19358  18121  15090   4839  -2521  -3256       O  
+ATOM   3720  CB  SER A 509      74.089  88.820  -0.410  1.00130.15           C  
+ANISOU 3720  CB  SER A 509    17685  17028  14738   4382  -3008  -3572       C  
+ATOM   3721  OG  SER A 509      74.991  89.319  -1.384  1.00125.27           O  
+ANISOU 3721  OG  SER A 509    16739  16248  14609   4003  -3376  -3749       O  
+ATOM   3722  N   SER A 510      72.520  86.615   1.263  1.00144.65           N  
+ANISOU 3722  N   SER A 510    20078  19123  15761   4999  -1878  -2899       N  
+ATOM   3723  CA  SER A 510      72.236  85.777   2.417  1.00148.97           C  
+ANISOU 3723  CA  SER A 510    21044  19713  15846   5526  -1487  -2677       C  
+ATOM   3724  C   SER A 510      72.424  84.315   2.046  1.00145.48           C  
+ANISOU 3724  C   SER A 510    20455  19266  15554   5204  -1182  -2426       C  
+ATOM   3725  O   SER A 510      73.001  83.552   2.811  1.00140.49           O  
+ANISOU 3725  O   SER A 510    20177  18606  14598   5539  -1145  -2415       O  
+ATOM   3726  CB  SER A 510      70.807  85.999   2.911  1.00149.05           C  
+ANISOU 3726  CB  SER A 510    21111  19824  15699   5832   -930  -2348       C  
+ATOM   3727  OG  SER A 510      70.346  84.882   3.643  1.00147.27           O  
+ANISOU 3727  OG  SER A 510    21097  19617  15240   6154   -337  -1962       O  
+ATOM   3728  N   SER A 511      71.946  83.946   0.858  1.00139.39           N  
+ANISOU 3728  N   SER A 511    19184  18508  15269   4583  -1004  -2249       N  
+ATOM   3729  CA  SER A 511      72.011  82.573   0.365  1.00129.68           C  
+ANISOU 3729  CA  SER A 511    17763  17254  14255   4236   -745  -2013       C  
+ATOM   3730  C   SER A 511      73.437  82.171   0.006  1.00129.30           C  
+ANISOU 3730  C   SER A 511    17762  17135  14232   4035  -1175  -2258       C  
+ATOM   3731  O   SER A 511      73.792  80.991   0.047  1.00109.17           O  
+ANISOU 3731  O   SER A 511    15254  14565  11659   3957  -1019  -2119       O  
+ATOM   3732  CB  SER A 511      71.108  82.416  -0.862  1.00125.40           C  
+ANISOU 3732  CB  SER A 511    16701  16703  14243   3684   -564  -1828       C  
+ATOM   3733  OG  SER A 511      71.167  81.106  -1.394  1.00137.08           O  
+ANISOU 3733  OG  SER A 511    17990  18125  15969   3359   -392  -1635       O  
+ATOM   3734  N   GLN A 512      74.247  83.164  -0.347  1.00131.12           N  
+ANISOU 3734  N   GLN A 512    17958  17304  14560   3957  -1699  -2603       N  
+ATOM   3735  CA  GLN A 512      75.621  82.941  -0.774  1.00120.52           C  
+ANISOU 3735  CA  GLN A 512    16587  15845  13361   3755  -2110  -2821       C  
+ATOM   3736  C   GLN A 512      76.508  82.796   0.431  1.00123.24           C  
+ANISOU 3736  C   GLN A 512    17385  16120  13322   4264  -2359  -3023       C  
+ATOM   3737  O   GLN A 512      77.569  82.174   0.385  1.00128.94           O  
+ANISOU 3737  O   GLN A 512    18162  16749  14081   4188  -2573  -3124       O  
+ATOM   3738  CB  GLN A 512      76.096  84.150  -1.546  1.00118.44           C  
+ANISOU 3738  CB  GLN A 512    16077  15483  13440   3537  -2531  -3073       C  
+ATOM   3739  CG  GLN A 512      76.798  83.822  -2.807  1.00125.91           C  
+ANISOU 3739  CG  GLN A 512    16711  16343  14787   3048  -2644  -3057       C  
+ATOM   3740  CD  GLN A 512      76.915  85.020  -3.690  1.00137.22           C  
+ANISOU 3740  CD  GLN A 512    17859  17690  16589   2843  -2877  -3186       C  
+ATOM   3741  OE1 GLN A 512      77.456  86.052  -3.281  1.00153.03           O  
+ANISOU 3741  OE1 GLN A 512    19904  19568  18672   3053  -3245  -3442       O  
+ATOM   3742  NE2 GLN A 512      76.353  84.922  -4.897  1.00128.62           N  
+ANISOU 3742  NE2 GLN A 512    16487  16643  15739   2470  -2678  -3017       N  
+ATOM   3743  N   GLY A 513      76.073  83.422   1.510  1.00128.93           N  
+ANISOU 3743  N   GLY A 513    18453  16869  13665   4826  -2362  -3100       N  
+ATOM   3744  CA  GLY A 513      76.767  83.309   2.761  1.00135.31           C  
+ANISOU 3744  CA  GLY A 513    19790  17602  14021   5449  -2610  -3308       C  
+ATOM   3745  C   GLY A 513      76.597  81.908   3.295  1.00133.10           C  
+ANISOU 3745  C   GLY A 513    19748  17398  13425   5620  -2132  -3010       C  
+ATOM   3746  O   GLY A 513      77.464  81.407   3.988  1.00135.48           O  
+ANISOU 3746  O   GLY A 513    20407  17622  13448   5952  -2344  -3163       O  
+ATOM   3747  N   THR A 514      75.478  81.270   2.971  1.00130.00           N  
+ANISOU 3747  N   THR A 514    19139  17128  13128   5402  -1499  -2584       N  
+ATOM   3748  CA  THR A 514      75.252  79.899   3.409  1.00136.05           C  
+ANISOU 3748  CA  THR A 514    20053  17932  13707   5525   -990  -2248       C  
+ATOM   3749  C   THR A 514      76.125  78.980   2.571  1.00136.91           C  
+ANISOU 3749  C   THR A 514    19914  17993  14111   4991  -1151  -2279       C  
+ATOM   3750  O   THR A 514      76.451  77.859   2.971  1.00136.85           O  
+ANISOU 3750  O   THR A 514    20093  17981  13923   5102   -944  -2139       O  
+ATOM   3751  CB  THR A 514      73.784  79.475   3.234  1.00141.83           C  
+ANISOU 3751  CB  THR A 514    20522  18734  14631   5408   -287  -1768       C  
+ATOM   3752  OG1 THR A 514      73.591  78.921   1.927  1.00142.93           O  
+ANISOU 3752  OG1 THR A 514    20113  18858  15336   4676   -233  -1640       O  
+ATOM   3753  CG2 THR A 514      72.861  80.659   3.413  1.00145.09           C  
+ANISOU 3753  CG2 THR A 514    20916  19192  15018   5620   -214  -1761       C  
+ATOM   3754  N   ILE A 515      76.491  79.473   1.394  1.00129.08           N  
+ANISOU 3754  N   ILE A 515    18518  16960  13565   4442  -1494  -2442       N  
+ATOM   3755  CA  ILE A 515      77.327  78.735   0.468  1.00119.39           C  
+ANISOU 3755  CA  ILE A 515    17053  15677  12633   3951  -1657  -2465       C  
+ATOM   3756  C   ILE A 515      78.787  78.796   0.895  1.00127.05           C  
+ANISOU 3756  C   ILE A 515    18275  16524  13475   4143  -2161  -2798       C  
+ATOM   3757  O   ILE A 515      79.482  77.778   0.906  1.00128.44           O  
+ANISOU 3757  O   ILE A 515    18530  16671  13600   4065  -2153  -2760       O  
+ATOM   3758  CB  ILE A 515      77.220  79.308  -0.945  1.00110.42           C  
+ANISOU 3758  CB  ILE A 515    15452  14516  11987   3401  -1815  -2500       C  
+ATOM   3759  CG1 ILE A 515      75.801  79.150  -1.481  1.00109.39           C  
+ANISOU 3759  CG1 ILE A 515    15038  14466  12060   3177  -1388  -2203       C  
+ATOM   3760  CG2 ILE A 515      78.168  78.587  -1.861  1.00112.80           C  
+ANISOU 3760  CG2 ILE A 515    15581  14743  12533   2997  -1979  -2516       C  
+ATOM   3761  CD1 ILE A 515      75.570  79.879  -2.780  1.00110.01           C  
+ANISOU 3761  CD1 ILE A 515    14740  14519  12538   2759  -1561  -2268       C  
+ATOM   3762  N   GLU A 516      79.240  79.996   1.247  1.00122.76           N  
+ANISOU 3762  N   GLU A 516    17837  15878  12927   4400  -2627  -3132       N  
+ATOM   3763  CA  GLU A 516      80.636  80.218   1.589  1.00124.18           C  
+ANISOU 3763  CA  GLU A 516    18172  15860  13149   4571  -3208  -3493       C  
+ATOM   3764  C   GLU A 516      81.099  79.274   2.681  1.00126.09           C  
+ANISOU 3764  C   GLU A 516    18894  16095  12918   5029  -3183  -3522       C  
+ATOM   3765  O   GLU A 516      82.117  78.595   2.539  1.00116.84           O  
+ANISOU 3765  O   GLU A 516    17721  14825  11848   4893  -3381  -3605       O  
+ATOM   3766  CB  GLU A 516      80.863  81.656   2.046  1.00135.21           C  
+ANISOU 3766  CB  GLU A 516    19658  17112  14603   4907  -3722  -3854       C  
+ATOM   3767  CG  GLU A 516      82.315  82.045   1.962  1.00146.41           C  
+ANISOU 3767  CG  GLU A 516    20992  18238  16400   4885  -4381  -4215       C  
+ATOM   3768  CD  GLU A 516      82.865  81.775   0.582  1.00151.28           C  
+ANISOU 3768  CD  GLU A 516    21100  18789  17590   4220  -4329  -4073       C  
+ATOM   3769  OE1 GLU A 516      82.373  82.413  -0.367  1.00146.52           O  
+ANISOU 3769  OE1 GLU A 516    20133  18221  17316   3867  -4198  -3957       O  
+ATOM   3770  OE2 GLU A 516      83.763  80.917   0.441  1.00154.24           O  
+ANISOU 3770  OE2 GLU A 516    21471  19078  18055   4089  -4397  -4065       O  
+END                                                                             
diff --git a/examples/testdata/4IM2_missing_noid.pdb b/examples/testdata/4IM2_missing_noid.pdb
new file mode 100644 (file)
index 0000000..5a5ef94
--- /dev/null
@@ -0,0 +1,524 @@
+TITLE     STRUCTURE OF TANK-BINDING KINASE 1  (Truncated test file)
+ATOM   3510  N   LEU A 468      76.337  90.332  -7.628  1.00168.53           N  
+ANISOU 3510  N   LEU A 468    19760  15191  29082   5189   1248  -1702       N  
+ATOM   3511  CA  LEU A 468      75.576  90.788  -6.476  1.00181.60           C  
+ANISOU 3511  CA  LEU A 468    21073  16927  30998   5158   1421  -2051       C  
+ATOM   3512  C   LEU A 468      76.285  91.971  -5.836  1.00196.57           C  
+ANISOU 3512  C   LEU A 468    23029  18636  33021   5053   1667  -2229       C  
+ATOM   3513  O   LEU A 468      75.727  93.067  -5.733  1.00197.97           O  
+ANISOU 3513  O   LEU A 468    23115  18581  33523   5166   1662  -2304       O  
+ATOM   3514  CB  LEU A 468      75.409  89.671  -5.449  1.00173.72           C  
+ANISOU 3514  CB  LEU A 468    19829  16340  29836   4981   1593  -2304       C  
+ATOM   3515  CG  LEU A 468      74.099  88.882  -5.420  1.00170.58           C  
+ANISOU 3515  CG  LEU A 468    19142  16142  29529   5074   1449  -2345       C  
+ATOM   3516  CD1 LEU A 468      74.023  87.940  -6.581  1.00167.98           C  
+ANISOU 3516  CD1 LEU A 468    18951  15858  29015   5180   1156  -2025       C  
+ATOM   3517  CD2 LEU A 468      73.988  88.096  -4.145  1.00165.16           C  
+ANISOU 3517  CD2 LEU A 468    18217  15818  28720   4865   1706  -2656       C  
+ATOM   3518  N   ASP A 469      77.529  91.753  -5.429  1.00208.80           N  
+ANISOU 3518  N   ASP A 469    24731  20282  34322   4836   1875  -2296       N  
+ATOM   3519  CA  ASP A 469      78.267  92.785  -4.720  1.00223.11           C  
+ANISOU 3519  CA  ASP A 469    26577  21960  36233   4701   2123  -2500       C  
+ATOM   3520  C   ASP A 469      78.653  93.973  -5.599  1.00231.96           C  
+ANISOU 3520  C   ASP A 469    27954  22654  37525   4817   2037  -2283       C  
+ATOM   3521  O   ASP A 469      78.718  95.100  -5.118  1.00235.71           O  
+ANISOU 3521  O   ASP A 469    28377  22940  38242   4798   2175  -2449       O  
+ATOM   3522  CB  ASP A 469      79.506  92.197  -4.057  1.00224.50           C  
+ANISOU 3522  CB  ASP A 469    26841  22370  36089   4433   2350  -2633       C  
+ATOM   3523  CG  ASP A 469      79.748  92.767  -2.679  1.00229.51           C  
+ANISOU 3523  CG  ASP A 469    27303  23107  36794   4254   2637  -3018       C  
+ATOM   3524  OD1 ASP A 469      79.958  93.992  -2.564  1.00232.36           O  
+ANISOU 3524  OD1 ASP A 469    27695  23203  37387   4262   2718  -3093       O  
+ATOM   3525  OD2 ASP A 469      79.751  91.979  -1.709  1.00229.48           O  
+ANISOU 3525  OD2 ASP A 469    27145  23450  36598   4098   2779  -3242       O  
+ATOM   3526  N   PHE A 470      78.904  93.735  -6.882  1.00233.88           N  
+ANISOU 3526  N   PHE A 470    28481  22742  37641   4931   1813  -1912       N  
+ATOM   3527  CA  PHE A 470      79.292  94.831  -7.766  1.00237.73           C  
+ANISOU 3527  CA  PHE A 470    29252  22819  38256   5033   1733  -1677       C  
+ATOM   3528  C   PHE A 470      78.103  95.470  -8.467  1.00232.15           C  
+ANISOU 3528  C   PHE A 470    28512  21868  37828   5322   1470  -1512       C  
+ATOM   3529  O   PHE A 470      78.021  96.691  -8.532  1.00230.69           O  
+ANISOU 3529  O   PHE A 470    28371  21372  37909   5402   1493  -1517       O  
+ATOM   3530  CB  PHE A 470      80.344  94.379  -8.784  1.00242.99           C  
+ANISOU 3530  CB  PHE A 470    30297  23410  38616   4976   1661  -1357       C  
+ATOM   3531  CG  PHE A 470      80.663  95.388  -9.839  1.00251.53           C  
+ANISOU 3531  CG  PHE A 470    31709  24074  39788   5089   1550  -1064       C  
+ATOM   3532  CD1 PHE A 470      81.658  96.315  -9.620  1.00252.90           C  
+ANISOU 3532  CD1 PHE A 470    32021  24036  40032   4942   1771  -1133       C  
+ATOM   3533  CD2 PHE A 470      80.005  95.389 -11.059  1.00255.02           C  
+ANISOU 3533  CD2 PHE A 470    32335  24339  40221   5330   1225   -715       C  
+ATOM   3534  CE1 PHE A 470      81.981  97.242 -10.575  1.00254.75           C  
+ANISOU 3534  CE1 PHE A 470    32569  23879  40344   5028   1688   -862       C  
+ATOM   3535  CE2 PHE A 470      80.321  96.320 -12.027  1.00256.76           C  
+ANISOU 3535  CE2 PHE A 470    32894  24173  40491   5427   1126   -433       C  
+ATOM   3536  CZ  PHE A 470      81.313  97.248 -11.784  1.00256.57           C  
+ANISOU 3536  CZ  PHE A 470    33006  23929  40550   5272   1365   -504       C  
+ATOM   3537  N   CYS A 471      77.190  94.665  -8.997  1.00176.88           N  
+ANISOU 3537  N   CYS A 471    20955  21812  24441   2225  -3821  -3711       N  
+ATOM   3538  CA  CYS A 471      76.124  95.233  -9.815  1.00176.41           C  
+ANISOU 3538  CA  CYS A 471    20820  21906  24301   2124  -3576  -3614       C  
+ATOM   3539  C   CYS A 471      74.868  95.631  -9.050  1.00176.82           C  
+ANISOU 3539  C   CYS A 471    21026  22206  23952   2247  -3588  -3734       C  
+ATOM   3540  O   CYS A 471      74.632  96.811  -8.854  1.00178.66           O  
+ANISOU 3540  O   CYS A 471    21175  22393  24314   2353  -3749  -3873       O  
+ATOM   3541  CB  CYS A 471      75.805  94.346 -11.019  1.00173.95           C  
+ANISOU 3541  CB  CYS A 471    20521  21694  23877   1925  -3217  -3359       C  
+ATOM   3542  SG  CYS A 471      76.954  94.571 -12.398  1.00273.04           S  
+ANISOU 3542  SG  CYS A 471    32850  33934  36957   1863  -3075  -3146       S  
+ATOM   3543  N   ILE A 472      74.068  94.670  -8.603  1.00173.50           N  
+ANISOU 3543  N   ILE A 472    20813  22023  23085   2253  -3402  -3662       N  
+ATOM   3544  CA  ILE A 472      72.771  95.033  -8.022  1.00171.09           C  
+ANISOU 3544  CA  ILE A 472    20616  21936  22453   2374  -3307  -3694       C  
+ATOM   3545  C   ILE A 472      72.832  96.022  -6.874  1.00177.82           C  
+ANISOU 3545  C   ILE A 472    21591  22750  23222   2698  -3611  -3931       C  
+ATOM   3546  O   ILE A 472      71.992  96.914  -6.776  1.00182.92           O  
+ANISOU 3546  O   ILE A 472    22220  23491  23792   2778  -3598  -3985       O  
+ATOM   3547  CB  ILE A 472      71.979  93.843  -7.518  1.00159.41           C  
+ANISOU 3547  CB  ILE A 472    19318  20653  20599   2393  -3038  -3546       C  
+ATOM   3548  CG1 ILE A 472      70.708  93.667  -8.341  1.00147.27           C  
+ANISOU 3548  CG1 ILE A 472    17653  19253  19051   2196  -2745  -3384       C  
+ATOM   3549  CG2 ILE A 472      71.526  94.090  -6.105  1.00155.95           C  
+ANISOU 3549  CG2 ILE A 472    19129  20312  19812   2752  -3085  -3637       C  
+ATOM   3550  CD1 ILE A 472      70.927  92.915  -9.606  1.00135.92           C  
+ANISOU 3550  CD1 ILE A 472    16097  17752  17794   1929  -2643  -3254       C  
+ATOM   3551  N   ARG A 473      73.812  95.865  -5.996  1.00180.33           N  
+ANISOU 3551  N   ARG A 473    22055  22917  23544   2920  -3920  -4092       N  
+ATOM   3552  CA  ARG A 473      73.860  96.762  -4.877  1.00189.65           C  
+ANISOU 3552  CA  ARG A 473    23415  24034  24610   3307  -4283  -4357       C  
+ATOM   3553  C   ARG A 473      74.328  98.021  -5.558  1.00185.98           C  
+ANISOU 3553  C   ARG A 473    22628  23344  24692   3195  -4530  -4477       C  
+ATOM   3554  O   ARG A 473      73.519  98.885  -5.866  1.00181.76           O  
+ANISOU 3554  O   ARG A 473    22002  22904  24153   3165  -4446  -4474       O  
+ATOM   3555  CB  ARG A 473      74.803  96.287  -3.768  1.00205.24           C  
+ANISOU 3555  CB  ARG A 473    25661  25862  26460   3635  -4629  -4545       C  
+ATOM   3556  CG  ARG A 473      74.468  96.921  -2.433  1.00220.50           C  
+ANISOU 3556  CG  ARG A 473    27955  27814  28011   4171  -4923  -4787       C  
+ATOM   3557  CD  ARG A 473      75.337  96.433  -1.295  1.00232.58           C  
+ANISOU 3557  CD  ARG A 473    29837  29192  29341   4591  -5300  -5003       C  
+ATOM   3558  NE  ARG A 473      76.763  96.602  -1.549  1.00237.46           N  
+ANISOU 3558  NE  ARG A 473    30220  29440  30564   4489  -5773  -5205       N  
+ATOM   3559  CZ  ARG A 473      77.716  95.991  -0.861  1.00240.32           C  
+ANISOU 3559  CZ  ARG A 473    30786  29623  30903   4725  -6098  -5371       C  
+ATOM   3560  NH1 ARG A 473      77.388  95.172   0.115  1.00241.12           N  
+ANISOU 3560  NH1 ARG A 473    31370  29900  30344   5100  -5978  -5355       N  
+ATOM   3561  NH2 ARG A 473      78.988  96.195  -1.159  1.00241.54           N  
+ANISOU 3561  NH2 ARG A 473    30651  29398  31724   4605  -6518  -5533       N  
+ATOM   3562  N   ASN A 474      75.608  98.073  -5.901  1.00188.74           N  
+ANISOU 3562  N   ASN A 474    22768  23381  25563   3107  -4772  -4534       N  
+ATOM   3563  CA  ASN A 474      76.168  99.264  -6.526  1.00188.61           C  
+ANISOU 3563  CA  ASN A 474    22395  23075  26194   3026  -4977  -4605       C  
+ATOM   3564  C   ASN A 474      75.420  99.892  -7.729  1.00173.98           C  
+ANISOU 3564  C   ASN A 474    20312  21326  24467   2781  -4623  -4414       C  
+ATOM   3565  O   ASN A 474      75.482 101.107  -7.891  1.00176.08           O  
+ANISOU 3565  O   ASN A 474    20374  21426  25104   2827  -4803  -4518       O  
+ATOM   3566  CB  ASN A 474      77.650  99.066  -6.857  1.00199.45           C  
+ANISOU 3566  CB  ASN A 474    23514  24060  28207   2939  -5170  -4595       C  
+ATOM   3567  CG  ASN A 474      78.504  98.809  -5.625  1.00210.23           C  
+ANISOU 3567  CG  ASN A 474    25062  25219  29597   3243  -5689  -4883       C  
+ATOM   3568  OD1 ASN A 474      78.174  99.240  -4.527  1.00215.26           O  
+ANISOU 3568  OD1 ASN A 474    25969  25891  29928   3596  -6043  -5160       O  
+ATOM   3569  ND2 ASN A 474      79.613  98.103  -5.810  1.00212.07           N  
+ANISOU 3569  ND2 ASN A 474    25176  25222  30179   3148  -5748  -4822       N  
+ATOM   3570  N   ILE A 475      74.722  99.114  -8.562  1.00161.86           N  
+ANISOU 3570  N   ILE A 475    18813  20033  22652   2556  -4165  -4158       N  
+ATOM   3571  CA  ILE A 475      74.022  99.727  -9.709  1.00154.84           C  
+ANISOU 3571  CA  ILE A 475    17755  19216  21861   2390  -3885  -4015       C  
+ATOM   3572  C   ILE A 475      72.785 100.520  -9.312  1.00159.27           C  
+ANISOU 3572  C   ILE A 475    18401  19995  22121   2490  -3891  -4125       C  
+ATOM   3573  O   ILE A 475      72.662 101.687  -9.663  1.00162.60           O  
+ANISOU 3573  O   ILE A 475    18651  20327  22804   2505  -3962  -4186       O  
+ATOM   3574  CB  ILE A 475      73.600  98.727 -10.818  1.00214.93           C  
+ANISOU 3574  CB  ILE A 475    25397  26975  29293   2173  -3475  -3753       C  
+ATOM   3575  CG1 ILE A 475      74.808  98.227 -11.613  1.00218.74           C  
+ANISOU 3575  CG1 ILE A 475    25753  27211  30145   2083  -3390  -3585       C  
+ATOM   3576  CG2 ILE A 475      72.618  99.379 -11.789  1.00210.37           C  
+ANISOU 3576  CG2 ILE A 475    24743  26509  28681   2097  -3256  -3677       C  
+ATOM   3577  CD1 ILE A 475      74.446  97.201 -12.664  1.00218.15           C  
+ANISOU 3577  CD1 ILE A 475    25777  27260  29850   1946  -3056  -3359       C  
+ATOM   3578  N   GLU A 476      71.856  99.893  -8.600  1.00156.45           N  
+ANISOU 3578  N   GLU A 476    18293  19907  21245   2571  -3778  -4120       N  
+ATOM   3579  CA  GLU A 476      70.623 100.594  -8.252  1.00142.72           C  
+ANISOU 3579  CA  GLU A 476    16627  18370  19231   2677  -3720  -4176       C  
+ATOM   3580  C   GLU A 476      70.845 101.560  -7.094  1.00144.41           C  
+ANISOU 3580  C   GLU A 476    16959  18500  19412   3012  -4114  -4440       C  
+ATOM   3581  O   GLU A 476      70.012 102.417  -6.811  1.00142.58           O  
+ANISOU 3581  O   GLU A 476    16768  18380  19024   3141  -4136  -4517       O  
+ATOM   3582  CB  GLU A 476      69.486  99.616  -7.958  1.00128.68           C  
+ANISOU 3582  CB  GLU A 476    15020  16860  17012   2665  -3395  -4018       C  
+ATOM   3583  CG  GLU A 476      69.562  98.905  -6.628  1.00128.73           C  
+ANISOU 3583  CG  GLU A 476    15321  16930  16662   2941  -3433  -4041       C  
+ATOM   3584  CD  GLU A 476      68.465  97.866  -6.484  1.00133.35           C  
+ANISOU 3584  CD  GLU A 476    15992  17719  16955   2900  -3026  -3806       C  
+ATOM   3585  OE1 GLU A 476      68.285  97.061  -7.422  1.00132.30           O  
+ANISOU 3585  OE1 GLU A 476    15710  17598  16962   2603  -2816  -3643       O  
+ATOM   3586  OE2 GLU A 476      67.768  97.860  -5.448  1.00136.37           O  
+ANISOU 3586  OE2 GLU A 476    16588  18223  17003   3196  -2912  -3773       O  
+ATOM   3587  N   LYS A 477      71.990 101.424  -6.439  1.00148.46           N  
+ANISOU 3587  N   LYS A 477    17534  18788  20087   3179  -4467  -4596       N  
+ATOM   3588  CA  LYS A 477      72.399 102.365  -5.405  1.00154.25           C  
+ANISOU 3588  CA  LYS A 477    18378  19348  20882   3544  -4976  -4907       C  
+ATOM   3589  C   LYS A 477      72.634 103.732  -5.994  1.00159.76           C  
+ANISOU 3589  C   LYS A 477    18754  19833  22117   3467  -5183  -5014       C  
+ATOM   3590  O   LYS A 477      72.568 104.742  -5.292  1.00158.01           O  
+ANISOU 3590  O   LYS A 477    18598  19516  21921   3749  -5568  -5266       O  
+ATOM   3591  CB  LYS A 477      73.667 101.883  -4.706  1.00154.03           C  
+ANISOU 3591  CB  LYS A 477    18446  19057  21022   3730  -5372  -5076       C  
+ATOM   3592  CG  LYS A 477      73.367 100.787  -3.750  1.00149.76           C  
+ANISOU 3592  CG  LYS A 477    18324  18717  19861   3975  -5258  -5042       C  
+ATOM   3593  CD  LYS A 477      74.437 100.554  -2.740  1.00143.39           C  
+ANISOU 3593  CD  LYS A 477    17737  17664  19082   4327  -5762  -5306       C  
+ATOM   3594  CE  LYS A 477      73.739 100.087  -1.488  1.00145.72           C  
+ANISOU 3594  CE  LYS A 477    18569  18186  18613   4805  -5697  -5337       C  
+ATOM   3595  NZ  LYS A 477      74.661  99.556  -0.469  1.00149.42           N  
+ANISOU 3595  NZ  LYS A 477    19366  18470  18936   5206  -6108  -5561       N  
+ATOM   3596  N   THR A 478      72.908 103.757  -7.293  1.00166.45           N  
+ANISOU 3596  N   THR A 478    19271  20589  23384   3124  -4918  -4810       N  
+ATOM   3597  CA  THR A 478      73.186 105.012  -7.968  1.00170.99           C  
+ANISOU 3597  CA  THR A 478    19509  20929  24531   3052  -5023  -4844       C  
+ATOM   3598  C   THR A 478      71.905 105.733  -8.391  1.00172.33           C  
+ANISOU 3598  C   THR A 478    19686  21358  24435   2999  -4780  -4796       C  
+ATOM   3599  O   THR A 478      71.021 105.170  -9.046  1.00163.10           O  
+ANISOU 3599  O   THR A 478    18581  20461  22928   2823  -4356  -4594       O  
+ATOM   3600  CB  THR A 478      74.217 104.856  -9.128  1.00146.93           C  
+ANISOU 3600  CB  THR A 478    16115  17589  22125   2815  -4843  -4626       C  
+ATOM   3601  OG1 THR A 478      74.873 106.106  -9.369  1.00147.15           O  
+ANISOU 3601  OG1 THR A 478    15790  17239  22881   2857  -5085  -4705       O  
+ATOM   3602  CG2 THR A 478      73.562 104.391 -10.425  1.00144.32           C  
+ANISOU 3602  CG2 THR A 478    15764  17473  21597   2570  -4282  -4318       C  
+ATOM   3603  N   VAL A 479      71.809 106.979  -7.944  1.00177.73           N  
+ANISOU 3603  N   VAL A 479    20309  21929  25291   3181  -5110  -5014       N  
+ATOM   3604  CA  VAL A 479      70.674 107.838  -8.218  1.00175.65           C  
+ANISOU 3604  CA  VAL A 479    20044  21875  24819   3171  -4959  -5013       C  
+ATOM   3605  C   VAL A 479      71.176 109.077  -8.939  1.00177.67           C  
+ANISOU 3605  C   VAL A 479    19928  21830  25749   3101  -5069  -5035       C  
+ATOM   3606  O   VAL A 479      72.353 109.417  -8.840  1.00179.02           O  
+ANISOU 3606  O   VAL A 479    19865  21600  26555   3150  -5387  -5121       O  
+ATOM   3607  CB  VAL A 479      69.983 108.266  -6.909  1.00168.37           C  
+ANISOU 3607  CB  VAL A 479    19442  21113  23418   3524  -5243  -5249       C  
+ATOM   3608  CG1 VAL A 479      69.008 109.403  -7.169  1.00162.23           C  
+ANISOU 3608  CG1 VAL A 479    18603  20470  22565   3531  -5176  -5285       C  
+ATOM   3609  CG2 VAL A 479      69.288 107.080  -6.256  1.00163.89           C  
+ANISOU 3609  CG2 VAL A 479    19236  20856  22180   3624  -4991  -5142       C  
+ATOM   3610  N   MET A 486      57.931 103.433  -6.150  1.00 92.00           N  
+ANISOU 3610  N   MET A 486    10154  13425  11375   3371  -1523  -3283       N  
+ATOM   3611  CA  MET A 486      56.824 104.104  -5.482  1.00110.61           C  
+ANISOU 3611  CA  MET A 486    12558  15882  13585   3644  -1287  -3161       C  
+ATOM   3612  C   MET A 486      56.182 105.150  -6.389  1.00111.38           C  
+ANISOU 3612  C   MET A 486    12428  16016  13877   3424  -1416  -3308       C  
+ATOM   3613  O   MET A 486      56.000 106.305  -6.002  1.00120.35           O  
+ANISOU 3613  O   MET A 486    13691  17243  14794   3639  -1526  -3430       O  
+ATOM   3614  CB  MET A 486      57.288 104.743  -4.170  1.00120.60           C  
+ANISOU 3614  CB  MET A 486    14253  17221  14347   4174  -1395  -3243       C  
+ATOM   3615  CG  MET A 486      58.405 105.768  -4.316  1.00126.87           C  
+ANISOU 3615  CG  MET A 486    15171  17987  15047   4196  -1944  -3629       C  
+ATOM   3616  SD  MET A 486      58.150 107.169  -3.210  1.00146.78           S  
+ANISOU 3616  SD  MET A 486    18044  20597  17128   4757  -2124  -3782       S  
+ATOM   3617  CE  MET A 486      58.003 106.326  -1.640  1.00102.35           C  
+ANISOU 3617  CE  MET A 486    12883  15002  11003   5391  -1797  -3524       C  
+ATOM   3618  N   GLY A 496      57.154  99.412  -2.065  1.00157.62           N  
+ANISOU 3618  N   GLY A 496    19150  21656  19083   4616     82  -2024       N  
+ATOM   3619  CA  GLY A 496      57.031  99.217  -3.498  1.00158.07           C  
+ANISOU 3619  CA  GLY A 496    18749  21636  19677   3991   -116  -2141       C  
+ATOM   3620  C   GLY A 496      57.664  97.914  -3.942  1.00163.71           C  
+ANISOU 3620  C   GLY A 496    19349  22235  20619   3709   -140  -2093       C  
+ATOM   3621  O   GLY A 496      58.040  97.090  -3.111  1.00167.17           O  
+ANISOU 3621  O   GLY A 496    20009  22652  20858   3970     79  -1915       O  
+ATOM   3622  N   GLU A 497      57.778  97.722  -5.254  1.00166.67           N  
+ANISOU 3622  N   GLU A 497    19413  22530  21383   3222   -403  -2250       N  
+ATOM   3623  CA  GLU A 497      58.431  96.534  -5.792  1.00170.73           C  
+ANISOU 3623  CA  GLU A 497    19840  22933  22098   2955   -485  -2239       C  
+ATOM   3624  C   GLU A 497      59.931  96.621  -5.556  1.00172.64           C  
+ANISOU 3624  C   GLU A 497    20403  23228  21965   3043   -809  -2472       C  
+ATOM   3625  O   GLU A 497      60.589  95.609  -5.311  1.00173.01           O  
+ANISOU 3625  O   GLU A 497    20547  23223  21965   3047   -764  -2395       O  
+ATOM   3626  CB  GLU A 497      58.173  96.394  -7.290  1.00169.62           C  
+ANISOU 3626  CB  GLU A 497    19361  22681  22408   2505   -727  -2374       C  
+ATOM   3627  CG  GLU A 497      56.882  97.012  -7.777  1.00173.11           C  
+ANISOU 3627  CG  GLU A 497    19518  23086  23171   2413   -663  -2350       C  
+ATOM   3628  CD  GLU A 497      56.703  96.840  -9.270  1.00173.61           C  
+ANISOU 3628  CD  GLU A 497    19329  23013  23621   2054   -974  -2531       C  
+ATOM   3629  OE1 GLU A 497      57.597  96.241  -9.908  1.00170.78           O  
+ANISOU 3629  OE1 GLU A 497    19040  22600  23250   1901  -1202  -2647       O  
+ATOM   3630  OE2 GLU A 497      55.672  97.300  -9.805  1.00174.73           O  
+ANISOU 3630  OE2 GLU A 497    19233  23093  24064   1967   -999  -2559       O  
+ATOM   3631  N   ILE A 498      60.466  97.836  -5.652  1.00171.44           N  
+ANISOU 3631  N   ILE A 498    20381  23149  21608   3107  -1147  -2757       N  
+ATOM   3632  CA  ILE A 498      61.883  98.078  -5.403  1.00167.93           C  
+ANISOU 3632  CA  ILE A 498    20186  22695  20926   3210  -1503  -2995       C  
+ATOM   3633  C   ILE A 498      62.251  97.620  -4.004  1.00173.82           C  
+ANISOU 3633  C   ILE A 498    21298  23465  21280   3659  -1379  -2903       C  
+ATOM   3634  O   ILE A 498      63.315  97.034  -3.789  1.00168.12           O  
+ANISOU 3634  O   ILE A 498    20732  22684  20462   3692  -1544  -2980       O  
+ATOM   3635  CB  ILE A 498      62.227  99.564  -5.520  1.00165.53           C  
+ANISOU 3635  CB  ILE A 498    19934  22420  20540   3287  -1855  -3283       C  
+ATOM   3636  CG1 ILE A 498      61.848 100.100  -6.901  1.00171.37           C  
+ANISOU 3636  CG1 ILE A 498    20360  23137  21618   2911  -1947  -3365       C  
+ATOM   3637  CG2 ILE A 498      63.706  99.798  -5.239  1.00153.61           C  
+ANISOU 3637  CG2 ILE A 498    18613  20820  18931   3398  -2256  -3525       C  
+ATOM   3638  CD1 ILE A 498      61.891 101.605  -6.996  1.00174.73           C  
+ANISOU 3638  CD1 ILE A 498    20793  23594  22004   2996  -2195  -3587       C  
+ATOM   3639  N   SER A 499      61.362  97.890  -3.054  1.00187.32           N  
+ANISOU 3639  N   SER A 499    23169  25252  22753   4049  -1074  -2727       N  
+ATOM   3640  CA  SER A 499      61.558  97.444  -1.683  1.00198.47           C  
+ANISOU 3640  CA  SER A 499    24999  26682  23729   4594   -880  -2593       C  
+ATOM   3641  C   SER A 499      61.611  95.926  -1.651  1.00206.95           C  
+ANISOU 3641  C   SER A 499    26003  27687  24943   4481   -548  -2315       C  
+ATOM   3642  O   SER A 499      62.302  95.334  -0.823  1.00211.11           O  
+ANISOU 3642  O   SER A 499    26867  28195  25151   4806   -533  -2293       O  
+ATOM   3643  CB  SER A 499      60.435  97.952  -0.780  1.00200.16           C  
+ANISOU 3643  CB  SER A 499    25385  26976  23690   5064   -498  -2368       C  
+ATOM   3644  OG  SER A 499      60.635  97.541   0.556  1.00203.12           O  
+ANISOU 3644  OG  SER A 499    26244  27359  23574   5697   -286  -2224       O  
+ATOM   3645  N   ASP A 500      60.877  95.300  -2.564  1.00209.43           N  
+ANISOU 3645  N   ASP A 500    25883  27938  25751   4040   -320  -2123       N  
+ATOM   3646  CA  ASP A 500      60.862  93.851  -2.655  1.00212.36           C  
+ANISOU 3646  CA  ASP A 500    26120  28207  26361   3883    -39  -1865       C  
+ATOM   3647  C   ASP A 500      62.097  93.307  -3.366  1.00203.21           C  
+ANISOU 3647  C   ASP A 500    24946  26999  25265   3557   -426  -2093       C  
+ATOM   3648  O   ASP A 500      62.511  92.195  -3.092  1.00204.96           O  
+ANISOU 3648  O   ASP A 500    25238  27165  25472   3573   -286  -1954       O  
+ATOM   3649  CB  ASP A 500      59.580  93.355  -3.335  1.00221.23           C  
+ANISOU 3649  CB  ASP A 500    26765  29217  28075   3573    287  -1593       C  
+ATOM   3650  CG  ASP A 500      58.346  93.583  -2.486  1.00233.55           C  
+ANISOU 3650  CG  ASP A 500    28310  30773  29657   3938    816  -1243       C  
+ATOM   3651  OD1 ASP A 500      58.502  93.807  -1.272  1.00239.09           O  
+ANISOU 3651  OD1 ASP A 500    29432  31558  29852   4492   1029  -1137       O  
+ATOM   3652  OD2 ASP A 500      57.226  93.532  -3.034  1.00237.24           O  
+ANISOU 3652  OD2 ASP A 500    28355  31128  30656   3709   1011  -1072       O  
+ATOM   3653  N   ILE A 501      62.692  94.094  -4.258  1.00192.67           N  
+ANISOU 3653  N   ILE A 501    23525  25673  24008   3292   -875  -2413       N  
+ATOM   3654  CA  ILE A 501      63.864  93.633  -5.007  1.00181.71           C  
+ANISOU 3654  CA  ILE A 501    22108  24215  22719   3006  -1195  -2587       C  
+ATOM   3655  C   ILE A 501      65.165  93.699  -4.196  1.00170.68           C  
+ANISOU 3655  C   ILE A 501    21064  22814  20972   3287  -1450  -2764       C  
+ATOM   3656  O   ILE A 501      65.940  92.735  -4.173  1.00164.70           O  
+ANISOU 3656  O   ILE A 501    20376  21999  20202   3222  -1476  -2736       O  
+ATOM   3657  CB  ILE A 501      64.023  94.385  -6.353  1.00138.03           C  
+ANISOU 3657  CB  ILE A 501    16341  18649  17453   2657  -1507  -2802       C  
+ATOM   3658  CG1 ILE A 501      63.385  93.589  -7.491  1.00130.33           C  
+ANISOU 3658  CG1 ILE A 501    15061  17587  16872   2296  -1411  -2683       C  
+ATOM   3659  CG2 ILE A 501      65.491  94.642  -6.682  1.00137.98           C  
+ANISOU 3659  CG2 ILE A 501    16437  18581  17410   2596  -1883  -3037       C  
+ATOM   3660  CD1 ILE A 501      61.877  93.516  -7.426  1.00127.50           C  
+ANISOU 3660  CD1 ILE A 501    14480  17217  16748   2292  -1111  -2482       C  
+ATOM   3661  N   HIS A 502      65.396  94.829  -3.532  1.00164.00           N  
+ANISOU 3661  N   HIS A 502    20439  22007  19869   3613  -1676  -2964       N  
+ATOM   3662  CA  HIS A 502      66.603  95.022  -2.737  1.00157.69           C  
+ANISOU 3662  CA  HIS A 502    19968  21147  18800   3931  -2029  -3195       C  
+ATOM   3663  C   HIS A 502      66.627  94.018  -1.594  1.00162.64           C  
+ANISOU 3663  C   HIS A 502    20933  21795  19069   4322  -1761  -3012       C  
+ATOM   3664  O   HIS A 502      67.677  93.496  -1.229  1.00164.35           O  
+ANISOU 3664  O   HIS A 502    21348  21935  19163   4433  -1971  -3125       O  
+ATOM   3665  CB  HIS A 502      66.654  96.452  -2.195  1.00150.83           C  
+ANISOU 3665  CB  HIS A 502    19272  20282  17755   4264  -2351  -3452       C  
+ATOM   3666  CG  HIS A 502      67.989  97.117  -2.348  1.00143.78           C  
+ANISOU 3666  CG  HIS A 502    18387  19223  17019   4248  -2929  -3804       C  
+ATOM   3667  ND1 HIS A 502      68.672  97.155  -3.544  1.00137.00           N  
+ANISOU 3667  ND1 HIS A 502    17193  18253  16609   3785  -3096  -3872       N  
+ATOM   3668  CD2 HIS A 502      68.759  97.787  -1.457  1.00140.15           C  
+ANISOU 3668  CD2 HIS A 502    18217  18647  16387   4674  -3387  -4099       C  
+ATOM   3669  CE1 HIS A 502      69.807  97.814  -3.383  1.00131.33           C  
+ANISOU 3669  CE1 HIS A 502    16503  17343  16055   3893  -3583  -4155       C  
+ATOM   3670  NE2 HIS A 502      69.883  98.208  -2.125  1.00130.34           N  
+ANISOU 3670  NE2 HIS A 502    16741  17200  15583   4416  -3815  -4326       N  
+ATOM   3671  N   THR A 503      65.450  93.751  -1.039  1.00163.37           N  
+ANISOU 3671  N   THR A 503    21080  21971  19022   4552  -1268  -2705       N  
+ATOM   3672  CA  THR A 503      65.295  92.771   0.033  1.00163.31           C  
+ANISOU 3672  CA  THR A 503    21384  21972  18696   4974   -872  -2437       C  
+ATOM   3673  C   THR A 503      65.393  91.327  -0.475  1.00151.49           C  
+ANISOU 3673  C   THR A 503    19659  20415  17484   4610   -614  -2206       C  
+ATOM   3674  O   THR A 503      65.955  90.464   0.198  1.00146.11           O  
+ANISOU 3674  O   THR A 503    19247  19705  16563   4853   -520  -2130       O  
+ATOM   3675  CB  THR A 503      63.956  92.972   0.783  1.00170.09           C  
+ANISOU 3675  CB  THR A 503    22342  22899  19387   5379   -334  -2106       C  
+ATOM   3676  OG1 THR A 503      64.026  94.162   1.576  1.00176.61           O  
+ANISOU 3676  OG1 THR A 503    23551  23775  19778   5902   -585  -2325       O  
+ATOM   3677  CG2 THR A 503      63.645  91.790   1.693  1.00171.53           C  
+ANISOU 3677  CG2 THR A 503    22747  23054  19374   5760    235  -1707       C  
+ATOM   3678  N   LYS A 504      64.865  91.071  -1.669  1.00141.41           N  
+ANISOU 3678  N   LYS A 504    17911  19106  16711   4058   -541  -2117       N  
+ATOM   3679  CA  LYS A 504      64.918  89.730  -2.251  1.00138.96           C  
+ANISOU 3679  CA  LYS A 504    17368  18711  16719   3706   -369  -1930       C  
+ATOM   3680  C   LYS A 504      66.335  89.382  -2.646  1.00142.04           C  
+ANISOU 3680  C   LYS A 504    17848  19062  17058   3520   -779  -2178       C  
+ATOM   3681  O   LYS A 504      66.762  88.230  -2.559  1.00134.46           O  
+ANISOU 3681  O   LYS A 504    16927  18053  16108   3463   -666  -2057       O  
+ATOM   3682  CB  LYS A 504      64.010  89.614  -3.477  1.00129.82           C  
+ANISOU 3682  CB  LYS A 504    15721  17488  16116   3223   -303  -1839       C  
+ATOM   3683  CG  LYS A 504      62.585  89.222  -3.164  1.00130.75           C  
+ANISOU 3683  CG  LYS A 504    15616  17544  16518   3307    225  -1458       C  
+ATOM   3684  N   LEU A 505      67.053  90.395  -3.100  1.00138.41           N  
+ANISOU 3684  N   LEU A 505    17394  18602  16593   3427  -1237  -2505       N  
+ATOM   3685  CA  LEU A 505      68.435  90.224  -3.458  1.00120.29           C  
+ANISOU 3685  CA  LEU A 505    15154  16230  14322   3285  -1619  -2726       C  
+ATOM   3686  C   LEU A 505      69.260  89.889  -2.223  1.00124.70           C  
+ANISOU 3686  C   LEU A 505    16126  16770  14484   3724  -1710  -2800       C  
+ATOM   3687  O   LEU A 505      70.166  89.052  -2.259  1.00124.02           O  
+ANISOU 3687  O   LEU A 505    16106  16619  14396   3643  -1808  -2823       O  
+ATOM   3688  CB  LEU A 505      68.966  91.499  -4.066  1.00109.42           C  
+ANISOU 3688  CB  LEU A 505    13673  14807  13094   3171  -2036  -3016       C  
+ATOM   3689  CG  LEU A 505      70.386  91.097  -4.404  1.00128.61           C  
+ANISOU 3689  CG  LEU A 505    16120  17111  15635   3034  -2336  -3157       C  
+ATOM   3690  CD1 LEU A 505      70.509  91.048  -5.920  1.00141.22           C  
+ANISOU 3690  CD1 LEU A 505    17393  18648  17616   2580  -2370  -3131       C  
+ATOM   3691  CD2 LEU A 505      71.441  91.946  -3.655  1.00114.58           C  
+ANISOU 3691  CD2 LEU A 505    14554  15224  13758   3350  -2784  -3462       C  
+ATOM   3692  N   LEU A 506      68.951  90.579  -1.132  1.00130.40           N  
+ANISOU 3692  N   LEU A 506    17159  17539  14847   4233  -1705  -2854       N  
+ATOM   3693  CA  LEU A 506      69.612  90.336   0.139  1.00131.63           C  
+ANISOU 3693  CA  LEU A 506    17798  17665  14549   4786  -1814  -2947       C  
+ATOM   3694  C   LEU A 506      69.501  88.865   0.507  1.00144.65           C  
+ANISOU 3694  C   LEU A 506    19548  19330  16080   4841  -1380  -2638       C  
+ATOM   3695  O   LEU A 506      70.448  88.287   1.024  1.00156.36           O  
+ANISOU 3695  O   LEU A 506    21304  20754  17351   5038  -1550  -2741       O  
+ATOM   3696  CB  LEU A 506      69.008  91.206   1.240  1.00120.17           C  
+ANISOU 3696  CB  LEU A 506    16713  16270  12675   5409  -1772  -2980       C  
+ATOM   3697  N   ARG A 507      68.351  88.260   0.214  1.00146.01           N  
+ANISOU 3697  N   ARG A 507    19470  19552  16453   4658   -839  -2263       N  
+ATOM   3698  CA  ARG A 507      68.159  86.831   0.443  1.00152.26           C  
+ANISOU 3698  CA  ARG A 507    20261  20320  17271   4649   -394  -1930       C  
+ATOM   3699  C   ARG A 507      69.164  86.004  -0.360  1.00145.14           C  
+ANISOU 3699  C   ARG A 507    19199  19352  16597   4200   -653  -2042       C  
+ATOM   3700  O   ARG A 507      69.471  84.873   0.007  1.00142.95           O  
+ANISOU 3700  O   ARG A 507    19044  19045  16224   4273   -450  -1882       O  
+ATOM   3701  CB  ARG A 507      66.724  86.401   0.111  1.00161.16           C  
+ANISOU 3701  CB  ARG A 507    21023  21434  18777   4463    165  -1523       C  
+ATOM   3702  CG  ARG A 507      65.661  86.975   1.046  1.00177.63           C  
+ANISOU 3702  CG  ARG A 507    23283  23565  20645   4978    585  -1290       C  
+ATOM   3703  CD  ARG A 507      64.263  86.468   0.696  1.00187.68           C  
+ANISOU 3703  CD  ARG A 507    24111  24757  22442   4769   1148   -857       C  
+ATOM   3704  NE  ARG A 507      63.229  87.082   1.527  1.00198.88           N  
+ANISOU 3704  NE  ARG A 507    25661  26202  23704   5259   1588   -600       N  
+ATOM   3705  CZ  ARG A 507      61.923  86.883   1.368  1.00205.71           C  
+ANISOU 3705  CZ  ARG A 507    26137  26967  25058   5169   2095   -210       C  
+ATOM   3706  NH1 ARG A 507      61.484  86.084   0.406  1.00207.33           N  
+ANISOU 3706  NH1 ARG A 507    25797  27020  25959   4610   2159    -75       N  
+ATOM   3707  NH2 ARG A 507      61.055  87.485   2.171  1.00208.11           N  
+ANISOU 3707  NH2 ARG A 507    26596  27293  25185   5668   2517     41       N  
+ATOM   3708  N   LEU A 508      69.681  86.579  -1.445  1.00134.19           N  
+ANISOU 3708  N   LEU A 508    17555  17933  15496   3776  -1072  -2294       N  
+ATOM   3709  CA  LEU A 508      70.654  85.888  -2.287  1.00125.06           C  
+ANISOU 3709  CA  LEU A 508    16259  16704  14555   3385  -1304  -2382       C  
+ATOM   3710  C   LEU A 508      72.070  85.983  -1.730  1.00133.75           C  
+ANISOU 3710  C   LEU A 508    17660  17739  15419   3609  -1705  -2655       C  
+ATOM   3711  O   LEU A 508      72.775  84.975  -1.650  1.00132.42           O  
+ANISOU 3711  O   LEU A 508    17581  17530  15202   3562  -1700  -2615       O  
+ATOM   3712  CB  LEU A 508      70.606  86.404  -3.726  1.00112.64           C  
+ANISOU 3712  CB  LEU A 508    14312  15095  13392   2907  -1513  -2482       C  
+ATOM   3713  CG  LEU A 508      69.271  86.186  -4.436  1.00113.17           C  
+ANISOU 3713  CG  LEU A 508    14056  15174  13769   2653  -1215  -2260       C  
+ATOM   3714  CD1 LEU A 508      69.459  86.184  -5.945  1.00106.22           C  
+ANISOU 3714  CD1 LEU A 508    12898  14225  13237   2216  -1432  -2343       C  
+ATOM   3715  CD2 LEU A 508      68.613  84.894  -3.968  1.00117.32           C  
+ANISOU 3715  CD2 LEU A 508    14560  15678  14341   2703   -779  -1932       C  
+ATOM   3716  N   SER A 509      72.491  87.188  -1.355  1.00130.72           N  
+ANISOU 3716  N   SER A 509    17414  17317  14936   3856  -2085  -2944       N  
+ATOM   3717  CA  SER A 509      73.767  87.347  -0.663  1.00136.66           C  
+ANISOU 3717  CA  SER A 509    18457  17948  15519   4156  -2531  -3236       C  
+ATOM   3718  C   SER A 509      73.698  86.583   0.646  1.00143.40           C  
+ANISOU 3718  C   SER A 509    19773  18845  15866   4687  -2319  -3138       C  
+ATOM   3719  O   SER A 509      74.680  85.977   1.082  1.00138.35           O  
+ANISOU 3719  O   SER A 509    19358  18121  15090   4839  -2521  -3256       O  
+ATOM   3720  CB  SER A 509      74.089  88.820  -0.410  1.00130.15           C  
+ANISOU 3720  CB  SER A 509    17685  17028  14738   4382  -3008  -3572       C  
+ATOM   3721  OG  SER A 509      74.991  89.319  -1.384  1.00125.27           O  
+ANISOU 3721  OG  SER A 509    16739  16248  14609   4003  -3376  -3749       O  
+ATOM   3722  N   SER A 510      72.520  86.615   1.263  1.00144.65           N  
+ANISOU 3722  N   SER A 510    20078  19123  15761   4999  -1878  -2899       N  
+ATOM   3723  CA  SER A 510      72.236  85.777   2.417  1.00148.97           C  
+ANISOU 3723  CA  SER A 510    21044  19713  15846   5526  -1487  -2677       C  
+ATOM   3724  C   SER A 510      72.424  84.315   2.046  1.00145.48           C  
+ANISOU 3724  C   SER A 510    20455  19266  15554   5204  -1182  -2426       C  
+ATOM   3725  O   SER A 510      73.001  83.552   2.811  1.00140.49           O  
+ANISOU 3725  O   SER A 510    20177  18606  14598   5539  -1145  -2415       O  
+ATOM   3726  CB  SER A 510      70.807  85.999   2.911  1.00149.05           C  
+ANISOU 3726  CB  SER A 510    21111  19824  15699   5832   -930  -2348       C  
+ATOM   3727  OG  SER A 510      70.346  84.882   3.643  1.00147.27           O  
+ANISOU 3727  OG  SER A 510    21097  19617  15240   6154   -337  -1962       O  
+ATOM   3728  N   SER A 511      71.946  83.946   0.858  1.00139.39           N  
+ANISOU 3728  N   SER A 511    19184  18508  15269   4583  -1004  -2249       N  
+ATOM   3729  CA  SER A 511      72.011  82.573   0.365  1.00129.68           C  
+ANISOU 3729  CA  SER A 511    17763  17254  14255   4236   -745  -2013       C  
+ATOM   3730  C   SER A 511      73.437  82.171   0.006  1.00129.30           C  
+ANISOU 3730  C   SER A 511    17762  17135  14232   4035  -1175  -2258       C  
+ATOM   3731  O   SER A 511      73.792  80.991   0.047  1.00109.17           O  
+ANISOU 3731  O   SER A 511    15254  14565  11659   3957  -1019  -2119       O  
+ATOM   3732  CB  SER A 511      71.108  82.416  -0.862  1.00125.40           C  
+ANISOU 3732  CB  SER A 511    16701  16703  14243   3684   -564  -1828       C  
+ATOM   3733  OG  SER A 511      71.167  81.106  -1.394  1.00137.08           O  
+ANISOU 3733  OG  SER A 511    17990  18125  15969   3359   -392  -1635       O  
+ATOM   3734  N   GLN A 512      74.247  83.164  -0.347  1.00131.12           N  
+ANISOU 3734  N   GLN A 512    17958  17304  14560   3957  -1699  -2603       N  
+ATOM   3735  CA  GLN A 512      75.621  82.941  -0.774  1.00120.52           C  
+ANISOU 3735  CA  GLN A 512    16587  15845  13361   3755  -2110  -2821       C  
+ATOM   3736  C   GLN A 512      76.508  82.796   0.431  1.00123.24           C  
+ANISOU 3736  C   GLN A 512    17385  16120  13322   4264  -2359  -3023       C  
+ATOM   3737  O   GLN A 512      77.569  82.174   0.385  1.00128.94           O  
+ANISOU 3737  O   GLN A 512    18162  16749  14081   4188  -2573  -3124       O  
+ATOM   3738  CB  GLN A 512      76.096  84.150  -1.546  1.00118.44           C  
+ANISOU 3738  CB  GLN A 512    16077  15483  13440   3537  -2531  -3073       C  
+ATOM   3739  CG  GLN A 512      76.798  83.822  -2.807  1.00125.91           C  
+ANISOU 3739  CG  GLN A 512    16711  16343  14787   3048  -2644  -3057       C  
+ATOM   3740  CD  GLN A 512      76.915  85.020  -3.690  1.00137.22           C  
+ANISOU 3740  CD  GLN A 512    17859  17690  16589   2843  -2877  -3186       C  
+ATOM   3741  OE1 GLN A 512      77.456  86.052  -3.281  1.00153.03           O  
+ANISOU 3741  OE1 GLN A 512    19904  19568  18672   3053  -3245  -3442       O  
+ATOM   3742  NE2 GLN A 512      76.353  84.922  -4.897  1.00128.62           N  
+ANISOU 3742  NE2 GLN A 512    16487  16643  15739   2470  -2678  -3017       N  
+ATOM   3743  N   GLY A 513      76.073  83.422   1.510  1.00128.93           N  
+ANISOU 3743  N   GLY A 513    18453  16869  13665   4826  -2362  -3100       N  
+ATOM   3744  CA  GLY A 513      76.767  83.309   2.761  1.00135.31           C  
+ANISOU 3744  CA  GLY A 513    19790  17602  14021   5449  -2610  -3308       C  
+ATOM   3745  C   GLY A 513      76.597  81.908   3.295  1.00133.10           C  
+ANISOU 3745  C   GLY A 513    19748  17398  13425   5620  -2132  -3010       C  
+ATOM   3746  O   GLY A 513      77.464  81.407   3.988  1.00135.48           O  
+ANISOU 3746  O   GLY A 513    20407  17622  13448   5952  -2344  -3163       O  
+ATOM   3747  N   THR A 514      75.478  81.270   2.971  1.00130.00           N  
+ANISOU 3747  N   THR A 514    19139  17128  13128   5402  -1499  -2584       N  
+ATOM   3748  CA  THR A 514      75.252  79.899   3.409  1.00136.05           C  
+ANISOU 3748  CA  THR A 514    20053  17932  13707   5525   -990  -2248       C  
+ATOM   3749  C   THR A 514      76.125  78.980   2.571  1.00136.91           C  
+ANISOU 3749  C   THR A 514    19914  17993  14111   4991  -1151  -2279       C  
+ATOM   3750  O   THR A 514      76.451  77.859   2.971  1.00136.85           O  
+ANISOU 3750  O   THR A 514    20093  17981  13923   5102   -944  -2139       O  
+ATOM   3751  CB  THR A 514      73.784  79.475   3.234  1.00141.83           C  
+ANISOU 3751  CB  THR A 514    20522  18734  14631   5408   -287  -1768       C  
+ATOM   3752  OG1 THR A 514      73.591  78.921   1.927  1.00142.93           O  
+ANISOU 3752  OG1 THR A 514    20113  18858  15336   4676   -233  -1640       O  
+ATOM   3753  CG2 THR A 514      72.861  80.659   3.413  1.00145.09           C  
+ANISOU 3753  CG2 THR A 514    20916  19192  15018   5620   -214  -1761       C  
+ATOM   3754  N   ILE A 515      76.491  79.473   1.394  1.00129.08           N  
+ANISOU 3754  N   ILE A 515    18518  16960  13565   4442  -1494  -2442       N  
+ATOM   3755  CA  ILE A 515      77.327  78.735   0.468  1.00119.39           C  
+ANISOU 3755  CA  ILE A 515    17053  15677  12633   3951  -1657  -2465       C  
+ATOM   3756  C   ILE A 515      78.787  78.796   0.895  1.00127.05           C  
+ANISOU 3756  C   ILE A 515    18275  16524  13475   4143  -2161  -2798       C  
+ATOM   3757  O   ILE A 515      79.482  77.778   0.906  1.00128.44           O  
+ANISOU 3757  O   ILE A 515    18530  16671  13600   4065  -2153  -2760       O  
+ATOM   3758  CB  ILE A 515      77.220  79.308  -0.945  1.00110.42           C  
+ANISOU 3758  CB  ILE A 515    15452  14516  11987   3401  -1815  -2500       C  
+ATOM   3759  CG1 ILE A 515      75.801  79.150  -1.481  1.00109.39           C  
+ANISOU 3759  CG1 ILE A 515    15038  14466  12060   3177  -1388  -2203       C  
+ATOM   3760  CG2 ILE A 515      78.168  78.587  -1.861  1.00112.80           C  
+ANISOU 3760  CG2 ILE A 515    15581  14743  12533   2997  -1979  -2516       C  
+ATOM   3761  CD1 ILE A 515      75.570  79.879  -2.780  1.00110.01           C  
+ANISOU 3761  CD1 ILE A 515    14740  14519  12538   2759  -1561  -2268       C  
+ATOM   3762  N   GLU A 516      79.240  79.996   1.247  1.00122.76           N  
+ANISOU 3762  N   GLU A 516    17837  15878  12927   4400  -2627  -3132       N  
+ATOM   3763  CA  GLU A 516      80.636  80.218   1.589  1.00124.18           C  
+ANISOU 3763  CA  GLU A 516    18172  15860  13149   4571  -3208  -3493       C  
+ATOM   3764  C   GLU A 516      81.099  79.274   2.681  1.00126.09           C  
+ANISOU 3764  C   GLU A 516    18894  16095  12918   5029  -3183  -3522       C  
+ATOM   3765  O   GLU A 516      82.117  78.595   2.539  1.00116.84           O  
+ANISOU 3765  O   GLU A 516    17721  14825  11848   4893  -3381  -3605       O  
+ATOM   3766  CB  GLU A 516      80.863  81.656   2.046  1.00135.21           C  
+ANISOU 3766  CB  GLU A 516    19658  17112  14603   4907  -3722  -3854       C  
+ATOM   3767  CG  GLU A 516      82.315  82.045   1.962  1.00146.41           C  
+ANISOU 3767  CG  GLU A 516    20992  18238  16400   4885  -4381  -4215       C  
+ATOM   3768  CD  GLU A 516      82.865  81.775   0.582  1.00151.28           C  
+ANISOU 3768  CD  GLU A 516    21100  18789  17590   4220  -4329  -4073       C  
+ATOM   3769  OE1 GLU A 516      82.373  82.413  -0.367  1.00146.52           O  
+ANISOU 3769  OE1 GLU A 516    20133  18221  17316   3867  -4198  -3957       O  
+ATOM   3770  OE2 GLU A 516      83.763  80.917   0.441  1.00154.24           O  
+ANISOU 3770  OE2 GLU A 516    21471  19078  18055   4089  -4397  -4065       O  
+END                                                                             
diff --git a/examples/testdata/4IM2_nterm.pdb b/examples/testdata/4IM2_nterm.pdb
new file mode 100644 (file)
index 0000000..48f7803
--- /dev/null
@@ -0,0 +1,139 @@
+HEADER    TRANSFERASE/TRANSFERASE INHIBITOR       01-JAN-13   4IM2              
+TITLE     STRUCTURE OF TANK-BINDING KINASE 1  (Truncated test file)
+DBREF  4IM2 A    1   657  UNP    Q9UHD2   TBK1_HUMAN       1    657             
+SEQADV 4IM2 GLY A   -5  UNP  Q9UHD2              EXPRESSION TAG                 
+SEQADV 4IM2 SER A   -4  UNP  Q9UHD2              EXPRESSION TAG                 
+SEQADV 4IM2 GLY A   -3  UNP  Q9UHD2              EXPRESSION TAG                 
+SEQADV 4IM2 SER A   -2  UNP  Q9UHD2              EXPRESSION TAG                 
+SEQADV 4IM2 GLY A   -1  UNP  Q9UHD2              EXPRESSION TAG                 
+SEQADV 4IM2 SER A    0  UNP  Q9UHD2              EXPRESSION TAG                 
+SEQRES   1 A  663  GLY SER GLY SER GLY SER MET GLN SER THR SER ASN HIS
+ATOM      1  N   GLY A  -1     126.784   4.226 -23.353  1.00158.13           N  
+ANISOU    1  N   GLY A  -1    19370  17517  23197   6628   1162   2075       N  
+ATOM      2  CA  GLY A  -1     125.521   4.306 -24.062  1.00150.94           C  
+ANISOU    2  CA  GLY A  -1    18746  16231  22374   6153   1277   1996       C  
+ATOM      3  C   GLY A  -1     125.742   4.361 -25.557  1.00146.29           C  
+ANISOU    3  C   GLY A  -1    18187  15453  21943   5900   1405   1498       C  
+ATOM      4  O   GLY A  -1     126.691   4.980 -26.029  1.00150.85           O  
+ANISOU    4  O   GLY A  -1    18536  16366  22413   5906   1385   1160       O  
+ATOM      5  N   SER A   0     124.869   3.710 -26.313  1.00137.36           N  
+ANISOU    5  N   SER A   0    17328  13796  21068   5675   1550   1432       N  
+ATOM      6  CA  SER A   0     125.052   3.672 -27.755  1.00139.44           C  
+ANISOU    6  CA  SER A   0    17634  13884  21461   5464   1674    953       C  
+ATOM      7  C   SER A   0     123.846   4.104 -28.574  1.00137.43           C  
+ANISOU    7  C   SER A   0    17591  13478  21149   4975   1714    755       C  
+ATOM      8  O   SER A   0     122.737   4.275 -28.071  1.00128.25           O  
+ANISOU    8  O   SER A   0    16566  12244  19921   4780   1667    988       O  
+ATOM      9  CB  SER A   0     125.578   2.312 -28.214  1.00155.09           C  
+ANISOU    9  CB  SER A   0    19671  15395  23862   5752   1827    887       C  
+ATOM     10  OG  SER A   0     126.993   2.281 -28.131  1.00164.59           O  
+ANISOU   10  OG  SER A   0    20608  16866  25062   6113   1802    781       O  
+ATOM     11  N   MET A   1     124.096   4.270 -29.861  1.00134.87           N  
+ANISOU   11  N   MET A   1    17283  13123  20839   4799   1805    311       N  
+ATOM     12  CA  MET A   1     123.214   5.039 -30.698  1.00125.22           C  
+ANISOU   12  CA  MET A   1    16193  11958  19428   4369   1804     70       C  
+ATOM     13  C   MET A   1     122.885   4.351 -32.006  1.00124.69           C  
+ANISOU   13  C   MET A   1    16293  11510  19572   4225   1942   -293       C  
+ATOM     14  O   MET A   1     123.723   3.686 -32.606  1.00129.61           O  
+ANISOU   14  O   MET A   1    16868  11984  20394   4414   2057   -519       O  
+ATOM     15  CB  MET A   1     123.867   6.392 -30.970  1.00122.51           C  
+ANISOU   15  CB  MET A   1    15675  12140  18732   4254   1762   -132       C  
+ATOM     16  CG  MET A   1     125.074   6.368 -31.866  1.00120.74           C  
+ANISOU   16  CG  MET A   1    15318  12013  18544   4369   1884   -495       C  
+ATOM     17  SD  MET A   1     125.834   7.982 -31.850  1.00196.33           S  
+ANISOU   17  SD  MET A   1    24655  22193  27747   4246   1861   -642       S  
+ATOM     18  CE  MET A   1     126.785   7.896 -30.345  1.00132.53           C  
+ANISOU   18  CE  MET A   1    16264  14397  19693   4639   1728   -355       C  
+ATOM     19  N   GLN A   2     121.643   4.496 -32.440  1.00123.78           N  
+ANISOU   19  N   GLN A   2    16363  11253  19416   3898   1925   -369       N  
+ATOM     20  CA  GLN A   2     121.313   4.161 -33.809  1.00128.51           C  
+ANISOU   20  CA  GLN A   2    17093  11643  20094   3714   2020   -799       C  
+ATOM     21  C   GLN A   2     121.639   5.396 -34.617  1.00121.06           C  
+ANISOU   21  C   GLN A   2    16105  11138  18752   3541   2004  -1065       C  
+ATOM     22  O   GLN A   2     121.959   6.442 -34.059  1.00107.32           O  
+ANISOU   22  O   GLN A   2    14247   9790  16739   3528   1928   -906       O  
+ATOM     23  CB  GLN A   2     119.832   3.845 -33.965  1.00136.11           C  
+ANISOU   23  CB  GLN A   2    18240  12314  21161   3439   1994   -809       C  
+ATOM     24  CG  GLN A   2     119.171   3.277 -32.740  1.00148.45           C  
+ANISOU   24  CG  GLN A   2    19841  13613  22949   3500   1973   -376       C  
+ATOM     25  CD  GLN A   2     117.773   2.799 -33.042  1.00158.28           C  
+ANISOU   25  CD  GLN A   2    21243  14511  24385   3221   1993   -473       C  
+ATOM     26  OE1 GLN A   2     116.827   3.097 -32.313  1.00163.28           O  
+ANISOU   26  OE1 GLN A   2    21915  15160  24966   3070   1921   -211       O  
+ATOM     27  NE2 GLN A   2     117.631   2.057 -34.134  1.00155.02           N  
+ANISOU   27  NE2 GLN A   2    20902  13797  24200   3144   2095   -885       N  
+ATOM     28  N   SER A   3     121.547   5.280 -35.931  1.00124.66           N  
+ANISOU   28  N   SER A   3    16661  11530  19175   3410   2090  -1474       N  
+ATOM     29  CA  SER A   3     121.742   6.432 -36.781  1.00120.35           C  
+ANISOU   29  CA  SER A   3    16121  11369  18236   3242   2107  -1703       C  
+ATOM     30  C   SER A   3     121.190   6.186 -38.161  1.00125.27           C  
+ANISOU   30  C   SER A   3    16914  11882  18801   3073   2165  -2108       C  
+ATOM     31  O   SER A   3     120.746   5.090 -38.489  1.00135.86           O  
+ANISOU   31  O   SER A   3    18337  12848  20436   3082   2197  -2268       O  
+ATOM     32  CB  SER A   3     123.217   6.751 -36.923  1.00116.70           C  
+ANISOU   32  CB  SER A   3    15475  11153  17711   3435   2220  -1807       C  
+ATOM     33  OG  SER A   3     123.770   5.974 -37.974  1.00112.78           O  
+ANISOU   33  OG  SER A   3    15005  10478  17367   3528   2375  -2175       O  
+ATOM     34  N   THR A   4     121.244   7.232 -38.972  1.00116.48           N  
+ANISOU   34  N   THR A   4    15849  11109  17301   2928   2191  -2281       N  
+ATOM     35  CA  THR A   4     120.978   7.130 -40.394  1.00113.12           C  
+ANISOU   35  CA  THR A   4    15568  10691  16723   2823   2262  -2692       C  
+ATOM     36  C   THR A   4     122.118   7.839 -41.119  1.00116.32           C  
+ANISOU   36  C   THR A   4    15918  11410  16867   2884   2432  -2867       C  
+ATOM     37  O   THR A   4     123.106   8.236 -40.495  1.00117.17           O  
+ANISOU   37  O   THR A   4    15851  11671  16998   3007   2493  -2706       O  
+ATOM     38  CB  THR A   4     119.615   7.756 -40.775  1.00106.20           C  
+ANISOU   38  CB  THR A   4    14854   9927  15568   2568   2121  -2713       C  
+ATOM     39  OG1 THR A   4     119.774   9.154 -41.043  1.00107.14           O  
+ANISOU   39  OG1 THR A   4    15001  10451  15258   2480   2138  -2644       O  
+ATOM     40  CG2 THR A   4     118.597   7.567 -39.657  1.00 99.42           C  
+ANISOU   40  CG2 THR A   4    13990   8889  14896   2483   1956  -2396       C  
+ATOM     41  N   SER A   5     121.981   7.982 -42.432  1.00116.70           N  
+ANISOU   41  N   SER A   5    16106  11564  16670   2803   2517  -3210       N  
+ATOM     42  CA  SER A   5     122.975   8.671 -43.247  1.00122.60           C  
+ANISOU   42  CA  SER A   5    16837  12604  17144   2839   2721  -3385       C  
+ATOM     43  C   SER A   5     123.317  10.064 -42.713  1.00128.43           C  
+ANISOU   43  C   SER A   5    17501  13666  17630   2768   2748  -3105       C  
+ATOM     44  O   SER A   5     124.481  10.467 -42.712  1.00133.01           O  
+ANISOU   44  O   SER A   5    17934  14410  18193   2852   2925  -3133       O  
+ATOM     45  CB  SER A   5     122.473   8.779 -44.687  1.00127.34           C  
+ANISOU   45  CB  SER A   5    17646  13318  17418   2741   2774  -3729       C  
+ATOM     46  OG  SER A   5     121.108   9.163 -44.712  1.00135.31           O  
+ANISOU   46  OG  SER A   5    18807  14370  18235   2568   2576  -3643       O  
+ATOM     47  N   ASN A   6     122.303  10.783 -42.240  1.00122.78           N  
+ANISOU   47  N   ASN A   6    16868  13033  16750   2608   2581  -2859       N  
+ATOM     48  CA  ASN A   6     122.460  12.194 -41.897  1.00114.44           C  
+ANISOU   48  CA  ASN A   6    15777  12275  15429   2507   2620  -2639       C  
+ATOM     49  C   ASN A   6     122.348  12.537 -40.412  1.00108.55           C  
+ANISOU   49  C   ASN A   6    14870  11544  14832   2498   2474  -2282       C  
+ATOM     50  O   ASN A   6     122.712  13.640 -40.004  1.00110.16           O  
+ANISOU   50  O   ASN A   6    14983  11984  14890   2435   2531  -2135       O  
+ATOM     51  CB  ASN A   6     121.461  13.034 -42.695  1.00113.00           C  
+ANISOU   51  CB  ASN A   6    15832  12254  14850   2339   2583  -2658       C  
+ATOM     52  CG  ASN A   6     121.646  12.886 -44.191  1.00118.14           C  
+ANISOU   52  CG  ASN A   6    16648  12982  15257   2366   2743  -2999       C  
+ATOM     53  OD1 ASN A   6     122.765  12.952 -44.698  1.00122.16           O  
+ANISOU   53  OD1 ASN A   6    17097  13580  15737   2449   2985  -3154       O  
+ATOM     54  ND2 ASN A   6     120.546  12.672 -44.905  1.00118.96           N  
+ANISOU   54  ND2 ASN A   6    16946  13074  15179   2302   2609  -3139       N  
+ATOM     55  N   HIS A   7     121.845  11.605 -39.608  1.00100.48           N  
+ANISOU   55  N   HIS A   7    13810  10269  14098   2560   2305  -2149       N  
+ATOM     56  CA  HIS A   7     121.657  11.871 -38.183  1.00 91.49           C  
+ANISOU   56  CA  HIS A   7    12535   9160  13066   2569   2162  -1799       C  
+ATOM     57  C   HIS A   7     122.134  10.725 -37.300  1.00 97.74           C  
+ANISOU   57  C   HIS A   7    13180   9725  14232   2793   2117  -1685       C  
+ATOM     58  O   HIS A   7     122.456   9.647 -37.791  1.00101.23           O  
+ANISOU   58  O   HIS A   7    13643   9923  14898   2924   2188  -1876       O  
+ATOM     59  CB  HIS A   7     120.189  12.175 -37.884  1.00 86.30           C  
+ANISOU   59  CB  HIS A   7    12021   8464  12306   2383   1977  -1628       C  
+ATOM     60  CG  HIS A   7     119.634  13.303 -38.694  1.00101.46           C  
+ANISOU   60  CG  HIS A   7    14094  10603  13853   2201   2000  -1699       C  
+ATOM     61  ND1 HIS A   7     119.265  13.157 -40.015  1.00 99.83           N  
+ANISOU   61  ND1 HIS A   7    14076  10384  13472   2153   2049  -1976       N  
+ATOM     62  CD2 HIS A   7     119.401  14.600 -38.379  1.00 89.60           C  
+ANISOU   62  CD2 HIS A   7    12588   9342  12114   2078   1988  -1527       C  
+ATOM     63  CE1 HIS A   7     118.823  14.314 -40.476  1.00 97.86           C  
+ANISOU   63  CE1 HIS A   7    13944  10363  12875   2029   2062  -1939       C  
+ATOM     64  NE2 HIS A   7     118.896  15.206 -39.503  1.00 93.14           N  
+ANISOU   64  NE2 HIS A   7    13235   9899  12253   1975   2035  -1667       N  
+END                                                                             
index 732f01b..d9a201a 100755 (executable)
@@ -22,7 +22,7 @@
    <mapID target="home" url="html/index.html" />
    
    <mapID target="new" url="html/whatsNew.html"/>
-   <mapID target="release" url="html/releases.html#Jalview.2.10.3b1"/>
+   <mapID target="release" url="html/releases.html#Jalview.2.10.4"/>
    <mapID target="alannotation" url="html/features/annotation.html"/>
    <mapID target="keys" url="html/keys.html"/>
    <mapID target="newkeys" url="html/features/newkeystrokes.html"/>
@@ -54,6 +54,7 @@
    <mapID target="pdbmcviewer" url="html/features/pdbviewer.html"/>
    <mapID target="pdbjmol" url="html/features/jmol.html"/>
    <mapID target="chimera" url="html/features/chimera.html"/>
+   <mapID target="chimera.annotxfer" url="html/features/chimera.html#annotxfer"/>
    <mapID target="varna" url="html/features/varna.html"/>
    <mapID target="xsspannotation" url="html/features/xsspannotation.html"/>
    <mapID target="preferences" url="html/features/preferences.html"/>     
index 20dd8db..b218b88 100755 (executable)
@@ -24,6 +24,8 @@
        <tocitem text="Jalview Documentation" target="home" expand="true">
                        <tocitem text="What's new" target="new" expand="true">
                                <tocitem text="Latest Release Notes" target="release"/>
+                               <tocitem text="Structure Chooser" target="pdbchooser"/>
+                               <tocitem text="Chimera Annotation Exchange" target="chimera.annotxfer"/>
                </tocitem>
                
                <tocitem text="Editing Alignments" target="edit" />
index 68ac465..e1227de 100644 (file)
             structure in the alignment. The regions used to calculate
             the superposition will be highlighted using the 'Cartoon'
             rendering style, and the remaining data shown as a chain
-            trace.<br/><br/>
+            trace.<br />
+          <br />
         </em></li>
-        <li><strong><a name="experimental">EXPERIMENTAL FEATURES</a></strong><br/>
-          <em>
-            These are only available if the <strong>Tools&#8594;Enable
-            Experimental Features</strong> option is enabled. (Since Jalview 2.10.2)</em>
-          <ul>
-            <li><strong>Write Jalview features</strong><br /> <em>Selecting
-                this option will create new residue attributes for any
-                features currently visible in the associated alignment
-                views, allowing those positions to be selected and
-                analysed with via Chimera's 'Render by Attribute' tool
-                (found in the Tools submenu called Structure Analysis).<br />
-                <br />If you use this option, please remember to select
-                the <em>Refresh Menus</em> option in Chimera's Render by
-                Attribute dialog box in order to see the attributes
-                derived from Jalview sequence features.
-            </em><br />
-            <a href="https://issues.jalview.org/browse/JAL-2295">View
-                this function's issue in Jalview's bug tracker</a></li>
-            <li><strong>Fetch Chimera Attributes</strong><br /> <em>This
-                submenu lists available Chimera residue attributes that
-                can be imported as Jalview features on associated
-                sequences.<br />This is particularly useful for
-                transferring quantitative positional annotation. For
-                example, structure similarity for an alignment can be
-                visualised by transferring the local RMSD attributes
-                generated by Chimera's Match->Align tool onto aligned
-                sequences and displayed with a <a
-                href="featureschemes.html">Graduated feature colour
-                  scheme</a>.
-            </em><a href="https://issues.jalview.org/browse/JAL-2296">View
-                this function's issue in Jalview's bug tracker</a></li>
-          </ul></li>
-        <li><strong>Help<br>
+        <li><a name="annotxfer"><strong>Write Jalview
+              features</strong></a><br /> <em>Selecting this option will create
+            new residue attributes for any features currently visible in
+            the associated alignment views, allowing those positions to
+            be selected and analysed with via Chimera's 'Render by
+            Attribute' tool (found in the Tools submenu called Structure
+            Analysis).<br /> <br />If you use this option, please
+            remember to select the <em>Refresh Menus</em> option in
+            Chimera's Render by Attribute dialog box in order to see the
+            attributes derived from Jalview sequence features.
+        </em></li>
+        <li><strong>Fetch Chimera Attributes</strong><br /> <em>This
+            submenu lists available Chimera residue attributes that can
+            be imported as Jalview features on associated sequences.<br />This
+            is particularly useful for transferring quantitative
+            positional annotation. For example, structure similarity for
+            an alignment can be visualised by transferring the local
+            RMSD attributes generated by Chimera's Match->Align tool
+            onto aligned sequences and displayed with a <a
+            href="featureschemes.html">Graduated feature colour
+              scheme</a>. </li>
+      </ul></li>
+    <li><strong>Help<br>
     </strong>
       <ul>
         <li><strong>Chimera Help<br>
index 0cd6168..ac2489b 100644 (file)
 </p> -->
   <p>
     <a name="align"><strong>Superposing structures based on
-        their aligned sequences</strong></a><br> If several structures are
-    available on the alignment, you may add additional structures to an
-    existing Jmol view by selecting their entry in the appropriate
-    pop-up menu. Jalview will ask you if you wish to add the structure
-    to the existing alignment, and if you do, it will import and
-    superimpose the new PDB file using the corresponding positions from
-    the alignment. If the alignment is subsequently edited, you can use
-    the <a href="#sAlign"><em>Jmol&#8594;Align</em></a> menu option from
-    the menu bar of the structure view window to superpose the
-    structures using the updated alignment.<br> <em>Sequence
+        their aligned sequences</strong></a><br> If several structures are shown
+    in a view, you can superimpose them using the corresponding
+    positions from the alignment via the <a href="#sAlign"><em>Jmol&#8594;Align</em></a>
+    menu option from the menu bar of the structure view window. <br> <em>Sequence
       based structure superposition was added in Jalview 2.6</em>
   </p>
   <p>
index b29b66b..50f864b 100755 (executable)
     and PDB file association (if available). The Jalview id/start-end
     option is ignored if Modeller output is selected.
   <p>
-    <a name="editing"><strong>e&quot;Editinge&quot; Preferences tab</strong></a>
+    <a name="editing"><strong>&quot;Editing&quot; Preferences tab</strong></a>
   </p>
   <p>There are currently three options available which can be
     selected / deselected.</p>
index f551c50..4af0d53 100644 (file)
Binary files a/help/html/features/schooser_enter-id.png and b/help/html/features/schooser_enter-id.png differ
index ab69427..ca793fd 100644 (file)
Binary files a/help/html/features/schooser_main.png and b/help/html/features/schooser_main.png differ
index be1bd66..e1c07c1 100644 (file)
@@ -76,7 +76,7 @@
       or <strong>"View&#8594;Nucleotide"</strong> (in the protein panel)
       allows you to show or hide one or other of the linked alignment
       panels.</li>
-    <li>Panel heights are adjusted dragging the divider between
+    <li>Panel heights are adjusted by dragging the divider between
       them using the mouse</li>
     <li><a href="../menus/alwview.html"><strong>"View&#8594;New
           View / Expand Views / Gather Views"</strong></a> behave as for a normal
index fc71826..785c429 100644 (file)
 
 <body>
   <p>
-    <strong>Structure Chooser</strong>
+    <strong>Structure Chooser Dialog Box</strong>
   </p>
 
   <p>
-    The Structure Chooser interface allows you to interactively select
-    which PDB structures to view for the currently selected set of
+    The Structure Chooser allows you to select
+    3D structures to view for the currently selected set of
     sequences. It is opened by selecting the <strong>"3D
-      Structure Data.."</strong> option from the Sequence ID panel's <a
+      Structure Data..."</strong> option from the Sequence ID panel's <a
       href="../menus/popupMenu.html">pop-up menu</a>. The dialog
     provides:
   </p>
   <p>
     <strong>Selecting and Viewing Structures</strong>
   </p>
+  <p>The drop-down menu offers different options for structure
+    discovery; the 'Cached' view is shown automatically if existing
+    structure data has been imported for the selected sequences, and if
+    none is available, the import PDB/mmCIF file options are shown.</p>
   <p>
     Once one or more structures have been selected, pressing the <strong>View</strong>
-    button will import them into <a
+    or <strong>Add</strong> button will import them <a
       href="viewingpdbs.html#afterviewbutton">a new or existing
-      structure view</a>.
+      structure view</a>. When multiple views are available, use the
+    drop-down menu to pick the target viewer for the structures.
   </p>
   <p>
     <strong>Automated discovery of structure data</strong>
     criteria (e.g. worst quality rather than best).</p>
   <p>
 
-    <img src="schooser_main.png" style="width: 464px; height: 369px;">
+    <img src="schooser_main.png" style="width: 499px; height: 437px;">
     <!-- <p><img src="schooser_config.png" style="width: 463px; height: 369px; ">
        <p><img src="schooser_drop-down.png" style="width: 464px; height: 368px; ">
        <p><img src="schooser_enter-id.png" style="width: 467px; height: 373px; ">
        <p><img src="schooser_from-file.png" style="width: 468px; height: 370px; ">
        <p><img src="schooser_cached.png"> -->
-    <br>The screenshot above shows the Structure Chooser interface
-    along with the meta-data of auto-discovered structures for the
-    sample alignment. If no structures were
-    auto-discovered, options for manually associating PDB records will be shown (see below).
-  <p>
+    <br>The screenshot above shows the Structure Chooser displayed after
+    selecting all the sequences in the Jalview example project. If no
+    structures were auto-discovered, options for manually associating
+    PDB records will be shown (see below).<p>
     <strong>Exploration of meta-data for available structures</strong>
   </p>
   <p>Information on each structure available is displayed in columns
     Columns' tab and tick the columns which you want to see.</p>
   <p>
     <img src="schooser_enter-id.png"
-      style="width: 464px; height: 369px;">
+      style="width: 464px; height: 173px;">
       <br/>
     <strong>Manual selection/association of PDB files with
       Sequences</strong>
index 45d979f..b1ad4ba 100755 (executable)
@@ -82,6 +82,7 @@
     provided it is installed and can be launched by Jalview. The default
     viewer can be configured in the <a href="preferences.html#structure">Structure
       tab</a> in the <strong>Tools&rarr;Preferences</strong> dialog box.
+  
   <p>
     Structure data imported into Jalview can also be processed to
     display secondary structure and temperature factor annotation. See
     for more information.
   </p>
   <p>
-    <strong><a name="afterviewbutton">After pressing the
-        'View' button in the Structure Chooser</a></strong><br /> The behaviour of
-    the 'View' button depends on the number of structures selected, and
-    whether structure views already exist for the selected structures or
-    aligned sequences.
+    <img src="schooser_viewbutton.png"
+      style="width: 465px; height: 81px" /><br/> <strong><a
+      name="afterviewbutton">Controlling where the new structures
+        will be shown</a></strong>
+        <br />The Structure Chooser offers several options
+    for viewing a structure. <br/><strong>New View</strong> will open a new
+    structure viewer for the selected structures, but if there are views
+    already open, you can select which one to use, and press the <strong>Add</strong>
+    button. Jalview can automatically superimpose new structures based
+    on the linked alignments - but if this is not desirable, simple
+    un-tick the <strong>Superpose Structures</strong> checkbox.
+
   </p>
-  <p>If multiple structures are selected, then Jalview will always
-    create a new structure view. The selected structures will be
-    imported into this view, and superposed with the matched positions
-    from the aligned sequences. A message in the structure viewer's
-    status bar will be shown if not enough aligned columns were
-    available to perform a superposition.</p>
   <p>
-    If a <strong>single</strong> PDB structure is selected, one of the
-    following will happen:
+    <em>Superposing structures</em><br/>Jalview superposes structures using
+    the visible portions of any associated sequence alignments. A
+    message in the structure viewer's status bar will be shown if not
+    enough aligned columns were available to perform a superposition.
   </p>
-
-  <ul>
-    <li>If no structures are open, then an interactive display of
-      the structure will be opened in a new window.</li>
-
-    <li>If another structure is already shown for the current
-      alignment, then you will be asked if you want to add and <a
-      href="jmol.html#align"></a> to the structure in the existing view.
-      (<em>new feature in Jalview 2.6</em>).
-    </li>
-
-    <li>If the structure is already shown, then you will be
-      prompted to associate the sequence with an existing view of the
-      selected structure. This is useful when working with multi-domain
-      or multi-chain PDB files.</li>
-
-    <li style="list-style: none">See the <a href="jmol.html">Jmol
-    </a> and <a href="chimera.html">Chimera</a> PDB viewer help pages for
-      more information about the display.
-    </li>
-  </ul>
-
+  <p>
+  See the <a href="jmol.html">Jmol
+    </a> and <a href="chimera.html">Chimera</a> help pages for
+      more information about their capabilities.</p>
+  
 
   <p>
     <strong>Retrieving sequences from the PDB</strong><br>You can
index a93ce4b..d716e33 100755 (executable)
@@ -86,7 +86,7 @@
             the <a href="../features/groovy.html">Groovy Console</a> for
             interactive scripting.
         </em><strong><br></strong></li>
-        <li><strong>Enable Experimental Features</strong> <em>Enable or disable <a href="../whatsNew.html#experimental">features still under development</a> in Jalview's user interface. This setting is remembered in your preferences.</em>
+        <!--         <li><strong>Enable Experimental Features</strong> <em>Enable or disable <a href="../whatsNew.html#experimental">features still under development</a> in Jalview's user interface. This setting is remembered in your preferences.</em> -->
 
       </ul></li>
     <li><strong>Vamsas</strong> <em>For more details, read the
index 83d2ce4..993fcd5 100755 (executable)
@@ -70,6 +70,158 @@ li:before {
     <tr>
       <td width="60" nowrap>
         <div align="center">
+          <strong><a name="Jalview.2.10.4">2.10.4</a><br /> <em>10/05/2018</em></strong>
+        </div>
+      </td>
+      <td><div align="left">
+          <em></em>
+          <ul>
+            <li>
+              <!-- JAL-1847 JAL-2944 -->New Structure Chooser control
+              for disabling automatic superposition of multiple
+              structures and open structures in existing views
+            </li>
+            <li>
+              <!-- JAL-984 -->Mouse cursor changes to indicate Sequence
+              ID and annotation area margins can be click-dragged to
+              adjust them.
+            </li>
+            <li>
+              <!-- JAL-2885 -->Jalview uses HTTPS for Uniprot, Xfam and
+              Ensembl services
+            </li>
+            <li>
+              <!-- JAL-2759 -->Improved performance for large alignments
+              and lots of hidden columns
+            </li>
+            <li>
+              <!-- JAL-2593 -->Improved performance when rendering lots
+              of features (particularly when transparency is disabled)
+            </li>
+          </ul>
+          </div>
+      </td>
+      <td><div align="left">
+          <ul>
+            <li>
+              <!-- JAL-2899 -->Structure and Overview aren't updated
+              when Colour By Annotation threshold slider is adjusted
+            </li>
+            <li>
+              <!-- JAL-2778 -->Slow redraw when Overview panel shown
+              overlapping alignment panel
+            </li>
+            <li>
+              <!--  JAL-2929 -->Overview doesn't show end of unpadded
+              sequence as gaps
+            </li>
+            <li>
+              <!-- JAL-2789, JAL-2893 -->Cross-reference handling
+              improved: CDS not handled correctly if transcript has no
+              UTR
+            </li>
+            <li>
+              <!-- JAL-2321 -->Secondary structure and temperature
+              factor annotation not added to sequence when local PDB
+              file associated with it by drag'n'drop or structure
+              chooser
+            </li>
+            <li>
+              <!--  JAL-2984 -->Answering 'No' to PDB Autoassociate
+              dialog doesn't import PDB files dropped on an alignment
+            </li>
+            <li>
+              <!-- JAL-2666 -->Linked scrolling via protein horizontal
+              scroll bar doesn't work for some CDS/Protein views
+            </li>
+            <li>
+              <!-- JAL-2930 -->Trackpad scrolling is broken on OSX on
+              Java 1.8u153 onwards and Java 1.9u4+.
+            </li>
+            <li>
+              <!-- JAL-2924 -->Tooltip shouldn't be displayed for empty
+              columns in annotation row
+            </li>
+            <li>
+              <!-- JAL-2913 -->Preferences panel's ID Width control is not
+              honored in batch mode
+            </li>
+            <li>
+              <!-- JAL-2945 -->Linked sequence highlighting doesn't work
+              for structures added to existing Jmol view
+            </li>
+            <li>
+              <!-- JAL-2223 -->'View Mappings' includes duplicate
+              entries after importing project with multiple views
+            </li>
+            <li>
+              <!-- JAL-2781 JAL-2780 -->Viewing or annotating Uniprot
+              protein sequences via SIFTS from associated PDB entries
+              with negative residue numbers or missing residues fails
+            </li>
+            <li>
+              <!-- JAL-2952 -->Exception when shading sequence with negative
+              Temperature Factor values from annotated PDB files (e.g.
+              as generated by CONSURF)
+            </li>
+            <li>
+              <!-- JAL-2922 -->Invert displayed features very slow when
+              structure and/or overview windows are also shown
+            </li>
+            <li>
+              <!-- JAL-2954 -->Selecting columns from highlighted regions
+              very slow for alignments with large numbers of sequences
+            </li>
+            <li>
+              <!-- JAL-2925 -->Copy Consensus fails for group consensus
+              with 'StringIndexOutOfBounds'
+            </li>
+            <li>
+              <!-- JAL-2976 -->VAqua(4) provided as fallback Look and Feel for OSX
+              platforms running Java 10
+            </li>
+            <li>
+              <!-- JAL-2960 -->Adding a structure to existing structure
+              view appears to do nothing because the view is hidden behind the alignment view
+            </li>
+          </ul>
+          <em>Applet</em>
+          <ul>
+            <li>
+              <!-- JAL-2926 -->Copy consensus sequence option in applet
+              should copy the group consensus when popup is opened on it
+            </li>
+          </ul>
+          <em>Batch Mode</em>
+          <ul>
+          <li>
+            <!-- JAL-2913 -->Fixed ID width preference is not respected
+          </li>
+          </ul>
+          <em>New Known Defects</em>
+          <ul>
+            <li>
+              <!-- JAL-2973 --> Exceptions occasionally raised when
+              editing a large alignment and overview is displayed
+            </li>
+            <li>
+              <!-- JAL-2974 -->'Overview updating' progress bar is shown
+              repeatedly after a series of edits even when the overview
+              is no longer reflecting updates
+            </li>
+            <li>
+              <!-- JAL-2946 -->'SIFTS Mapping Error' when viewing
+              structures for protein subsequence (if 'Trim Retrieved
+              Sequences' enabled) or Ensembl isoforms (Workaround in
+              2.10.4 is to fail back to N&amp;W mapping)
+            </li>
+          </ul>
+        </div>
+          </td>
+    </tr>
+    <tr>
+      <td width="60" nowrap>
+        <div align="center">
           <strong><a name="Jalview.2.10.3b1">2.10.3b1</a><br /> <em>24/1/2018</em></strong>
         </div>
       </td>
index 6d75f0f..0abd2a7 100755 (executable)
 </head>
 <body>
   <p>
-    <strong>What's new in Jalview 2.10.3b1 ?</strong>
+    <strong>What's new in Jalview 2.10.4 ?</strong>
   </p>
   <p>
-    This is the January 2018 patch release, which addresses critical bugs including trackpad function in OSX, and display of multiple 3D structures. 
-    The full list bugs fixed in this release can be found in the <a href="releases.html#Jalview.2.10.3b1">2.10.3b1
-      Release Notes</a>. In addition, Jalview 2.10.3 provides:
+    This is the May 2018 release of Jalview, and the last in the 2.10.x series. Jalview 2.10.4 includes:
   </p>
   <ul>
-    <li>Faster and more responsive UI when importing and working
-      with wide alignments and handling hundreds and thousands of
-      sequence features</li>
-    <li>Improved usability with <a
-      href="features/pdbsequencefetcher.html">PDB</a> and <a
-      href="features/uniprotsequencefetcher.html">UniProt</a> Free Text
-      Search dialog, and new tab for retrieval of sequences for lists of
-      IDs.
-    </li>
-    <li>Short names assigned to sequences retrieved from UniProt</li>
-    <li>Groovy console upgraded to 2.4.12 (improved support for Java 9)</li>
+    <li>Numerous efficiency improvements in the renderer and overview when working with large alignments with lots of hidden columns</li>
+    <li>Use of HTTPS when connecting to Uniprot, Ensembl and other EBI web services</li>
+    <li>Critical patches for running Jalview on OSX with Java 10</li>
+    <li>Easier adjustment of the Alignment ID panel and Annotation panel</li>
+    <li>Improved support for mapping between 3D Structures and Uniprot Protein Sequences</li>
+    <li>Improved support for discovering CDS and transcripts for Proteins and Ensembl gene IDs</li>
+    <li>New buttons on the Structure Chooser for adding structures
+      to an existing view, and disabling automatic superposition
+      according to linked alignments</li>
+    <li>Annotation transfer between Chimera and Jalview <em>(formerly only
+        available in 'Experimental' mode)</em></li>
   </ul>
   <p>
-    <strong><a name="experimental">Experimental Features</a></strong>
+    The full list of bugs fixed in this release can be found in the <a href="releases.html#Jalview.2.10.4">2.10.4
+      Release Notes</a>. 
   </p>
-  <p>
-    Remember, please enable the <em>Experimental Features</em> option in
-    the Jalview Desktop's <em>Tools</em> menu, and then restart Jalview
-    if you want to try out features below:
-  </p>
-  <ul>
-    <li><em>Annotation transfer between Chimera and Jalview</em><br />Two
-      <a href="features/chimera.html#experimental">new entries in
-        the Chimera viewer's Chimera menu</a> allow positional annotation to
-      be exchanged between Chimera and Jalview.</li>
-  </ul>
-  
 </body>
 </html>
diff --git a/lib/VAqua4.jar b/lib/VAqua4.jar
new file mode 100644 (file)
index 0000000..c1e7cfc
Binary files /dev/null and b/lib/VAqua4.jar differ
diff --git a/resources/images/idwidth.gif b/resources/images/idwidth.gif
deleted file mode 100755 (executable)
index c1bd8cb..0000000
Binary files a/resources/images/idwidth.gif and /dev/null differ
index f526699..a80ac17 100644 (file)
@@ -400,10 +400,6 @@ 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
-label.pdb_entry_is_already_displayed = {0} is already displayed.\nDo you want to re-use this viewer ?
-label.map_sequences_to_visible_window = Map Sequences to Visible Window: {0}
-label.add_pdbentry_to_view = Do you want to add {0} to the view called\n{1}\n
-label.align_to_existing_structure_view = Align to existing structure view
 label.pdb_entries_couldnt_be_retrieved = The following pdb entries could not be retrieved from the PDB\:\n{0}\nPlease retry, or try downloading them manually.
 label.couldnt_load_file = Couldn't load file
 label.couldnt_find_pdb_id_in_file = Couldn't find a PDB id in the file supplied. Please enter an Id to identify this structure.
@@ -1213,7 +1209,6 @@ label.pdb_sequence_fetcher = 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
index 77f053e..61bf42a 100644 (file)
@@ -367,10 +367,6 @@ label.ignore_unmatched_dropped_files = Ignorar los ficheros sin coincidencias?
 label.enter_view_name = Introduzca un nombre para la vista
 label.enter_label = Introducir etiqueta
 label.enter_label_for_the_structure = Introducir una etiqueta para la estructura
-label.pdb_entry_is_already_displayed = {0} Ya est\u00E1 mostrado.\nQuieres volver a usar este visor?
-label.map_sequences_to_visible_window = Mapa de secuencias en ventana visible: {0}
-label.add_pdbentry_to_view = Quieres a\u00F1adir {0} a la vista llamada\n{1}\n
-label.align_to_existing_structure_view = Alinear a una estructura ya existente
 label.pdb_entries_couldnt_be_retrieved = Las siguientes entradas pdb no pueden ser extra\u00EDdas del PDB\:\n{0}\nPor favor, prueba descarg\u00E1ndolas manualmente.
 label.couldnt_load_file = No se pudo cargar el fichero
 label.couldnt_find_pdb_id_in_file = No se pudo encontrar un Id PDB en el fichero suministrado. Por favor, introduzca un Id para identificar esta estructura.
@@ -1172,7 +1168,6 @@ label.structures_filter=Filtro de Estructuras
 label.scale_protein_to_cdna=Adaptar proteína a cDNA
 label.scale_protein_to_cdna_tip=Hacer a los residuos de proteínas de la misma anchura que los codones en ventanas divididas
 status.loading_cached_pdb_entries=Cargando Entradas PDB en Caché
-label.select=Seleccionar :
 label.select_by_annotation=Seleccionar/Ocultar Columnas por Anotación
 action.select_by_annotation=Seleccionar/Ocultar Columnas por Anotación...
 action.export_features=Exportar Características
index f4bd31c..904a860 100755 (executable)
@@ -45,11 +45,11 @@ public class PDBChain
 
   public String id;
 
-  public Vector<Bond> bonds = new Vector<Bond>();
+  public Vector<Bond> bonds = new Vector<>();
 
-  public Vector<Atom> atoms = new Vector<Atom>();
+  public Vector<Atom> atoms = new Vector<>();
 
-  public Vector<Residue> residues = new Vector<Residue>();
+  public Vector<Residue> residues = new Vector<>();
 
   public int offset;
 
@@ -162,6 +162,50 @@ public class PDBChain
   }
 
   /**
+   * Annotate the residues with their corresponding positions in s1 using the
+   * alignment in as NOTE: This clears all atom.alignmentMapping values on the
+   * structure.
+   * 
+   * @param as
+   * @param s1
+   */
+  public void makeExactMapping(StructureMapping mapping, SequenceI s1)
+  {
+    // first clear out any old alignmentMapping values:
+    for (Atom atom : atoms)
+    {
+      atom.alignmentMapping = -1;
+    }
+    SequenceI ds = s1;
+    while (ds.getDatasetSequence() != null)
+    {
+      ds = ds.getDatasetSequence();
+    }
+    int pdboffset = 0;
+    for (Residue res : residues)
+    {
+      // res.number isn't set correctly for discontinuous/mismapped residues
+      int seqpos = mapping.getSeqPos(res.atoms.get(0).resNumber);
+      char strchar = sequence.getCharAt(pdboffset++);
+      if (seqpos == StructureMapping.UNASSIGNED_VALUE)
+      {
+        continue;
+      }
+      char seqchar = ds.getCharAt(seqpos - ds.getStart());
+
+      boolean sameResidue = Comparison.isSameResidue(
+              seqchar, strchar, false);
+      if (sameResidue)
+      {
+        for (Atom atom : res.atoms)
+        {
+          atom.alignmentMapping = seqpos - 1;
+        }
+      }
+    }
+  }
+
+  /**
    * Copies over the RESNUM seqfeatures from the internal chain sequence to the
    * mapped sequence
    * 
@@ -299,12 +343,13 @@ public class PDBChain
     boolean deoxyn = false;
     boolean nucleotide = false;
     StringBuilder seq = new StringBuilder(256);
-    Vector<SequenceFeature> resFeatures = new Vector<SequenceFeature>();
-    Vector<Annotation> resAnnotation = new Vector<Annotation>();
-    int i, iSize = atoms.size() - 1;
+    Vector<SequenceFeature> resFeatures = new Vector<>();
+    Vector<Annotation> resAnnotation = new Vector<>();
+    int iSize = atoms.size() - 1;
     int resNumber = -1;
     char insCode = ' ';
-    for (i = 0; i <= iSize; i++)
+
+    for (int i = 0; i <= iSize; i++)
     {
       Atom tmp = atoms.elementAt(i);
       resNumber = tmp.resNumber;
@@ -318,7 +363,7 @@ public class PDBChain
         offset = resNumber;
       }
 
-      Vector<Atom> resAtoms = new Vector<Atom>();
+      Vector<Atom> resAtoms = new Vector<>();
       // Add atoms to a vector while the residue number
       // remains the same as the first atom's resNumber (res)
       while ((resNumber == res) && (ins == insCode) && (i < atoms.size()))
@@ -425,7 +470,8 @@ public class PDBChain
 
     if (StructureImportSettings.isShowSeqFeatures())
     {
-      for (i = 0, iSize = resFeatures.size(); i < iSize; i++)
+      iSize = resFeatures.size();
+      for (int i = 0; i < iSize; i++)
       {
         sequence.addSequenceFeature(resFeatures.elementAt(i));
         resFeatures.setElementAt(null, i);
@@ -434,20 +480,20 @@ public class PDBChain
     if (visibleChainAnnotation)
     {
       Annotation[] annots = new Annotation[resAnnotation.size()];
-      float max = 0;
-      for (i = 0, iSize = annots.length; i < iSize; i++)
+      float max = 0f;
+      float min = 0f;
+      iSize = annots.length;
+      for (int i = 0; i < iSize; i++)
       {
         annots[i] = resAnnotation.elementAt(i);
-        if (annots[i].value > max)
-        {
-          max = annots[i].value;
-        }
+        max = Math.max(max, annots[i].value);
+        min = Math.min(min, annots[i].value);
         resAnnotation.setElementAt(null, i);
       }
 
       AlignmentAnnotation tfactorann = new AlignmentAnnotation(
               "Temperature Factor", "Temperature Factor for " + pdbid + id,
-              annots, 0, max, AlignmentAnnotation.LINE_GRAPH);
+              annots, min, max, AlignmentAnnotation.LINE_GRAPH);
       tfactorann.setSequenceRef(sequence);
       sequence.addAlignmentAnnotation(tfactorann);
     }
@@ -550,6 +596,12 @@ public class PDBChain
   {
     SequenceI sq = mapping.getSequence();
     SequenceI dsq = sq;
+    if (sqmpping == null)
+    {
+      // SIFTS mappings are recorded in the StructureMapping object...
+
+      sqmpping = mapping.getSeqToPdbMapping();
+    }
     if (sq != null)
     {
       while (dsq.getDatasetSequence() != null)
index 90d9197..343ebc7 100644 (file)
@@ -117,7 +117,7 @@ public class AlignmentUtils
    */
   public static AlignmentI expandContext(AlignmentI core, int flankSize)
   {
-    List<SequenceI> sq = new ArrayList<SequenceI>();
+    List<SequenceI> sq = new ArrayList<>();
     int maxoffset = 0;
     for (SequenceI s : core.getSequences())
     {
@@ -247,7 +247,7 @@ public class AlignmentUtils
   public static Map<String, List<SequenceI>> getSequencesByName(
           AlignmentI al)
   {
-    Map<String, List<SequenceI>> theMap = new LinkedHashMap<String, List<SequenceI>>();
+    Map<String, List<SequenceI>> theMap = new LinkedHashMap<>();
     for (SequenceI seq : al.getSequences())
     {
       String name = seq.getName();
@@ -256,7 +256,7 @@ public class AlignmentUtils
         List<SequenceI> seqs = theMap.get(name);
         if (seqs == null)
         {
-          seqs = new ArrayList<SequenceI>();
+          seqs = new ArrayList<>();
           theMap.put(name, seqs);
         }
         seqs.add(seq);
@@ -283,8 +283,8 @@ public class AlignmentUtils
       return false;
     }
 
-    Set<SequenceI> mappedDna = new HashSet<SequenceI>();
-    Set<SequenceI> mappedProtein = new HashSet<SequenceI>();
+    Set<SequenceI> mappedDna = new HashSet<>();
+    Set<SequenceI> mappedProtein = new HashSet<>();
 
     /*
      * First pass - map sequences where cross-references exist. This include
@@ -870,7 +870,7 @@ public class AlignmentUtils
       System.err.println("Wrong alignment type in alignProteinAsDna");
       return 0;
     }
-    List<SequenceI> unmappedProtein = new ArrayList<SequenceI>();
+    List<SequenceI> unmappedProtein = new ArrayList<>();
     Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons = buildCodonColumnsMap(
             protein, dna, unmappedProtein);
     return alignProteinAs(protein, alignedCodons, unmappedProtein);
@@ -1081,7 +1081,7 @@ public class AlignmentUtils
      * {dnaSequence, {proteinSequence, codonProduct}} at that position. The
      * comparator keeps the codon positions ordered.
      */
-    Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons = new TreeMap<AlignedCodon, Map<SequenceI, AlignedCodon>>(
+    Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons = new TreeMap<>(
             new CodonComparator());
 
     for (SequenceI dnaSeq : dna.getSequences())
@@ -1127,9 +1127,9 @@ public class AlignmentUtils
     // TODO delete this ugly hack once JAL-2022 is resolved
     // i.e. we can model startPhase > 0 (incomplete start codon)
 
-    List<SequenceI> sequencesChecked = new ArrayList<SequenceI>();
+    List<SequenceI> sequencesChecked = new ArrayList<>();
     AlignedCodon lastCodon = null;
-    Map<SequenceI, AlignedCodon> toAdd = new HashMap<SequenceI, AlignedCodon>();
+    Map<SequenceI, AlignedCodon> toAdd = new HashMap<>();
 
     for (Entry<AlignedCodon, Map<SequenceI, AlignedCodon>> entry : alignedCodons
             .entrySet())
@@ -1308,7 +1308,7 @@ public class AlignmentUtils
     Map<SequenceI, AlignedCodon> seqProduct = alignedCodons.get(codon);
     if (seqProduct == null)
     {
-      seqProduct = new HashMap<SequenceI, AlignedCodon>();
+      seqProduct = new HashMap<>();
       alignedCodons.put(codon, seqProduct);
     }
     seqProduct.put(protein, codon);
@@ -1445,7 +1445,7 @@ public class AlignmentUtils
       {
         continue;
       }
-      final List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
+      final List<AlignmentAnnotation> result = new ArrayList<>();
       for (AlignmentAnnotation dsann : datasetAnnotations)
       {
         /*
@@ -1627,13 +1627,13 @@ public class AlignmentUtils
       throw new IllegalArgumentException(
               "IMPLEMENTATION ERROR: dataset.getDataset() must be null!");
     }
-    List<SequenceI> foundSeqs = new ArrayList<SequenceI>();
-    List<SequenceI> cdsSeqs = new ArrayList<SequenceI>();
+    List<SequenceI> foundSeqs = new ArrayList<>();
+    List<SequenceI> cdsSeqs = new ArrayList<>();
     List<AlignedCodonFrame> mappings = dataset.getCodonFrames();
     HashSet<SequenceI> productSeqs = null;
     if (products != null)
     {
-      productSeqs = new HashSet<SequenceI>();
+      productSeqs = new HashSet<>();
       for (SequenceI seq : products)
       {
         productSeqs.add(seq.getDatasetSequence() == null ? seq
@@ -1833,7 +1833,7 @@ public class AlignmentUtils
    * @param seqMappings
    *          the set of mappings involving dnaSeq
    * @param aMapping
-   *          an initial candidate from seqMappings
+   *          a transcript-to-peptide mapping
    * @return
    */
   static SequenceI findCdsForProtein(List<AlignedCodonFrame> mappings,
@@ -1858,7 +1858,15 @@ public class AlignmentUtils
     if (mappedFromLength == dnaLength
             || mappedFromLength == dnaLength - CODON_LENGTH)
     {
-      return seqDss;
+      /*
+       * if sequence has CDS features, this is a transcript with no UTR
+       * - do not take this as the CDS sequence! (JAL-2789)
+       */
+      if (seqDss.getFeatures().getFeaturesByOntology(SequenceOntologyI.CDS)
+              .isEmpty())
+      {
+        return seqDss;
+      }
     }
 
     /*
@@ -1883,10 +1891,12 @@ public class AlignmentUtils
           {
             /*
             * found a 3:1 mapping to the protein product which covers
-            * the whole dna sequence i.e. is from CDS; finally check it
-            * is from the dna start sequence
+            * the whole dna sequence i.e. is from CDS; finally check the CDS
+            * is mapped from the given dna start sequence
             */
             SequenceI cdsSeq = map.getFromSeq();
+            // todo this test is weak if seqMappings contains multiple mappings;
+            // we get away with it if transcript:cds relationship is 1:1
             List<AlignedCodonFrame> dnaToCdsMaps = MappingUtils
                     .findMappingsForSequence(cdsSeq, seqMappings);
             if (!dnaToCdsMaps.isEmpty())
@@ -2002,8 +2012,8 @@ public class AlignmentUtils
   {
 
     // gather direct refs from contig congrent with mapping
-    List<DBRefEntry> direct = new ArrayList<DBRefEntry>();
-    HashSet<String> directSources = new HashSet<String>();
+    List<DBRefEntry> direct = new ArrayList<>();
+    HashSet<String> directSources = new HashSet<>();
     if (contig.getDBRefs() != null)
     {
       for (DBRefEntry dbr : contig.getDBRefs())
@@ -2023,7 +2033,7 @@ public class AlignmentUtils
     DBRefEntry[] onSource = DBRefUtils.selectRefs(
             proteinProduct.getDBRefs(),
             directSources.toArray(new String[0]));
-    List<DBRefEntry> propagated = new ArrayList<DBRefEntry>();
+    List<DBRefEntry> propagated = new ArrayList<>();
 
     // and generate appropriate mappings
     for (DBRefEntry cdsref : direct)
@@ -2180,12 +2190,13 @@ public class AlignmentUtils
     int mappedDnaLength = MappingUtils.getLength(ranges);
 
     /*
-     * if not a whole number of codons, something is wrong,
-     * abort mapping
+     * if not a whole number of codons, truncate mapping
      */
-    if (mappedDnaLength % CODON_LENGTH > 0)
+    int codonRemainder = mappedDnaLength % CODON_LENGTH;
+    if (codonRemainder > 0)
     {
-      return null;
+      mappedDnaLength -= codonRemainder;
+      MappingUtils.removeEndPositions(codonRemainder, ranges);
     }
 
     int proteinLength = proteinSeq.getLength();
@@ -2202,7 +2213,7 @@ public class AlignmentUtils
       proteinStart++;
       proteinLength--;
     }
-    List<int[]> proteinRange = new ArrayList<int[]>();
+    List<int[]> proteinRange = new ArrayList<>();
 
     /*
      * dna length should map to protein (or protein plus stop codon)
@@ -2237,7 +2248,7 @@ public class AlignmentUtils
    */
   public static List<int[]> findCdsPositions(SequenceI dnaSeq)
   {
-    List<int[]> result = new ArrayList<int[]>();
+    List<int[]> result = new ArrayList<>();
 
     List<SequenceFeature> sfs = dnaSeq.getFeatures().getFeaturesByOntology(
             SequenceOntologyI.CDS);
@@ -2523,7 +2534,7 @@ public class AlignmentUtils
      * map from peptide position to all variants of the codon which codes for it
      * LinkedHashMap ensures we keep the peptide features in sequence order
      */
-    LinkedHashMap<Integer, List<DnaVariant>[]> variants = new LinkedHashMap<Integer, List<DnaVariant>[]>();
+    LinkedHashMap<Integer, List<DnaVariant>[]> variants = new LinkedHashMap<>();
 
     List<SequenceFeature> dnaFeatures = dnaSeq.getFeatures()
             .getFeaturesByOntology(SequenceOntologyI.SEQUENCE_VARIANT);
@@ -2558,9 +2569,9 @@ public class AlignmentUtils
       if (codonVariants == null)
       {
         codonVariants = new ArrayList[CODON_LENGTH];
-        codonVariants[0] = new ArrayList<DnaVariant>();
-        codonVariants[1] = new ArrayList<DnaVariant>();
-        codonVariants[2] = new ArrayList<DnaVariant>();
+        codonVariants[0] = new ArrayList<>();
+        codonVariants[1] = new ArrayList<>();
+        codonVariants[2] = new ArrayList<>();
         variants.put(peptidePosition, codonVariants);
       }
 
@@ -2699,7 +2710,7 @@ public class AlignmentUtils
     /*
      * fancy case - aligning via mappings between sequences
      */
-    List<SequenceI> unmapped = new ArrayList<SequenceI>();
+    List<SequenceI> unmapped = new ArrayList<>();
     Map<Integer, Map<SequenceI, Character>> columnMap = buildMappedColumnsMap(
             unaligned, aligned, unmapped);
     int width = columnMap.size();
@@ -2774,7 +2785,7 @@ public class AlignmentUtils
     }
 
     // map from dataset sequence to alignment sequence(s)
-    Map<SequenceI, List<SequenceI>> alignedDatasets = new HashMap<SequenceI, List<SequenceI>>();
+    Map<SequenceI, List<SequenceI>> alignedDatasets = new HashMap<>();
     for (SequenceI seq : aligned.getSequences())
     {
       SequenceI ds = seq.getDatasetSequence();
@@ -2837,7 +2848,7 @@ public class AlignmentUtils
      * {unalignedSequence, characterPerSequence} at that position.
      * TreeMap keeps the entries in ascending column order. 
      */
-    SortedMap<Integer, Map<SequenceI, Character>> map = new TreeMap<Integer, Map<SequenceI, Character>>();
+    SortedMap<Integer, Map<SequenceI, Character>> map = new TreeMap<>();
 
     /*
      * record any sequences that have no mapping so can't be realigned
@@ -2942,7 +2953,7 @@ public class AlignmentUtils
             Map<SequenceI, Character> seqsMap = map.get(fromCol);
             if (seqsMap == null)
             {
-              seqsMap = new HashMap<SequenceI, Character>();
+              seqsMap = new HashMap<>();
               map.put(fromCol, seqsMap);
             }
             seqsMap.put(seq, seq.getCharAt(mappedCharPos - toStart));
index a10b037..d534c8f 100644 (file)
@@ -44,6 +44,7 @@ import jalview.util.ShiftList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.Iterator;
 import java.util.List;
 
 public class Dna
@@ -56,19 +57,23 @@ public class Dna
    * 'final' variables describe the inputs to the translation, which should not
    * be modified.
    */
-  final private List<SequenceI> selection;
+  private final List<SequenceI> selection;
 
-  final private String[] seqstring;
+  private final String[] seqstring;
 
-  final private int[] contigs;
+  private final Iterator<int[]> contigs;
 
-  final private char gapChar;
+  private final char gapChar;
 
-  final private AlignmentAnnotation[] annotations;
+  private final AlignmentAnnotation[] annotations;
 
-  final private int dnaWidth;
+  private final int dnaWidth;
 
-  final private AlignmentI dataset;
+  private final AlignmentI dataset;
+
+  private ShiftList vismapping;
+
+  private int[] startcontigs;
 
   /*
    * Working variables for the translation.
@@ -91,7 +96,7 @@ public class Dna
    * @param viewport
    * @param visibleContigs
    */
-  public Dna(AlignViewportI viewport, int[] visibleContigs)
+  public Dna(AlignViewportI viewport, Iterator<int[]> visibleContigs)
   {
     this.selection = Arrays.asList(viewport.getSequenceSelection());
     this.seqstring = viewport.getViewAsString(true);
@@ -100,6 +105,45 @@ public class Dna
     this.annotations = viewport.getAlignment().getAlignmentAnnotation();
     this.dnaWidth = viewport.getAlignment().getWidth();
     this.dataset = viewport.getAlignment().getDataset();
+    initContigs();
+  }
+
+  /**
+   * Initialise contigs used as starting point for translateCodingRegion
+   */
+  private void initContigs()
+  {
+    vismapping = new ShiftList(); // map from viscontigs to seqstring
+    // intervals
+
+    int npos = 0;
+    int[] lastregion = null;
+    ArrayList<Integer> tempcontigs = new ArrayList<>();
+    while (contigs.hasNext())
+    {
+      int[] region = contigs.next();
+      if (lastregion == null)
+      {
+        vismapping.addShift(npos, region[0]);
+      }
+      else
+      {
+        // hidden region
+        vismapping.addShift(npos, region[0] - lastregion[1] + 1);
+      }
+      lastregion = region;
+      tempcontigs.add(region[0]);
+      tempcontigs.add(region[1]);
+    }
+
+    startcontigs = new int[tempcontigs.size()];
+    int i = 0;
+    for (Integer val : tempcontigs)
+    {
+      startcontigs[i] = val;
+      i++;
+    }
+    tempcontigs = null;
   }
 
   /**
@@ -161,7 +205,7 @@ public class Dna
 
     int s;
     int sSize = selection.size();
-    List<SequenceI> pepseqs = new ArrayList<SequenceI>();
+    List<SequenceI> pepseqs = new ArrayList<>();
     for (s = 0; s < sSize; s++)
     {
       SequenceI newseq = translateCodingRegion(selection.get(s),
@@ -213,7 +257,7 @@ public class Dna
       if (dnarefs != null)
       {
         // intersect with pep
-        List<DBRefEntry> mappedrefs = new ArrayList<DBRefEntry>();
+        List<DBRefEntry> mappedrefs = new ArrayList<>();
         DBRefEntry[] refs = dna.getDBRefs();
         for (int d = 0; d < refs.length; d++)
         {
@@ -391,27 +435,13 @@ public class Dna
           String seqstring, AlignedCodonFrame acf,
           List<SequenceI> proteinSeqs)
   {
-    List<int[]> skip = new ArrayList<int[]>();
-    int skipint[] = null;
-    ShiftList vismapping = new ShiftList(); // map from viscontigs to seqstring
-    // intervals
-    int vc;
-    int[] scontigs = new int[contigs.length];
+    List<int[]> skip = new ArrayList<>();
+    int[] skipint = null;
     int npos = 0;
-    for (vc = 0; vc < contigs.length; vc += 2)
-    {
-      if (vc == 0)
-      {
-        vismapping.addShift(npos, contigs[vc]);
-      }
-      else
-      {
-        // hidden region
-        vismapping.addShift(npos, contigs[vc] - contigs[vc - 1] + 1);
-      }
-      scontigs[vc] = contigs[vc];
-      scontigs[vc + 1] = contigs[vc + 1];
-    }
+    int vc = 0;
+
+    int[] scontigs = new int[startcontigs.length];
+    System.arraycopy(startcontigs, 0, scontigs, 0, startcontigs.length);
 
     // allocate a roughly sized buffer for the protein sequence
     StringBuilder protein = new StringBuilder(seqstring.length() / 2);
@@ -800,7 +830,7 @@ public class Dna
   public AlignmentI reverseCdna(boolean complement)
   {
     int sSize = selection.size();
-    List<SequenceI> reversed = new ArrayList<SequenceI>();
+    List<SequenceI> reversed = new ArrayList<>();
     for (int s = 0; s < sSize; s++)
     {
       SequenceI newseq = reverseSequence(selection.get(s).getName(),
index fd66388..8f778f7 100644 (file)
@@ -20,6 +20,9 @@
  */
 package jalview.api.structures;
 
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
 import jalview.structures.models.AAStructureBindingModel;
 
@@ -62,4 +65,64 @@ public interface JalviewStructureDisplayI
    */
   void setJalviewColourScheme(ColourSchemeI colourScheme);
 
+  /**
+   * 
+   * @return true if all background sequence/structure binding threads have
+   *         completed for this viewer instance
+   */
+  boolean hasMapping();
+
+  /**
+   * Checks if the PDB file is already loaded in this viewer, if so just adds
+   * mappings as necessary and answers true, else answers false. This supports
+   * the use case of adding additional chains of the same structure to a viewer.
+   * 
+   * @param seq
+   * @param chains
+   * @param apanel
+   * @param pdbId
+   * @return
+   */
+  boolean addAlreadyLoadedFile(SequenceI[] seq, String[] chains,
+          AlignmentViewPanel apanel, String pdbId);
+
+  /**
+   * Adds one or more chains (sequences) of a PDB structure to this structure
+   * viewer
+   * 
+   * @param pdbentry
+   * @param seq
+   * @param chains
+   * @param apanel
+   * @param pdbId
+   * @return
+   */
+  void addToExistingViewer(PDBEntry pdbentry, SequenceI[] seq,
+          String[] chains, AlignmentViewPanel apanel, String pdbId);
+
+  /**
+   * refresh GUI after reconfiguring structure(s) and alignment panels
+   */
+  void updateTitleAndMenus();
+
+  /**
+   * Answers true if the viewer should attempt to align any added structures,
+   * else false
+   * 
+   * @return
+   */
+  boolean isAlignAddedStructures();
+
+  /**
+   * Sets the flag for whether added structures should be aligned
+   * 
+   * @param alignAdded
+   */
+  void setAlignAddedStructures(boolean alignAdded);
+
+  /**
+   * Raise the panel to the top of the stack...
+   */
+  void raiseViewer();
+
 }
index ef87671..63f2745 100644 (file)
@@ -1905,7 +1905,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   static StringBuffer copiedSequences;
 
-  static Vector<int[]> copiedHiddenColumns;
+  static HiddenColumns copiedHiddenColumns;
 
   protected void copy_actionPerformed()
   {
@@ -1929,14 +1929,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
     if (viewport.hasHiddenColumns() && viewport.getSelectionGroup() != null)
     {
-      copiedHiddenColumns = new Vector<>(viewport.getAlignment()
-              .getHiddenColumns().getHiddenColumnsCopy());
       int hiddenOffset = viewport.getSelectionGroup().getStartRes();
-      for (int[] region : copiedHiddenColumns)
-      {
-        region[0] = region[0] - hiddenOffset;
-        region[1] = region[1] - hiddenOffset;
-      }
+      int hiddenCutoff = viewport.getSelectionGroup().getEndRes();
+
+      // create new HiddenColumns object with copy of hidden regions
+      // between startRes and endRes, offset by startRes
+      copiedHiddenColumns = new HiddenColumns(
+              viewport.getAlignment().getHiddenColumns(), hiddenOffset,
+              hiddenCutoff, hiddenOffset);
     }
     else
     {
@@ -2005,13 +2005,13 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   {
     try
     {
-
       if (copiedSequences == null)
       {
         return;
       }
 
-      StringTokenizer st = new StringTokenizer(copiedSequences.toString());
+      StringTokenizer st = new StringTokenizer(copiedSequences.toString(),
+              "\t");
       Vector seqs = new Vector();
       while (st.hasMoreElements())
       {
@@ -2043,14 +2043,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
         }
         AlignFrame af = new AlignFrame(new Alignment(newSeqs),
                 viewport.applet, newtitle, false);
-        if (copiedHiddenColumns != null)
-        {
-          for (int i = 0; i < copiedHiddenColumns.size(); i++)
-          {
-            int[] region = copiedHiddenColumns.elementAt(i);
-            af.viewport.hideColumns(region[0], region[1]);
-          }
-        }
+        af.viewport.setHiddenColumns(copiedHiddenColumns);
 
         jalview.bin.JalviewLite.addFrame(af, newtitle, frameWidth,
                 frameHeight);
index 270b2f7..83d8ade 100644 (file)
@@ -421,8 +421,8 @@ public class AlignmentPanel extends Panel
     if (av.hasHiddenColumns())
     {
       AlignmentI al = av.getAlignment();
-      start = al.getHiddenColumns().findColumnPosition(ostart);
-      end = al.getHiddenColumns().findColumnPosition(end);
+      start = al.getHiddenColumns().absoluteToVisibleColumn(ostart);
+      end = al.getHiddenColumns().absoluteToVisibleColumn(end);
       if (start == end)
       {
         if (!scrollToNearest && !al.getHiddenColumns().isVisible(ostart))
@@ -675,7 +675,7 @@ public class AlignmentPanel extends Panel
       if (av.hasHiddenColumns())
       {
         width = av.getAlignment().getHiddenColumns()
-                .findColumnPosition(width);
+                .absoluteToVisibleColumn(width);
       }
       if (x < 0)
       {
index d8f65a5..1366f31 100755 (executable)
@@ -23,6 +23,7 @@ package jalview.appletgui;
 import jalview.analysis.AlignmentUtils;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.util.MessageManager;
@@ -31,6 +32,7 @@ import jalview.util.ParseHtmlBodyAndLinks;
 import java.awt.Checkbox;
 import java.awt.CheckboxMenuItem;
 import java.awt.Color;
+import java.awt.Cursor;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.FontMetrics;
@@ -50,13 +52,22 @@ import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Vector;
 
 public class AnnotationLabels extends Panel
         implements ActionListener, MouseListener, MouseMotionListener
 {
   Image image;
 
+  /**
+   * width in pixels within which height adjuster arrows are shown and active
+   */
+  private static final int HEIGHT_ADJUSTER_WIDTH = 50;
+
+  /**
+   * height in pixels for allowing height adjuster to be active
+   */
+  private static int HEIGHT_ADJUSTER_HEIGHT = 10;
+
   boolean active = false;
 
   AlignmentPanel ap;
@@ -92,23 +103,6 @@ public class AnnotationLabels extends Panel
     this.ap = ap;
     this.av = ap.av;
     setLayout(null);
-
-    /**
-     * this retrieves the adjustable height glyph from resources. we don't use
-     * it at the moment. java.net.URL url =
-     * getClass().getResource("/images/idwidth.gif"); Image temp = null;
-     * 
-     * if (url != null) { temp =
-     * java.awt.Toolkit.getDefaultToolkit().createImage(url); }
-     * 
-     * try { MediaTracker mt = new MediaTracker(this); mt.addImage(temp, 0);
-     * mt.waitForID(0); } catch (Exception ex) { }
-     * 
-     * BufferedImage bi = new BufferedImage(temp.getHeight(this),
-     * temp.getWidth(this), BufferedImage.TYPE_INT_RGB); Graphics2D g =
-     * (Graphics2D) bi.getGraphics(); g.rotate(Math.toRadians(90));
-     * g.drawImage(temp, 0, -bi.getWidth(this), this); image = (Image) bi;
-     */
     addMouseListener(this);
     addMouseMotionListener(this);
   }
@@ -208,7 +202,9 @@ public class AnnotationLabels extends Panel
     }
     else if (evt.getActionCommand().equals(COPYCONS_SEQ))
     {
-      SequenceI cons = av.getConsensusSeq();
+      SequenceGroup group = aa[selectedRow].groupRef;
+      SequenceI cons = group == null ? av.getConsensusSeq()
+              : group.getConsensusSeq();
       if (cons != null)
       {
         copy_annotseqtoclipboard(cons);
@@ -268,7 +264,10 @@ public class AnnotationLabels extends Panel
   @Override
   public void mouseMoved(MouseEvent evt)
   {
-    resizePanel = evt.getY() < 10 && evt.getX() < 14;
+    resizePanel = evt.getY() < HEIGHT_ADJUSTER_HEIGHT
+            && evt.getX() < HEIGHT_ADJUSTER_WIDTH;
+    setCursor(Cursor.getPredefinedCursor(
+            resizePanel ? Cursor.S_RESIZE_CURSOR : Cursor.DEFAULT_CURSOR));
     int row = getSelectedRow(evt.getY() + scrollOffset);
 
     if (row > -1)
@@ -406,6 +405,7 @@ public class AnnotationLabels extends Panel
     resizePanel = false;
     dragEvent = null;
     dragCancelled = false;
+    setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
     repaint();
     ap.annotationPanel.repaint();
   }
@@ -418,6 +418,8 @@ public class AnnotationLabels extends Panel
       resizePanel = true;
       repaint();
     }
+    setCursor(Cursor.getPredefinedCursor(
+            resizePanel ? Cursor.S_RESIZE_CURSOR : Cursor.DEFAULT_CURSOR));
   }
 
   @Override
@@ -843,8 +845,8 @@ public class AnnotationLabels extends Panel
                     + "\t" + sq.getSequenceAsString() + "\n");
     if (av.hasHiddenColumns())
     {
-      jalview.appletgui.AlignFrame.copiedHiddenColumns = new Vector<>(
-              av.getAlignment().getHiddenColumns().getHiddenColumnsCopy());
+      jalview.appletgui.AlignFrame.copiedHiddenColumns = new HiddenColumns(
+              av.getAlignment().getHiddenColumns());
     }
   }
 
@@ -903,14 +905,8 @@ public class AnnotationLabels extends Panel
       }
     }
     g.translate(0, +scrollOffset);
-    if (resizePanel)
-    {
-      g.setColor(Color.red);
-      g.setPaintMode();
-      g.drawLine(2, 8, 5, 2);
-      g.drawLine(5, 2, 8, 8);
-    }
-    else if (!dragCancelled && dragEvent != null && aa != null)
+
+    if (!resizePanel && !dragCancelled && dragEvent != null && aa != null)
     {
       g.setColor(Color.lightGray);
       g.drawString(aa[selectedRow].label, dragEvent.getX(),
index 50a9e33..50bc184 100755 (executable)
@@ -480,7 +480,7 @@ public class AnnotationPanel extends Panel
     if (av.hasHiddenColumns())
     {
       column = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(column);
+              .visibleToAbsoluteColumn(column);
     }
 
     if (row > -1 && column < aa[row].annotations.length
index 9a67499..cd85ab7 100755 (executable)
@@ -65,7 +65,7 @@ import java.util.Set;
 
 public class FeatureSettings extends Panel
         implements ItemListener, MouseListener, MouseMotionListener,
-        ActionListener, AdjustmentListener, FeatureSettingsControllerI
+        AdjustmentListener, FeatureSettingsControllerI
 {
   FeatureRenderer fr;
 
@@ -120,8 +120,17 @@ public class FeatureSettings extends Panel
       add(scrollPane, BorderLayout.CENTER);
     }
 
-    Button invert = new Button("Invert Selection");
-    invert.addActionListener(this);
+    Button invert = new Button(
+            MessageManager.getString("label.invert_selection"));
+    invert.addActionListener(new ActionListener()
+    {
+
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        invertSelection();
+      }
+    });
 
     Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));
     lowerPanel.add(invert);
@@ -545,8 +554,7 @@ public class FeatureSettings extends Panel
     }
   }
 
-  @Override
-  public void actionPerformed(ActionEvent evt)
+  protected void invertSelection()
   {
     for (int i = 0; i < featurePanel.getComponentCount(); i++)
     {
index f5ea12e..296f898 100755 (executable)
@@ -286,7 +286,7 @@ public class IdCanvas extends Panel implements ViewportListenerI
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
+              .absoluteToVisibleColumn(maxwidth) - 1;
     }
 
     int annotationHeight = 0;
index 75e3243..2602268 100755 (executable)
@@ -21,9 +21,8 @@
 package jalview.appletgui;
 
 import java.awt.Color;
+import java.awt.Cursor;
 import java.awt.Dimension;
-import java.awt.Graphics;
-import java.awt.Image;
 import java.awt.Panel;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
@@ -36,29 +35,24 @@ public class IdwidthAdjuster extends Panel
 
   int oldX = 0;
 
-  Image image;
-
   AlignmentPanel ap;
 
   public IdwidthAdjuster(AlignmentPanel ap)
   {
     setLayout(null);
     this.ap = ap;
-    java.net.URL url = getClass().getResource("/images/idwidth.gif");
-    if (url != null)
-    {
-      image = java.awt.Toolkit.getDefaultToolkit().getImage(url);
-    }
-
+    setBackground(Color.WHITE);
     addMouseListener(this);
     addMouseMotionListener(this);
   }
 
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     oldX = evt.getX();
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     active = false;
@@ -85,18 +79,24 @@ public class IdwidthAdjuster extends Panel
     // }
   }
 
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
     active = true;
+    setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
+
     repaint();
   }
 
+  @Override
   public void mouseExited(MouseEvent evt)
   {
     active = false;
+    setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
     repaint();
   }
 
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     active = true;
@@ -112,25 +112,13 @@ public class IdwidthAdjuster extends Panel
     }
   }
 
+  @Override
   public void mouseMoved(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
   }
-
-  public void paint(Graphics g)
-  {
-    g.setColor(Color.white);
-    g.fillRect(0, 0, getSize().width, getSize().height);
-    if (active)
-    {
-      if (image != null)
-      {
-        g.drawImage(image, getSize().width - 20, 2, this);
-      }
-    }
-  }
-
 }
index 9597b44..a556bdb 100644 (file)
@@ -128,8 +128,7 @@ public class OverviewCanvas extends Component
     {
       mg.translate(0, od.getSequencesHeight());
       or.drawGraph(mg, av.getAlignmentConservationAnnotation(),
-              av.getCharWidth(), od.getGraphHeight(),
-              od.getColumns(av.getAlignment()));
+              od.getGraphHeight(), od.getColumns(av.getAlignment()));
       mg.translate(0, -od.getSequencesHeight());
     }
     System.gc();
index 8ce597d..3bbbe95 100755 (executable)
@@ -155,6 +155,10 @@ public class OverviewPanel extends Panel implements Runnable,
       if (!od.isPositionInBox(evt.getX(), evt.getY()))
       { 
        draggingBox = false;
+
+        // display drag cursor at mouse position
+        setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+
         od.updateViewportFromMouse(evt.getX(), evt.getY(),
                 av.getAlignment().getHiddenSequences(),
                 av.getAlignment().getHiddenColumns());
@@ -172,6 +176,7 @@ public class OverviewPanel extends Panel implements Runnable,
   @Override
   public void mouseReleased(MouseEvent evt)
   {
+    draggingBox = false;
   }
 
   @Override
index 04fb22b..c91449f 100755 (executable)
@@ -42,6 +42,7 @@ import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.beans.PropertyChangeEvent;
+import java.util.Iterator;
 import java.util.List;
 
 public class ScalePanel extends Panel
@@ -86,7 +87,7 @@ public class ScalePanel extends Panel
 
     if (av.hasHiddenColumns())
     {
-      res = av.getAlignment().getHiddenColumns().adjustForHiddenColumns(x);
+      res = av.getAlignment().getHiddenColumns().visibleToAbsoluteColumn(x);
     }
     else
     {
@@ -173,7 +174,7 @@ public class ScalePanel extends Panel
       });
       pop.add(item);
 
-      if (av.getAlignment().getHiddenColumns().hasManyHiddenColumns())
+      if (av.getAlignment().getHiddenColumns().hasMultiHiddenColumnRegions())
       {
         item = new MenuItem(MessageManager.getString("action.reveal_all"));
         item.addActionListener(new ActionListener()
@@ -234,7 +235,7 @@ public class ScalePanel extends Panel
     if (av.hasHiddenColumns())
     {
       res = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(res);
+              .visibleToAbsoluteColumn(res);
     }
 
     if (!stretchingGroup)
@@ -275,7 +276,7 @@ public class ScalePanel extends Panel
     int res = (evt.getX() / av.getCharWidth())
             + av.getRanges().getStartRes();
     res = Math.max(0, res);
-    res = av.getAlignment().getHiddenColumns().adjustForHiddenColumns(res);
+    res = av.getAlignment().getHiddenColumns().visibleToAbsoluteColumn(res);
     res = Math.min(res, av.getAlignment().getWidth() - 1);
     min = Math.min(res, min);
     max = Math.max(res, max);
@@ -377,7 +378,7 @@ public class ScalePanel extends Panel
         {
           if (hidden.isVisible(sel))
           {
-            sel = hidden.findColumnPosition(sel);
+            sel = hidden.absoluteToVisibleColumn(sel);
           }
           else
           {
@@ -436,24 +437,17 @@ public class ScalePanel extends Panel
       if (av.getShowHiddenMarkers())
       {
         int widthx = 1 + endx - startx;
-        List<Integer> positions = hidden.findHiddenRegionPositions();
-        for (int pos : positions)
+        Iterator<Integer> it = hidden.getStartRegionIterator(startx,
+                startx + widthx + 1);
+        while (it.hasNext())
         {
-
-          res = pos - startx;
-
-          if (res < 0 || res > widthx)
-          {
-            continue;
-          }
+          res = it.next() - startx;
 
           gg.fillPolygon(
                   new int[]
-                  { -1 + res * avCharWidth - avcharHeight / 4,
-                      -1 + res * avCharWidth + avcharHeight / 4,
-                      -1 + res * avCharWidth },
-                  new int[]
-                  { y, y, y + 2 * yOf }, 3);
+                  { -1 + res * avCharWidth - avcharHeight / 4, -1 + res * avCharWidth + avcharHeight / 4,
+              -1 + res * avCharWidth }, new int[]
+          { y, y, y + 2 * yOf }, 3);
         }
       }
     }
index 2420cf7..35d73de 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.VisibleContigsIterator;
 import jalview.renderer.ScaleRenderer;
 import jalview.renderer.ScaleRenderer.ScaleMark;
 import jalview.viewmodel.AlignmentViewport;
@@ -37,7 +38,7 @@ import java.awt.Graphics;
 import java.awt.Image;
 import java.awt.Panel;
 import java.beans.PropertyChangeEvent;
-import java.util.List;
+import java.util.Iterator;
 
 public class SeqCanvas extends Panel implements ViewportListenerI
 {
@@ -130,16 +131,16 @@ public class SeqCanvas extends Panel implements ViewportListenerI
     if (av.hasHiddenColumns())
     {
       startx = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(startx);
+              .visibleToAbsoluteColumn(startx);
       endx = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(endx);
+              .visibleToAbsoluteColumn(endx);
     }
 
     int maxwidth = av.getAlignment().getWidth();
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
+              .absoluteToVisibleColumn(maxwidth) - 1;
     }
 
     // WEST SCALE
@@ -180,7 +181,7 @@ public class SeqCanvas extends Panel implements ViewportListenerI
     if (av.hasHiddenColumns())
     {
       endx = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(endx);
+              .visibleToAbsoluteColumn(endx);
     }
 
     SequenceI seq;
@@ -417,71 +418,71 @@ public class SeqCanvas extends Panel implements ViewportListenerI
           int canvasHeight, int startRes)
   {
     AlignmentI al = av.getAlignment();
-
+  
     FontMetrics fm = getFontMetrics(av.getFont());
-
+  
     LABEL_EAST = 0;
     LABEL_WEST = 0;
-
+  
     if (av.getScaleRightWrapped())
     {
       LABEL_EAST = fm.stringWidth(getMask());
     }
-
+  
     if (av.getScaleLeftWrapped())
     {
       LABEL_WEST = fm.stringWidth(getMask());
     }
-
+  
     int hgap = avcharHeight;
     if (av.getScaleAboveWrapped())
     {
       hgap += avcharHeight;
     }
-
+  
     int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / avcharWidth;
     int cHeight = av.getAlignment().getHeight() * avcharHeight;
-
+  
     av.setWrappedWidth(cWidth);
-
+  
     av.getRanges().setViewportStartAndWidth(startRes, cWidth);
-
+  
     int endx;
     int ypos = hgap;
-
+  
     int maxwidth = av.getAlignment().getWidth();
-
+  
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth);
+              .absoluteToVisibleColumn(maxwidth);
     }
-
+  
     while ((ypos <= canvasHeight) && (startRes < maxwidth))
     {
       endx = startRes + cWidth - 1;
-
+  
       if (endx > maxwidth)
       {
         endx = maxwidth;
       }
-
+  
       g.setColor(Color.black);
-
+  
       if (av.getScaleLeftWrapped())
       {
         drawWestScale(g, startRes, endx, ypos);
       }
-
+  
       if (av.getScaleRightWrapped())
       {
         g.translate(canvasWidth - LABEL_EAST, 0);
         drawEastScale(g, startRes, endx, ypos);
         g.translate(-(canvasWidth - LABEL_EAST), 0);
       }
-
+  
       g.translate(LABEL_WEST, 0);
-
+  
       if (av.getScaleAboveWrapped())
       {
         drawNorthScale(g, startRes, endx, ypos);
@@ -491,37 +492,27 @@ public class SeqCanvas extends Panel implements ViewportListenerI
         HiddenColumns hidden = av.getAlignment().getHiddenColumns();
         g.setColor(Color.blue);
         int res;
-        List<Integer> positions = hidden.findHiddenRegionPositions();
-        for (int pos : positions)
+        Iterator<Integer> it = hidden.getStartRegionIterator(startRes,
+                endx + 1);
+        while (it.hasNext())
         {
-          res = pos - startRes;
-
-          if (res < 0 || res > endx - startRes)
-          {
-            continue;
-          }
-
+          res = it.next() - startRes;
           gg.fillPolygon(
                   new int[]
-                  { res * avcharWidth - avcharHeight / 4,
-                      res * avcharWidth + avcharHeight / 4,
-                      res * avcharWidth },
+                  { res * avcharWidth - avcharHeight / 4, res * avcharWidth + avcharHeight / 4, res * avcharWidth },
                   new int[]
-                  { ypos - (avcharHeight / 2), ypos - (avcharHeight / 2),
-                      ypos - (avcharHeight / 2) + 8 },
-                  3);
-
+                  { ypos - (avcharHeight / 2), ypos - (avcharHeight / 2), ypos - (avcharHeight / 2) + 8 }, 3);
         }
       }
-
+  
       if (g.getClip() == null)
       {
         g.setClip(0, 0, cWidth * avcharWidth, canvasHeight);
       }
-
+  
       drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
       g.setClip(null);
-
+  
       if (av.isShowAnnotation())
       {
         g.translate(0, cHeight + ypos + 4);
@@ -529,17 +520,17 @@ public class SeqCanvas extends Panel implements ViewportListenerI
         {
           annotations = new AnnotationPanel(av);
         }
-
+  
         annotations.drawComponent(g, startRes, endx + 1);
         g.translate(0, -cHeight - ypos - 4);
       }
       g.translate(-LABEL_WEST, 0);
-
+  
       ypos += cHeight + getAnnotationHeight() + hgap;
-
+  
       startRes += cWidth;
     }
-
+  
   }
 
   AnnotationPanel annotations;
@@ -570,70 +561,44 @@ public class SeqCanvas extends Panel implements ViewportListenerI
     else
     {
       int screenY = 0;
-      final int screenYMax = endRes - startRes;
-      int blockStart = startRes;
-      int blockEnd = endRes;
-
-      if (av.hasHiddenColumns())
-      {
-        HiddenColumns hidden = av.getAlignment().getHiddenColumns();
-        for (int[] region : hidden.getHiddenColumnsCopy())
-        {
-          int hideStart = region[0];
-          int hideEnd = region[1];
-
-          if (hideStart <= blockStart)
-          {
-            blockStart += (hideEnd - hideStart) + 1;
-            continue;
-          }
-
-          /*
-           * draw up to just before the next hidden region, or the end of
-           * the visible region, whichever comes first
-           */
-          blockEnd = Math.min(hideStart - 1, blockStart + screenYMax
-                  - screenY);
-
-          g1.translate(screenY * avcharWidth, 0);
+      int blockStart;
+      int blockEnd;
 
-          draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
+      HiddenColumns hidden = av.getAlignment().getHiddenColumns();
+      VisibleContigsIterator regions = (VisibleContigsIterator) hidden
+              .getVisContigsIterator(startRes, endRes + 1, true);
 
-          /*
-           * draw the downline of the hidden column marker (ScalePanel draws the
-           * triangle on top) if we reached it
-           */
-          if (av.getShowHiddenMarkers() && blockEnd == hideStart - 1)
-          {
-            g1.setColor(Color.blue);
-            g1.drawLine((blockEnd - blockStart + 1) * avcharWidth - 1,
-                    0 + offset,
-                    (blockEnd - blockStart + 1) * avcharWidth - 1,
-                    (endSeq - startSeq + 1) * avcharHeight + offset);
-          }
-
-          g1.translate(-screenY * avcharWidth, 0);
-          screenY += blockEnd - blockStart + 1;
-          blockStart = hideEnd + 1;
-
-          if (screenY > screenYMax)
-          {
-            // already rendered last block
-            return;
-          }
-        }
-      }
-      if (screenY <= screenYMax)
+      while (regions.hasNext())
       {
-        // remaining visible region to render
-        blockEnd = blockStart + (endRes - startRes) - screenY;
+        int[] region = regions.next();
+        blockEnd = region[1];
+        blockStart = region[0];
+
+        /*
+         * draw up to just before the next hidden region, or the end of
+         * the visible region, whichever comes first
+         */
         g1.translate(screenY * avcharWidth, 0);
+
         draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
 
+        /*
+         * draw the downline of the hidden column marker (ScalePanel draws the
+         * triangle on top) if we reached it
+         */
+        if (av.getShowHiddenMarkers()
+                && (regions.hasNext() || regions.endsAtHidden()))
+        {
+          g1.setColor(Color.blue);
+          g1.drawLine((blockEnd - blockStart + 1) * avcharWidth - 1,
+                  0 + offset, (blockEnd - blockStart + 1) * avcharWidth - 1,
+                  (endSeq - startSeq + 1) * avcharHeight + offset);
+        }
+
         g1.translate(-screenY * avcharWidth, 0);
+        screenY += blockEnd - blockStart + 1;
       }
     }
-
   }
 
   // int startRes, int endRes, int startSeq, int endSeq, int x, int y,
index d74bbb7..e07dae6 100644 (file)
@@ -647,7 +647,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     if (av.hasHiddenColumns())
     {
       res = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(res);
+              .visibleToAbsoluteColumn(res);
     }
 
     return res;
@@ -1123,9 +1123,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     {
       fixedColumns = true;
       int y1 = av.getAlignment().getHiddenColumns()
-              .getHiddenBoundaryLeft(startres);
+              .getNextHiddenBoundary(true, startres);
       int y2 = av.getAlignment().getHiddenColumns()
-              .getHiddenBoundaryRight(startres);
+              .getNextHiddenBoundary(false, startres);
 
       if ((insertGap && startres > y1 && lastres < y1)
               || (!insertGap && startres < y2 && lastres > y2))
@@ -1197,7 +1197,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
           if (sg.getSize() == av.getAlignment().getHeight())
           {
             if ((av.hasHiddenColumns() && startres < av.getAlignment()
-                    .getHiddenColumns().getHiddenBoundaryRight(startres)))
+                    .getHiddenColumns()
+                    .getNextHiddenBoundary(false, startres)))
             {
               endEditing();
               return;
index dc50843..dcd6546 100755 (executable)
@@ -281,7 +281,7 @@ public class Cache
     @Override
     public synchronized Enumeration<Object> keys()
     {
-      return Collections.enumeration(new TreeSet<Object>(super.keySet()));
+      return Collections.enumeration(new TreeSet<>(super.keySet()));
     }
   };
 
@@ -334,7 +334,10 @@ public class Cache
     }
   }
 
-  /** Called when Jalview is started */
+  /**
+   * Loads properties from the given properties file. Any existing properties
+   * are first cleared.
+   */
   public static void loadProperties(String propsFile)
   {
     propertiesFile = propsFile;
@@ -369,6 +372,7 @@ public class Cache
       {
         fis = new FileInputStream(propertiesFile);
       }
+      applicationProperties.clear();
       applicationProperties.load(fis);
 
       // remove any old build properties
@@ -621,14 +625,14 @@ public class Cache
    * @param obj
    *          String value of property
    * 
-   * @return String value of property
+   * @return previous value of property (or null)
    */
-  public static String setProperty(String key, String obj)
+  public static Object setProperty(String key, String obj)
   {
-
+    Object oldValue = null;
     try
     {
-      applicationProperties.setProperty(key, obj);
+      oldValue = applicationProperties.setProperty(key, obj);
       if (!propsAreReadOnly)
       {
         FileOutputStream out = new FileOutputStream(propertiesFile);
@@ -640,7 +644,7 @@ public class Cache
       System.out.println(
               "Error setting property: " + key + " " + obj + "\n" + ex);
     }
-    return obj;
+    return oldValue;
   }
 
   /**
index 9ec0033..30620a1 100755 (executable)
@@ -20,9 +20,6 @@
  */
 package jalview.bin;
 
-import groovy.lang.Binding;
-import groovy.util.GroovyScriptEngine;
-
 import jalview.ext.so.SequenceOntology;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
@@ -64,8 +61,12 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Vector;
 
+import javax.swing.LookAndFeel;
 import javax.swing.UIManager;
 
+import groovy.lang.Binding;
+import groovy.util.GroovyScriptEngine;
+
 /**
  * Main class for Jalview Application <br>
  * <br>
@@ -275,20 +276,43 @@ public class Jalview
       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
     } catch (Exception ex)
     {
+      System.err.println("Unexpected Look and Feel Exception");
+      ex.printStackTrace();
     }
     if (Platform.isAMac())
     {
+
+      LookAndFeel lookAndFeel = ch.randelshofer.quaqua.QuaquaManager
+              .getLookAndFeel();
       System.setProperty("com.apple.mrj.application.apple.menu.about.name",
               "Jalview");
       System.setProperty("apple.laf.useScreenMenuBar", "true");
-      try
+      if (lookAndFeel != null)
       {
-        UIManager.setLookAndFeel(
-                ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel());
-      } catch (Throwable e)
+        try
+        {
+          UIManager.setLookAndFeel(lookAndFeel);
+        } catch (Throwable e)
+        {
+          System.err.println(
+                  "Failed to set QuaQua look and feel: " + e.toString());
+        }
+      }
+      if (lookAndFeel == null || !(lookAndFeel.getClass()
+              .isAssignableFrom(UIManager.getLookAndFeel().getClass()))
+              || !UIManager.getLookAndFeel().getClass().toString()
+                      .toLowerCase().contains("quaqua"))
       {
-        System.err.println(
-                "Failed to set QuaQua look and feel: " + e.toString());
+        try
+        {
+          System.err.println(
+                  "Quaqua LaF not available on this plaform. Using VAqua(4).\nSee https://issues.jalview.org/browse/JAL-2976");
+          UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
+        } catch (Throwable e)
+        {
+          System.err.println(
+                  "Failed to reset look and feel: " + e.toString());
+        }
       }
     }
 
@@ -970,7 +994,7 @@ public class Jalview
     }
     try
     {
-      Map<String, Object> vbinding = new HashMap<String, Object>();
+      Map<String, Object> vbinding = new HashMap<>();
       vbinding.put("Jalview", this);
       if (af != null)
       {
@@ -1036,7 +1060,7 @@ public class Jalview
                         + nickname + "|" + url);
         if (source == null)
         {
-          source = new Vector<String>();
+          source = new Vector<>();
         }
         source.addElement(nickname);
       }
@@ -1054,7 +1078,7 @@ public class Jalview
       System.out.println("adding source '" + data + "'");
       if (source == null)
       {
-        source = new Vector<String>();
+        source = new Vector<>();
       }
       source.addElement(data);
     }
index 6504290..a60496c 100644 (file)
@@ -31,7 +31,6 @@ import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.ColumnSelection;
-import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
@@ -471,7 +470,7 @@ public class JalviewLite extends Applet
         SequenceI rs = sel.getSequenceAt(0);
         start = rs.findIndex(start);
         end = rs.findIndex(end);
-        List<Integer> cs = new ArrayList<Integer>(csel.getSelected());
+        List<Integer> cs = new ArrayList<>(csel.getSelected());
         csel.clear();
         for (Integer selectedCol : cs)
         {
@@ -921,7 +920,7 @@ public class JalviewLite extends Applet
     setMouseoverListener(currentAlignFrame, listener);
   }
 
-  private Vector<jalview.javascript.JSFunctionExec> javascriptListeners = new Vector<jalview.javascript.JSFunctionExec>();
+  private Vector<jalview.javascript.JSFunctionExec> javascriptListeners = new Vector<>();
 
   /*
    * (non-Javadoc)
@@ -2165,8 +2164,8 @@ public class JalviewLite extends Applet
           else
           {
             param = st.nextToken();
-            List<SequenceI> tmp = new ArrayList<SequenceI>();
-            List<String> tmp2 = new ArrayList<String>();
+            List<SequenceI> tmp = new ArrayList<>();
+            List<String> tmp2 = new ArrayList<>();
 
             while (st.hasMoreTokens())
             {
@@ -2279,12 +2278,9 @@ public class JalviewLite extends Applet
           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);
-          HiddenColumns cs = new HiddenColumns();
-          cs.hideInsertionsFor(repseq);
-          alignFrame.viewport.getAlignment().setHiddenColumns(cs);
+
+          alignFrame.viewport.getAlignment().setupJPredAlignment();
+
           alignFrame.alignPanel.fontChanged();
           alignFrame.alignPanel.setScrollValues(0, 0);
           result = true;
@@ -2802,9 +2798,9 @@ public class JalviewLite extends Applet
     // callInitCallback();
   }
 
-  private Hashtable<String, long[]> jshashes = new Hashtable<String, long[]>();
+  private Hashtable<String, long[]> jshashes = new Hashtable<>();
 
-  private Hashtable<String, Hashtable<String, String[]>> jsmessages = new Hashtable<String, Hashtable<String, String[]>>();
+  private Hashtable<String, Hashtable<String, String[]>> jsmessages = new Hashtable<>();
 
   public void setJsMessageSet(String messageclass, String viewId,
           String[] colcommands)
@@ -2812,7 +2808,7 @@ public class JalviewLite extends Applet
     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
     if (msgset == null)
     {
-      msgset = new Hashtable<String, String[]>();
+      msgset = new Hashtable<>();
       jsmessages.put(messageclass, msgset);
     }
     msgset.put(viewId, colcommands);
index f268d37..3ba35b6 100755 (executable)
@@ -29,10 +29,12 @@ import jalview.util.MessageManager;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -49,7 +51,7 @@ public class Alignment implements AlignmentI
 {
   private Alignment dataset;
 
-  protected List<SequenceI> sequences;
+  private List<SequenceI> sequences;
 
   protected List<SequenceGroup> groups;
 
@@ -198,6 +200,7 @@ public class Alignment implements AlignmentI
         return sequences.get(i);
       }
     }
+
     return null;
   }
 
@@ -706,7 +709,7 @@ public class Alignment implements AlignmentI
   public int getWidth()
   {
     int maxLength = -1;
-
+  
     for (int i = 0; i < sequences.size(); i++)
     {
       if (getSequenceAt(i).getLength() > maxLength)
@@ -714,9 +717,34 @@ public class Alignment implements AlignmentI
         maxLength = getSequenceAt(i).getLength();
       }
     }
-
+  
     return maxLength;
   }
+  /*
+  @Override
+  public int getWidth()
+  {
+    final Wrapper temp = new Wrapper();
+  
+    forEachSequence(new Consumer<SequenceI>()
+    {
+      @Override
+      public void accept(SequenceI s)
+      {
+        if (s.getLength() > temp.inner)
+        {
+          temp.inner = s.getLength();
+        }
+      }
+    }, 0, sequences.size() - 1);
+  
+    return temp.inner;
+  }
+  
+  public static class Wrapper
+  {
+    public int inner;
+  }*/
 
   /**
    * DOCUMENT ME!
@@ -1603,7 +1631,10 @@ public class Alignment implements AlignmentI
     AlignmentAnnotation annot = new AlignmentAnnotation(name, name,
             new Annotation[1], 0f, 0f, AlignmentAnnotation.BAR_GRAPH);
     annot.hasText = false;
-    annot.setCalcId(new String(calcId));
+    if (calcId != null)
+    {
+      annot.setCalcId(new String(calcId));
+    }
     annot.autoCalculated = autoCalc;
     if (seqRef != null)
     {
@@ -1895,4 +1926,117 @@ public class Alignment implements AlignmentI
   {
     hiddenCols = cols;
   }
+
+  @Override
+  public void setupJPredAlignment()
+  {
+    SequenceI repseq = getSequenceAt(0);
+    setSeqrep(repseq);
+    HiddenColumns cs = new HiddenColumns();
+    cs.hideList(repseq.getInsertions());
+    setHiddenColumns(cs);
+  }
+
+  @Override
+  public HiddenColumns propagateInsertions(SequenceI profileseq,
+          AlignmentView input)
+  {
+    int profsqpos = 0;
+
+    char gc = getGapCharacter();
+    Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
+    HiddenColumns nview = (HiddenColumns) alandhidden[1];
+    SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
+    return propagateInsertions(profileseq, origseq, nview);
+  }
+
+  /**
+   * 
+   * @param profileseq
+   *          sequence in al which corresponds to origseq
+   * @param al
+   *          alignment which is to have gaps inserted into it
+   * @param origseq
+   *          sequence corresponding to profileseq which defines gap map for
+   *          modifying al
+   */
+  private HiddenColumns propagateInsertions(SequenceI profileseq,
+          SequenceI origseq, HiddenColumns hc)
+  {
+    // take the set of hidden columns, and the set of gaps in origseq,
+    // and remove all the hidden gaps from hiddenColumns
+
+    // first get the gaps as a Bitset
+    // then calculate hidden ^ not(gap)
+    BitSet gaps = origseq.gapBitset();
+    hc.andNot(gaps);
+
+    // for each sequence in the alignment, except the profile sequence,
+    // insert gaps corresponding to each hidden region but where each hidden
+    // column region is shifted backwards by the number of preceding visible
+    // gaps update hidden columns at the same time
+    HiddenColumns newhidden = new HiddenColumns();
+
+    int numGapsBefore = 0;
+    int gapPosition = 0;
+    Iterator<int[]> it = hc.iterator();
+    while (it.hasNext())
+    {
+      int[] region = it.next();
+
+      // get region coordinates accounting for gaps
+      // we can rely on gaps not being *in* hidden regions because we already
+      // removed those
+      while (gapPosition < region[0])
+      {
+        gapPosition++;
+        if (gaps.get(gapPosition))
+        {
+          numGapsBefore++;
+        }
+      }
+
+      int left = region[0] - numGapsBefore;
+      int right = region[1] - numGapsBefore;
+
+      newhidden.hideColumns(left, right);
+      padGaps(left, right, profileseq);
+    }
+    return newhidden;
+  }
+
+  /**
+   * Pad gaps in all sequences in alignment except profileseq
+   * 
+   * @param left
+   *          position of first gap to insert
+   * @param right
+   *          position of last gap to insert
+   * @param profileseq
+   *          sequence not to pad
+   */
+  private void padGaps(int left, int right, SequenceI profileseq)
+  {
+    char gc = getGapCharacter();
+
+    // make a string with number of gaps = length of hidden region
+    StringBuilder sb = new StringBuilder();
+    for (int g = 0; g < right - left + 1; g++)
+    {
+      sb.append(gc);
+    }
+
+    // loop over the sequences and pad with gaps where required
+    for (int s = 0, ns = getHeight(); s < ns; s++)
+    {
+      SequenceI sqobj = getSequenceAt(s);
+      if ((sqobj != profileseq) && (sqobj.getLength() >= left))
+      {
+        String sq = sqobj.getSequenceAsString();
+        sqobj.setSequence(
+                sq.substring(0, left) + sb.toString() + sq.substring(left));
+      }
+    }
+  }
+
 }
index f7bf4d8..0098d76 100755 (executable)
@@ -666,7 +666,7 @@ public class AlignmentAnnotation
     this.calcId = annotation.calcId;
     if (annotation.properties != null)
     {
-      properties = new HashMap<String, String>();
+      properties = new HashMap<>();
       for (Map.Entry<String, String> val : annotation.properties.entrySet())
       {
         properties.put(val.getKey(), val.getValue());
@@ -702,7 +702,7 @@ public class AlignmentAnnotation
       if (annotation.sequenceMapping != null)
       {
         Integer p = null;
-        sequenceMapping = new HashMap<Integer, Annotation>();
+        sequenceMapping = new HashMap<>();
         Iterator<Integer> pos = annotation.sequenceMapping.keySet()
                 .iterator();
         while (pos.hasNext())
@@ -782,7 +782,7 @@ public class AlignmentAnnotation
       int epos = sequenceRef.findPosition(endRes);
       if (sequenceMapping != null)
       {
-        Map<Integer, Annotation> newmapping = new HashMap<Integer, Annotation>();
+        Map<Integer, Annotation> newmapping = new HashMap<>();
         Iterator<Integer> e = sequenceMapping.keySet().iterator();
         while (e.hasNext())
         {
@@ -909,7 +909,7 @@ public class AlignmentAnnotation
     {
       return;
     }
-    sequenceMapping = new HashMap<Integer, Annotation>();
+    sequenceMapping = new HashMap<>();
 
     int seqPos;
 
@@ -1124,7 +1124,7 @@ public class AlignmentAnnotation
     {
       return;
     }
-    hidden.makeVisibleAnnotation(this);
+    makeVisibleAnnotation(hidden);
   }
 
   public void setPadGaps(boolean padgaps, char gapchar)
@@ -1190,7 +1190,7 @@ public class AlignmentAnnotation
   /**
    * properties associated with the calcId
    */
-  protected Map<String, String> properties = new HashMap<String, String>();
+  protected Map<String, String> properties = new HashMap<>();
 
   /**
    * base colour for line graphs. If null, will be set automatically by
@@ -1236,7 +1236,7 @@ public class AlignmentAnnotation
             : false;
 
     // TODO build a better annotation element map and get rid of annotations[]
-    Map<Integer, Annotation> mapForsq = new HashMap<Integer, Annotation>();
+    Map<Integer, Annotation> mapForsq = new HashMap<>();
     if (sequenceMapping != null)
     {
       if (sp2sq != null)
@@ -1289,7 +1289,7 @@ public class AlignmentAnnotation
     if (mapping != null)
     {
       Map<Integer, Annotation> old = sequenceMapping;
-      Map<Integer, Annotation> remap = new HashMap<Integer, Annotation>();
+      Map<Integer, Annotation> remap = new HashMap<>();
       int index = -1;
       for (int mp[] : mapping.values())
       {
@@ -1347,7 +1347,7 @@ public class AlignmentAnnotation
   {
     if (properties == null)
     {
-      properties = new HashMap<String, String>();
+      properties = new HashMap<>();
     }
     properties.put(property, value);
   }
@@ -1473,6 +1473,115 @@ public class AlignmentAnnotation
     return graphMin < graphMax;
   }
 
+  /**
+   * delete any columns in alignmentAnnotation that are hidden (including
+   * sequence associated annotation).
+   * 
+   * @param hiddenColumns
+   *          the set of hidden columns
+   */
+  public void makeVisibleAnnotation(HiddenColumns hiddenColumns)
+  {
+    if (annotations != null)
+    {
+      makeVisibleAnnotation(0, annotations.length, hiddenColumns);
+    }
+  }
+
+  /**
+   * delete any columns in alignmentAnnotation that are hidden (including
+   * sequence associated annotation).
+   * 
+   * @param start
+   *          remove any annotation to the right of this column
+   * @param end
+   *          remove any annotation to the left of this column
+   * @param hiddenColumns
+   *          the set of hidden columns
+   */
+  public void makeVisibleAnnotation(int start, int end,
+          HiddenColumns hiddenColumns)
+  {
+    if (annotations != null)
+    {
+      if (hiddenColumns.hasHiddenColumns())
+      {
+        removeHiddenAnnotation(start, end, hiddenColumns);
+      }
+      else
+      {
+        restrict(start, end);
+      }
+    }
+  }
+
+  /**
+   * The actual implementation of deleting hidden annotation columns
+   * 
+   * @param start
+   *          remove any annotation to the right of this column
+   * @param end
+   *          remove any annotation to the left of this column
+   * @param hiddenColumns
+   *          the set of hidden columns
+   */
+  private void removeHiddenAnnotation(int start, int end,
+          HiddenColumns hiddenColumns)
+  {
+    // mangle the alignmentAnnotation annotation array
+    ArrayList<Annotation[]> annels = new ArrayList<>();
+    Annotation[] els = null;
+
+    int w = 0;
+
+    Iterator<int[]> blocks = hiddenColumns.getVisContigsIterator(start,
+            end + 1, false);
+
+    int copylength;
+    int annotationLength;
+    while (blocks.hasNext())
+    {
+      int[] block = blocks.next();
+      annotationLength = block[1] - block[0] + 1;
+
+      if (blocks.hasNext())
+      {
+        // copy just the visible segment of the annotation row
+        copylength = annotationLength;
+      }
+      else
+      {
+        if (annotationLength + block[0] <= annotations.length)
+        {
+          // copy just the visible segment of the annotation row
+          copylength = annotationLength;
+        }
+        else
+        {
+          // copy to the end of the annotation row
+          copylength = annotations.length - block[0];
+        }
+      }
+
+      els = new Annotation[annotationLength];
+      annels.add(els);
+      System.arraycopy(annotations, block[0], els, 0, copylength);
+      w += annotationLength;
+    }
+
+    if (w != 0)
+    {
+      annotations = new Annotation[w];
+
+      w = 0;
+      for (Annotation[] chnk : annels)
+      {
+        System.arraycopy(chnk, 0, annotations, w, chnk.length);
+        w += chnk.length;
+      }
+    }
+  }
+
   public static Iterable<AlignmentAnnotation> findAnnotations(
           Iterable<AlignmentAnnotation> list, SequenceI seq, String calcId,
           String label)
index 084b80e..5fb16d6 100755 (executable)
@@ -580,6 +580,32 @@ public interface AlignmentI extends AnnotatedCollectionI
    */
   AlignedCodonFrame getMapping(SequenceI mapFrom, SequenceI mapTo);
 
+  /**
+   * Set the hidden columns collection on the alignment
+   * 
+   * @param cols
+   */
   public void setHiddenColumns(HiddenColumns cols);
 
+  /**
+   * Set the first sequence as representative and hide its insertions. Typically
+   * used when loading JPred files.
+   */
+  public void setupJPredAlignment();
+
+  /**
+   * Add gaps into the sequences aligned to profileseq under the given
+   * AlignmentView
+   * 
+   * @param profileseq
+   *          sequence in al which sequences are aligned to
+   * @param input
+   *          alignment view where sequence corresponding to profileseq is first
+   *          entry
+   * @return new HiddenColumns for new alignment view, with insertions into
+   *         profileseq marked as hidden.
+   */
+  public HiddenColumns propagateInsertions(SequenceI profileseq,
+          AlignmentView input);
+
 }
index 1723f1d..17e9ea6 100644 (file)
@@ -20,7 +20,7 @@
  */
 package jalview.datamodel;
 
-import java.util.List;
+import java.util.Iterator;
 
 public class CigarArray extends CigarBase
 {
@@ -90,9 +90,7 @@ public class CigarArray extends CigarBase
           SequenceGroup selectionGroup)
   {
     this(constructSeqCigarArray(alignment, selectionGroup));
-    constructFromAlignment(alignment,
-            hidden != null ? hidden.getHiddenColumnsCopy() : null,
-            selectionGroup);
+    constructFromAlignment(alignment, hidden, selectionGroup);
   }
 
   private static int[] _calcStartEndBounds(AlignmentI alignment,
@@ -154,33 +152,25 @@ public class CigarArray extends CigarBase
    * @param selectionGroup
    */
   private void constructFromAlignment(AlignmentI alignment,
-          List<int[]> list, SequenceGroup selectionGroup)
+          HiddenColumns hidden, SequenceGroup selectionGroup)
   {
     int[] _startend = _calcStartEndBounds(alignment, selectionGroup);
-    int start = _startend[1], end = _startend[2];
+    int start = _startend[1];
+    int end = _startend[2];
     // now construct the CigarArray operations
-    if (list != null)
+    if (hidden != null)
     {
       int[] region;
-      int hideStart, hideEnd;
+      int hideStart;
+      int hideEnd;
       int last = start;
-      for (int j = 0; last < end & j < list.size(); j++)
+
+      Iterator<int[]> regions = hidden.getBoundedIterator(start, end);
+      while (regions.hasNext())
       {
-        region = list.get(j);
+        region = regions.next();
         hideStart = region[0];
         hideEnd = region[1];
-        // edit hidden regions to selection range
-
-        // just move on if hideEnd is before last
-        if (hideEnd < last)
-        {
-          continue;
-        }
-        // exit if next region is after end
-        if (hideStart > end)
-        {
-          break;
-        }
 
         // truncate region at start if last falls in region
         if ((hideStart < last) && (hideEnd >= last))
@@ -204,6 +194,7 @@ public class CigarArray extends CigarBase
         addOperation(CigarArray.D, 1 + hideEnd - hideStart);
         last = hideEnd + 1;
       }
+
       // Final match if necessary.
       if (last <= end)
       {
index 0ac14e5..7a30141 100755 (executable)
@@ -96,7 +96,7 @@ public class DBRefSource
    * List of databases whose sequences might have coding regions annotated
    */
   public static final String[] DNACODINGDBS = { EMBL, EMBLCDS, GENEDB,
-      ENSEMBL };
+      ENSEMBL, ENSEMBLGENOMES };
 
   public static final String[] CODINGDBS = { EMBLCDS, GENEDB, ENSEMBL };
 
@@ -105,7 +105,7 @@ public class DBRefSource
 
   public static String[] allSources()
   {
-    List<String> src = new ArrayList<String>();
+    List<String> src = new ArrayList<>();
     for (Field f : DBRefSource.class.getFields())
     {
       if (String.class.equals(f.getType()))
index c0a43ee..a7e93da 100644 (file)
  */
 package jalview.datamodel;
 
-import jalview.util.Comparison;
-import jalview.util.ShiftList;
-
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.BitSet;
-import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
-import java.util.Vector;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+/**
+ * This class manages the collection of hidden columns associated with an
+ * alignment. To iterate over the collection, or over visible columns/regions,
+ * use an iterator obtained from one of:
+ * 
+ * - getBoundedIterator: iterates over the hidden regions, within some bounds,
+ * returning *absolute* positions
+ * 
+ * - getBoundedStartIterator: iterates over the start positions of hidden
+ * regions, within some bounds, returning *visible* positions
+ * 
+ * - getVisContigsIterator: iterates over visible regions in a range, returning
+ * *absolute* positions
+ * 
+ * - getVisibleColsIterator: iterates over the visible *columns*
+ * 
+ * For performance reasons, provide bounds where possible. Note that column
+ * numbering begins at 0 throughout this class.
+ * 
+ * @author kmourao
+ */
+
+/* Implementation notes:
+ * 
+ * Methods which change the hiddenColumns collection should use a writeLock to
+ * prevent other threads accessing the hiddenColumns collection while changes
+ * are being made. They should also reset the hidden columns cursor, and either
+ * update the hidden columns count, or set it to 0 (so that it will later be
+ * updated when needed).
+ * 
+ * 
+ * Methods which only need read access to the hidden columns collection should
+ * use a readLock to prevent other threads changing the hidden columns
+ * collection while it is in use.
+ */
 public class HiddenColumns
 {
+  private static final int HASH_MULTIPLIER = 31;
+
   private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
 
   /*
+   * Cursor which tracks the last used hidden columns region, and the number 
+   * of hidden columns up to (but not including) that region.
+   */
+  private HiddenColumnsCursor cursor = new HiddenColumnsCursor();
+
+  /*
+   * cache of the number of hidden columns: must be kept up to date by methods 
+   * which add or remove hidden columns
+   */
+  private int numColumns = 0;
+
+  /*
    * list of hidden column [start, end] ranges; the list is maintained in
    * ascending start column order
    */
-  private ArrayList<int[]> hiddenColumns;
+  private List<int[]> hiddenColumns = new ArrayList<>();
 
   /**
    * Constructor
@@ -51,19 +97,263 @@ public class HiddenColumns
    * Copy constructor
    * 
    * @param copy
+   *          the HiddenColumns object to copy from
    */
   public HiddenColumns(HiddenColumns copy)
   {
+    this(copy, Integer.MIN_VALUE, Integer.MAX_VALUE, 0);
+  }
+
+  /**
+   * Copy constructor within bounds and with offset. Copies hidden column
+   * regions fully contained between start and end, and offsets positions by
+   * subtracting offset.
+   * 
+   * @param copy
+   *          HiddenColumns instance to copy from
+   * @param start
+   *          lower bound to copy from
+   * @param end
+   *          upper bound to copy to
+   * @param offset
+   *          offset to subtract from each region boundary position
+   * 
+   */
+  public HiddenColumns(HiddenColumns copy, int start, int end, int offset)
+  {
     try
     {
       LOCK.writeLock().lock();
       if (copy != null)
       {
-        if (copy.hiddenColumns != null)
+        numColumns = 0;
+        Iterator<int[]> it = copy.getBoundedIterator(start, end);
+        while (it.hasNext())
+        {
+          int[] region = it.next();
+          // still need to check boundaries because iterator returns
+          // all overlapping regions and we need contained regions
+          if (region[0] >= start && region[1] <= end)
+          {
+            hiddenColumns.add(
+                    new int[]
+            { region[0] - offset, region[1] - offset });
+            numColumns += region[1] - region[0] + 1;
+          }
+        }
+        cursor = new HiddenColumnsCursor(hiddenColumns);
+      }
+    } finally
+    {
+      LOCK.writeLock().unlock();
+    }
+  }
+
+  /**
+   * Adds the specified column range to the hidden columns collection
+   * 
+   * @param start
+   *          start of range to add (absolute position in alignment)
+   * @param end
+   *          end of range to add (absolute position in alignment)
+   */
+  public void hideColumns(int start, int end)
+  {
+    try
+    {
+      LOCK.writeLock().lock();
+
+      int previndex = 0;
+      int prevHiddenCount = 0;
+      int regionindex = 0;
+      if (!hiddenColumns.isEmpty())
+      {
+        // set up cursor reset values
+        HiddenCursorPosition cursorPos = cursor.findRegionForColumn(start, false);
+        regionindex = cursorPos.getRegionIndex();
+
+        if (regionindex > 0)
+        {
+          // get previous index and hidden count for updating the cursor later
+          previndex = regionindex - 1;
+          int[] prevRegion = hiddenColumns.get(previndex);
+          prevHiddenCount = cursorPos.getHiddenSoFar()
+                  - (prevRegion[1] - prevRegion[0] + 1);
+        }
+      }
+
+      // new range follows everything else; check first to avoid looping over
+      // whole hiddenColumns collection
+      if (hiddenColumns.isEmpty()
+              || start > hiddenColumns.get(hiddenColumns.size() - 1)[1])
+      {
+        hiddenColumns.add(new int[] { start, end });
+        numColumns += end - start + 1;
+      }
+      else
+      {
+        /*
+         * traverse existing hidden ranges and insert / amend / append as
+         * appropriate
+         */
+        boolean added = false;
+        if (regionindex > 0)
+        {
+          added = insertRangeAtRegion(regionindex - 1, start, end);
+        }
+        if (!added && regionindex < hiddenColumns.size())
+        {
+          insertRangeAtRegion(regionindex, start, end);
+        }
+      }
+
+      // reset the cursor to just before our insertion point: this saves
+      // a lot of reprocessing in large alignments
+      cursor = new HiddenColumnsCursor(hiddenColumns, previndex,
+              prevHiddenCount);
+    } finally
+    {
+      LOCK.writeLock().unlock();
+    }
+  }
+
+  /**
+   * Insert [start, range] at the region at index i in hiddenColumns, if
+   * feasible
+   * 
+   * @param i
+   *          index to insert at
+   * @param start
+   *          start of range to insert
+   * @param end
+   *          end of range to insert
+   * @return true if range was successfully inserted
+   */
+  private boolean insertRangeAtRegion(int i, int start, int end)
+  {
+    boolean added = false;
+
+    int[] region = hiddenColumns.get(i);
+    if (end < region[0] - 1)
+    {
+      /*
+       * insert discontiguous preceding range
+       */
+      hiddenColumns.add(i, new int[] { start, end });
+      numColumns += end - start + 1;
+      added = true;
+    }
+    else if (end <= region[1])
+    {
+      /*
+       * new range overlaps existing, or is contiguous preceding it - adjust
+       * start column
+       */
+      int oldstart = region[0];
+      region[0] = Math.min(region[0], start);
+      numColumns += oldstart - region[0]; // new columns are between old and
+                                              // adjusted starts
+      added = true;
+    }
+    else if (start <= region[1] + 1)
+    {
+      /*
+       * new range overlaps existing, or is contiguous following it - adjust
+       * start and end columns
+       */
+      insertRangeAtOverlap(i, start, end, region);
+      added = true;
+    }
+    return added;
+  }
+
+  /**
+   * Insert a range whose start position overlaps an existing region and/or is
+   * contiguous to the right of the region
+   * 
+   * @param i
+   *          index to insert at
+   * @param start
+   *          start of range to insert
+   * @param end
+   *          end of range to insert
+   * @param region
+   *          the overlapped/continued region
+   */
+  private void insertRangeAtOverlap(int i, int start, int end, int[] region)
+  {
+    int oldstart = region[0];
+    int oldend = region[1];
+    region[0] = Math.min(region[0], start);
+    region[1] = Math.max(region[1], end);
+
+    numColumns += oldstart - region[0];
+
+    /*
+     * also update or remove any subsequent ranges 
+     * that are overlapped
+     */
+    int endi = i;
+    while (endi < hiddenColumns.size() - 1)
+    {
+      int[] nextRegion = hiddenColumns.get(endi + 1);
+      if (nextRegion[0] > end + 1)
+      {
+        /*
+         * gap to next hidden range - no more to update
+         */
+        break;
+      }
+      numColumns -= nextRegion[1] - nextRegion[0] + 1;
+      region[1] = Math.max(nextRegion[1], end);
+      endi++;
+    }
+    numColumns += region[1] - oldend;
+    hiddenColumns.subList(i + 1, endi + 1).clear();
+  }
+
+  /**
+   * hide a list of ranges
+   * 
+   * @param ranges
+   */
+  public void hideList(List<int[]> ranges)
+  {
+    try
+    {
+      LOCK.writeLock().lock();
+      for (int[] r : ranges)
+      {
+        hideColumns(r[0], r[1]);
+      }
+      cursor = new HiddenColumnsCursor(hiddenColumns);
+
+    } finally
+    {
+      LOCK.writeLock().unlock();
+    }
+  }
+
+  /**
+   * Unhides, and adds to the selection list, all hidden columns
+   */
+  public void revealAllHiddenColumns(ColumnSelection sel)
+  {
+    try
+    {
+      LOCK.writeLock().lock();
+
+      for (int[] region : hiddenColumns)
+      {
+        for (int j = region[0]; j < region[1] + 1; j++)
         {
-          hiddenColumns = copy.copyHiddenRegionsToArrayList();
+          sel.addElement(j);
         }
       }
+      hiddenColumns.clear();
+      cursor = new HiddenColumnsCursor(hiddenColumns);
+      numColumns = 0;
+
     } finally
     {
       LOCK.writeLock().unlock();
@@ -71,16 +361,46 @@ public class HiddenColumns
   }
 
   /**
-   * This method is used to return all the HiddenColumn regions and is intended
-   * to remain private. External callers which need a copy of the regions can
-   * call getHiddenColumnsCopyAsList.
+   * Reveals, and marks as selected, the hidden column range with the given
+   * start column
    * 
-   * @return empty list or List of hidden column intervals
+   * @param start
+   *          the start column to look for
+   * @param sel
+   *          the column selection to add the hidden column range to
    */
-  private List<int[]> getHiddenRegions()
+  public void revealHiddenColumns(int start, ColumnSelection sel)
   {
-    return hiddenColumns == null ? Collections.<int[]> emptyList()
-            : hiddenColumns;
+    try
+    {
+      LOCK.writeLock().lock();
+
+      if (!hiddenColumns.isEmpty())
+      {
+        int regionIndex = cursor.findRegionForColumn(start, false)
+                .getRegionIndex();
+
+        if (regionIndex != -1 && regionIndex != hiddenColumns.size())
+        {
+          // regionIndex is the region which either contains start
+          // or lies to the right of start
+          int[] region = hiddenColumns.get(regionIndex);
+          if (start == region[0])
+          {
+            for (int j = region[0]; j < region[1] + 1; j++)
+            {
+              sel.addElement(j);
+            }
+            int colsToRemove = region[1] - region[0] + 1;
+            hiddenColumns.remove(regionIndex);
+            numColumns -= colsToRemove;
+          }
+        }
+      }
+    } finally
+    {
+      LOCK.writeLock().unlock();
+    }
   }
 
   /**
@@ -99,16 +419,22 @@ public class HiddenColumns
     {
       LOCK.readLock().lock();
       StringBuilder regionBuilder = new StringBuilder();
-      if (hiddenColumns != null)
+
+      boolean first = true;
+      for (int[] range : hiddenColumns)
       {
-        for (int[] range : hiddenColumns)
+        if (!first)
+        {
+          regionBuilder.append(delimiter);
+        }
+        else
         {
-          regionBuilder.append(delimiter).append(range[0]).append(between)
-                  .append(range[1]);
+          first = false;
         }
+        regionBuilder.append(range[0]).append(between).append(range[1]);
 
-        regionBuilder.deleteCharAt(0);
       }
+
       return regionBuilder.toString();
     } finally
     {
@@ -123,18 +449,20 @@ public class HiddenColumns
    */
   public int getSize()
   {
+    return numColumns;
+  }
+
+  /**
+   * Get the number of distinct hidden regions
+   * 
+   * @return number of regions
+   */
+  public int getNumberOfRegions()
+  {
     try
     {
       LOCK.readLock().lock();
-      int size = 0;
-      if (hasHiddenColumns())
-      {
-        for (int[] range : hiddenColumns)
-        {
-          size += range[1] - range[0] + 1;
-        }
-      }
-      return size;
+      return hiddenColumns.size();
     } finally
     {
       LOCK.readLock().unlock();
@@ -157,25 +485,23 @@ public class HiddenColumns
       /*
        * check hidden columns are either both null, or match
        */
-      if (this.hiddenColumns == null)
-      {
-        return (that.hiddenColumns == null);
-      }
-      if (that.hiddenColumns == null
-              || that.hiddenColumns.size() != this.hiddenColumns.size())
+
+      if (that.hiddenColumns.size() != this.hiddenColumns.size())
       {
         return false;
       }
-      int i = 0;
-      for (int[] thisRange : hiddenColumns)
+
+      Iterator<int[]> it = this.iterator();
+      Iterator<int[]> thatit = that.iterator();
+      while (it.hasNext())
       {
-        int[] thatRange = that.hiddenColumns.get(i++);
-        if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1])
+        if (!(Arrays.equals(it.next(), thatit.next())))
         {
           return false;
         }
       }
       return true;
+
     } finally
     {
       LOCK.readLock().unlock();
@@ -189,23 +515,19 @@ public class HiddenColumns
    *          int column index in alignment view (count from zero)
    * @return alignment column index for column
    */
-  public int adjustForHiddenColumns(int column)
+  public int visibleToAbsoluteColumn(int column)
   {
     try
     {
       LOCK.readLock().lock();
       int result = column;
-      if (hiddenColumns != null)
+
+      if (!hiddenColumns.isEmpty())
       {
-        for (int i = 0; i < hiddenColumns.size(); i++)
-        {
-          int[] region = hiddenColumns.get(i);
-          if (result >= region[0])
-          {
-            result += region[1] - region[0] + 1;
-          }
-        }
+        result += cursor.findRegionForColumn(column, true)
+                .getHiddenSoFar();
       }
+
       return result;
     } finally
     {
@@ -216,54 +538,52 @@ public class HiddenColumns
   /**
    * Use this method to find out where a column will appear in the visible
    * alignment when hidden columns exist. If the column is not visible, then the
-   * left-most visible column will always be returned.
+   * index of the next visible column on the left will be returned (or 0 if
+   * there is no visible column on the left)
    * 
    * @param hiddenColumn
    *          the column index in the full alignment including hidden columns
    * @return the position of the column in the visible alignment
    */
-  public int findColumnPosition(int hiddenColumn)
+  public int absoluteToVisibleColumn(int hiddenColumn)
   {
     try
     {
       LOCK.readLock().lock();
       int result = hiddenColumn;
-      if (hiddenColumns != null)
-      {
-        int index = 0;
-        int[] region;
-        do
-        {
-          region = hiddenColumns.get(index++);
-          if (hiddenColumn > region[1])
-          {
-            result -= region[1] + 1 - region[0];
-          }
-        } while ((hiddenColumn > region[1])
-                && (index < hiddenColumns.size()));
 
-        if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
+      if (!hiddenColumns.isEmpty())
+      {
+        HiddenCursorPosition cursorPos = cursor
+                .findRegionForColumn(hiddenColumn, false);
+        int index = cursorPos.getRegionIndex();
+        int hiddenBeforeCol = cursorPos.getHiddenSoFar();
+    
+        // just subtract hidden cols count - this works fine if column is
+        // visible
+        result = hiddenColumn - hiddenBeforeCol;
+    
+        // now check in case column is hidden - it will be in the returned
+        // hidden region
+        if (index < hiddenColumns.size())
         {
-          // Here the hidden column is within a region, so
-          // we want to return the position of region[0]-1, adjusted for any
-          // earlier hidden columns.
-          // Calculate the difference between the actual hidden col position
-          // and region[0]-1, and then subtract from result to convert result
-          // from
-          // the adjusted hiddenColumn value to the adjusted region[0]-1 value
-
-          // However, if the region begins at 0 we cannot return region[0]-1
-          // just return 0
-          if (region[0] == 0)
-          {
-            return 0;
-          }
-          else
+          int[] region = hiddenColumns.get(index);
+          if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
           {
-            return result - (hiddenColumn - region[0] + 1);
+            // actually col is hidden, return region[0]-1
+            // unless region[0]==0 in which case return 0
+            if (region[0] == 0)
+            {
+              result = 0;
+            }
+            else
+            {
+              result = region[0] - 1 - hiddenBeforeCol;
+            }
           }
         }
       }
+
       return result; // return the shifted position after removing hidden
                      // columns.
     } finally
@@ -274,113 +594,75 @@ public class HiddenColumns
 
   /**
    * Find the visible column which is a given visible number of columns to the
-   * left of another visible column. i.e. for a startColumn x, the column which
-   * is distance 1 away will be column x-1.
+   * left (negative visibleDistance) or right (positive visibleDistance) of
+   * startColumn. If startColumn is not visible, we use the visible column at
+   * the left boundary of the hidden region containing startColumn.
    * 
    * @param visibleDistance
-   *          the number of visible columns to offset by
+   *          the number of visible columns to offset by (left offset = negative
+   *          value; right offset = positive value)
    * @param startColumn
-   *          the column to start from
-   * @return the position of the column in the visible alignment
+   *          the position of the column to start from (absolute position)
+   * @return the position of the column which is <visibleDistance> away
+   *         (absolute position)
    */
-  public int subtractVisibleColumns(int visibleDistance, int startColumn)
+  public int offsetByVisibleColumns(int visibleDistance, int startColumn)
   {
     try
     {
-
       LOCK.readLock().lock();
-      int distance = visibleDistance;
-
-      // in case startColumn is in a hidden region, move it to the left
-      int start = adjustForHiddenColumns(findColumnPosition(startColumn));
-
-      // get index of hidden region to left of start
-      int index = getHiddenIndexLeft(start);
-      if (index == -1)
-      {
-        // no hidden regions to left of startColumn
-        return start - distance;
-      }
-
-      // walk backwards through the alignment subtracting the counts of visible
-      // columns from distance
-      int[] region;
-      int gap = 0;
-      int nextstart = start;
-
-      while ((index > -1) && (distance - gap > 0))
-      {
-        // subtract the gap to right of region from distance
-        distance -= gap;
-        start = nextstart;
-
-        // calculate the next gap
-        region = hiddenColumns.get(index);
-        gap = start - region[1];
-
-        // set start to just to left of current region
-        nextstart = region[0] - 1;
-        index--;
-      }
+      int start = absoluteToVisibleColumn(startColumn);
+      return visibleToAbsoluteColumn(start + visibleDistance);
 
-      if (distance - gap > 0)
-      {
-        // fell out of loop because there are no more hidden regions
-        distance -= gap;
-        return nextstart - distance;
-      }
-      return start - distance;
     } finally
     {
       LOCK.readLock().unlock();
     }
-
   }
 
   /**
-   * Use this method to determine the set of hiddenRegion start positions
+   * This method returns the rightmost limit of a region of an alignment with
+   * hidden columns. In otherwords, the next hidden column.
    * 
-   * @return list of column number in visible view where hidden regions start
+   * @param alPos
+   *          the absolute (visible) alignmentPosition to find the next hidden
+   *          column for
+   * @return the index of the next hidden column, or alPos if there is no next
+   *         hidden column
    */
-  public List<Integer> findHiddenRegionPositions()
+  public int getNextHiddenBoundary(boolean left, int alPos)
   {
     try
     {
       LOCK.readLock().lock();
-      List<Integer> positions = null;
-
-      if (hiddenColumns != null)
+      if (!hiddenColumns.isEmpty())
       {
-        positions = new ArrayList<>(hiddenColumns.size());
+        int index = cursor.findRegionForColumn(alPos, false)
+                .getRegionIndex();
 
-        positions.add(hiddenColumns.get(0)[0]);
-        for (int i = 1; i < hiddenColumns.size(); ++i)
+        if (left && index > 0)
         {
-
-          int result = 0;
-          if (hiddenColumns != null)
+          int[] region = hiddenColumns.get(index - 1);
+          return region[1];
+        }
+        else if (!left && index < hiddenColumns.size())
+        {
+          int[] region = hiddenColumns.get(index);
+          if (alPos < region[0])
           {
-            int index = 0;
-            int gaps = 0;
-            do
-            {
-              int[] region = hiddenColumns.get(index);
-              gaps += region[1] + 1 - region[0];
-              result = region[1] + 1;
-              index++;
-            } while (index <= i);
-
-            result -= gaps;
+            return region[0];
+          }
+          else if ((alPos <= region[1])
+                  && (index + 1 < hiddenColumns.size()))
+          {
+            // alPos is within a hidden region, return the next one
+            // if there is one
+            region = hiddenColumns.get(index + 1);
+            return region[0];
           }
-          positions.add(result);
         }
       }
-      else
-      {
-        positions = new ArrayList<>();
-      }
-
-      return positions;
+      return alPos;
     } finally
     {
       LOCK.readLock().unlock();
@@ -388,69 +670,54 @@ public class HiddenColumns
   }
 
   /**
-   * This method returns the rightmost limit of a region of an alignment with
-   * hidden columns. In otherwords, the next hidden column.
+   * Answers if a column in the alignment is visible
    * 
-   * @param index
-   *          int
+   * @param column
+   *          absolute position of column in the alignment
+   * @return true if column is visible
    */
-  public int getHiddenBoundaryRight(int alPos)
+  public boolean isVisible(int column)
   {
     try
     {
       LOCK.readLock().lock();
-      if (hiddenColumns != null)
+
+      if (!hiddenColumns.isEmpty())
       {
-        int index = 0;
-        do
+        int regionindex = cursor.findRegionForColumn(column, false)
+                .getRegionIndex();
+        if (regionindex > -1 && regionindex < hiddenColumns.size())
         {
-          int[] region = hiddenColumns.get(index);
-          if (alPos < region[0])
+          int[] region = hiddenColumns.get(regionindex);
+          // already know that column <= region[1] as cursor returns containing
+          // region or region to right
+          if (column >= region[0])
           {
-            return region[0];
+            return false;
           }
-
-          index++;
-        } while (index < hiddenColumns.size());
+        }
       }
+      return true;
 
-      return alPos;
     } finally
     {
       LOCK.readLock().unlock();
     }
-
   }
 
   /**
-   * This method returns the leftmost limit of a region of an alignment with
-   * hidden columns. In otherwords, the previous hidden column.
    * 
-   * @param index
-   *          int
+   * @return true if there are columns hidden
    */
-  public int getHiddenBoundaryLeft(int alPos)
+  public boolean hasHiddenColumns()
   {
     try
     {
       LOCK.readLock().lock();
 
-      if (hiddenColumns != null)
-      {
-        int index = hiddenColumns.size() - 1;
-        do
-        {
-          int[] region = hiddenColumns.get(index);
-          if (alPos > region[1])
-          {
-            return region[1];
-          }
-
-          index--;
-        } while (index > -1);
-      }
-
-      return alPos;
+      // we don't use getSize()>0 here because it has to iterate over
+      // the full hiddenColumns collection and so will be much slower
+      return (!hiddenColumns.isEmpty());
     } finally
     {
       LOCK.readLock().unlock();
@@ -458,258 +725,169 @@ public class HiddenColumns
   }
 
   /**
-   * This method returns the index of the hidden region to the left of a column
-   * position. If the column is in a hidden region it returns the index of the
-   * region to the left. If there is no hidden region to the left it returns -1.
    * 
-   * @param pos
-   *          int
+   * @return true if there is more than one hidden column region
    */
-  private int getHiddenIndexLeft(int pos)
+  public boolean hasMultiHiddenColumnRegions()
   {
     try
     {
-
       LOCK.readLock().lock();
-      if (hiddenColumns != null)
-      {
-        int index = hiddenColumns.size() - 1;
-        do
-        {
-          int[] region = hiddenColumns.get(index);
-          if (pos > region[1])
-          {
-            return index;
-          }
-
-          index--;
-        } while (index > -1);
-      }
-
-      return -1;
+      return !hiddenColumns.isEmpty() && hiddenColumns.size() > 1;
     } finally
     {
       LOCK.readLock().unlock();
     }
-
   }
 
+
   /**
-   * Adds the specified column range to the hidden columns
-   * 
-   * @param start
-   * @param end
+   * Returns a hashCode built from hidden column ranges
    */
-  public void hideColumns(int start, int end)
+  @Override
+  public int hashCode()
   {
-    boolean wasAlreadyLocked = false;
     try
     {
-      // check if the write lock was already locked by this thread,
-      // as this method can be called internally in loops within HiddenColumns
-      if (!LOCK.isWriteLockedByCurrentThread())
-      {
-        LOCK.writeLock().lock();
-      }
-      else
-      {
-        wasAlreadyLocked = true;
-      }
-
-      if (hiddenColumns == null)
-      {
-        hiddenColumns = new ArrayList<>();
-      }
+      LOCK.readLock().lock();
+      int hashCode = 1;
 
-      /*
-       * traverse existing hidden ranges and insert / amend / append as
-       * appropriate
-       */
-      for (int i = 0; i < hiddenColumns.size(); i++)
+      for (int[] hidden : hiddenColumns)
       {
-        int[] region = hiddenColumns.get(i);
-
-        if (end < region[0] - 1)
-        {
-          /*
-           * insert discontiguous preceding range
-           */
-          hiddenColumns.add(i, new int[] { start, end });
-          return;
-        }
-
-        if (end <= region[1])
-        {
-          /*
-           * new range overlaps existing, or is contiguous preceding it - adjust
-           * start column
-           */
-          region[0] = Math.min(region[0], start);
-          return;
-        }
-
-        if (start <= region[1] + 1)
-        {
-          /*
-           * new range overlaps existing, or is contiguous following it - adjust
-           * start and end columns
-           */
-          region[0] = Math.min(region[0], start);
-          region[1] = Math.max(region[1], end);
-
-          /*
-           * also update or remove any subsequent ranges 
-           * that are overlapped
-           */
-          while (i < hiddenColumns.size() - 1)
-          {
-            int[] nextRegion = hiddenColumns.get(i + 1);
-            if (nextRegion[0] > end + 1)
-            {
-              /*
-               * gap to next hidden range - no more to update
-               */
-              break;
-            }
-            region[1] = Math.max(nextRegion[1], end);
-            hiddenColumns.remove(i + 1);
-          }
-          return;
-        }
+        hashCode = HASH_MULTIPLIER * hashCode + hidden[0];
+        hashCode = HASH_MULTIPLIER * hashCode + hidden[1];
       }
-
-      /*
-       * remaining case is that the new range follows everything else
-       */
-      hiddenColumns.add(new int[] { start, end });
+      return hashCode;
     } finally
     {
-      if (!wasAlreadyLocked)
-      {
-        LOCK.writeLock().unlock();
-      }
+      LOCK.readLock().unlock();
     }
   }
 
-  public boolean isVisible(int column)
+  /**
+   * Hide columns corresponding to the marked bits
+   * 
+   * @param inserts
+   *          - columns mapped to bits starting from zero
+   */
+  public void hideColumns(BitSet inserts)
+  {
+    hideColumns(inserts, 0, inserts.length() - 1);
+  }
+
+  /**
+   * Hide columns corresponding to the marked bits, within the range
+   * [start,end]. Entries in tohide which are outside [start,end] are ignored.
+   * 
+   * @param tohide
+   *          columns mapped to bits starting from zero
+   * @param start
+   *          start of range to hide columns within
+   * @param end
+   *          end of range to hide columns within
+   */
+  private void hideColumns(BitSet tohide, int start, int end)
   {
     try
     {
-      LOCK.readLock().lock();
-
-      if (hiddenColumns != null)
+      LOCK.writeLock().lock();
+      for (int firstSet = tohide
+              .nextSetBit(start), lastSet = start; firstSet >= start
+                      && lastSet <= end; firstSet = tohide
+                      .nextSetBit(lastSet))
       {
-        for (int[] region : hiddenColumns)
+        lastSet = tohide.nextClearBit(firstSet);
+        if (lastSet <= end)
         {
-          if (column >= region[0] && column <= region[1])
-          {
-            return false;
-          }
+          hideColumns(firstSet, lastSet - 1);
+        }
+        else if (firstSet <= end)
+        {
+          hideColumns(firstSet, end);
         }
       }
-
-      return true;
+      cursor = new HiddenColumnsCursor(hiddenColumns);
     } finally
     {
-      LOCK.readLock().unlock();
-    }
-  }
-
-  private ArrayList<int[]> copyHiddenRegionsToArrayList()
-  {
-    int size = 0;
-    if (hiddenColumns != null)
-    {
-      size = hiddenColumns.size();
-    }
-    ArrayList<int[]> copy = new ArrayList<>(size);
-
-    for (int i = 0, j = size; i < j; i++)
-    {
-      int[] rh;
-      int[] cp;
-      rh = hiddenColumns.get(i);
-      if (rh != null)
-      {
-        cp = new int[rh.length];
-        System.arraycopy(rh, 0, cp, 0, rh.length);
-        copy.add(cp);
-      }
+      LOCK.writeLock().unlock();
     }
-
-    return copy;
   }
 
   /**
-   * Returns a copy of the vector of hidden regions, as an ArrayList. Before
-   * using this method please consider if you really need access to the hidden
-   * regions - a new (or existing!) method on HiddenColumns might be more
-   * appropriate.
+   * Hide columns corresponding to the marked bits, within the range
+   * [start,end]. Entries in tohide which are outside [start,end] are ignored.
+   * NB Existing entries in [start,end] are cleared.
    * 
-   * @return hidden regions as an ArrayList of [start,end] pairs
+   * @param tohide
+   *          columns mapped to bits starting from zero
+   * @param start
+   *          start of range to hide columns within
+   * @param end
+   *          end of range to hide columns within
    */
-  public ArrayList<int[]> getHiddenColumnsCopy()
+  public void clearAndHideColumns(BitSet tohide, int start, int end)
   {
-    try
-    {
-      LOCK.readLock().lock();
-      return copyHiddenRegionsToArrayList();
-    } finally
-    {
-      LOCK.readLock().unlock();
-    }
+    clearHiddenColumnsInRange(start, end);
+    hideColumns(tohide, start, end);
   }
 
   /**
-   * propagate shift in alignment columns to column selection
+   * Make all columns in the range [start,end] visible
    * 
    * @param start
-   *          beginning of edit
-   * @param left
-   *          shift in edit (+ve for removal, or -ve for inserts)
+   *          start of range to show columns
+   * @param end
+   *          end of range to show columns
    */
-  public List<int[]> compensateForEdit(int start, int change,
-          ColumnSelection sel)
+  private void clearHiddenColumnsInRange(int start, int end)
   {
     try
     {
       LOCK.writeLock().lock();
-      List<int[]> deletedHiddenColumns = null;
-
-      if (hiddenColumns != null)
+      
+      if (!hiddenColumns.isEmpty())
       {
-        deletedHiddenColumns = new ArrayList<>();
-        int hSize = hiddenColumns.size();
-        for (int i = 0; i < hSize; i++)
+        HiddenCursorPosition pos = cursor.findRegionForColumn(start, false);
+        int index = pos.getRegionIndex();
+
+        if (index != -1 && index != hiddenColumns.size())
         {
-          int[] region = hiddenColumns.get(i);
-          if (region[0] > start && start + change > region[1])
+          // regionIndex is the region which either contains start
+          // or lies to the right of start
+          int[] region = hiddenColumns.get(index);
+          if (region[0] < start && region[1] >= start)
           {
-            deletedHiddenColumns.add(region);
-
-            hiddenColumns.remove(i);
-            i--;
-            hSize--;
-            continue;
+            // region contains start, truncate so that it ends just before start
+            numColumns -= region[1] - start + 1;
+            region[1] = start - 1;
+            index++;
           }
 
-          if (region[0] > start)
+          int endi = index;
+          while (endi < hiddenColumns.size())
           {
-            region[0] -= change;
-            region[1] -= change;
-          }
+            region = hiddenColumns.get(endi);
 
-          if (region[0] < 0)
-          {
-            region[0] = 0;
+            if (region[1] > end)
+            {
+              if (region[0] <= end)
+              {
+                // region contains end, truncate so it starts just after end
+                numColumns -= end - region[0] + 1;
+                region[0] = end + 1;
+              }
+              break;
+            }
+
+            numColumns -= region[1] - region[0] + 1;
+            endi++;
           }
+          hiddenColumns.subList(index, endi).clear();
 
         }
 
-        this.revealHiddenColumns(0, sel);
+        cursor = new HiddenColumnsCursor(hiddenColumns);
       }
-
-      return deletedHiddenColumns;
     } finally
     {
       LOCK.writeLock().unlock();
@@ -717,47 +895,24 @@ public class HiddenColumns
   }
 
   /**
-   * propagate shift in alignment columns to column selection special version of
-   * compensateForEdit - allowing for edits within hidden regions
    * 
-   * @param start
-   *          beginning of edit
-   * @param left
-   *          shift in edit (+ve for removal, or -ve for inserts)
+   * @param updates
+   *          BitSet where hidden columns will be marked
    */
-  public void compensateForDelEdits(int start, int change)
+  protected void andNot(BitSet updates)
   {
     try
     {
       LOCK.writeLock().lock();
-      if (hiddenColumns != null)
-      {
-        for (int i = 0; i < hiddenColumns.size(); i++)
-        {
-          int[] region = hiddenColumns.get(i);
-          if (region[0] >= start)
-          {
-            region[0] -= change;
-          }
-          if (region[1] >= start)
-          {
-            region[1] -= change;
-          }
-          if (region[1] < region[0])
-          {
-            hiddenColumns.remove(i--);
-          }
 
-          if (region[0] < 0)
-          {
-            region[0] = 0;
-          }
-          if (region[1] < 0)
-          {
-            region[1] = 0;
-          }
-        }
+      BitSet hiddenBitSet = new BitSet();
+      for (int[] range : hiddenColumns)
+      {
+        hiddenBitSet.set(range[0], range[1] + 1);
       }
+      hiddenBitSet.andNot(updates);
+      hiddenColumns.clear();
+      hideColumns(hiddenBitSet);
     } finally
     {
       LOCK.writeLock().unlock();
@@ -765,135 +920,80 @@ public class HiddenColumns
   }
 
   /**
-   * return all visible segments between the given start and end boundaries
+   * Calculate the visible start and end index of an alignment.
    * 
-   * @param start
-   *          (first column inclusive from 0)
-   * @param end
-   *          (last column - not inclusive)
-   * @return int[] {i_start, i_end, ..} where intervals lie in
-   *         start<=i_start<=i_end<end
+   * @param width
+   *          full alignment width
+   * @return integer array where: int[0] = startIndex, and int[1] = endIndex
    */
-  public int[] getVisibleContigs(int start, int end)
+  public int[] getVisibleStartAndEndIndex(int width)
   {
     try
     {
       LOCK.readLock().lock();
-      if (hiddenColumns != null && hiddenColumns.size() > 0)
-      {
-        List<int[]> visiblecontigs = new ArrayList<>();
-        List<int[]> regions = getHiddenRegions();
 
-        int vstart = start;
-        int[] region;
-        int hideStart;
-        int hideEnd;
+      int firstVisible = 0;
+      int lastVisible = width - 1;
 
-        for (int j = 0; vstart < end && j < regions.size(); j++)
-        {
-          region = regions.get(j);
-          hideStart = region[0];
-          hideEnd = region[1];
-
-          if (hideEnd < vstart)
-          {
-            continue;
-          }
-          if (hideStart > vstart)
-          {
-            visiblecontigs.add(new int[] { vstart, hideStart - 1 });
-          }
-          vstart = hideEnd + 1;
-        }
+      if (!hiddenColumns.isEmpty())
+      {
+        // first visible col with index 0, convert to absolute index
+        firstVisible = visibleToAbsoluteColumn(0);
 
-        if (vstart < end)
+        // last visible column is either immediately to left of
+        // last hidden region, or is just the last column in the alignment
+        int[] lastregion = hiddenColumns.get(hiddenColumns.size() - 1);
+        if (lastregion[1] == width - 1)
         {
-          visiblecontigs.add(new int[] { vstart, end - 1 });
+          // last region is at very end of alignment
+          // last visible column immediately precedes it
+          lastVisible = lastregion[0] - 1;
         }
-        int[] vcontigs = new int[visiblecontigs.size() * 2];
-        for (int i = 0, j = visiblecontigs.size(); i < j; i++)
-        {
-          int[] vc = visiblecontigs.get(i);
-          visiblecontigs.set(i, null);
-          vcontigs[i * 2] = vc[0];
-          vcontigs[i * 2 + 1] = vc[1];
-        }
-        visiblecontigs.clear();
-        return vcontigs;
-      }
-      else
-      {
-        return new int[] { start, end - 1 };
       }
+      return new int[] { firstVisible, lastVisible };
+
     } finally
     {
       LOCK.readLock().unlock();
     }
   }
 
-  public String[] getVisibleSequenceStrings(int start, int end,
-          SequenceI[] seqs)
+  /**
+   * Finds the hidden region (if any) which starts or ends at res
+   * 
+   * @param res
+   *          visible residue position, unadjusted for hidden columns
+   * @return region as [start,end] or null if no matching region is found. If
+   *         res is adjacent to two regions, returns the left region.
+   */
+  public int[] getRegionWithEdgeAtRes(int res)
   {
     try
     {
       LOCK.readLock().lock();
-      int iSize = seqs.length;
-      String[] selections = new String[iSize];
-      if (hiddenColumns != null && hiddenColumns.size() > 0)
-      {
-        for (int i = 0; i < iSize; i++)
-        {
-          StringBuffer visibleSeq = new StringBuffer();
-          List<int[]> regions = getHiddenRegions();
-
-          int blockStart = start;
-          int blockEnd = end;
-          int[] region;
-          int hideStart;
-          int hideEnd;
+      int adjres = visibleToAbsoluteColumn(res);
 
-          for (int j = 0; j < regions.size(); j++)
-          {
-            region = regions.get(j);
-            hideStart = region[0];
-            hideEnd = region[1];
-
-            if (hideStart < start)
-            {
-              continue;
-            }
-
-            blockStart = Math.min(blockStart, hideEnd + 1);
-            blockEnd = Math.min(blockEnd, hideStart);
-
-            if (blockStart > blockEnd)
-            {
-              break;
-            }
-
-            visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
-
-            blockStart = hideEnd + 1;
-            blockEnd = end;
-          }
-
-          if (end > blockStart)
-          {
-            visibleSeq.append(seqs[i].getSequence(blockStart, end));
-          }
+      int[] reveal = null;
 
-          selections[i] = visibleSeq.toString();
-        }
-      }
-      else
+      if (!hiddenColumns.isEmpty())
       {
-        for (int i = 0; i < iSize; i++)
+        // look for a region ending just before adjres
+        int regionindex = cursor.findRegionForColumn(adjres - 1, false)
+                .getRegionIndex();
+        if (regionindex < hiddenColumns.size()
+                && hiddenColumns.get(regionindex)[1] == adjres - 1)
+        {
+          reveal = hiddenColumns.get(regionindex);
+        }
+        // check if the region ends just after adjres
+        else if (regionindex < hiddenColumns.size()
+                && hiddenColumns.get(regionindex)[0] == adjres + 1)
         {
-          selections[i] = seqs[i].getSequenceAsString(start, end);
+          reveal = hiddenColumns.get(regionindex);
         }
       }
+      return reveal;
 
-      return selections;
     } finally
     {
       LOCK.readLock().unlock();
@@ -901,92 +1001,14 @@ public class HiddenColumns
   }
 
   /**
-   * Locate the first and last position visible for this sequence. if seq isn't
-   * visible then return the position of the left and right of the hidden
-   * boundary region, and the corresponding alignment column indices for the
-   * extent of the sequence
-   * 
-   * @param seq
-   * @return int[] { visible start, visible end, first seqpos, last seqpos,
-   *         alignment index for seq start, alignment index for seq end }
+   * Return an iterator over the hidden regions
    */
-  public int[] locateVisibleBoundsOfSequence(SequenceI seq)
+  public Iterator<int[]> iterator()
   {
     try
     {
       LOCK.readLock().lock();
-      int fpos = seq.getStart();
-      int lpos = seq.getEnd();
-      int start = 0;
-
-      if (hiddenColumns == null || hiddenColumns.size() == 0)
-      {
-        int ifpos = seq.findIndex(fpos) - 1;
-        int ilpos = seq.findIndex(lpos) - 1;
-        return new int[] { ifpos, ilpos, fpos, lpos, ifpos, ilpos };
-      }
-
-      // Simply walk along the sequence whilst watching for hidden column
-      // boundaries
-      List<int[]> regions = getHiddenRegions();
-      int spos = fpos;
-      int lastvispos = -1;
-      int rcount = 0;
-      int hideStart = seq.getLength();
-      int hideEnd = -1;
-      int visPrev = 0;
-      int visNext = 0;
-      int firstP = -1;
-      int lastP = -1;
-      boolean foundStart = false;
-      for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd()
-              && p < pLen; p++)
-      {
-        if (!Comparison.isGap(seq.getCharAt(p)))
-        {
-          // keep track of first/last column
-          // containing sequence data regardless of visibility
-          if (firstP == -1)
-          {
-            firstP = p;
-          }
-          lastP = p;
-          // update hidden region start/end
-          while (hideEnd < p && rcount < regions.size())
-          {
-            int[] region = regions.get(rcount++);
-            visPrev = visNext;
-            visNext += region[0] - visPrev;
-            hideStart = region[0];
-            hideEnd = region[1];
-          }
-          if (hideEnd < p)
-          {
-            hideStart = seq.getLength();
-          }
-          // update visible boundary for sequence
-          if (p < hideStart)
-          {
-            if (!foundStart)
-            {
-              fpos = spos;
-              start = p;
-              foundStart = true;
-            }
-            lastvispos = p;
-            lpos = spos;
-          }
-          // look for next sequence position
-          spos++;
-        }
-      }
-      if (foundStart)
-      {
-        return new int[] { findColumnPosition(start),
-            findColumnPosition(lastvispos), fpos, lpos, firstP, lastP };
-      }
-      // otherwise, sequence was completely hidden
-      return new int[] { visPrev, visNext, 0, 0, firstP, lastP };
+      return new RangeIterator(hiddenColumns);
     } finally
     {
       LOCK.readLock().unlock();
@@ -994,120 +1016,54 @@ public class HiddenColumns
   }
 
   /**
-   * delete any columns in alignmentAnnotation that are hidden (including
-   * sequence associated annotation).
+   * Return a bounded iterator over the hidden regions
    * 
-   * @param alignmentAnnotation
+   * @param start
+   *          position to start from (inclusive, absolute column position)
+   * @param end
+   *          position to end at (inclusive, absolute column position)
+   * @return
    */
-  public void makeVisibleAnnotation(AlignmentAnnotation alignmentAnnotation)
+  public Iterator<int[]> getBoundedIterator(int start, int end)
   {
-    makeVisibleAnnotation(-1, -1, alignmentAnnotation);
+    try
+    {
+      LOCK.readLock().lock();
+      return new RangeIterator(start, end, hiddenColumns);
+    } finally
+    {
+      LOCK.readLock().unlock();
+    }
   }
 
   /**
-   * delete any columns in alignmentAnnotation that are hidden (including
-   * sequence associated annotation).
+   * Return a bounded iterator over the *visible* start positions of hidden
+   * regions
    * 
    * @param start
-   *          remove any annotation to the right of this column
+   *          position to start from (inclusive, visible column position)
    * @param end
-   *          remove any annotation to the left of this column
-   * @param alignmentAnnotation
-   *          the annotation to operate on
+   *          position to end at (inclusive, visible column position)
    */
-  public void makeVisibleAnnotation(int start, int end,
-          AlignmentAnnotation alignmentAnnotation)
+  public Iterator<Integer> getStartRegionIterator(int start, int end)
   {
     try
     {
       LOCK.readLock().lock();
-      if (alignmentAnnotation.annotations == null)
-      {
-        return;
-      }
-      if (start == end && end == -1)
-      {
-        start = 0;
-        end = alignmentAnnotation.annotations.length;
-      }
-      if (hiddenColumns != null && hiddenColumns.size() > 0)
-      {
-        // then mangle the alignmentAnnotation annotation array
-        Vector<Annotation[]> annels = new Vector<>();
-        Annotation[] els = null;
-        List<int[]> regions = getHiddenRegions();
-        int blockStart = start;
-        int blockEnd = end;
-        int[] region;
-        int hideStart;
-        int hideEnd;
-        int w = 0;
-
-        for (int j = 0; j < regions.size(); j++)
-        {
-          region = regions.get(j);
-          hideStart = region[0];
-          hideEnd = region[1];
-
-          if (hideStart < start)
-          {
-            continue;
-          }
-
-          blockStart = Math.min(blockStart, hideEnd + 1);
-          blockEnd = Math.min(blockEnd, hideStart);
-
-          if (blockStart > blockEnd)
-          {
-            break;
-          }
-
-          annels.addElement(els = new Annotation[blockEnd - blockStart]);
-          System.arraycopy(alignmentAnnotation.annotations, blockStart, els,
-                  0, els.length);
-          w += els.length;
-          blockStart = hideEnd + 1;
-          blockEnd = end;
-        }
 
-        if (end > blockStart)
-        {
-          annels.addElement(els = new Annotation[end - blockStart + 1]);
-          if ((els.length
-                  + blockStart) <= alignmentAnnotation.annotations.length)
-          {
-            // copy just the visible segment of the annotation row
-            System.arraycopy(alignmentAnnotation.annotations, blockStart,
-                    els, 0, els.length);
-          }
-          else
-          {
-            // copy to the end of the annotation row
-            System.arraycopy(alignmentAnnotation.annotations, blockStart,
-                    els, 0,
-                    (alignmentAnnotation.annotations.length - blockStart));
-          }
-          w += els.length;
-        }
-        if (w == 0)
-        {
-          return;
-        }
+      // get absolute position of column in alignment
+      int absoluteStart = visibleToAbsoluteColumn(start);
 
-        alignmentAnnotation.annotations = new Annotation[w];
-        w = 0;
+      // Get cursor position and supply it to the iterator:
+      // Since we want visible region start, we look for a cursor for the
+      // (absoluteStart-1), then if absoluteStart is the start of a visible
+      // region we'll get the cursor pointing to the region before, which is
+      // what we want
+      HiddenCursorPosition pos = cursor
+              .findRegionForColumn(absoluteStart - 1, false);
 
-        for (Annotation[] chnk : annels)
-        {
-          System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w,
-                  chnk.length);
-          w += chnk.length;
-        }
-      }
-      else
-      {
-        alignmentAnnotation.restrict(start, end);
-      }
+      return new StartRegionIterator(pos, start, end,
+              hiddenColumns);
     } finally
     {
       LOCK.readLock().unlock();
@@ -1115,15 +1071,21 @@ public class HiddenColumns
   }
 
   /**
+   * Return an iterator over visible *columns* (not regions) between the given
+   * start and end boundaries
    * 
-   * @return true if there are columns hidden
+   * @param start
+   *          first column (inclusive)
+   * @param end
+   *          last column (inclusive)
    */
-  public boolean hasHiddenColumns()
+  public Iterator<Integer> getVisibleColsIterator(int start, int end)
   {
     try
     {
       LOCK.readLock().lock();
-      return hiddenColumns != null && hiddenColumns.size() > 0;
+      return new RangeElementsIterator(
+              new VisibleContigsIterator(start, end + 1, hiddenColumns));
     } finally
     {
       LOCK.readLock().unlock();
@@ -1131,577 +1093,36 @@ public class HiddenColumns
   }
 
   /**
+   * return an iterator over visible segments between the given start and end
+   * boundaries
    * 
-   * @return true if there are more than one set of columns hidden
+   * @param start
+   *          first column, inclusive from 0
+   * @param end
+   *          last column - not inclusive
+   * @param useVisibleCoords
+   *          if true, start and end are visible column positions, not absolute
+   *          positions*
    */
-  public boolean hasManyHiddenColumns()
+  public VisibleContigsIterator getVisContigsIterator(int start,
+          int end,
+          boolean useVisibleCoords)
   {
+    int adjstart = start;
+    int adjend = end;
+    if (useVisibleCoords)
+    {
+      adjstart = visibleToAbsoluteColumn(start);
+      adjend = visibleToAbsoluteColumn(end);
+    }
+
     try
     {
       LOCK.readLock().lock();
-      return hiddenColumns != null && hiddenColumns.size() > 1;
+      return new VisibleContigsIterator(adjstart, adjend, hiddenColumns);
     } finally
     {
       LOCK.readLock().unlock();
     }
   }
-
-  /**
-   * mark the columns corresponding to gap characters as hidden in the column
-   * selection
-   * 
-   * @param sr
-   */
-  public void hideInsertionsFor(SequenceI sr)
-  {
-    try
-    {
-      LOCK.writeLock().lock();
-      List<int[]> inserts = sr.getInsertions();
-      for (int[] r : inserts)
-      {
-        hideColumns(r[0], r[1]);
-      }
-    } finally
-    {
-      LOCK.writeLock().unlock();
-    }
-  }
-
-  /**
-   * Unhides, and adds to the selection list, all hidden columns
-   */
-  public void revealAllHiddenColumns(ColumnSelection sel)
-  {
-    try
-    {
-      LOCK.writeLock().lock();
-      if (hiddenColumns != null)
-      {
-        for (int i = 0; i < hiddenColumns.size(); i++)
-        {
-          int[] region = hiddenColumns.get(i);
-          for (int j = region[0]; j < region[1] + 1; j++)
-          {
-            sel.addElement(j);
-          }
-        }
-      }
-
-      hiddenColumns = null;
-    } finally
-    {
-      LOCK.writeLock().unlock();
-    }
-  }
-
-  /**
-   * Reveals, and marks as selected, the hidden column range with the given
-   * start column
-   * 
-   * @param start
-   */
-  public void revealHiddenColumns(int start, ColumnSelection sel)
-  {
-    try
-    {
-      LOCK.writeLock().lock();
-      for (int i = 0; i < hiddenColumns.size(); i++)
-      {
-        int[] region = hiddenColumns.get(i);
-        if (start == region[0])
-        {
-          for (int j = region[0]; j < region[1] + 1; j++)
-          {
-            sel.addElement(j);
-          }
-
-          hiddenColumns.remove(region);
-          break;
-        }
-      }
-      if (hiddenColumns.size() == 0)
-      {
-        hiddenColumns = null;
-      }
-    } finally
-    {
-      LOCK.writeLock().unlock();
-    }
-  }
-
-  /**
-   * removes intersection of position,length ranges in deletions from the
-   * start,end regions marked in intervals.
-   * 
-   * @param shifts
-   * @param intervals
-   * @return
-   */
-  private boolean pruneIntervalList(final List<int[]> shifts,
-          ArrayList<int[]> intervals)
-  {
-    boolean pruned = false;
-    int i = 0;
-    int j = intervals.size() - 1;
-    int s = 0;
-    int t = shifts.size() - 1;
-    int[] hr = intervals.get(i);
-    int[] sr = shifts.get(s);
-    while (i <= j && s <= t)
-    {
-      boolean trailinghn = hr[1] >= sr[0];
-      if (!trailinghn)
-      {
-        if (i < j)
-        {
-          hr = intervals.get(++i);
-        }
-        else
-        {
-          i++;
-        }
-        continue;
-      }
-      int endshift = sr[0] + sr[1]; // deletion ranges - -ve means an insert
-      if (endshift < hr[0] || endshift < sr[0])
-      { // leadinghc disjoint or not a deletion
-        if (s < t)
-        {
-          sr = shifts.get(++s);
-        }
-        else
-        {
-          s++;
-        }
-        continue;
-      }
-      boolean leadinghn = hr[0] >= sr[0];
-      boolean leadinghc = hr[0] < endshift;
-      boolean trailinghc = hr[1] < endshift;
-      if (leadinghn)
-      {
-        if (trailinghc)
-        { // deleted hidden region.
-          intervals.remove(i);
-          pruned = true;
-          j--;
-          if (i <= j)
-          {
-            hr = intervals.get(i);
-          }
-          continue;
-        }
-        if (leadinghc)
-        {
-          hr[0] = endshift; // clip c terminal region
-          leadinghn = !leadinghn;
-          pruned = true;
-        }
-      }
-      if (!leadinghn)
-      {
-        if (trailinghc)
-        {
-          if (trailinghn)
-          {
-            hr[1] = sr[0] - 1;
-            pruned = true;
-          }
-        }
-        else
-        {
-          // sr contained in hr
-          if (s < t)
-          {
-            sr = shifts.get(++s);
-          }
-          else
-          {
-            s++;
-          }
-          continue;
-        }
-      }
-    }
-    return pruned; // true if any interval was removed or modified by
-    // operations.
-  }
-
-  /**
-   * remove any hiddenColumns or selected columns and shift remaining based on a
-   * series of position, range deletions.
-   * 
-   * @param deletions
-   */
-  public void pruneDeletions(List<int[]> shifts)
-  {
-    try
-    {
-      LOCK.writeLock().lock();
-      // delete any intervals intersecting.
-      if (hiddenColumns != null)
-      {
-        pruneIntervalList(shifts, hiddenColumns);
-        if (hiddenColumns != null && hiddenColumns.size() == 0)
-        {
-          hiddenColumns = null;
-        }
-      }
-    } finally
-    {
-      LOCK.writeLock().unlock();
-    }
-  }
-
-  /**
-   * Add gaps into the sequences aligned to profileseq under the given
-   * AlignmentView
-   * 
-   * @param profileseq
-   * @param al
-   *          - alignment to have gaps inserted into it
-   * @param input
-   *          - alignment view where sequence corresponding to profileseq is
-   *          first entry
-   * @return new HiddenColumns for new alignment view, with insertions into
-   *         profileseq marked as hidden.
-   */
-  public static HiddenColumns propagateInsertions(SequenceI profileseq,
-          AlignmentI al, AlignmentView input)
-  {
-    int profsqpos = 0;
-
-    char gc = al.getGapCharacter();
-    Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
-    HiddenColumns nview = (HiddenColumns) alandhidden[1];
-    SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
-    nview.propagateInsertions(profileseq, al, origseq);
-    return nview;
-  }
-
-  /**
-   * 
-   * @param profileseq
-   *          - sequence in al which corresponds to origseq
-   * @param al
-   *          - alignment which is to have gaps inserted into it
-   * @param origseq
-   *          - sequence corresponding to profileseq which defines gap map for
-   *          modifying al
-   */
-  private void propagateInsertions(SequenceI profileseq, AlignmentI al,
-          SequenceI origseq)
-  {
-    char gc = al.getGapCharacter();
-    // recover mapping between sequence's non-gap positions and positions
-    // mapping to view.
-    pruneDeletions(ShiftList.parseMap(origseq.gapMap()));
-    int[] viscontigs = getVisibleContigs(0, profileseq.getLength());
-    int spos = 0;
-    int offset = 0;
-
-    // add profile to visible contigs
-    for (int v = 0; v < viscontigs.length; v += 2)
-    {
-      if (viscontigs[v] > spos)
-      {
-        StringBuffer sb = new StringBuffer();
-        for (int s = 0, ns = viscontigs[v] - spos; s < ns; s++)
-        {
-          sb.append(gc);
-        }
-        for (int s = 0, ns = al.getHeight(); s < ns; s++)
-        {
-          SequenceI sqobj = al.getSequenceAt(s);
-          if (sqobj != profileseq)
-          {
-            String sq = al.getSequenceAt(s).getSequenceAsString();
-            if (sq.length() <= spos + offset)
-            {
-              // pad sequence
-              int diff = spos + offset - sq.length() - 1;
-              if (diff > 0)
-              {
-                // pad gaps
-                sq = sq + sb;
-                while ((diff = spos + offset - sq.length() - 1) > 0)
-                {
-                  // sq = sq
-                  // + ((diff >= sb.length()) ? sb.toString() : sb
-                  // .substring(0, diff));
-                  if (diff >= sb.length())
-                  {
-                    sq += sb.toString();
-                  }
-                  else
-                  {
-                    char[] buf = new char[diff];
-                    sb.getChars(0, diff, buf, 0);
-                    sq += buf.toString();
-                  }
-                }
-              }
-              sq += sb.toString();
-            }
-            else
-            {
-              al.getSequenceAt(s).setSequence(sq.substring(0, spos + offset)
-                      + sb.toString() + sq.substring(spos + offset));
-            }
-          }
-        }
-        // offset+=sb.length();
-      }
-      spos = viscontigs[v + 1] + 1;
-    }
-    if ((offset + spos) < profileseq.getLength())
-    {
-      // pad the final region with gaps.
-      StringBuffer sb = new StringBuffer();
-      for (int s = 0, ns = profileseq.getLength() - spos
-              - offset; s < ns; s++)
-      {
-        sb.append(gc);
-      }
-      for (int s = 0, ns = al.getHeight(); s < ns; s++)
-      {
-        SequenceI sqobj = al.getSequenceAt(s);
-        if (sqobj == profileseq)
-        {
-          continue;
-        }
-        String sq = sqobj.getSequenceAsString();
-        // pad sequence
-        int diff = origseq.getLength() - sq.length();
-        while (diff > 0)
-        {
-          // sq = sq
-          // + ((diff >= sb.length()) ? sb.toString() : sb
-          // .substring(0, diff));
-          if (diff >= sb.length())
-          {
-            sq += sb.toString();
-          }
-          else
-          {
-            char[] buf = new char[diff];
-            sb.getChars(0, diff, buf, 0);
-            sq += buf.toString();
-          }
-          diff = origseq.getLength() - sq.length();
-        }
-      }
-    }
-  }
-
-  /**
-   * remove any hiddenColumns or selected columns and shift remaining based on a
-   * series of position, range deletions.
-   * 
-   * @param deletions
-   */
-  private void pruneDeletions(ShiftList deletions)
-  {
-    if (deletions != null)
-    {
-      final List<int[]> shifts = deletions.getShifts();
-      if (shifts != null && shifts.size() > 0)
-      {
-        pruneDeletions(shifts);
-
-        // and shift the rest.
-        this.compensateForEdits(deletions);
-      }
-    }
-  }
-
-  /**
-   * Adjust hidden column boundaries based on a series of column additions or
-   * deletions in visible regions.
-   * 
-   * @param shiftrecord
-   * @return
-   */
-  private ShiftList compensateForEdits(ShiftList shiftrecord)
-  {
-    if (shiftrecord != null)
-    {
-      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 = shifts.get(i);
-          compensateForDelEdits(shifted + sh[0], sh[1]);
-          shifted -= sh[1];
-        }
-      }
-      return shiftrecord.getInverse();
-    }
-    return null;
-  }
-
-  /**
-   * Returns a hashCode built from hidden column ranges
-   */
-  @Override
-  public int hashCode()
-  {
-    try
-    {
-      LOCK.readLock().lock();
-      int hashCode = 1;
-      if (hiddenColumns != null)
-      {
-        for (int[] hidden : hiddenColumns)
-        {
-          hashCode = 31 * hashCode + hidden[0];
-          hashCode = 31 * hashCode + hidden[1];
-        }
-      }
-      return hashCode;
-    } finally
-    {
-      LOCK.readLock().unlock();
-    }
-  }
-
-  /**
-   * Hide columns corresponding to the marked bits
-   * 
-   * @param inserts
-   *          - columns map to bits starting from zero
-   */
-  public void hideMarkedBits(BitSet inserts)
-  {
-    try
-    {
-      LOCK.writeLock().lock();
-      for (int firstSet = inserts
-              .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts
-                      .nextSetBit(lastSet))
-      {
-        lastSet = inserts.nextClearBit(firstSet);
-        hideColumns(firstSet, lastSet - 1);
-      }
-    } finally
-    {
-      LOCK.writeLock().unlock();
-    }
-  }
-
-  /**
-   * 
-   * @param inserts
-   *          BitSet where hidden columns will be marked
-   */
-  public void markHiddenRegions(BitSet inserts)
-  {
-    try
-    {
-      LOCK.readLock().lock();
-      if (hiddenColumns == null)
-      {
-        return;
-      }
-      for (int[] range : hiddenColumns)
-      {
-        inserts.set(range[0], range[1] + 1);
-      }
-    } finally
-    {
-      LOCK.readLock().unlock();
-    }
-  }
-
-  /**
-   * Calculate the visible start and end index of an alignment.
-   * 
-   * @param width
-   *          full alignment width
-   * @return integer array where: int[0] = startIndex, and int[1] = endIndex
-   */
-  public int[] getVisibleStartAndEndIndex(int width)
-  {
-    try
-    {
-      LOCK.readLock().lock();
-      int[] alignmentStartEnd = new int[] { 0, width - 1 };
-      int startPos = alignmentStartEnd[0];
-      int endPos = alignmentStartEnd[1];
-
-      int[] lowestRange = new int[] { -1, -1 };
-      int[] higestRange = new int[] { -1, -1 };
-
-      if (hiddenColumns == null)
-      {
-        return new int[] { startPos, endPos };
-      }
-
-      for (int[] hiddenCol : hiddenColumns)
-      {
-        lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
-        higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
-      }
-
-      if (lowestRange[0] == -1 && lowestRange[1] == -1)
-      {
-        startPos = alignmentStartEnd[0];
-      }
-      else
-      {
-        startPos = lowestRange[1] + 1;
-      }
-
-      if (higestRange[0] == -1 && higestRange[1] == -1)
-      {
-        endPos = alignmentStartEnd[1];
-      }
-      else
-      {
-        endPos = higestRange[0] - 1;
-      }
-      return new int[] { startPos, endPos };
-    } finally
-    {
-      LOCK.readLock().unlock();
-    }
-
-  }
-
-  /**
-   * Finds the hidden region (if any) which starts or ends at res
-   * 
-   * @param res
-   *          visible residue position, unadjusted for hidden columns
-   * @return region as [start,end] or null if no matching region is found
-   */
-  public int[] getRegionWithEdgeAtRes(int res)
-  {
-    try
-    {
-      LOCK.readLock().lock();
-      int adjres = adjustForHiddenColumns(res);
-
-      int[] reveal = null;
-      if (hiddenColumns != null)
-      {
-        for (int[] region : hiddenColumns)
-        {
-          if (adjres + 1 == region[0] || adjres - 1 == region[1])
-          {
-            reveal = region;
-            break;
-          }
-        }
-      }
-      return reveal;
-    } finally
-    {
-      LOCK.readLock().unlock();
-    }
-  }
-
 }
diff --git a/src/jalview/datamodel/HiddenColumnsCursor.java b/src/jalview/datamodel/HiddenColumnsCursor.java
new file mode 100644 (file)
index 0000000..2e9d798
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * 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.datamodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HiddenColumnsCursor
+{
+  // absolute position of first hidden column
+  private int firstColumn;
+
+  private List<int[]> hiddenColumns = new ArrayList<>();
+
+  private HiddenCursorPosition cursorPos = new HiddenCursorPosition(0, 0);
+
+  protected HiddenColumnsCursor()
+  {
+
+  }
+
+  protected HiddenColumnsCursor(List<int[]> hiddenCols)
+  {
+    resetCursor(hiddenCols, 0, 0);
+  }
+
+  protected HiddenColumnsCursor(List<int[]> hiddenCols, int index,
+          int hiddencount)
+  {
+    resetCursor(hiddenCols, index, hiddencount);
+  }
+
+  /**
+   * Reset the cursor with a new hidden columns collection, where we know in
+   * advance the index and hidden columns count of a particular location.
+   * 
+   * @param hiddenCols
+   *          new hidden columns collection
+   * @param index
+   *          cursor index to reset to
+   * @param hiddencount
+   *          hidden columns count to reset to
+   */
+  private void resetCursor(List<int[]> hiddenCols, int index,
+          int hiddencount)
+  {
+    hiddenColumns = hiddenCols;
+    if (!hiddenCols.isEmpty())
+    {
+      firstColumn = hiddenColumns.get(0)[0];
+      cursorPos = new HiddenCursorPosition(index,
+              hiddencount);
+    }
+  }
+
+  /**
+   * Get the cursor pointing to the hidden region that column is within (if
+   * column is hidden) or which is to the right of column (if column is
+   * visible). If no hidden columns are to the right, returns a cursor pointing
+   * to an imaginary hidden region beyond the end of the hidden columns
+   * collection (this ensures the count of previous hidden columns is correct).
+   * If hidden columns is empty returns null.
+   * 
+   * @param column
+   *          index of column in visible or absolute coordinates
+   * @param useVisible
+   *          true if column is in visible coordinates, false if absolute
+   * @return cursor pointing to hidden region containing the column (if hidden)
+   *         or to the right of the column (if visible)
+   */
+  protected HiddenCursorPosition findRegionForColumn(int column,
+          boolean useVisible)
+  {
+    if (hiddenColumns.isEmpty())
+    {
+      return null;
+    }
+
+    // used to add in hiddenColumns offset when working with visible columns
+    int offset = (useVisible ? 1 : 0);
+
+    HiddenCursorPosition pos = cursorPos;
+    int index = pos.getRegionIndex();
+    int hiddenCount = pos.getHiddenSoFar();
+
+    if (column < firstColumn)
+    {
+      pos = new HiddenCursorPosition(0, 0);
+    }
+
+    // column is after current region
+    else if ((index < hiddenColumns.size())
+            && (hiddenColumns.get(index)[0] <= column
+                    + offset * hiddenCount))
+    {
+      // iterate from where we are now, if we're lucky we'll be close by
+      // (but still better than iterating from 0)
+      // stop when we find the region *before* column
+      // i.e. the next region starts after column or if not, ends after column
+      pos = searchForward(pos, column, useVisible);
+    }
+
+    // column is before current region
+    else
+    {
+      pos = searchBackward(pos, column, useVisible);
+    }
+    cursorPos = pos;
+    return pos;
+  }
+
+  /**
+   * Search forwards through the hidden columns collection to find the hidden
+   * region immediately before a column
+   * 
+   * @param pos
+   *          current position
+   * @param column
+   *          column to locate
+   * @param useVisible
+   *          whether using visible or absolute coordinates
+   * @return position of region before column
+   */
+  private HiddenCursorPosition searchForward(HiddenCursorPosition pos,
+          int column, boolean useVisible)
+  {
+    HiddenCursorPosition p = pos;
+    if (useVisible)
+    {
+      while ((p.getRegionIndex() < hiddenColumns.size())
+              && hiddenColumns.get(p.getRegionIndex())[0] <= column
+                      + p.getHiddenSoFar())
+      {
+        p = stepForward(p);
+      }
+    }
+    else
+    {
+      while ((p.getRegionIndex() < hiddenColumns.size())
+              && hiddenColumns.get(p.getRegionIndex())[1] < column)
+      {
+        p = stepForward(p);
+      }
+    }
+    return p;
+  }
+
+  /**
+   * Move to the next (rightwards) hidden region after a given cursor position
+   * 
+   * @param p
+   *          current position of cursor
+   * @return new position of cursor at next region
+   */
+  private HiddenCursorPosition stepForward(HiddenCursorPosition p)
+  {
+    int[] region = hiddenColumns.get(p.getRegionIndex());
+
+    // increment the index, and add this region's hidden columns to the hidden
+    // column count
+    return new HiddenCursorPosition(p.getRegionIndex() + 1,
+            p.getHiddenSoFar() + region[1] - region[0] + 1);
+  }
+
+  /**
+   * Search backwards through the hidden columns collection to find the hidden
+   * region immediately before (left of) a given column
+   * 
+   * @param pos
+   *          current position
+   * @param column
+   *          column to locate
+   * @param useVisible
+   *          whether using visible or absolute coordinates
+   * @return position of region immediately to left of column
+   */
+  private HiddenCursorPosition searchBackward(HiddenCursorPosition p,
+          int column, boolean useVisible)
+  {
+    int i = p.getRegionIndex();
+    int h = p.getHiddenSoFar();
+
+    // used to add in hiddenColumns offset when working with visible columns
+    int offset = (useVisible ? 1 : 0);
+
+    while ((i > 0) && (hiddenColumns.get(i - 1)[1] >= column + offset * h))
+    {
+      i--;
+      int[] region = hiddenColumns.get(i);
+      h -= region[1] - region[0] + 1;
+    }
+    return new HiddenCursorPosition(i, h);
+  }
+
+}
diff --git a/src/jalview/datamodel/HiddenCursorPosition.java b/src/jalview/datamodel/HiddenCursorPosition.java
new file mode 100644 (file)
index 0000000..bdca6d0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.datamodel;
+
+public final class HiddenCursorPosition
+{
+  // index of last visited region
+  private final int regionIndex;
+
+  // number of hidden columns before last visited region
+  private final int hiddenSoFar;
+
+  public HiddenCursorPosition(int index, int hiddencount)
+  {
+    regionIndex = index;
+    hiddenSoFar = hiddencount;
+  }
+
+  public int getRegionIndex()
+  {
+    return regionIndex;
+  }
+
+  public int getHiddenSoFar()
+  {
+    return hiddenSoFar;
+  }
+}
diff --git a/src/jalview/datamodel/RangeElementsIterator.java b/src/jalview/datamodel/RangeElementsIterator.java
new file mode 100644 (file)
index 0000000..9ca6b2a
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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.datamodel;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Iterator over each element in a set of ranges i.e. if ranges is {[3,6],
+ * [12,15]} it will iterate over {3,4,5,6,12,13,14,15}. Uses a local copy of the
+ * set of ranges.
+ * 
+ * @author kmourao
+ *
+ */
+public class RangeElementsIterator implements Iterator<Integer>
+{
+  private int last;
+
+  private int current;
+
+  private int next;
+
+  private Iterator<int[]> rangeIterator;
+
+  private int[] nextRange = null;
+
+  RangeElementsIterator(Iterator<int[]> it)
+  {
+    rangeIterator = it;
+    if (rangeIterator.hasNext())
+    {
+      nextRange = rangeIterator.next();
+      next = nextRange[0];
+      last = nextRange[1];
+    }
+  }
+
+  @Override
+  public boolean hasNext()
+  {
+    return rangeIterator.hasNext() || next <= last;
+  }
+
+  @Override
+  public Integer next()
+  {
+    if (!hasNext())
+    {
+      throw new NoSuchElementException();
+    }
+
+    current = next;
+
+    // recalculate next
+    next++;
+
+    // if there are more ranges need to check if next is in a range
+    checkNextRange();
+    return current;
+  }
+
+  /**
+   * Check how next position relates to next range, and update next position if
+   * necessary
+   */
+  private void checkNextRange()
+  {
+    if (nextRange != null && next > nextRange[1])
+    {
+      if (rangeIterator.hasNext())
+      {
+        nextRange = rangeIterator.next();
+        next = nextRange[0];
+        last = nextRange[1];
+      }
+      else
+      {
+        nextRange = null;
+      }
+
+    }
+  }
+
+  @Override
+  public void remove()
+  {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/src/jalview/datamodel/RangeIterator.java b/src/jalview/datamodel/RangeIterator.java
new file mode 100644 (file)
index 0000000..5d45236
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * 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.datamodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An iterator which iterates over a list of ranges. Works with a copy of the
+ * collection of ranges.
+ */
+public class RangeIterator implements Iterator<int[]>
+{
+  // current index in rangeList
+  private int currentPosition = 0;
+
+  // current range in rangeList
+  private int[] currentRange;
+
+  // local copy or reference to rangeList
+  private List<int[]> localRanges;
+
+  /**
+   * Unbounded constructor
+   * 
+   * @param rangeList
+   *          list of ranges to iterate over
+   */
+  RangeIterator(List<int[]> rangeList)
+  {
+    if (!rangeList.isEmpty())
+    {
+      int last = rangeList.get(rangeList.size() - 1)[1];
+      init(0, last, rangeList);
+    }
+    else
+    {
+      init(0, 0, rangeList);
+    }
+  }
+
+  /**
+   * Construct an iterator over rangeList bounded at [lowerBound,upperBound]
+   * 
+   * @param lowerBound
+   *          lower bound to iterate from
+   * @param upperBound
+   *          upper bound to iterate to
+   * @param rangeList
+   *          list of ranges to iterate over
+   */
+  RangeIterator(int lowerBound, int upperBound,
+          List<int[]> rangeList)
+  {
+    init(lowerBound, upperBound, rangeList);
+  }
+
+  /**
+   * Construct an iterator over rangeList bounded at [lowerBound,upperBound]
+   * 
+   * @param lowerBound
+   *          lower bound to iterate from
+   * @param upperBound
+   *          upper bound to iterate to
+   */
+  private void init(int lowerBound, int upperBound,
+          List<int[]> rangeList)
+  {
+    int start = lowerBound;
+    int end = upperBound;
+
+    if (rangeList != null)
+    {
+      localRanges = new ArrayList<>();
+
+      // iterate until a range overlaps with [start,end]
+      int i = 0;
+      while ((i < rangeList.size()) && (rangeList.get(i)[1] < start))
+      {
+        i++;
+      }
+
+      // iterate from start to end, adding each range. Positions are
+      // absolute, and all ranges which *overlap* [start,end] are added.
+      while (i < rangeList.size() && (rangeList.get(i)[0] <= end))
+      {
+        int[] rh = rangeList.get(i);
+        int[] cp = new int[2];
+        System.arraycopy(rh, 0, cp, 0, rh.length);
+        localRanges.add(cp);
+        i++;
+      }
+    }
+  }
+
+  @Override
+  public boolean hasNext()
+  {
+    return (localRanges != null) && (currentPosition < localRanges.size());
+  }
+
+  @Override
+  public int[] next()
+  {
+    currentRange = localRanges.get(currentPosition);
+    currentPosition++;
+    return currentRange;
+  }
+
+  @Override
+  public void remove()
+  {
+    localRanges.remove(--currentPosition);
+  }
+}
index cde50e5..d1e3fcc 100755 (executable)
@@ -267,9 +267,12 @@ public class SearchResults implements SearchResultsI
   {
     int count = 0;
     BitSet mask = new BitSet();
+    int startRes = sqcol.getStartRes();
+    int endRes = sqcol.getEndRes();
+
     for (SequenceI s : sqcol.getSequences())
     {
-      int[] cols = getResults(s, sqcol.getStartRes(), sqcol.getEndRes());
+      int[] cols = getResults(s, startRes, endRes);
       if (cols != null)
       {
         for (int pair = 0; pair < cols.length; pair += 2)
index 15d1378..abf334c 100755 (executable)
@@ -34,6 +34,7 @@ import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Vector;
@@ -408,7 +409,7 @@ public class Sequence extends ASequence implements SequenceI
   {
     if (pdbIds == null)
     {
-      pdbIds = new Vector<PDBEntry>();
+      pdbIds = new Vector<>();
       pdbIds.add(entry);
       return true;
     }
@@ -1071,6 +1072,27 @@ public class Sequence extends ASequence implements SequenceI
     return map;
   }
 
+  /**
+   * Build a bitset corresponding to sequence gaps
+   * 
+   * @return a BitSet where set values correspond to gaps in the sequence
+   */
+  @Override
+  public BitSet gapBitset()
+  {
+    BitSet gaps = new BitSet(sequence.length);
+    int j = 0;
+    while (j < sequence.length)
+    {
+      if (jalview.util.Comparison.isGap(sequence[j]))
+      {
+        gaps.set(j);
+      }
+      j++;
+    }
+    return gaps;
+  }
+
   @Override
   public int[] findPositionMap()
   {
@@ -1094,7 +1116,7 @@ public class Sequence extends ASequence implements SequenceI
   @Override
   public List<int[]> getInsertions()
   {
-    ArrayList<int[]> map = new ArrayList<int[]>();
+    ArrayList<int[]> map = new ArrayList<>();
     int lastj = -1, j = 0;
     int pos = start;
     int seqlen = sequence.length;
@@ -1405,7 +1427,7 @@ public class Sequence extends ASequence implements SequenceI
   {
     if (this.annotation == null)
     {
-      this.annotation = new Vector<AlignmentAnnotation>();
+      this.annotation = new Vector<>();
     }
     if (!this.annotation.contains(annotation))
     {
@@ -1572,7 +1594,7 @@ public class Sequence extends ASequence implements SequenceI
       return null;
     }
 
-    Vector<AlignmentAnnotation> subset = new Vector<AlignmentAnnotation>();
+    Vector<AlignmentAnnotation> subset = new Vector<>();
     Enumeration<AlignmentAnnotation> e = annotation.elements();
     while (e.hasMoreElements())
     {
@@ -1706,7 +1728,7 @@ public class Sequence extends ASequence implements SequenceI
   public List<AlignmentAnnotation> getAlignmentAnnotations(String calcId,
           String label)
   {
-    List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
+    List<AlignmentAnnotation> result = new ArrayList<>();
     if (this.annotation != null)
     {
       for (AlignmentAnnotation ann : annotation)
@@ -1762,7 +1784,7 @@ public class Sequence extends ASequence implements SequenceI
     }
     synchronized (dbrefs)
     {
-      List<DBRefEntry> primaries = new ArrayList<DBRefEntry>();
+      List<DBRefEntry> primaries = new ArrayList<>();
       DBRefEntry[] tmp = new DBRefEntry[1];
       for (DBRefEntry ref : dbrefs)
       {
@@ -1904,4 +1926,73 @@ public class Sequence extends ASequence implements SequenceI
 
     return count;
   }
+
+  @Override
+  public String getSequenceStringFromIterator(Iterator<int[]> it)
+  {
+    StringBuilder newSequence = new StringBuilder();
+    while (it.hasNext())
+    {
+      int[] block = it.next();
+      if (it.hasNext())
+      {
+        newSequence.append(getSequence(block[0], block[1] + 1));
+      }
+      else
+      {
+        newSequence.append(getSequence(block[0], block[1]));
+      }
+    }
+
+    return newSequence.toString();
+  }
+
+  @Override
+  public int firstResidueOutsideIterator(Iterator<int[]> regions)
+  {
+    int start = 0;
+
+    if (!regions.hasNext())
+    {
+      return findIndex(getStart()) - 1;
+    }
+
+    // Simply walk along the sequence whilst watching for region
+    // boundaries
+    int hideStart = getLength();
+    int hideEnd = -1;
+    boolean foundStart = false;
+
+    // step through the non-gapped positions of the sequence
+    for (int i = getStart(); i <= getEnd() && (!foundStart); i++)
+    {
+      // get alignment position of this residue in the sequence
+      int p = findIndex(i) - 1;
+
+      // update region start/end
+      while (hideEnd < p && regions.hasNext())
+      {
+        int[] region = regions.next();
+        hideStart = region[0];
+        hideEnd = region[1];
+      }
+      if (hideEnd < p)
+      {
+        hideStart = getLength();
+      }
+      // update boundary for sequence
+      if (p < hideStart)
+      {
+        start = p;
+        foundStart = true;
+      }
+    }
+
+    if (foundStart)
+    {
+      return start;
+    }
+    // otherwise, sequence was completely hidden
+    return 0;
+  }
 }
index 6b797d7..944f263 100755 (executable)
@@ -1189,9 +1189,10 @@ public class SequenceGroup implements AnnotatedCollectionI
     {
       if (consensus.annotations[i] != null)
       {
-        if (consensus.annotations[i].description.charAt(0) == '[')
+        String desc = consensus.annotations[i].description;
+        if (desc.length() > 1 && desc.charAt(0) == '[')
         {
-          seqs.append(consensus.annotations[i].description.charAt(1));
+          seqs.append(desc.charAt(1));
         }
         else
         {
index 2f3e925..b22e48f 100755 (executable)
@@ -23,6 +23,7 @@ package jalview.datamodel;
 import jalview.datamodel.features.SequenceFeaturesI;
 
 import java.util.BitSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Vector;
 
@@ -223,6 +224,13 @@ public interface SequenceI extends ASequenceI
   public int[] gapMap();
 
   /**
+   * Build a bitset corresponding to sequence gaps
+   * 
+   * @return a BitSet where set values correspond to gaps in the sequence
+   */
+  public BitSet gapBitset();
+
+  /**
    * Returns an int array where indices correspond to each position in sequence
    * char array and the element value gives the result of findPosition for that
    * index in the sequence.
@@ -524,4 +532,25 @@ public interface SequenceI extends ASequenceI
    * @param c2
    */
   public int replace(char c1, char c2);
+
+  /**
+   * Returns the sequence string constructed from the substrings of a sequence
+   * defined by the int[] ranges provided by an iterator. E.g. the iterator
+   * could iterate over all visible regions of the alignment
+   * 
+   * @param it
+   *          the iterator to use
+   * @return a String corresponding to the sequence
+   */
+  public String getSequenceStringFromIterator(Iterator<int[]> it);
+
+  /**
+   * Locate the first position in this sequence which is not contained in an
+   * iterator region. If no such position exists, return 0
+   * 
+   * @param it
+   *          iterator over regions
+   * @return first residue not contained in regions
+   */
+  public int firstResidueOutsideIterator(Iterator<int[]> it);
 }
diff --git a/src/jalview/datamodel/StartRegionIterator.java b/src/jalview/datamodel/StartRegionIterator.java
new file mode 100644 (file)
index 0000000..c21f04a
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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.datamodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An iterator which iterates over visible start positions of hidden column
+ * regions in a range.
+ */
+public class StartRegionIterator implements Iterator<Integer>
+{
+  // start position to iterate from
+  private int start;
+
+  // end position to iterate to
+  private int end;
+
+  // current index in hiddenColumns
+  private int currentPosition = 0;
+
+  // local copy or reference to hiddenColumns
+  private List<Integer> positions = null;
+
+  /**
+   * Construct an iterator over hiddenColums bounded at [lowerBound,upperBound]
+   * 
+   * @param lowerBound
+   *          lower bound to iterate from
+   * @param upperBound
+   *          upper bound to iterate to
+   * @param useCopyCols
+   *          whether to make a local copy of hiddenColumns for iteration (set
+   *          to true if calling from outwith the HiddenColumns class)
+   */
+  StartRegionIterator(int lowerBound, int upperBound,
+          List<int[]> hiddenColumns)
+  {
+    this(null, lowerBound, upperBound, hiddenColumns);
+  }
+
+  /**
+   * Construct an iterator over hiddenColums bounded at [lowerBound,upperBound]
+   * 
+   * @param pos
+   *          a hidden cursor position to start from - may be null
+   * @param lowerBound
+   *          lower bound to iterate from - will be ignored if pos != null
+   * @param upperBound
+   *          upper bound to iterate to
+   * @param hiddenColumns
+   *          the hidden columns collection to use
+   */
+  StartRegionIterator(HiddenCursorPosition pos, int lowerBound,
+          int upperBound, List<int[]> hiddenColumns)
+  {
+    start = lowerBound;
+    end = upperBound;
+
+    if (hiddenColumns != null)
+    {
+      positions = new ArrayList<>(hiddenColumns.size());
+
+      // navigate to start, keeping count of hidden columns
+      int i = 0;
+      int hiddenSoFar = 0;
+      
+      if (pos != null)
+      {
+        // use the cursor position provided
+        i = pos.getRegionIndex();
+        hiddenSoFar = pos.getHiddenSoFar();
+      }
+      else
+      {
+        // navigate to start
+        while ((i < hiddenColumns.size())
+                && (hiddenColumns.get(i)[0] < start + hiddenSoFar))
+        {
+          int[] region = hiddenColumns.get(i);
+          hiddenSoFar += region[1] - region[0] + 1;
+          i++;
+        }
+      }
+
+      // iterate from start to end, adding start positions of each
+      // hidden region. Positions are visible columns count, not absolute
+      while (i < hiddenColumns.size()
+              && (hiddenColumns.get(i)[0] <= end + hiddenSoFar))
+      {
+        int[] region = hiddenColumns.get(i);
+        positions.add(region[0] - hiddenSoFar);
+        hiddenSoFar += region[1] - region[0] + 1;
+        i++;
+      }
+    }
+    else
+    {
+      positions = new ArrayList<>();
+    }
+
+  }
+
+  @Override
+  public boolean hasNext()
+  {
+    return (currentPosition < positions.size());
+  }
+
+  /**
+   * Get next hidden region start position
+   * 
+   * @return the start position in *visible* coordinates
+   */
+  @Override
+  public Integer next()
+  {
+    int result = positions.get(currentPosition);
+    currentPosition++;
+    return result;
+  }
+}
+
index e9437a7..4ca51b5 100644 (file)
@@ -32,17 +32,17 @@ public class VisibleColsCollection implements AlignmentColsCollectionI
 
   HiddenColumns hidden;
 
-  public VisibleColsCollection(int s, int e, AlignmentI al)
+  public VisibleColsCollection(int s, int e, HiddenColumns h)
   {
     start = s;
     end = e;
-    hidden = al.getHiddenColumns();
+    hidden = h;
   }
 
   @Override
   public Iterator<Integer> iterator()
   {
-    return new VisibleColsIterator(start, end, hidden);
+    return hidden.getVisibleColsIterator(start, end);
   }
 
   @Override
diff --git a/src/jalview/datamodel/VisibleColsIterator.java b/src/jalview/datamodel/VisibleColsIterator.java
deleted file mode 100644 (file)
index 9de468d..0000000
+++ /dev/null
@@ -1,130 +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.datamodel;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-/**
- * An iterator which iterates over all visible columns in an alignment
- * 
- * @author kmourao
- *
- */
-public class VisibleColsIterator implements Iterator<Integer>
-{
-  private int last;
-
-  private int current;
-
-  private int next;
-
-  private List<int[]> hidden;
-
-  private int lasthiddenregion;
-
-  public VisibleColsIterator(int firstcol, int lastcol,
-          HiddenColumns hiddenCols)
-  {
-    last = lastcol;
-    current = firstcol;
-    next = firstcol;
-    hidden = hiddenCols.getHiddenColumnsCopy();
-    lasthiddenregion = -1;
-
-    if (hidden != null)
-    {
-      int i = 0;
-      for (i = 0; i < hidden.size(); ++i)
-      {
-        if (current >= hidden.get(i)[0] && current <= hidden.get(i)[1])
-        {
-          // current is hidden, move to right
-          current = hidden.get(i)[1] + 1;
-          next = current;
-        }
-        if (current < hidden.get(i)[0])
-        {
-          break;
-        }
-      }
-      lasthiddenregion = i - 1;
-
-      for (i = hidden.size() - 1; i >= 0; --i)
-      {
-        if (last >= hidden.get(i)[0] && last <= hidden.get(i)[1])
-        {
-          // last is hidden, move to left
-          last = hidden.get(i)[0] - 1;
-        }
-        if (last > hidden.get(i)[1])
-        {
-          break;
-        }
-      }
-    }
-  }
-
-  @Override
-  public boolean hasNext()
-  {
-    return next <= last;
-  }
-
-  @Override
-  public Integer next()
-  {
-    if (next > last)
-    {
-      throw new NoSuchElementException();
-    }
-    current = next;
-    if ((hidden != null) && (lasthiddenregion + 1 < hidden.size()))
-    {
-      // still some more hidden regions
-      if (next + 1 < hidden.get(lasthiddenregion + 1)[0])
-      {
-        // next+1 is still before the next hidden region
-        next++;
-      }
-      else if ((next + 1 >= hidden.get(lasthiddenregion + 1)[0])
-              && (next + 1 <= hidden.get(lasthiddenregion + 1)[1]))
-      {
-        // next + 1 is in the next hidden region
-        next = hidden.get(lasthiddenregion + 1)[1] + 1;
-        lasthiddenregion++;
-      }
-    }
-    else
-    {
-      // finished with hidden regions, just increment normally
-      next++;
-    }
-    return current;
-  }
-
-  @Override
-  public void remove()
-  {
-    throw new UnsupportedOperationException();
-  }
-}
diff --git a/src/jalview/datamodel/VisibleContigsIterator.java b/src/jalview/datamodel/VisibleContigsIterator.java
new file mode 100644 (file)
index 0000000..0185978
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.datamodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An iterator which iterates over visible regions in a range. Provides a
+ * special "endsAtHidden" indicator to allow callers to determine if the final
+ * visible column is adjacent to a hidden region.
+ */
+public class VisibleContigsIterator implements Iterator<int[]>
+{
+  private List<int[]> vcontigs = new ArrayList<>();
+
+  private int currentPosition = 0;
+
+  private boolean endsAtHidden = false;
+
+  VisibleContigsIterator(int start, int end,
+          List<int[]> hiddenColumns)
+  {
+    if (hiddenColumns != null && hiddenColumns.size() > 0)
+    {
+      int vstart = start;
+      int hideStart;
+      int hideEnd;
+
+      for (int[] region : hiddenColumns)
+      {
+        endsAtHidden = false;
+        hideStart = region[0];
+        hideEnd = region[1];
+
+        // navigate to start
+        if (hideEnd < vstart)
+        {
+          continue;
+        }
+        if (hideStart > vstart)
+        {
+          if (end - 1 > hideStart - 1)
+          {
+            int[] contig = new int[] { vstart, hideStart - 1 };
+            vcontigs.add(contig);
+            endsAtHidden = true;
+          }
+          else
+          {
+            int[] contig = new int[] { vstart, end - 1 };
+            vcontigs.add(contig);
+          }
+        }
+        vstart = hideEnd + 1;
+
+        // exit if we're past the end
+        if (vstart >= end)
+        {
+          break;
+        }
+      }
+
+      if (vstart < end)
+      {
+        int[] contig = new int[] { vstart, end - 1 };
+        vcontigs.add(contig);
+        endsAtHidden = false;
+      }
+    }
+    else
+    {
+      int[] contig = new int[] { start, end - 1 };
+      vcontigs.add(contig);
+    }
+  }
+
+  @Override
+  public boolean hasNext()
+  {
+    return (currentPosition < vcontigs.size());
+  }
+
+  @Override
+  public int[] next()
+  {
+    int[] result = vcontigs.get(currentPosition);
+    currentPosition++;
+    return result;
+  }
+
+  public boolean endsAtHidden()
+  {
+    return endsAtHidden;
+  }
+}
+
index bbd1f26..9fc6a53 100644 (file)
@@ -20,6 +20,9 @@
  */
 package jalview.ext.ensembl;
 
+import jalview.bin.Cache;
+import jalview.datamodel.DBRefSource;
+
 /**
  * A class to behave much like EnsemblGene but referencing the ensemblgenomes
  * domain and data
@@ -35,13 +38,15 @@ public class EnsemblGenomes extends EnsemblGene
    */
   public EnsemblGenomes()
   {
-    super(ENSEMBL_GENOMES_REST);
+    super();
+    setDomain(Cache.getDefault(ENSEMBL_GENOMES_BASEURL,
+            DEFAULT_ENSEMBL_GENOMES_BASEURL));
   }
 
   @Override
   public String getDbName()
   {
-    return "EnsemblGenomes";
+    return DBRefSource.ENSEMBLGENOMES;
   }
 
   @Override
@@ -56,7 +61,7 @@ public class EnsemblGenomes extends EnsemblGene
   @Override
   public String getDbSource()
   {
-    return "EnsemblGenomes";
+    return DBRefSource.ENSEMBLGENOMES;
   }
 
 }
index 92763a1..877331d 100644 (file)
@@ -135,12 +135,11 @@ public class EnsemblLookup extends EnsemblRestClient
   }
 
   /**
-   * Calls the Ensembl lookup REST endpoint and retrieves the 'Parent' for the
-   * given identifier, or null if not found
+   * Returns the gene id related to the given identifier (which may be for a
+   * gene, transcript or protein)
    * 
    * @param identifier
    * @param objectType
-   *          (optional)
    * @return
    */
   public String getGeneId(String identifier, String objectType)
index b1bc8e5..b19f557 100644 (file)
@@ -72,10 +72,7 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
 
   private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log";
 
-  private static Map<String, EnsemblInfo> domainData;
-
-  // @see https://github.com/Ensembl/ensembl-rest/wiki/Output-formats
-  private static final String PING_URL = "http://rest.ensembl.org/info/ping.json";
+  private static Map<String, EnsemblInfo> domainData = new HashMap<>();
 
   private final static long AVAILABILITY_RETEST_INTERVAL = 10000L; // 10 seconds
 
@@ -85,11 +82,11 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
 
   static
   {
-    domainData = new HashMap<>();
-    domainData.put(ENSEMBL_REST,
-            new EnsemblInfo(ENSEMBL_REST, LATEST_ENSEMBL_REST_VERSION));
-    domainData.put(ENSEMBL_GENOMES_REST, new EnsemblInfo(
-            ENSEMBL_GENOMES_REST, LATEST_ENSEMBLGENOMES_REST_VERSION));
+    domainData.put(DEFAULT_ENSEMBL_BASEURL,
+            new EnsemblInfo(DEFAULT_ENSEMBL_BASEURL, LATEST_ENSEMBL_REST_VERSION));
+    domainData.put(DEFAULT_ENSEMBL_GENOMES_BASEURL,
+            new EnsemblInfo(
+            DEFAULT_ENSEMBL_GENOMES_BASEURL, LATEST_ENSEMBLGENOMES_REST_VERSION));
   }
 
   protected volatile boolean inProgress = false;
@@ -99,7 +96,21 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
    */
   public EnsemblRestClient()
   {
-    this(ENSEMBL_REST);
+    super();
+
+    /*
+     * initialise domain info lazily
+     */
+    if (!domainData.containsKey(ensemblDomain))
+    {
+      domainData.put(ensemblDomain,
+              new EnsemblInfo(ensemblDomain, LATEST_ENSEMBL_REST_VERSION));
+    }
+    if (!domainData.containsKey(ensemblGenomesDomain))
+    {
+      domainData.put(ensemblGenomesDomain, new EnsemblInfo(
+              ensemblGenomesDomain, LATEST_ENSEMBLGENOMES_REST_VERSION));
+    }
   }
 
   /**
@@ -169,11 +180,12 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
   boolean checkEnsembl()
   {
     BufferedReader br = null;
+    String pingUrl = getDomain() + "/info/ping" + CONTENT_TYPE_JSON;
     try
     {
       // note this format works for both ensembl and ensemblgenomes
       // info/ping.json works for ensembl only (March 2016)
-      URL ping = new URL(getDomain() + "/info/ping" + CONTENT_TYPE_JSON);
+      URL ping = new URL(pingUrl);
 
       /*
        * expect {"ping":1} if ok
@@ -192,7 +204,7 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
     } catch (Throwable t)
     {
       System.err.println(
-              "Error connecting to " + PING_URL + ": " + t.getMessage());
+              "Error connecting to " + pingUrl + ": " + t.getMessage());
     } finally
     {
       if (br != null)
index c4abb20..0aaaf93 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.ext.ensembl;
 
+import jalview.bin.Cache;
 import jalview.datamodel.DBRefSource;
 import jalview.ws.seqfetcher.DbSourceProxyImpl;
 
@@ -32,6 +33,16 @@ import com.stevesoft.pat.Regex;
  */
 abstract class EnsemblSequenceFetcher extends DbSourceProxyImpl
 {
+  // domain properties lookup keys:
+  protected static final String ENSEMBL_BASEURL = "ENSEMBL_BASEURL";
+
+  protected static final String ENSEMBL_GENOMES_BASEURL = "ENSEMBL_GENOMES_BASEURL";
+
+  // domain properties default values:
+  protected static final String DEFAULT_ENSEMBL_BASEURL = "https://rest.ensembl.org";
+
+  protected static final String DEFAULT_ENSEMBL_GENOMES_BASEURL = "https://rest.ensemblgenomes.org";
+
   /*
    * accepts ENSG/T/E/P with 11 digits
    * or ENSMUSP or similar for other species
@@ -41,9 +52,9 @@ abstract class EnsemblSequenceFetcher extends DbSourceProxyImpl
           "(ENS([A-Z]{3}|)[GTEP]{1}[0-9]{11}$)" + "|"
                   + "(CCDS[0-9.]{3,}$)");
 
-  protected static final String ENSEMBL_GENOMES_REST = "http://rest.ensemblgenomes.org";
+  protected final String ensemblGenomesDomain;
 
-  protected static final String ENSEMBL_REST = "http://rest.ensembl.org";
+  protected final String ensemblDomain;
 
   protected static final String OBJECT_TYPE_TRANSLATION = "Translation";
 
@@ -68,13 +79,29 @@ abstract class EnsemblSequenceFetcher extends DbSourceProxyImpl
     constrained, regulatory
   }
 
-  private String domain = ENSEMBL_REST;
+  private String domain;
+
+  /**
+   * Constructor
+   */
+  public EnsemblSequenceFetcher()
+  {
+    /*
+     * the default domain names may be overridden in .jalview_properties;
+     * this allows an easy change from http to https in future if needed
+     */
+    ensemblDomain = Cache.getDefault(ENSEMBL_BASEURL,
+            DEFAULT_ENSEMBL_BASEURL);
+    ensemblGenomesDomain = Cache.getDefault(ENSEMBL_GENOMES_BASEURL,
+            DEFAULT_ENSEMBL_GENOMES_BASEURL);
+    domain = ensemblDomain;
+  }
 
   @Override
   public String getDbSource()
   {
     // NB ensure Uniprot xrefs are canonicalised from "Ensembl" to "ENSEMBL"
-    if (ENSEMBL_GENOMES_REST.equals(getDomain()))
+    if (ensemblGenomesDomain.equals(getDomain()))
     {
       return DBRefSource.ENSEMBLGENOMES;
     }
index 41bc116..8832278 100644 (file)
@@ -478,6 +478,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     lastCommand = command;
   }
 
+  Thread colourby = null;
   /**
    * Sends a set of colour commands to the structure viewer
    * 
@@ -485,15 +486,28 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
    */
   @Override
   protected void colourBySequence(
-          StructureMappingcommandSet[] colourBySequenceCommands)
+          final StructureMappingcommandSet[] colourBySequenceCommands)
   {
-    for (StructureMappingcommandSet cpdbbyseq : colourBySequenceCommands)
+    if (colourby != null)
     {
-      for (String cbyseq : cpdbbyseq.commands)
+      colourby.interrupt();
+      colourby = null;
+    }
+    colourby = new Thread(new Runnable()
+    {
+      @Override
+      public void run()
       {
-        executeWhenReady(cbyseq);
+        for (StructureMappingcommandSet cpdbbyseq : colourBySequenceCommands)
+        {
+          for (String cbyseq : cpdbbyseq.commands)
+          {
+            executeWhenReady(cbyseq);
+          }
+        }
       }
-    }
+    });
+    colourby.start();
   }
 
   /**
@@ -862,19 +876,30 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       try
       {
         // recover PDB filename for the model hovered over.
-        int _mp = _modelFileNameMap.length - 1,
-                mnumber = new Integer(mdlId).intValue() - 1;
-        while (mnumber < _modelFileNameMap[_mp])
+        int mnumber = new Integer(mdlId).intValue() - 1;
+        if (_modelFileNameMap != null)
         {
-          _mp--;
+          int _mp = _modelFileNameMap.length - 1;
+
+          while (mnumber < _modelFileNameMap[_mp])
+          {
+            _mp--;
+          }
+          pdbfilename = modelFileNames[_mp];
         }
-        pdbfilename = modelFileNames[_mp];
-        if (pdbfilename == null)
+        else
         {
-          pdbfilename = new File(viewer.ms.getModelFileName(mnumber))
-                  .getAbsolutePath();
-        }
+          if (mnumber >= 0 && mnumber < modelFileNames.length)
+          {
+            pdbfilename = modelFileNames[mnumber];
+          }
 
+          if (pdbfilename == null)
+          {
+            pdbfilename = new File(viewer.ms.getModelFileName(mnumber))
+                    .getAbsolutePath();
+          }
+        }
       } catch (Exception e)
       {
       }
index 6bf7010..8fb0de6 100644 (file)
@@ -77,7 +77,6 @@ public class JmolCommands
         continue;
       }
 
-      int lastPos = -1;
       for (int s = 0; s < sequence[pdbfnum].length; s++)
       {
         for (int sp, m = 0; m < mapping.length; m++)
@@ -85,6 +84,7 @@ public class JmolCommands
           if (mapping[m].getSequence() == sequence[pdbfnum][s]
                   && (sp = al.findIndex(sequence[pdbfnum][s])) > -1)
           {
+            int lastPos = StructureMapping.UNASSIGNED_VALUE;
             SequenceI asp = al.getSequenceAt(sp);
             for (int r = 0; r < asp.getLength(); r++)
             {
@@ -95,10 +95,22 @@ public class JmolCommands
               }
               int pos = mapping[m].getPDBResNum(asp.findPosition(r));
 
-              if (pos < 1 || pos == lastPos)
+              if (pos == lastPos)
               {
                 continue;
               }
+              if (pos == StructureMapping.UNASSIGNED_VALUE)
+              {
+                // terminate current colour op
+                if (command.length() > 0
+                        && command.charAt(command.length() - 1) != ';')
+                {
+                  command.append(";");
+                }
+                // reset lastPos
+                lastPos = StructureMapping.UNASSIGNED_VALUE;
+                continue;
+              }
 
               lastPos = pos;
 
@@ -128,7 +140,12 @@ public class JmolCommands
               // TODO: deal with case when buffer is too large for Jmol to parse
               // - execute command and flush
 
-              command.append(";");
+              if (command.length() > 0
+                      && command.charAt(command.length() - 1) != ';')
+              {
+                command.append(";");
+              }
+
               if (command.length() > 51200)
               {
                 // add another chunk
index dc3d0ee..2a510a2 100644 (file)
@@ -28,7 +28,6 @@ import jalview.io.DataSourceType;
 import jalview.io.FileParse;
 import jalview.io.StructureFile;
 import jalview.schemes.ResidueProperties;
-import jalview.structure.StructureImportSettings;
 import jalview.util.Format;
 import jalview.util.MessageManager;
 
@@ -60,6 +59,12 @@ public class JmolParser extends StructureFile implements JmolStatusListener
 {
   Viewer viewer = null;
 
+  public JmolParser(boolean immediate, String inFile,
+          DataSourceType sourceType) throws IOException
+  {
+    super(immediate, inFile, sourceType);
+  }
+
   public JmolParser(String inFile, DataSourceType sourceType)
           throws IOException
   {
@@ -183,7 +188,11 @@ public class JmolParser extends StructureFile implements JmolStatusListener
         }
         lastID = tmpatom.resNumIns.trim();
       }
-      xferSettings();
+      if (isParseImmediately())
+      {
+        // configure parsing settings from the static singleton
+        xferSettings();
+      }
 
       makeResidueList();
       makeCaBondList();
@@ -200,7 +209,8 @@ public class JmolParser extends StructureFile implements JmolStatusListener
           prot.add(chainseq);
         }
 
-        if (StructureImportSettings.isProcessSecondaryStructure())
+        // look at local setting for adding secondary tructure
+        if (predictSecondaryStructure)
         {
           createAnnotation(chainseq, chain, ms.at);
         }
index 076e212..f94d455 100644 (file)
@@ -284,7 +284,8 @@ public abstract class FTSRestClient implements FTSRestClientI
               public boolean equals(Object otherObject)
               {
                 FTSDataColumnI that = (FTSDataColumnI) otherObject;
-                return this.getCode().equals(that.getCode())
+                return otherObject == null ? false
+                        : this.getCode().equals(that.getCode())
                         && this.getName().equals(that.getName())
                         && this.getGroup().equals(that.getGroup());
               }
index 250fba0..262ed86 100644 (file)
@@ -21,6 +21,7 @@
 
 package jalview.fts.service.uniprot;
 
+import jalview.bin.Cache;
 import jalview.fts.api.FTSData;
 import jalview.fts.api.FTSDataColumnI;
 import jalview.fts.api.FTSRestClientI;
@@ -44,9 +45,18 @@ import com.sun.jersey.api.client.config.DefaultClientConfig;
 
 public class UniProtFTSRestClient extends FTSRestClient
 {
+  private static final String DEFAULT_UNIPROT_DOMAIN = "https://www.uniprot.org";
+
   private static FTSRestClientI instance = null;
 
-  public static final String UNIPROT_SEARCH_ENDPOINT = "http://www.uniprot.org/uniprot/?";
+  public final String uniprotSearchEndpoint;
+
+  public UniProtFTSRestClient()
+  {
+    super();
+    uniprotSearchEndpoint = Cache.getDefault("UNIPROT_DOMAIN",
+            DEFAULT_UNIPROT_DOMAIN) + "/uniprot/?";
+  }
 
   @Override
   public FTSRestResponse executeRequest(FTSRestRequest uniportRestRequest)
@@ -81,7 +91,7 @@ public class UniProtFTSRestClient extends FTSRestClient
       }
 
       WebResource webResource = null;
-      webResource = client.resource(UNIPROT_SEARCH_ENDPOINT)
+      webResource = client.resource(uniprotSearchEndpoint)
               .queryParam("format", "tab")
               .queryParam("columns", wantedFields)
               .queryParam("limit", String.valueOf(responseSize))
@@ -158,7 +168,7 @@ public class UniProtFTSRestClient extends FTSRestClient
     String[] foundDataRow = uniProtTabDelimittedResponseString.split("\n");
     if (foundDataRow != null && foundDataRow.length > 0)
     {
-      result = new ArrayList<FTSData>();
+      result = new ArrayList<>();
       boolean firstRow = true;
       for (String dataRow : foundDataRow)
       {
index 298688b..9821e9e 100644 (file)
@@ -1862,23 +1862,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    ArrayList<int[]> hiddenColumns = null;
+    HiddenColumns hiddenColumns = null;
     if (viewport.hasHiddenColumns())
     {
-      hiddenColumns = new ArrayList<>();
       int hiddenOffset = viewport.getSelectionGroup().getStartRes();
       int hiddenCutoff = viewport.getSelectionGroup().getEndRes();
-      ArrayList<int[]> hiddenRegions = viewport.getAlignment()
-              .getHiddenColumns().getHiddenColumnsCopy();
-      for (int[] region : hiddenRegions)
-      {
-        if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff)
-        {
-          hiddenColumns
-                  .add(new int[]
-                  { region[0] - hiddenOffset, region[1] - hiddenOffset });
-        }
-      }
+
+      // create new HiddenColumns object with copy of hidden regions
+      // between startRes and endRes, offset by startRes
+      hiddenColumns = new HiddenColumns(
+              viewport.getAlignment().getHiddenColumns(), hiddenOffset,
+              hiddenCutoff, hiddenOffset);
     }
 
     Desktop.jalviewClipboard = new Object[] { seqs,
@@ -2207,11 +2201,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         if (Desktop.jalviewClipboard != null
                 && Desktop.jalviewClipboard[2] != null)
         {
-          List<int[]> hc = (List<int[]>) Desktop.jalviewClipboard[2];
-          for (int[] region : hc)
-          {
-            af.viewport.hideColumns(region[0], region[1]);
-          }
+          HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
+          af.viewport.setHiddenColumns(hc);
         }
 
         // >>>This is a fix for the moment, until a better solution is
@@ -2266,11 +2257,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       if (Desktop.jalviewClipboard != null
               && Desktop.jalviewClipboard[2] != null)
       {
-        List<int[]> hc = (List<int[]>) Desktop.jalviewClipboard[2];
-        for (int region[] : hc)
-        {
-          af.viewport.hideColumns(region[0], region[1]);
-        }
+        HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
+        af.viewport.setHiddenColumns(hc);
       }
 
       // >>>This is a fix for the moment, until a better solution is
@@ -4471,17 +4459,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             int assocfiles = 0;
             if (filesmatched.size() > 0)
             {
-              if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
-                      || JvOptionPane.showConfirmDialog(thisaf,
-                              MessageManager.formatMessage(
-                                      "label.automatically_associate_structure_files_with_sequences_same_name",
-                                      new Object[]
-                                      { Integer.valueOf(filesmatched.size())
-                                              .toString() }),
-                              MessageManager.getString(
-                                      "label.automatically_associate_structure_files_by_name"),
-                              JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION)
-
+              boolean autoAssociate = Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false);
+              if (!autoAssociate)
+              {
+                String msg = MessageManager.formatMessage(
+                        "label.automatically_associate_structure_files_with_sequences_same_name",
+                        new Object[]
+                        { Integer.valueOf(filesmatched.size())
+                                .toString() });
+                String ttl = MessageManager.getString(
+                        "label.automatically_associate_structure_files_by_name");
+                int choice = JvOptionPane.showConfirmDialog(thisaf, msg,
+                        ttl, JvOptionPane.YES_NO_OPTION);
+                autoAssociate = choice == JvOptionPane.YES_OPTION;
+              }
+              if (autoAssociate)
               {
                 for (Object[] fm : filesmatched)
                 {
@@ -4507,6 +4499,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                   alignPanel.paintAlignment(true, false);
                 }
               }
+              else
+              {
+                /*
+                 * add declined structures as sequences
+                 */
+                for (Object[] o : filesmatched)
+                {
+                  filesnotmatched.add((String) o[0]);
+                }
+              }
             }
             if (filesnotmatched.size() > 0)
             {
@@ -4639,11 +4641,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             new JnetAnnotationMaker();
             JnetAnnotationMaker.add_annotation(predictions,
                     viewport.getAlignment(), 0, false);
-            SequenceI repseq = viewport.getAlignment().getSequenceAt(0);
-            viewport.getAlignment().setSeqrep(repseq);
-            HiddenColumns cs = new HiddenColumns();
-            cs.hideInsertionsFor(repseq);
-            viewport.getAlignment().setHiddenColumns(cs);
+            viewport.getAlignment().setupJPredAlignment();
             isAnnotation = true;
           }
           // else if (IdentifyFile.FeaturesFile.equals(format))
@@ -4865,14 +4863,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             MessageManager.getString("option.trim_retrieved_seqs"));
     trimrs.setToolTipText(
             MessageManager.getString("label.trim_retrieved_sequences"));
-    trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true));
+    trimrs.setSelected(
+            Cache.getDefault(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES, true));
     trimrs.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
         trimrs.setSelected(trimrs.isSelected());
-        Cache.setProperty("TRIM_FETCHED_DATASET_SEQS",
+        Cache.setProperty(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES,
                 Boolean.valueOf(trimrs.isSelected()).toString());
       };
     });
index 4d09084..7e77bec 100644 (file)
@@ -22,7 +22,6 @@ package jalview.gui;
 
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
-import jalview.analysis.TreeModel;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureColourI;
@@ -36,7 +35,6 @@ import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
@@ -58,10 +56,9 @@ import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Rectangle;
-import java.util.ArrayList;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
-import java.util.Vector;
 
 import javax.swing.JInternalFrame;
 
@@ -453,10 +450,10 @@ public class AlignViewport extends AlignmentViewport
    *          area
    * @return
    */
-  public int[] getViewAsVisibleContigs(boolean selectedRegionOnly)
+  public Iterator<int[]> getViewAsVisibleContigs(boolean selectedRegionOnly)
   {
-    int[] viscontigs = null;
-    int start = 0, end = 0;
+    int start = 0;
+    int end = 0;
     if (selectedRegionOnly && selectionGroup != null)
     {
       start = selectionGroup.getStartRes();
@@ -466,8 +463,8 @@ public class AlignViewport extends AlignmentViewport
     {
       end = alignment.getWidth();
     }
-    viscontigs = alignment.getHiddenColumns().getVisibleContigs(start, end);
-    return viscontigs;
+    return (alignment.getHiddenColumns().getVisContigsIterator(start, end,
+            false));
   }
 
   /**
@@ -604,7 +601,7 @@ public class AlignViewport extends AlignmentViewport
     return validCharWidth;
   }
 
-  private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<String, AutoCalcSetting>();
+  private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<>();
 
   public AutoCalcSetting getCalcIdSettingsFor(String calcId)
   {
index 3a1dbe8..f5634d2 100644 (file)
@@ -419,8 +419,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (av.hasHiddenColumns())
     {
       HiddenColumns hidden = av.getAlignment().getHiddenColumns();
-      start = hidden.findColumnPosition(start);
-      end = hidden.findColumnPosition(end);
+      start = hidden.absoluteToVisibleColumn(start);
+      end = hidden.absoluteToVisibleColumn(end);
       if (start == end)
       {
         if (!hidden.isVisible(r[0]))
@@ -678,7 +678,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       {
         // reset the width to exclude hidden columns
         width = av.getAlignment().getHiddenColumns()
-                .findColumnPosition(width);
+                .absoluteToVisibleColumn(width);
       }
 
       hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
@@ -874,15 +874,17 @@ public class AlignmentPanel extends GAlignmentPanel implements
   @Override
   public void paintComponent(Graphics g)
   {
-    invalidate();
+    invalidate(); // needed so that the id width adjuster works correctly
 
     Dimension d = getIdPanel().getIdCanvas().getPreferredSize();
     idPanelHolder.setPreferredSize(d);
     hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12));
-    validate();
+
+    validate(); // needed so that the id width adjuster works correctly
 
     /*
-     * set scroll bar positions
+     * set scroll bar positions - tried to remove but necessary for split panel to resize correctly
+     * though I still think this call should be elsewhere.
      */
     ViewportRanges ranges = av.getRanges();
     setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
@@ -1174,7 +1176,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
+              .absoluteToVisibleColumn(maxwidth) - 1;
     }
 
     int resWidth = getSeqPanel().seqCanvas
@@ -1366,7 +1368,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth);
+              .absoluteToVisibleColumn(maxwidth);
     }
 
     int height = ((av.getAlignment().getHeight() + 1) * av.getCharHeight())
@@ -1584,7 +1586,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
+              .absoluteToVisibleColumn(maxwidth) - 1;
     }
 
     int height = ((maxwidth / chunkWidth) + 1) * cHeight;
@@ -1813,35 +1815,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   protected void scrollToCentre(SearchResultsI sr, int verticalOffset)
   {
-    /*
-     * To avoid jumpy vertical scrolling (if some sequences are gapped or not
-     * mapped), we can make the scroll-to location a sequence above the one
-     * actually mapped.
-     */
-    SequenceI mappedTo = sr.getResults().get(0).getSequence();
-    List<SequenceI> seqs = av.getAlignment().getSequences();
-
-    /*
-     * This is like AlignmentI.findIndex(seq) but here we are matching the
-     * dataset sequence not the aligned sequence
-     */
-    boolean matched = false;
-    for (SequenceI seq : seqs)
-    {
-      if (mappedTo == seq.getDatasetSequence())
-      {
-        matched = true;
-        break;
-      }
-    }
-    if (!matched)
-    {
-      return; // failsafe, shouldn't happen
-    }
-
-    /*
-     * Scroll to position but centring the target residue.
-     */
     scrollToPosition(sr, verticalOffset, true, true);
   }
 
index 153f70c..384635b 100644 (file)
@@ -459,4 +459,11 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     }
   }
 
+  @Override
+  protected void sliderDragReleased()
+  {
+    super.sliderDragReleased();
+    ap.paintAlignment(true, true);
+  }
+
 }
index b94a615..b58269d 100755 (executable)
  */
 package jalview.gui;
 
+import jalview.analysis.AlignSeq;
 import jalview.analysis.AlignmentUtils;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.io.FileFormat;
 import jalview.io.FormatAdapter;
+import jalview.util.Comparison;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.Color;
+import java.awt.Cursor;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.MediaTracker;
 import java.awt.RenderingHints;
 import java.awt.Toolkit;
 import java.awt.datatransfer.StringSelection;
@@ -52,6 +55,7 @@ import java.awt.image.BufferedImage;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.regex.Pattern;
 
 import javax.swing.JCheckBoxMenuItem;
@@ -62,63 +66,74 @@ import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
 
 /**
- * DOCUMENT ME!
- * 
- * @author $author$
- * @version $Revision$
+ * The panel that holds the labels for alignment annotations, providing
+ * tooltips, context menus, drag to reorder rows, and drag to adjust panel
+ * height
  */
 public class AnnotationLabels extends JPanel
         implements MouseListener, MouseMotionListener, ActionListener
 {
+  /**
+   * width in pixels within which height adjuster arrows are shown and active
+   */
+  private static final int HEIGHT_ADJUSTER_WIDTH = 50;
+
+  /**
+   * height in pixels for allowing height adjuster to be active
+   */
+  private static int HEIGHT_ADJUSTER_HEIGHT = 10;
+
   private static final Pattern LEFT_ANGLE_BRACKET_PATTERN = Pattern
           .compile("<");
 
-  String TOGGLE_LABELSCALE = MessageManager
+  private static final Font font = new Font("Arial", Font.PLAIN, 11);
+
+  private static final String TOGGLE_LABELSCALE = MessageManager
           .getString("label.scale_label_to_column");
 
-  String ADDNEW = MessageManager.getString("label.add_new_row");
+  private static final String ADDNEW = MessageManager
+          .getString("label.add_new_row");
 
-  String EDITNAME = MessageManager
+  private static final String EDITNAME = MessageManager
           .getString("label.edit_label_description");
 
-  String HIDE = MessageManager.getString("label.hide_row");
+  private static final String HIDE = MessageManager
+          .getString("label.hide_row");
 
-  String DELETE = MessageManager.getString("label.delete_row");
+  private static final String DELETE = MessageManager
+          .getString("label.delete_row");
 
-  String SHOWALL = MessageManager.getString("label.show_all_hidden_rows");
+  private static final String SHOWALL = MessageManager
+          .getString("label.show_all_hidden_rows");
 
-  String OUTPUT_TEXT = MessageManager.getString("label.export_annotation");
+  private static final String OUTPUT_TEXT = MessageManager
+          .getString("label.export_annotation");
 
-  String COPYCONS_SEQ = MessageManager
+  private static final String COPYCONS_SEQ = MessageManager
           .getString("label.copy_consensus_sequence");
 
-  boolean resizePanel = false;
-
-  Image image;
+  private final boolean debugRedraw = false;
 
-  AlignmentPanel ap;
+  private AlignmentPanel ap;
 
   AlignViewport av;
 
-  boolean resizing = false;
-
-  MouseEvent dragEvent;
+  private MouseEvent dragEvent;
 
-  int oldY;
+  private int oldY;
 
-  int selectedRow;
+  private int selectedRow;
 
   private int scrollOffset = 0;
 
-  Font font = new Font("Arial", Font.PLAIN, 11);
-
   private boolean hasHiddenRows;
 
+  private boolean resizePanel = false;
+
   /**
-   * Creates a new AnnotationLabels object.
+   * Creates a new AnnotationLabels object
    * 
    * @param ap
-   *          DOCUMENT ME!
    */
   public AnnotationLabels(AlignmentPanel ap)
   {
@@ -126,30 +141,6 @@ public class AnnotationLabels extends JPanel
     av = ap.av;
     ToolTipManager.sharedInstance().registerComponent(this);
 
-    java.net.URL url = getClass().getResource("/images/idwidth.gif");
-    Image temp = null;
-
-    if (url != null)
-    {
-      temp = java.awt.Toolkit.getDefaultToolkit().createImage(url);
-    }
-
-    try
-    {
-      MediaTracker mt = new MediaTracker(this);
-      mt.addImage(temp, 0);
-      mt.waitForID(0);
-    } catch (Exception ex)
-    {
-    }
-
-    BufferedImage bi = new BufferedImage(temp.getHeight(this),
-            temp.getWidth(this), BufferedImage.TYPE_INT_RGB);
-    Graphics2D g = (Graphics2D) bi.getGraphics();
-    g.rotate(Math.toRadians(90));
-    g.drawImage(temp, 0, -bi.getWidth(this), this);
-    image = bi;
-
     addMouseListener(this);
     addMouseMotionListener(this);
     addMouseWheelListener(ap.getAnnotationPanel());
@@ -607,10 +598,9 @@ public class AnnotationLabels extends JPanel
   }
 
   /**
-   * DOCUMENT ME!
+   * Reorders annotation rows after a drag of a label
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseReleased(MouseEvent evt)
@@ -625,6 +615,9 @@ public class AnnotationLabels extends JPanel
     getSelectedRow(evt.getY() - getScrollOffset());
     int end = selectedRow;
 
+    /*
+     * if dragging to resize instead, start == end
+     */
     if (start != end)
     {
       // Swap these annotations
@@ -648,31 +641,13 @@ public class AnnotationLabels extends JPanel
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void mouseEntered(MouseEvent evt)
-  {
-    if (evt.getY() < 10)
-    {
-      resizePanel = true;
-      repaint();
-    }
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
+   * Removes the height adjuster image on leaving the panel, unless currently
+   * dragging it
    */
   @Override
   public void mouseExited(MouseEvent evt)
   {
-    if (dragEvent == null)
+    if (resizePanel && dragEvent == null)
     {
       resizePanel = false;
       repaint();
@@ -680,10 +655,11 @@ public class AnnotationLabels extends JPanel
   }
 
   /**
-   * DOCUMENT ME!
+   * A mouse drag may be either an adjustment of the panel height (if flag
+   * resizePanel is set on), or a reordering of the annotation rows. The former
+   * is dealt with by this method, the latter in mouseReleased.
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseDragged(MouseEvent evt)
@@ -717,15 +693,14 @@ public class AnnotationLabels extends JPanel
   }
 
   /**
-   * DOCUMENT ME!
+   * Updates the tooltip as the mouse moves over the labels
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseMoved(MouseEvent evt)
   {
-    resizePanel = evt.getY() < 10;
+    showOrHideAdjuster(evt);
 
     getSelectedRow(evt.getY() - getScrollOffset());
 
@@ -801,6 +776,26 @@ public class AnnotationLabels extends JPanel
     }
   }
 
+  /**
+   * Shows the height adjuster image if the mouse moves into the top left
+   * region, or hides it if the mouse leaves the regio
+   * 
+   * @param evt
+   */
+  protected void showOrHideAdjuster(MouseEvent evt)
+  {
+    boolean was = resizePanel;
+    resizePanel = evt.getY() < HEIGHT_ADJUSTER_HEIGHT && evt.getX() < HEIGHT_ADJUSTER_WIDTH;
+
+    if (resizePanel != was)
+    {
+      setCursor(Cursor.getPredefinedCursor(
+              resizePanel ? Cursor.S_RESIZE_CURSOR
+                      : Cursor.DEFAULT_CURSOR));
+      repaint();
+    }
+  }
+
   @Override
   public void mouseClicked(MouseEvent evt)
   {
@@ -820,11 +815,9 @@ public class AnnotationLabels extends JPanel
             // process modifiers
             SequenceGroup sg = ap.av.getSelectionGroup();
             if (sg == null || sg == aa[selectedRow].groupRef
-                    || !(jalview.util.Platform.isControlDown(evt)
-                            || evt.isShiftDown()))
+                    || !(Platform.isControlDown(evt) || evt.isShiftDown()))
             {
-              if (jalview.util.Platform.isControlDown(evt)
-                      || evt.isShiftDown())
+              if (Platform.isControlDown(evt) || evt.isShiftDown())
               {
                 // clone a new selection group from the associated group
                 ap.av.setSelectionGroup(
@@ -883,8 +876,7 @@ public class AnnotationLabels extends JPanel
               // we make a copy rather than edit the current selection if no
               // modifiers pressed
               // see Enhancement JAL-1557
-              if (!(jalview.util.Platform.isControlDown(evt)
-                      || evt.isShiftDown()))
+              if (!(Platform.isControlDown(evt) || evt.isShiftDown()))
               {
                 sg = new SequenceGroup(sg);
                 sg.clear();
@@ -892,7 +884,7 @@ public class AnnotationLabels extends JPanel
               }
               else
               {
-                if (jalview.util.Platform.isControlDown(evt))
+                if (Platform.isControlDown(evt))
                 {
                   sg.addOrRemove(aa[selectedRow].sequenceRef, true);
                 }
@@ -937,16 +929,17 @@ public class AnnotationLabels extends JPanel
     if (dseqs[0] == null)
     {
       dseqs[0] = new Sequence(sq);
-      dseqs[0].setSequence(jalview.analysis.AlignSeq.extractGaps(
-              jalview.util.Comparison.GapChars, sq.getSequenceAsString()));
+      dseqs[0].setSequence(AlignSeq.extractGaps(Comparison.GapChars,
+              sq.getSequenceAsString()));
 
       sq.setDatasetSequence(dseqs[0]);
     }
     Alignment ds = new Alignment(dseqs);
     if (av.hasHiddenColumns())
     {
-      omitHidden = av.getAlignment().getHiddenColumns()
-              .getVisibleSequenceStrings(0, sq.getLength(), seqs);
+      Iterator<int[]> it = av.getAlignment().getHiddenColumns()
+              .getVisContigsIterator(0, sq.getLength(), false);
+      omitHidden = new String[] { sq.getSequenceStringFromIterator(it) };
     }
 
     int[] alignmentStartEnd = new int[] { 0, ds.getWidth() - 1 };
@@ -962,12 +955,12 @@ public class AnnotationLabels extends JPanel
     Toolkit.getDefaultToolkit().getSystemClipboard()
             .setContents(new StringSelection(output), Desktop.instance);
 
-    ArrayList<int[]> hiddenColumns = null;
+    HiddenColumns hiddenColumns = null;
 
     if (av.hasHiddenColumns())
     {
-      hiddenColumns = av.getAlignment().getHiddenColumns()
-              .getHiddenColumnsCopy();
+      hiddenColumns = new HiddenColumns(
+              av.getAlignment().getHiddenColumns());
     }
 
     Desktop.jalviewClipboard = new Object[] { seqs, ds, // what is the dataset
@@ -1020,8 +1013,6 @@ public class AnnotationLabels extends JPanel
     drawComponent(g, false, width);
   }
 
-  private final boolean debugRedraw = false;
-
   /**
    * Draw the full set of annotation Labels for the alignment at the given
    * cursor
@@ -1204,11 +1195,7 @@ public class AnnotationLabels extends JPanel
       }
     }
 
-    if (resizePanel)
-    {
-      g.drawImage(image, 2, 0 - getScrollOffset(), this);
-    }
-    else if (dragEvent != null && aa != null)
+    if (!resizePanel && dragEvent != null && aa != null)
     {
       g.setColor(Color.lightGray);
       g.drawString(aa[selectedRow].label, dragEvent.getX(),
@@ -1227,4 +1214,9 @@ public class AnnotationLabels extends JPanel
   {
     return scrollOffset;
   }
+
+  @Override
+  public void mouseEntered(MouseEvent e)
+  {
+  }
 }
index 438e81b..dee56b0 100755 (executable)
@@ -175,11 +175,12 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     if (e.isShiftDown())
     {
       e.consume();
-      if (e.getWheelRotation() > 0)
+      double wheelRotation = e.getPreciseWheelRotation();
+      if (wheelRotation > 0)
       {
         av.getRanges().scrollRight(true);
       }
-      else
+      else if (wheelRotation < 0)
       {
         av.getRanges().scrollRight(false);
       }
@@ -724,7 +725,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     if (av.hasHiddenColumns())
     {
       column = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(column);
+              .visibleToAbsoluteColumn(column);
     }
 
     AlignmentAnnotation ann = aa[row];
@@ -782,6 +783,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       {
         this.setToolTipText(JvSwingUtils.wrapTooltip(true, description));
       }
+      else
+      {
+        this.setToolTipText(null); // no tooltip if null or empty description
+      }
     }
     else
     {
@@ -904,6 +909,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   @Override
   public void paintComponent(Graphics g)
   {
+    super.paintComponent(g);
+
     g.setColor(Color.white);
     g.fillRect(0, 0, getWidth(), getHeight());
 
@@ -959,7 +966,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       gg.fillRect(0, 0, imgWidth, image.getHeight());
       imageFresh = true;
     }
-
+    
     drawComponent(gg, av.getRanges().getStartRes(),
             av.getRanges().getEndRes() + 1);
     imageFresh = false;
@@ -992,10 +999,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     int er = av.getRanges().getEndRes() + 1;
     int transX = 0;
 
-    long stime = System.currentTimeMillis();
     gg.copyArea(0, 0, imgWidth, getHeight(),
             -horizontal * av.getCharWidth(), 0);
-    long mtime = System.currentTimeMillis();
 
     if (horizontal > 0) // scrollbar pulled right, image to the left
     {
@@ -1012,17 +1017,13 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     drawComponent(gg, sr, er);
 
     gg.translate(-transX, 0);
-    long dtime = System.currentTimeMillis();
+
     fastPaint = true;
-    repaint();
-    long rtime = System.currentTimeMillis();
-    if (debugRedraw)
-    {
-      System.err.println("Scroll:\t" + horizontal + "\tCopyArea:\t"
-              + (mtime - stime) + "\tDraw component:\t" + (dtime - mtime)
-              + "\tRepaint call:\t" + (rtime - dtime));
-    }
 
+    // Call repaint on alignment panel so that repaints from other alignment
+    // panel components can be aggregated. Otherwise performance of the overview
+    // window and others may be adversely affected.
+    av.getAlignPanel().repaint();
   }
 
   private volatile boolean lastImageGood = false;
index 71ad6a5..f13cb10 100644 (file)
@@ -172,11 +172,7 @@ public abstract class AnnotationRowFilter extends JPanel
       @Override
       public void mouseReleased(MouseEvent evt)
       {
-        if (sliderDragging)
-        {
-          sliderDragging = false;
-          valueChanged(true);
-        }
+        sliderDragReleased();
       }
     });
   }
@@ -523,4 +519,13 @@ public abstract class AnnotationRowFilter extends JPanel
   {
     this.annotations = anns;
   }
+
+  protected void sliderDragReleased()
+  {
+    if (sliderDragging)
+    {
+      sliderDragging = false;
+      valueChanged(true);
+    }
+  }
 }
index fef7451..6c934c8 100644 (file)
@@ -44,7 +44,6 @@ import java.util.List;
 import java.util.Vector;
 
 import javax.swing.JCheckBoxMenuItem;
-import javax.swing.JInternalFrame;
 import javax.swing.JPanel;
 import javax.swing.JSplitPane;
 import javax.swing.SwingUtilities;
@@ -58,7 +57,7 @@ public class AppJmol extends StructureViewerBase
 
   private static final String SPACE = " ";
 
-  private static final String BACKSLASH = "\"";
+  private static final String QUOTE = "\"";
 
   AppJmolBinding jmb;
 
@@ -162,8 +161,9 @@ public class AppJmol extends StructureViewerBase
   {
     return progressBar;
   }
+  
   /**
-   * add a single PDB structure to a new or existing Jmol view
+   * display a single PDB structure in a new Jmol view
    * 
    * @param pdbentry
    * @param seq
@@ -174,33 +174,14 @@ public class AppJmol extends StructureViewerBase
           final AlignmentPanel ap)
   {
     progressBar = ap.alignFrame;
-    String pdbId = pdbentry.getId();
 
-    /*
-     * If the PDB file is already loaded, the user may just choose to add to an
-     * existing viewer (or cancel)
-     */
-    if (addAlreadyLoadedFile(seq, chains, ap, pdbId))
-    {
-      return;
-    }
-
-    /*
-     * Check if there are other Jmol views involving this alignment and prompt
-     * user about adding this molecule to one of them
-     */
-    if (addToExistingViewer(pdbentry, seq, chains, ap, pdbId))
-    {
-      return;
-    }
-
-    /*
-     * If the options above are declined or do not apply, open a new viewer
-     */
-    openNewJmol(ap, new PDBEntry[] { pdbentry }, new SequenceI[][] { seq });
+    openNewJmol(ap, alignAddedStructures, new PDBEntry[] { pdbentry },
+            new SequenceI[][]
+            { seq });
   }
 
-  private void openNewJmol(AlignmentPanel ap, PDBEntry[] pdbentrys,
+  private void openNewJmol(AlignmentPanel ap, boolean alignAdded,
+          PDBEntry[] pdbentrys,
           SequenceI[][] seqs)
   {
     progressBar = ap.alignFrame;
@@ -209,11 +190,9 @@ public class AppJmol extends StructureViewerBase
     addAlignmentPanel(ap);
     useAlignmentPanelForColourbyseq(ap);
 
-    if (pdbentrys.length > 1)
-    {
-      alignAddedStructures = true;
-      useAlignmentPanelForSuperposition(ap);
-    }
+    alignAddedStructures = alignAdded;
+    useAlignmentPanelForSuperposition(ap);
+
     jmb.setColourBySequence(true);
     setSize(400, 400); // probably should be a configurable/dynamic default here
     initMenus();
@@ -234,41 +213,21 @@ public class AppJmol extends StructureViewerBase
   }
 
   /**
-   * create a new Jmol containing several structures superimposed using the
-   * given alignPanel.
+   * create a new Jmol containing several structures optionally superimposed
+   * using the given alignPanel.
    * 
    * @param ap
+   * @param alignAdded
+   *          - true to superimpose
    * @param pe
    * @param seqs
    */
-  public AppJmol(AlignmentPanel ap, PDBEntry[] pe, SequenceI[][] seqs)
+  public AppJmol(AlignmentPanel ap, boolean alignAdded, PDBEntry[] pe,
+          SequenceI[][] seqs)
   {
-    openNewJmol(ap, pe, seqs);
+    openNewJmol(ap, alignAdded, pe, seqs);
   }
 
-  /**
-   * Returns a list of any Jmol viewers. The list is restricted to those linked
-   * to the given alignment panel if it is not null.
-   */
-  @Override
-  protected List<StructureViewerBase> getViewersFor(AlignmentPanel apanel)
-  {
-    List<StructureViewerBase> result = new ArrayList<>();
-    JInternalFrame[] frames = Desktop.instance.getAllFrames();
-
-    for (JInternalFrame frame : frames)
-    {
-      if (frame instanceof AppJmol)
-      {
-        if (apanel == null
-                || ((StructureViewerBase) frame).isLinkedWith(apanel))
-        {
-          result.add((StructureViewerBase) frame);
-        }
-      }
-    }
-    return result;
-  }
 
   void initJmol(String command)
   {
@@ -300,8 +259,6 @@ public class AppJmol extends StructureViewerBase
     jmb.setFinishedInit(true);
   }
 
-  boolean allChainsSelected = false;
-
   @Override
   void showSelectedChains()
   {
@@ -368,8 +325,8 @@ public class AppJmol extends StructureViewerBase
     StringBuilder fileList = new StringBuilder();
     for (String s : files)
     {
-      fileList.append(SPACE).append(BACKSLASH)
-              .append(Platform.escapeString(s)).append(BACKSLASH);
+      fileList.append(SPACE).append(QUOTE)
+              .append(Platform.escapeString(s)).append(QUOTE);
     }
     String filesString = fileList.toString();
 
@@ -444,7 +401,7 @@ public class AppJmol extends StructureViewerBase
       jmb.updateColours(ap);
     }
     // do superposition if asked to
-    if (Cache.getDefault("AUTOSUPERIMPOSE", true) && alignAddedStructures)
+    if (alignAddedStructures)
     {
       alignAddedStructures();
     }
@@ -478,7 +435,7 @@ public class AppJmol extends StructureViewerBase
         }
       }
     });
-    alignAddedStructures = false;
+
   }
 
   /**
@@ -507,6 +464,7 @@ public class AppJmol extends StructureViewerBase
         String file = jmb.getPdbEntry(pi).getFile();
         if (file == null)
         {
+          // todo: extract block as method and pull up (also ChimeraViewFrame)
           // retrieve the pdb and store it locally
           AlignmentI pdbseq = null;
           pdbid = jmb.getPdbEntry(pi).getId();
index ea809eb..956c119 100644 (file)
@@ -60,7 +60,7 @@ import javax.swing.JInternalFrame;
  * around to the bottom of the window stack (as the original implementation
  * does)
  * 
- * @see com.sun.java.swing.plaf.windows.WindowsDesktopManager
+ * see com.sun.java.swing.plaf.windows.WindowsDesktopManager
  */
 public class AquaInternalFrameManager extends DefaultDesktopManager
 {
index 89de2e8..d07a7c2 100644 (file)
@@ -100,41 +100,34 @@ public class ChimeraViewFrame extends StructureViewerBase
     savemenu.setVisible(false); // not yet implemented
     viewMenu.add(fitToWindow);
 
-    /*
-     * exchange of Jalview features and Chimera attributes is for now
-     * an optionally enabled experimental feature
-     */
-    if (Desktop.instance.showExperimental())
+    JMenuItem writeFeatures = new JMenuItem(
+            MessageManager.getString("label.create_chimera_attributes"));
+    writeFeatures.setToolTipText(MessageManager
+            .getString("label.create_chimera_attributes_tip"));
+    writeFeatures.addActionListener(new ActionListener()
     {
-      JMenuItem writeFeatures = new JMenuItem(
-              MessageManager.getString("label.create_chimera_attributes"));
-      writeFeatures.setToolTipText(MessageManager
-              .getString("label.create_chimera_attributes_tip"));
-      writeFeatures.addActionListener(new ActionListener()
-      {
-        @Override
-        public void actionPerformed(ActionEvent e)
-        {
-          sendFeaturesToChimera();
-        }
-      });
-      viewerActionMenu.add(writeFeatures);
-
-      final JMenu fetchAttributes = new JMenu(
-              MessageManager.getString("label.fetch_chimera_attributes"));
-      fetchAttributes.setToolTipText(MessageManager
-              .getString("label.fetch_chimera_attributes_tip"));
-      fetchAttributes.addMouseListener(new MouseAdapter()
+      @Override
+      public void actionPerformed(ActionEvent e)
       {
+        sendFeaturesToChimera();
+      }
+    });
+    viewerActionMenu.add(writeFeatures);
 
-        @Override
-        public void mouseEntered(MouseEvent e)
-        {
-          buildAttributesMenu(fetchAttributes);
-        }
-      });
-      viewerActionMenu.add(fetchAttributes);
-    }
+    final JMenu fetchAttributes = new JMenu(
+            MessageManager.getString("label.fetch_chimera_attributes"));
+    fetchAttributes.setToolTipText(
+            MessageManager.getString("label.fetch_chimera_attributes_tip"));
+    fetchAttributes.addMouseListener(new MouseAdapter()
+    {
+
+      @Override
+      public void mouseEntered(MouseEvent e)
+      {
+        buildAttributesMenu(fetchAttributes);
+      }
+    });
+    viewerActionMenu.add(fetchAttributes);
   }
 
   /**
@@ -202,7 +195,7 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   /**
-   * add a single PDB structure to a new or existing Chimera view
+   * open a single PDB structure in a new Chimera view
    * 
    * @param pdbentry
    * @param seq
@@ -213,30 +206,7 @@ public class ChimeraViewFrame extends StructureViewerBase
           String[] chains, final AlignmentPanel ap)
   {
     this();
-    String pdbId = pdbentry.getId();
-
-    /*
-     * If the PDB file is already loaded, the user may just choose to add to an
-     * existing viewer (or cancel)
-     */
-    if (addAlreadyLoadedFile(seq, chains, ap, pdbId))
-    {
-      return;
-    }
 
-    /*
-     * Check if there are other Chimera views involving this alignment and give
-     * user the option to add and align this molecule to one of them (or cancel)
-     */
-    if (addToExistingViewer(pdbentry, seq, chains, ap, pdbId))
-    {
-      return;
-    }
-
-    /*
-     * If the options above are declined or do not apply, show the structure in
-     * a new viewer
-     */
     openNewChimera(ap, new PDBEntry[] { pdbentry },
             new SequenceI[][]
             { seq });
@@ -264,7 +234,6 @@ public class ChimeraViewFrame extends StructureViewerBase
 
     if (pdbentrys.length > 1)
     {
-      alignAddedStructures = true;
       useAlignmentPanelForSuperposition(ap);
     }
     jmb.setColourBySequence(true);
@@ -323,17 +292,19 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   /**
-   * create a new viewer containing several structures superimposed using the
-   * given alignPanel.
+   * create a new viewer containing several structures, optionally superimposed
+   * using the given alignPanel.
    * 
    * @param pe
    * @param seqs
    * @param ap
    */
-  public ChimeraViewFrame(PDBEntry[] pe, SequenceI[][] seqs,
+  public ChimeraViewFrame(PDBEntry[] pe, boolean alignAdded,
+          SequenceI[][] seqs,
           AlignmentPanel ap)
   {
     this();
+    setAlignAddedStructures(alignAdded);
     openNewChimera(ap, pe, seqs);
   }
 
@@ -352,29 +323,6 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   /**
-   * Returns a list of any Chimera viewers in the desktop. The list is
-   * restricted to those linked to the given alignment panel if it is not null.
-   */
-  @Override
-  protected List<StructureViewerBase> getViewersFor(AlignmentPanel ap)
-  {
-    List<StructureViewerBase> result = new ArrayList<>();
-    JInternalFrame[] frames = Desktop.instance.getAllFrames();
-
-    for (JInternalFrame frame : frames)
-    {
-      if (frame instanceof ChimeraViewFrame)
-      {
-        if (ap == null || ((StructureViewerBase) frame).isLinkedWith(ap))
-        {
-          result.add((StructureViewerBase) frame);
-        }
-      }
-    }
-    return result;
-  }
-
-  /**
    * Launch Chimera. If we have a chimera session file name, send Chimera the
    * command to open its saved session file.
    */
@@ -641,7 +589,7 @@ public class ChimeraViewFrame extends StructureViewerBase
         jmb.updateColours(ap);
       }
       // do superposition if asked to
-      if (Cache.getDefault("AUTOSUPERIMPOSE", true) && alignAddedStructures)
+      if (alignAddedStructures)
       {
         new Thread(new Runnable()
         {
@@ -651,7 +599,6 @@ public class ChimeraViewFrame extends StructureViewerBase
             alignStructs_withAllAlignPanels();
           }
         }).start();
-        alignAddedStructures = false;
       }
       addingStructures = false;
     }
@@ -681,7 +628,6 @@ public class ChimeraViewFrame extends StructureViewerBase
 
   private String fetchPdbFile(PDBEntry processingEntry) throws Exception
   {
-    // FIXME: this is duplicated code with Jmol frame ?
     String filePath = null;
     Pdb pdbclient = new Pdb();
     AlignmentI pdbseq = null;
index 128481c..9a696e9 100644 (file)
@@ -849,6 +849,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     frame.setResizable(resizable);
     frame.setMaximizable(resizable);
     frame.setIconifiable(resizable);
+    frame.setOpaque(false);
 
     if (frame.getX() < 1 && frame.getY() < 1)
     {
@@ -3398,4 +3399,41 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
   }
+
+  /**
+   * Answers a (possibly empty) list of any structure viewer frames (currently
+   * for either Jmol or Chimera) which are currently open. This may optionally
+   * be restricted to viewers of a specified class, or viewers linked to a
+   * specified alignment panel.
+   * 
+   * @param apanel
+   *          if not null, only return viewers linked to this panel
+   * @param structureViewerClass
+   *          if not null, only return viewers of this class
+   * @return
+   */
+  public List<StructureViewerBase> getStructureViewers(
+          AlignmentPanel apanel,
+          Class<? extends StructureViewerBase> structureViewerClass)
+  {
+    List<StructureViewerBase> result = new ArrayList<>();
+    JInternalFrame[] frames = Desktop.instance.getAllFrames();
+
+    for (JInternalFrame frame : frames)
+    {
+      if (frame instanceof StructureViewerBase)
+      {
+        if (structureViewerClass == null
+                || structureViewerClass.isInstance(frame))
+        {
+          if (apanel == null
+                  || ((StructureViewerBase) frame).isLinkedWith(apanel))
+          {
+            result.add((StructureViewerBase) frame);
+          }
+        }
+      }
+    }
+    return result;
+  }
 }
index 3f1d9c7..12f9db9 100644 (file)
@@ -961,12 +961,13 @@ public class FeatureSettings extends JPanel
 
   public void invertSelection()
   {
-    for (int i = 0; i < table.getRowCount(); i++)
+    Object[][] data = ((FeatureTableModel) table.getModel()).getData();
+    for (int i = 0; i < data.length; i++)
     {
-      Boolean value = (Boolean) table.getValueAt(i, 2);
-
-      table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
+      data[i][2] = !(Boolean) data[i][2];
     }
+    updateFeatureRenderer(data, true);
+    table.repaint();
   }
 
   public void orderByAvWidth()
index 085b259..cd7b0b7 100755 (executable)
@@ -83,7 +83,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     this.av = av;
     PaintRefresher.Register(this, av.getSequenceSetId());
     av.getRanges().addPropertyChangeListener(this);
-  }
+    }
 
   /**
    * DOCUMENT ME!
@@ -204,7 +204,11 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     gg.translate(0, -transY);
 
     fastPaint = true;
-    repaint();
+
+    // Call repaint on alignment panel so that repaints from other alignment
+    // panel components can be aggregated. Otherwise performance of the overview
+    // window and others may be adversely affected.
+    av.getAlignPanel().repaint();
   }
 
   /**
@@ -216,41 +220,43 @@ public class IdCanvas extends JPanel implements ViewportListenerI
   @Override
   public void paintComponent(Graphics g)
   {
+    super.paintComponent(g);
+
     g.setColor(Color.white);
     g.fillRect(0, 0, getWidth(), getHeight());
-
+    
     if (fastPaint)
     {
       fastPaint = false;
       g.drawImage(image, 0, 0, this);
-
+    
       return;
     }
-
+    
     int oldHeight = imgHeight;
-
+    
     imgHeight = getHeight();
     imgHeight -= (imgHeight % av.getCharHeight());
-
+    
     if (imgHeight < 1)
     {
       return;
     }
-
+    
     if (oldHeight != imgHeight || image.getWidth(this) != getWidth())
     {
-      image = new BufferedImage(getWidth(), imgHeight,
-              BufferedImage.TYPE_INT_RGB);
+       image = new BufferedImage(getWidth(), imgHeight,
+                BufferedImage.TYPE_INT_RGB);
     }
-
+    
     gg = (Graphics2D) image.getGraphics();
-
+    
     // Fill in the background
     gg.setColor(Color.white);
     gg.fillRect(0, 0, getWidth(), imgHeight);
-
+    
     drawIds(av.getRanges().getStartSeq(), av.getRanges().getEndSeq());
-
+    
     g.drawImage(image, 0, 0, this);
   }
 
@@ -374,7 +380,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
+              .absoluteToVisibleColumn(maxwidth) - 1;
     }
 
     int annotationHeight = 0;
index 1f2a3ad..f2761ab 100755 (executable)
@@ -148,7 +148,8 @@ public class IdPanel extends JPanel
   public void mouseWheelMoved(MouseWheelEvent e)
   {
     e.consume();
-    if (e.getWheelRotation() > 0)
+    double wheelRotation = e.getPreciseWheelRotation();
+    if (wheelRotation > 0)
     {
       if (e.isShiftDown())
       {
@@ -159,7 +160,7 @@ public class IdPanel extends JPanel
         av.getRanges().scrollUp(false);
       }
     }
-    else
+    else if (wheelRotation < 0)
     {
       if (e.isShiftDown())
       {
index 8400543..0cffc3b 100755 (executable)
@@ -23,8 +23,8 @@ package jalview.gui;
 import jalview.api.AlignViewportI;
 
 import java.awt.Color;
+import java.awt.Cursor;
 import java.awt.Graphics;
-import java.awt.Image;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
@@ -44,8 +44,6 @@ public class IdwidthAdjuster extends JPanel
 
   int oldX = 0;
 
-  Image image;
-
   AlignmentPanel ap;
 
   /**
@@ -57,14 +55,7 @@ public class IdwidthAdjuster extends JPanel
   public IdwidthAdjuster(AlignmentPanel ap)
   {
     this.ap = ap;
-
-    java.net.URL url = getClass().getResource("/images/idwidth.gif");
-
-    if (url != null)
-    {
-      image = java.awt.Toolkit.getDefaultToolkit().createImage(url);
-    }
-
+    setBackground(Color.white);
     addMouseListener(this);
     addMouseMotionListener(this);
   }
@@ -196,10 +187,7 @@ public class IdwidthAdjuster extends JPanel
 
     if (active)
     {
-      if (image != null)
-      {
-        g.drawImage(image, getWidth() - 20, 2, this);
-      }
+        setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
     }
   }
 }
index 0a6b9d6..1018d6e 100644 (file)
@@ -101,10 +101,10 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
      * identical DB sources, and should be collapsed.
      */
     DefaultMutableTreeNode tn = null, root = new DefaultMutableTreeNode();
-    Hashtable<String, DefaultMutableTreeNode> source = new Hashtable<String, DefaultMutableTreeNode>();
+    Hashtable<String, DefaultMutableTreeNode> source = new Hashtable<>();
     sfetcher = sfetch;
     String dbs[] = sfetch.getSupportedDb();
-    Hashtable<String, String> ht = new Hashtable<String, String>();
+    Hashtable<String, String> ht = new Hashtable<>();
     for (int i = 0; i < dbs.length; i++)
     {
       tn = source.get(dbs[i]);
@@ -370,7 +370,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
 
     tsel = dbviews.getSelectionPaths();
     boolean forcedFirstChild = false;
-    List<DbSourceProxy> srcs = new ArrayList<DbSourceProxy>();
+    List<DbSourceProxy> srcs = new ArrayList<>();
     if (tsel != null)
     {
       for (TreePath tp : tsel)
@@ -489,7 +489,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
       return null;
     }
     StringBuffer sb = new StringBuffer();
-    HashSet<String> hs = new HashSet<String>();
+    HashSet<String> hs = new HashSet<>();
     for (DbSourceProxy dbs : getSelectedSources())
     {
       String tq = dbs.getTestQuery();
@@ -506,7 +506,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
     return sb.toString();
   }
 
-  List<ActionListener> lstners = new Vector<ActionListener>();
+  List<ActionListener> lstners = new Vector<>();
 
   public void addActionListener(ActionListener actionListener)
   {
@@ -596,4 +596,11 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
     // TODO Auto-generated method stub
 
   }
+
+  @Override
+  public void setVisible(boolean arg0)
+  {
+    System.out.println("setVisible: " + arg0);
+    super.setVisible(arg0);
+  }
 }
index 4a15024..c7ec757 100644 (file)
@@ -1389,9 +1389,10 @@ public class Jalview2XML
         }
         else
         {
-          ArrayList<int[]> hiddenRegions = hidden.getHiddenColumnsCopy();
-          for (int[] region : hiddenRegions)
+          Iterator<int[]> hiddenRegions = hidden.iterator();
+          while (hiddenRegions.hasNext())
           {
+            int[] region = hiddenRegions.next();
             HiddenColumns hc = new HiddenColumns();
             hc.setStart(region[0]);
             hc.setEnd(region[1]);
index 2991889..1f55ea6 100644 (file)
@@ -157,8 +157,7 @@ public class OverviewCanvas extends JComponent
     {
       mg.translate(0, od.getSequencesHeight());
       or.drawGraph(mg, av.getAlignmentConservationAnnotation(),
-              av.getCharWidth(), od.getGraphHeight(),
-              od.getColumns(av.getAlignment()));
+              od.getGraphHeight(), od.getColumns(av.getAlignment()));
       mg.translate(0, -od.getSequencesHeight());
     }
     System.gc();
@@ -183,7 +182,7 @@ public class OverviewCanvas extends JComponent
   @Override
   public void paintComponent(Graphics g)
   {
-    // super.paintComponent(g);
+    super.paintComponent(g);
 
     if (restart)
     {
index 43b4310..02d54a8 100755 (executable)
@@ -170,15 +170,20 @@ public class OverviewPanel extends JPanel
       @Override
       public void mouseMoved(MouseEvent evt)
       {
-        if (od.isPositionInBox(evt.getX(), evt.getY()))
+        if (!draggingBox)
+        // don't bother changing the cursor if we're dragging the box
+        // as we can't have moved inside or out of the box in that case
         {
-          // display drag cursor at mouse position
-          setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
-        }
-        else
-        {
-          // reset cursor
-          setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+          if (od.isPositionInBox(evt.getX(), evt.getY()))
+          {
+            // display drag cursor at mouse position
+            setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+          }
+          else
+          {
+            // reset cursor
+            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+          }
         }
       }
     });
@@ -203,6 +208,10 @@ public class OverviewPanel extends JPanel
           if (!od.isPositionInBox(evt.getX(), evt.getY()))
           {
             draggingBox = false;
+
+            // display drag cursor at mouse position
+            setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+
             od.updateViewportFromMouse(evt.getX(), evt.getY(),
                     av.getAlignment().getHiddenSequences(),
                     av.getAlignment().getHiddenColumns());
@@ -225,6 +234,13 @@ public class OverviewPanel extends JPanel
           showPopupMenu(evt);
         }
       }
+
+      @Override
+      public void mouseReleased(MouseEvent evt)
+      {
+        draggingBox = false;
+      }
+
     });
   }
 
index 850a09a..759c63b 100644 (file)
@@ -1453,15 +1453,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
   protected void hideInsertions_actionPerformed(ActionEvent actionEvent)
   {
-
-    HiddenColumns hidden = new HiddenColumns();
-    BitSet inserts = new BitSet(), mask = new BitSet();
-
-    // set mask to preserve existing hidden columns outside selected group
-    if (ap.av.hasHiddenColumns())
-    {
-      ap.av.getAlignment().getHiddenColumns().markHiddenRegions(mask);
-    }
+    HiddenColumns hidden = ap.av.getAlignment().getHiddenColumns();
+    BitSet inserts = new BitSet();
 
     boolean markedPopup = false;
     // mark inserts in current selection
@@ -1469,10 +1462,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     {
       // mark just the columns in the selection group to be hidden
       inserts.set(ap.av.getSelectionGroup().getStartRes(),
-              ap.av.getSelectionGroup().getEndRes() + 1);
-
-      // and clear that part of the mask
-      mask.andNot(inserts);
+              ap.av.getSelectionGroup().getEndRes() + 1); // TODO why +1?
 
       // now clear columns without gaps
       for (SequenceI sq : ap.av.getSelectionGroup().getSequences())
@@ -1483,29 +1473,18 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         }
         inserts.and(sq.getInsertionsAsBits());
       }
-    }
-    else
-    {
-      // initially, mark all columns to be hidden
-      inserts.set(0, ap.av.getAlignment().getWidth());
-
-      // and clear out old hidden regions completely
-      mask.clear();
+      hidden.clearAndHideColumns(inserts, ap.av.getSelectionGroup().getStartRes(),
+              ap.av.getSelectionGroup().getEndRes());
     }
 
     // now mark for sequence under popup if we haven't already done it
-    if (!markedPopup && sequence != null)
+    else if (!markedPopup && sequence != null)
     {
-      inserts.and(sequence.getInsertionsAsBits());
-    }
+      inserts.or(sequence.getInsertionsAsBits());
 
-    // finally, preserve hidden regions outside selection
-    inserts.or(mask);
-
-    // and set hidden columns accordingly
-    hidden.hideMarkedBits(inserts);
-
-    ap.av.getAlignment().setHiddenColumns(hidden);
+      // and set hidden columns accordingly
+      hidden.hideColumns(inserts);
+    }
     refresh();
   }
 
index aa8369a..5aab26d 100755 (executable)
@@ -525,7 +525,7 @@ public class Preferences extends GPreferences
     autoIdWidth.setSelected(Cache.getDefault("FIGURE_AUTOIDWIDTH", false));
     userIdWidth.setEnabled(!autoIdWidth.isSelected());
     userIdWidthlabel.setEnabled(!autoIdWidth.isSelected());
-    Integer wi = Cache.getIntegerProperty("FIGURE_USERIDWIDTH");
+    Integer wi = Cache.getIntegerProperty("FIGURE_FIXEDIDWIDTH");
     userIdWidth.setText(wi == null ? "" : wi.toString());
     // TODO: refactor to use common enum via FormatAdapter and allow extension
     // for new flat file formats
@@ -784,7 +784,7 @@ public class Preferences extends GPreferences
     Cache.applicationProperties.setProperty("FIGURE_AUTOIDWIDTH",
             Boolean.toString(autoIdWidth.isSelected()));
     userIdWidth_actionPerformed();
-    Cache.applicationProperties.setProperty("FIGURE_USERIDWIDTH",
+    Cache.applicationProperties.setProperty("FIGURE_FIXEDIDWIDTH",
             userIdWidth.getText());
 
     /*
index 4ef18d4..02368df 100755 (executable)
@@ -128,7 +128,6 @@ public class RotatableCanvas extends JPanel implements MouseListener,
 
   boolean applyToAllViews = false;
 
-  // Controller controller;
   public RotatableCanvas(AlignmentPanel ap)
   {
     this.av = ap.av;
@@ -136,16 +135,23 @@ public class RotatableCanvas extends JPanel implements MouseListener,
 
     addMouseWheelListener(new MouseWheelListener()
     {
+      @Override
       public void mouseWheelMoved(MouseWheelEvent e)
       {
-        if (e.getWheelRotation() > 0)
+        double wheelRotation = e.getPreciseWheelRotation();
+        if (wheelRotation > 0)
         {
+          /*
+           * zoom in
+           */
           scale = (float) (scale * 1.1);
           repaint();
         }
-
-        else
+        else if (wheelRotation < 0)
         {
+          /*
+           * zoom out
+           */
           scale = (float) (scale * 0.9);
           repaint();
         }
@@ -162,6 +168,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
 
   boolean first = true;
 
+  @Override
   public void setPoints(Vector points, int npoint)
   {
     this.points = points;
@@ -327,7 +334,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
       dim = height;
     }
 
-    return (float) ((dim * scalefactor) / (2 * maxwidth));
+    return (dim * scalefactor) / (2 * maxwidth);
   }
 
   /**
@@ -352,6 +359,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public Dimension getPreferredSize()
   {
     if (prefsize != null)
@@ -369,6 +377,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public Dimension getMinimumSize()
   {
     return getPreferredSize();
@@ -380,6 +389,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * @param g
    *          DOCUMENT ME!
    */
+  @Override
   public void paintComponent(Graphics g1)
   {
 
@@ -475,8 +485,8 @@ public class RotatableCanvas extends JPanel implements MouseListener,
     for (int i = 0; i < npoint; i++)
     {
       SequencePoint sp = (SequencePoint) points.elementAt(i);
-      int x = (int) ((float) (sp.coord[0] - centre[0]) * scale) + halfwidth;
-      int y = (int) ((float) (sp.coord[1] - centre[1]) * scale)
+      int x = (int) ((sp.coord[0] - centre[0]) * scale) + halfwidth;
+      int y = (int) ((sp.coord[1] - centre[1]) * scale)
               + halfheight;
       float z = sp.coord[1] - centre[2];
 
@@ -547,6 +557,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void keyTyped(KeyEvent evt)
   {
   }
@@ -557,6 +568,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void keyReleased(KeyEvent evt)
   {
   }
@@ -567,6 +579,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void keyPressed(KeyEvent evt)
   {
     if (evt.getKeyCode() == KeyEvent.VK_UP)
@@ -598,6 +611,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
   }
@@ -608,6 +622,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
   }
@@ -618,6 +633,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseExited(MouseEvent evt)
   {
   }
@@ -628,6 +644,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
   }
@@ -638,6 +655,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     int x = evt.getX();
@@ -690,6 +708,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
   // controller.handleSequenceSelectionEvent(new
   // SequenceSelectionEvent(this,sel));
   // }
+  @Override
   public void mouseMoved(MouseEvent evt)
   {
     SequenceI found = findPoint(evt.getX(), evt.getY());
@@ -710,6 +729,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     mx = evt.getX();
@@ -725,8 +745,8 @@ public class RotatableCanvas extends JPanel implements MouseListener,
     {
       rotmat.setIdentity();
 
-      rotmat.rotate((float) (my - omy), 'x');
-      rotmat.rotate((float) (mx - omx), 'y');
+      rotmat.rotate(my - omy, 'x');
+      rotmat.rotate(mx - omx, 'y');
 
       for (int i = 0; i < npoint; i++)
       {
@@ -774,9 +794,9 @@ public class RotatableCanvas extends JPanel implements MouseListener,
     {
       SequencePoint sp = (SequencePoint) points.elementAt(i);
       int tmp1 = (int) (((sp.coord[0] - centre[0]) * scale)
-              + ((float) getWidth() / 2.0));
+              + (getWidth() / 2.0));
       int tmp2 = (int) (((sp.coord[1] - centre[1]) * scale)
-              + ((float) getHeight() / 2.0));
+              + (getHeight() / 2.0));
 
       if ((tmp1 > x1) && (tmp1 < x2) && (tmp2 > y1) && (tmp2 < y2))
       {
@@ -816,9 +836,9 @@ public class RotatableCanvas extends JPanel implements MouseListener,
     for (int i = 0; i < npoint; i++)
     {
       SequencePoint sp = (SequencePoint) points.elementAt(i);
-      int px = (int) ((float) (sp.coord[0] - centre[0]) * scale)
+      int px = (int) ((sp.coord[0] - centre[0]) * scale)
               + halfwidth;
-      int py = (int) ((float) (sp.coord[1] - centre[1]) * scale)
+      int py = (int) ((sp.coord[1] - centre[1]) * scale)
               + halfheight;
 
       if ((Math.abs(px - x) < 3) && (Math.abs(py - y) < 3))
index 798c833..e6bba02 100755 (executable)
@@ -42,6 +42,7 @@ import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.beans.PropertyChangeEvent;
+import java.util.Iterator;
 import java.util.List;
 
 import javax.swing.JMenuItem;
@@ -112,7 +113,7 @@ public class ScalePanel extends JPanel
 
     if (av.hasHiddenColumns())
     {
-      x = av.getAlignment().getHiddenColumns().adjustForHiddenColumns(x);
+      x = av.getAlignment().getHiddenColumns().visibleToAbsoluteColumn(x);
     }
 
     if (x >= av.getAlignment().getWidth())
@@ -174,7 +175,7 @@ public class ScalePanel extends JPanel
       });
       pop.add(item);
 
-      if (av.getAlignment().getHiddenColumns().hasHiddenColumns())
+      if (av.getAlignment().getHiddenColumns().hasMultiHiddenColumnRegions())
       {
         item = new JMenuItem(MessageManager.getString("action.reveal_all"));
         item.addActionListener(new ActionListener()
@@ -281,7 +282,7 @@ public class ScalePanel extends JPanel
     if (av.hasHiddenColumns())
     {
       res = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(res);
+              .visibleToAbsoluteColumn(res);
     }
 
     if (res >= av.getAlignment().getWidth())
@@ -336,7 +337,7 @@ public class ScalePanel extends JPanel
     int res = (evt.getX() / av.getCharWidth())
             + av.getRanges().getStartRes();
     res = Math.max(0, res);
-    res = hidden.adjustForHiddenColumns(res);
+    res = hidden.visibleToAbsoluteColumn(res);
     res = Math.min(res, av.getAlignment().getWidth() - 1);
     min = Math.min(res, min);
     max = Math.max(res, max);
@@ -392,7 +393,7 @@ public class ScalePanel extends JPanel
     reveal = av.getAlignment().getHiddenColumns()
             .getRegionWithEdgeAtRes(res);
 
-    res = av.getAlignment().getHiddenColumns().adjustForHiddenColumns(res);
+    res = av.getAlignment().getHiddenColumns().visibleToAbsoluteColumn(res);
 
     ToolTipManager.sharedInstance().registerComponent(this);
     this.setToolTipText(
@@ -409,6 +410,8 @@ public class ScalePanel extends JPanel
   @Override
   public void paintComponent(Graphics g)
   {
+    super.paintComponent(g);
+
     /*
      * shouldn't get called in wrapped mode as the scale above is
      * drawn instead by SeqCanvas.drawNorthScale
@@ -457,7 +460,7 @@ public class ScalePanel extends JPanel
         {
           if (hidden.isVisible(sel))
           {
-            sel = hidden.findColumnPosition(sel);
+            sel = hidden.absoluteToVisibleColumn(sel);
           }
           else
           {
@@ -487,23 +490,18 @@ public class ScalePanel extends JPanel
 
       if (av.getShowHiddenMarkers())
       {
-        List<Integer> positions = hidden.findHiddenRegionPositions();
-        for (int pos : positions)
+        Iterator<Integer> it = hidden.getStartRegionIterator(startx,
+                startx + widthx + 1);
+        while (it.hasNext())
         {
-          res = pos - startx;
-
-          if (res < 0 || res > widthx)
-          {
-            continue;
-          }
+          res = it.next() - startx;
 
           gg.fillPolygon(
                   new int[]
-                  { -1 + res * avCharWidth - avCharHeight / 4,
-                      -1 + res * avCharWidth + avCharHeight / 4,
-                      -1 + res * avCharWidth },
-                  new int[]
-                  { y, y, y + 2 * yOf }, 3);
+          { -1 + res * avCharWidth - avCharHeight / 4,
+              -1 + res * avCharWidth + avCharHeight / 4,
+              -1 + res * avCharWidth }, new int[]
+          { y, y, y + 2 * yOf }, 3);
         }
       }
     }
@@ -554,7 +552,11 @@ public class ScalePanel extends JPanel
             || evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
     {
       // scroll event, repaint panel
-      repaint();
+       
+       // Call repaint on alignment panel so that repaints from other alignment
+    // panel components can be aggregated. Otherwise performance of the overview
+    // window and others may be adversely affected.
+      av.getAlignPanel().repaint();
     }
   }
 
index 1e1105f..2d8eb7d 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.VisibleContigsIterator;
 import jalview.renderer.ScaleRenderer;
 import jalview.renderer.ScaleRenderer.ScaleMark;
 import jalview.util.Comparison;
@@ -42,6 +43,7 @@ import java.awt.RenderingHints;
 import java.awt.Shape;
 import java.awt.image.BufferedImage;
 import java.beans.PropertyChangeEvent;
+import java.util.Iterator;
 import java.util.List;
 
 import javax.swing.JComponent;
@@ -198,8 +200,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     if (av.hasHiddenColumns())
     {
       HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
-      startX = hiddenColumns.adjustForHiddenColumns(startx);
-      endX = hiddenColumns.adjustForHiddenColumns(endx);
+      startX = hiddenColumns.visibleToAbsoluteColumn(startx);
+      endX = hiddenColumns.visibleToAbsoluteColumn(endx);
     }
     FontMetrics fm = getFontMetrics(av.getFont());
 
@@ -295,7 +297,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       int endSeq = ranges.getEndSeq();
       int transX = 0;
       int transY = 0;
-
+      
       gg.copyArea(horizontal * charWidth, vertical * charHeight,
               img.getWidth(), img.getHeight(), -horizontal * charWidth,
               -vertical * charHeight);
@@ -337,7 +339,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
       gg.translate(-transX, -transY);
 
-      repaint();
+      // Call repaint on alignment panel so that repaints from other alignment
+      // panel components can be aggregated. Otherwise performance of the
+      // overview window and others may be adversely affected.
+      av.getAlignPanel().repaint();
     } finally
     {
       fastpainting = false;
@@ -351,20 +356,20 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     
     int charHeight = av.getCharHeight();
     int charWidth = av.getCharWidth();
-
+    
     ViewportRanges ranges = av.getRanges();
-
+    
     int width = getWidth();
     int height = getHeight();
-
+    
     width -= (width % charWidth);
     height -= (height % charHeight);
-
+    
     // selectImage is the selection group outline image
     BufferedImage selectImage = drawSelectionGroup(
             ranges.getStartRes(), ranges.getEndRes(),
             ranges.getStartSeq(), ranges.getEndSeq());
-
+    
     if ((img != null) && (fastPaint
             || (getVisibleRect().width != g.getClipBounds().width)
             || (getVisibleRect().height != g.getClipBounds().height)))
@@ -388,16 +393,16 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
         gg = (Graphics2D) img.getGraphics();
         gg.setFont(av.getFont());
       }
-
+    
       if (av.antiAlias)
       {
         gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                 RenderingHints.VALUE_ANTIALIAS_ON);
       }
-
+    
       gg.setColor(Color.white);
       gg.fillRect(0, 0, img.getWidth(), img.getHeight());
-
+    
       if (av.getWrapAlignment())
       {
         drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
@@ -407,7 +412,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
         drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
                 ranges.getStartSeq(), ranges.getEndSeq(), 0);
       }
-
+    
       // lcimg is a local *copy* of img which we'll draw selectImage on top of
       BufferedImage lcimg = buildLocalImage(selectImage);
       g.drawImage(lcimg, 0, 0, this);
@@ -504,8 +509,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
   private BufferedImage buildLocalImage(BufferedImage selectImage)
   {
     // clone the cached image
-    BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
-            img.getType());
+         BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
+                   img.getType());
+
+    // BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
+    // img.getType());
     Graphics2D g2d = lcimg.createGraphics();
     g2d.drawImage(img, 0, 0, null);
 
@@ -545,8 +553,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
     try
     {
-      lcimg = new BufferedImage(width, height,
-              BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
+       lcimg = new BufferedImage(width, height,
+                BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
     } catch (OutOfMemoryError er)
     {
       System.gc();
@@ -890,11 +898,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     int charWidth = av.getCharWidth();
 
     g.setColor(Color.blue);
+    int res;
     HiddenColumns hidden = av.getAlignment().getHiddenColumns();
-    List<Integer> positions = hidden.findHiddenRegionPositions();
-    for (int pos : positions)
+
+    Iterator<Integer> it = hidden.getStartRegionIterator(startColumn,
+            endColumn);
+    while (it.hasNext())
     {
-      int res = pos - startColumn;
+      res = it.next() - startColumn;
 
       if (res < 0 || res > endColumn - startColumn + 1)
       {
@@ -943,7 +954,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth);
+              .absoluteToVisibleColumn(maxwidth);
     }
 
     // chop the wrapped alignment extent up into panel-sized blocks and treat
@@ -1021,29 +1032,23 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     else
     {
       int screenY = 0;
-      final int screenYMax = endRes - startRes;
-      int blockStart = startRes;
-      int blockEnd = endRes;
+      int blockStart;
+      int blockEnd;
 
-      for (int[] region : av.getAlignment().getHiddenColumns()
-              .getHiddenColumnsCopy())
-      {
-        int hideStart = region[0];
-        int hideEnd = region[1];
+      HiddenColumns hidden = av.getAlignment().getHiddenColumns();
+      VisibleContigsIterator regions = hidden
+              .getVisContigsIterator(startRes, endRes + 1, true);
 
-        if (hideStart <= blockStart)
-        {
-          blockStart += (hideEnd - hideStart) + 1;
-          continue;
-        }
+      while (regions.hasNext())
+      {
+        int[] region = regions.next();
+        blockEnd = region[1];
+        blockStart = region[0];
 
         /*
          * draw up to just before the next hidden region, or the end of
          * the visible region, whichever comes first
          */
-        blockEnd = Math.min(hideStart - 1, blockStart + screenYMax
-                - screenY);
-
         g1.translate(screenY * charWidth, 0);
 
         draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
@@ -1052,7 +1057,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
          * draw the downline of the hidden column marker (ScalePanel draws the
          * triangle on top) if we reached it
          */
-        if (av.getShowHiddenMarkers() && blockEnd == hideStart - 1)
+        if (av.getShowHiddenMarkers()
+                && (regions.hasNext() || regions.endsAtHidden()))
         {
           g1.setColor(Color.blue);
 
@@ -1063,23 +1069,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
         g1.translate(-screenY * charWidth, 0);
         screenY += blockEnd - blockStart + 1;
-        blockStart = hideEnd + 1;
-
-        if (screenY > screenYMax)
-        {
-          // already rendered last block
-          return;
-        }
-      }
-
-      if (screenY <= screenYMax)
-      {
-        // remaining visible region to render
-        blockEnd = blockStart + screenYMax - screenY;
-        g1.translate(screenY * charWidth, 0);
-        draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
-
-        g1.translate(-screenY * charWidth, 0);
       }
     }
 
@@ -1137,16 +1126,16 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       if (av.hasSearchResults())
       {
         SearchResultsI searchResults = av.getSearchResults();
-        int[] visibleResults = searchResults.getResults(nextSeq,
-                startRes, endRes);
+        int[] visibleResults = searchResults.getResults(nextSeq, startRes,
+                endRes);
         if (visibleResults != null)
         {
           for (int r = 0; r < visibleResults.length; r += 2)
           {
             seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
-                    visibleResults[r + 1], (visibleResults[r] - startRes)
-                            * charWidth, offset
-                            + ((i - startSeq) * charHeight));
+                    visibleResults[r + 1],
+                    (visibleResults[r] - startRes) * charWidth,
+                    offset + ((i - startSeq) * charHeight));
           }
         }
       }
@@ -1278,7 +1267,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
       // convert the cursorX into a position on the visible alignment
       int cursor_xpos = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(cursorX);
+              .absoluteToVisibleColumn(cursorX);
 
       if (av.getAlignment().getHiddenColumns().isVisible(cursorX))
       {
@@ -1377,22 +1366,17 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     {
       // package into blocks of visible columns
       int screenY = 0;
-      int blockStart = startRes;
-      int blockEnd = endRes;
+      int blockStart;
+      int blockEnd;
 
-      for (int[] region : av.getAlignment().getHiddenColumns()
-              .getHiddenColumnsCopy())
+      HiddenColumns hidden = av.getAlignment().getHiddenColumns();
+      VisibleContigsIterator regions = hidden
+              .getVisContigsIterator(startRes, endRes + 1, true);
+      while (regions.hasNext())
       {
-        int hideStart = region[0];
-        int hideEnd = region[1];
-
-        if (hideStart <= blockStart)
-        {
-          blockStart += (hideEnd - hideStart) + 1;
-          continue;
-        }
-
-        blockEnd = hideStart - 1;
+        int[] region = regions.next();
+        blockEnd = region[1];
+        blockStart = region[0];
 
         g.translate(screenY * charWidth, 0);
         drawPartialGroupOutline(g, group,
@@ -1400,24 +1384,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
         g.translate(-screenY * charWidth, 0);
         screenY += blockEnd - blockStart + 1;
-        blockStart = hideEnd + 1;
-
-        if (screenY > (endRes - startRes))
-        {
-          // already rendered last block
-          break;
-        }
-      }
-
-      if (screenY <= (endRes - startRes))
-      {
-        // remaining visible region to render
-        blockEnd = blockStart + (endRes - startRes) - screenY;
-        g.translate(screenY * charWidth, 0);
-        drawPartialGroupOutline(g, group,
-                blockStart, blockEnd, startSeq, endSeq, offset);
-        
-        g.translate(-screenY * charWidth, 0);
       }
     }
   }
@@ -1681,9 +1647,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     if (av.hasHiddenColumns())
     {
       firstVisibleColumn = alignment.getHiddenColumns()
-              .adjustForHiddenColumns(firstVisibleColumn);
+              .visibleToAbsoluteColumn(firstVisibleColumn);
       lastVisibleColumn = alignment.getHiddenColumns()
-              .adjustForHiddenColumns(lastVisibleColumn);
+              .visibleToAbsoluteColumn(lastVisibleColumn);
     }
 
     for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
@@ -1726,8 +1692,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       if (av.hasHiddenColumns())
       {
         firstCol = alignment.getHiddenColumns()
-                .findColumnPosition(firstCol);
-        lastCol = alignment.getHiddenColumns().findColumnPosition(lastCol);
+                .absoluteToVisibleColumn(firstCol);
+        lastCol = alignment.getHiddenColumns().absoluteToVisibleColumn(lastCol);
       }
       int transX = (firstCol - ranges.getStartRes()) * av.getCharWidth();
       int transY = (firstSeq - ranges.getStartSeq()) * av.getCharHeight();
@@ -1784,14 +1750,30 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
         scrollX = -range;
       }
     }
-    // Both scrolling and resizing change viewport ranges: scrolling changes
-    // both start and end points, but resize only changes end values.
-    // Here we only want to fastpaint on a scroll, with resize using a normal
-    // paint, so scroll events are identified as changes to the horizontal or
-    // vertical start value.
-    if (eventName.equals(ViewportRanges.STARTRES))
-    {
-      if (av.getWrapAlignment())
+      // Both scrolling and resizing change viewport ranges: scrolling changes
+      // both start and end points, but resize only changes end values.
+      // Here we only want to fastpaint on a scroll, with resize using a normal
+      // paint, so scroll events are identified as changes to the horizontal or
+      // vertical start value.
+      if (eventName.equals(ViewportRanges.STARTRES))
+      {
+         if (av.getWrapAlignment())
+          {
+            fastPaintWrapped(scrollX);
+          }
+          else
+          {
+            fastPaint(scrollX, 0);
+          }
+      }
+      else if (eventName.equals(ViewportRanges.STARTSEQ))
+      {
+        // scroll
+        fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+      }
+      else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+      {
+        if (av.getWrapAlignment())
         {
           fastPaintWrapped(scrollX);
         }
@@ -1811,14 +1793,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       {
         fastPaintWrapped(scrollX);
       }
-      else
-      {
-        fastPaint(scrollX, 0);
-      }
-      // bizarrely, we only need to scroll on the x value here as fastpaint
-      // copies the full height of the image anyway. Passing in the y value
-      // causes nasty repaint artefacts, which only disappear on a full
-      // repaint.
     }
   }
 
@@ -1835,9 +1809,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
   {
     ViewportRanges ranges = av.getRanges();
 
-    // if (Math.abs(scrollX) > ranges.getViewportWidth())
-    // JAL-2836, 2836 temporarily removed wrapped fastpaint for release 2.10.3
-    if (true)
+    if (Math.abs(scrollX) > ranges.getViewportWidth())
     {
       /*
        * shift of more than one view width is 
@@ -2115,9 +2087,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     if (av.hasHiddenColumns())
     {
       firstVisibleColumn = alignment.getHiddenColumns()
-              .adjustForHiddenColumns(firstVisibleColumn);
+              .visibleToAbsoluteColumn(firstVisibleColumn);
       lastVisibleColumn = alignment.getHiddenColumns()
-              .adjustForHiddenColumns(lastVisibleColumn);
+              .visibleToAbsoluteColumn(lastVisibleColumn);
     }
 
     int gapHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1);
@@ -2156,7 +2128,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
               if (av.hasHiddenColumns())
               {
                 displayColumn = alignment.getHiddenColumns()
-                        .findColumnPosition(displayColumn);
+                        .absoluteToVisibleColumn(displayColumn);
               }
 
               /*
index 61cac46..e36fa53 100644 (file)
@@ -250,7 +250,7 @@ public class SeqPanel extends JPanel
     if (av.hasHiddenColumns())
     {
       res = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(res);
+              .visibleToAbsoluteColumn(res);
     }
 
     return res;
@@ -363,13 +363,25 @@ public class SeqPanel extends JPanel
       int original = seqCanvas.cursorX - dx;
       int maxWidth = av.getAlignment().getWidth();
 
-      // TODO: once JAL-2759 is ready, change this loop to something more
-      // efficient
-      while (!hidden.isVisible(seqCanvas.cursorX)
-              && seqCanvas.cursorX < maxWidth && seqCanvas.cursorX > 0
-              && dx != 0)
+      if (!hidden.isVisible(seqCanvas.cursorX))
       {
-        seqCanvas.cursorX += dx;
+        int visx = hidden.absoluteToVisibleColumn(seqCanvas.cursorX - dx);
+        int[] region = hidden.getRegionWithEdgeAtRes(visx);
+
+        if (region != null) // just in case
+        {
+          if (dx == 1)
+          {
+            // moving right
+            seqCanvas.cursorX = region[1] + 1;
+          }
+          else if (dx == -1)
+          {
+            // moving left
+            seqCanvas.cursorX = region[0] - 1;
+          }
+        }
+        seqCanvas.cursorX = (seqCanvas.cursorX < 0) ? 0 : seqCanvas.cursorX;
       }
 
       if (seqCanvas.cursorX >= maxWidth
@@ -424,7 +436,7 @@ public class SeqPanel extends JPanel
       {
         // scrollToWrappedVisible expects x-value to have hidden cols subtracted
         int x = av.getAlignment().getHiddenColumns()
-                .findColumnPosition(seqCanvas.cursorX);
+                .absoluteToVisibleColumn(seqCanvas.cursorX);
         av.getRanges().scrollToWrappedVisible(x);
       }
       else
@@ -1259,9 +1271,9 @@ public class SeqPanel extends JPanel
     {
       fixedColumns = true;
       int y1 = av.getAlignment().getHiddenColumns()
-              .getHiddenBoundaryLeft(startres);
+              .getNextHiddenBoundary(true, startres);
       int y2 = av.getAlignment().getHiddenColumns()
-              .getHiddenBoundaryRight(startres);
+              .getNextHiddenBoundary(false, startres);
 
       if ((insertGap && startres > y1 && lastres < y1)
               || (!insertGap && startres < y2 && lastres > y2))
@@ -1337,7 +1349,8 @@ public class SeqPanel extends JPanel
           if (sg.getSize() == av.getAlignment().getHeight())
           {
             if ((av.hasHiddenColumns() && startres < av.getAlignment()
-                    .getHiddenColumns().getHiddenBoundaryRight(startres)))
+                    .getHiddenColumns()
+                    .getNextHiddenBoundary(false, startres)))
             {
               endEditing();
               return;
@@ -1656,7 +1669,8 @@ public class SeqPanel extends JPanel
   public void mouseWheelMoved(MouseWheelEvent e)
   {
     e.consume();
-    if (e.getWheelRotation() > 0)
+    double wheelRotation = e.getPreciseWheelRotation();
+    if (wheelRotation > 0)
     {
       if (e.isShiftDown())
       {
@@ -1668,7 +1682,7 @@ public class SeqPanel extends JPanel
         av.getRanges().scrollUp(false);
       }
     }
-    else
+    else if (wheelRotation < 0)
     {
       if (e.isShiftDown())
       {
@@ -2288,4 +2302,13 @@ public class SeqPanel extends JPanel
 
     return true;
   }
+
+  /**
+   * 
+   * @return null or last search results handled by this panel
+   */
+  public SearchResultsI getLastSearchResults()
+  {
+    return lastSearchResults;
+  }
 }
index 8d46792..f545e70 100755 (executable)
@@ -273,7 +273,7 @@ public class SequenceFetcher extends JPanel implements Runnable
         return Collections.emptyList();
       }
     }
-    sf.newAlframes = new ArrayList<AlignFrame>();
+    sf.newAlframes = new ArrayList<>();
     sf.run();
     return sf.newAlframes;
   }
@@ -674,10 +674,10 @@ public class SequenceFetcher extends JPanel implements Runnable
     // TODO: Refactor to GUI independent code and write tests.
     // indicate if successive sources should be merged into one alignment.
     boolean addToLast = false;
-    List<String> aresultq = new ArrayList<String>();
-    List<String> presultTitle = new ArrayList<String>();
-    List<AlignmentI> presult = new ArrayList<AlignmentI>();
-    List<AlignmentI> aresult = new ArrayList<AlignmentI>();
+    List<String> aresultq = new ArrayList<>();
+    List<String> presultTitle = new ArrayList<>();
+    List<AlignmentI> presult = new ArrayList<>();
+    List<AlignmentI> aresult = new ArrayList<>();
     Iterator<DbSourceProxy> proxies = database.getSelectedSources()
             .iterator();
     String[] qries;
@@ -695,7 +695,7 @@ public class SequenceFetcher extends JPanel implements Runnable
         nqueries = nextFetch.size();
         // save the remaining queries in the original array
         qries = nextFetch.toArray(new String[nqueries]);
-        nextFetch = new ArrayList<String>();
+        nextFetch = new ArrayList<>();
       }
 
       DbSourceProxy proxy = proxies.next();
@@ -861,7 +861,7 @@ public class SequenceFetcher extends JPanel implements Runnable
           List<AlignmentI> aresult, List<String> nextFetch) throws Exception
   {
     StringBuilder multiacc = new StringBuilder();
-    List<String> tosend = new ArrayList<String>();
+    List<String> tosend = new ArrayList<>();
     while (accessions.hasNext())
     {
       String nel = accessions.next();
index 217f653..e18d6af 100644 (file)
@@ -21,6 +21,8 @@
 
 package jalview.gui;
 
+import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.bin.Cache;
 import jalview.bin.Jalview;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
@@ -53,6 +55,8 @@ import java.util.Vector;
 import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.SwingUtilities;
 import javax.swing.table.AbstractTableModel;
 
 /**
@@ -65,6 +69,8 @@ import javax.swing.table.AbstractTableModel;
 public class StructureChooser extends GStructureChooser
         implements IProgressIndicator
 {
+  private static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
+
   private static int MAX_QLENGTH = 7820;
 
   private SequenceI selectedSequence;
@@ -85,6 +91,8 @@ public class StructureChooser extends GStructureChooser
 
   private boolean cachedPDBExists;
 
+  private static StructureViewer lastTargetedView = null;
+
   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
           AlignmentPanel ap)
   {
@@ -98,13 +106,15 @@ public class StructureChooser extends GStructureChooser
   /**
    * Initializes parameters used by the Structure Chooser Panel
    */
-  public void init()
+  protected void init()
   {
     if (!Jalview.isHeadlessMode())
     {
       progressBar = new ProgressBar(this.statusPanel, this.statusBar);
     }
 
+    chk_superpose.setSelected(Cache.getDefault(AUTOSUPERIMPOSE, true));
+
     // ensure a filter option is in force for search
     populateFilterComboBox(true, cachedPDBExists);
     Thread discoverPDBStructuresThread = new Thread(new Runnable()
@@ -122,6 +132,7 @@ public class StructureChooser extends GStructureChooser
         fetchStructuresMetaData();
         // revise filter options if no results were found
         populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
+        discoverStructureViews();
         updateProgressIndicator(null, startTime);
         mainFrame.setVisible(true);
         updateCurrentView();
@@ -131,6 +142,59 @@ public class StructureChooser extends GStructureChooser
   }
 
   /**
+   * Builds a drop-down choice list of existing structure viewers to which new
+   * structures may be added. If this list is empty then it, and the 'Add'
+   * button, are hidden.
+   */
+  private void discoverStructureViews()
+  {
+    if (Desktop.instance != null)
+    {
+      targetView.removeAllItems();
+      if (lastTargetedView != null && !lastTargetedView.isVisible())
+      {
+        lastTargetedView = null;
+      }
+      int linkedViewsAt = 0;
+      for (StructureViewerBase view : Desktop.instance
+              .getStructureViewers(null, null))
+      {
+        StructureViewer viewHandler = (lastTargetedView != null
+                && lastTargetedView.sview == view) ? lastTargetedView
+                        : StructureViewer.reconfigure(view);
+
+        if (view.isLinkedWith(ap))
+        {
+          targetView.insertItemAt(viewHandler,
+                  linkedViewsAt++);
+        }
+        else
+        {
+          targetView.addItem(viewHandler);
+        }
+      }
+
+      /*
+       * show option to Add to viewer if at least 1 viewer found
+       */
+      targetView.setVisible(false);
+      if (targetView.getItemCount() > 0)
+      {
+        targetView.setVisible(true);
+        if (lastTargetedView != null)
+        {
+          targetView.setSelectedItem(lastTargetedView);
+        }
+        else
+        {
+          targetView.setSelectedIndex(0);
+        }
+      }
+      btn_add.setVisible(targetView.isVisible());
+    }
+  }
+
+  /**
    * Updates the progress indicator with the specified message
    * 
    * @param message
@@ -138,7 +202,7 @@ public class StructureChooser extends GStructureChooser
    * @param id
    *          unique handle for this indicator
    */
-  public void updateProgressIndicator(String message, long id)
+  protected void updateProgressIndicator(String message, long id)
   {
     if (progressIndicator != null)
     {
@@ -150,7 +214,7 @@ public class StructureChooser extends GStructureChooser
    * Retrieve meta-data for all the structure(s) for a given sequence(s) in a
    * selection group
    */
-  public void fetchStructuresMetaData()
+  void fetchStructuresMetaData()
   {
     long startTime = System.currentTimeMillis();
     pdbRestCleint = PDBFTSRestClient.getInstance();
@@ -221,7 +285,7 @@ public class StructureChooser extends GStructureChooser
     }
   }
 
-  public void loadLocalCachedPDBEntries()
+  protected void loadLocalCachedPDBEntries()
   {
     ArrayList<CachedPDB> entries = new ArrayList<>();
     for (SequenceI seq : selectedSequences)
@@ -252,7 +316,7 @@ public class StructureChooser extends GStructureChooser
    * @return the built query string
    */
 
-  public static String buildQuery(SequenceI seq)
+  static String buildQuery(SequenceI seq)
   {
     boolean isPDBRefsFound = false;
     boolean isUniProtRefsFound = false;
@@ -354,7 +418,7 @@ public class StructureChooser extends GStructureChooser
    * @param seqName
    * @return
    */
-  public static boolean isValidSeqName(String seqName)
+  static boolean isValidSeqName(String seqName)
   {
     // System.out.println("seqName : " + seqName);
     String ignoreList = "pdb,uniprot,swiss-prot";
@@ -377,7 +441,7 @@ public class StructureChooser extends GStructureChooser
     return true;
   }
 
-  public static String getDBRefId(DBRefEntry dbRef)
+  static String getDBRefId(DBRefEntry dbRef)
   {
     String ref = dbRef.getAccessionId().replaceAll("GO:", "");
     return ref;
@@ -389,7 +453,7 @@ public class StructureChooser extends GStructureChooser
    * @param fieldToFilterBy
    *          the field to filter by
    */
-  public void filterResultSet(final String fieldToFilterBy)
+  void filterResultSet(final String fieldToFilterBy)
   {
     Thread filterThread = new Thread(new Runnable()
     {
@@ -499,7 +563,7 @@ public class StructureChooser extends GStructureChooser
    * Handles action event for btn_pdbFromFile
    */
   @Override
-  public void pdbFromFile_actionPerformed()
+  protected void pdbFromFile_actionPerformed()
   {
     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
@@ -600,28 +664,37 @@ public class StructureChooser extends GStructureChooser
   }
 
   /**
-   * Validates user selection and activates the view button if all parameters
-   * are correct
+   * Validates user selection and enables the 'Add' and 'New View' buttons if
+   * all parameters are correct (the Add button will only be visible if there is
+   * at least one existing structure viewer open). This basically means at least
+   * one structure selected and no error messages.
+   * <p>
+   * The 'Superpose Structures' option is enabled if either more than one
+   * structure is selected, or the 'Add' to existing view option is enabled, and
+   * disabled if the only option is to open a new view of a single structure.
    */
   @Override
-  public void validateSelections()
+  protected void validateSelections()
   {
     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
             .getSelectedItem());
-    btn_view.setEnabled(false);
+    btn_add.setEnabled(false);
     String currentView = selectedFilterOpt.getView();
+    int selectedCount = 0;
     if (currentView == VIEWS_FILTER)
     {
-      if (getResultTable().getSelectedRows().length > 0)
+      selectedCount = getResultTable().getSelectedRows().length;
+      if (selectedCount > 0)
       {
-        btn_view.setEnabled(true);
+        btn_add.setEnabled(true);
       }
     }
     else if (currentView == VIEWS_LOCAL_PDB)
     {
-      if (tbl_local_pdb.getSelectedRows().length > 0)
+      selectedCount = tbl_local_pdb.getSelectedRows().length;
+      if (selectedCount > 0)
       {
-        btn_view.setEnabled(true);
+        btn_add.setEnabled(true);
       }
     }
     else if (currentView == VIEWS_ENTER_ID)
@@ -632,12 +705,21 @@ public class StructureChooser extends GStructureChooser
     {
       validateAssociationFromFile();
     }
+
+    btn_newView.setEnabled(btn_add.isEnabled());
+
+    /*
+     * enable 'Superpose' option if more than one structure is selected,
+     * or there are view(s) available to add structure(s) to
+     */
+    chk_superpose
+            .setEnabled(selectedCount > 1 || targetView.getItemCount() > 0);
   }
 
   /**
    * Validates inputs from the Manual PDB entry panel
    */
-  public void validateAssociationEnterPdb()
+  protected void validateAssociationEnterPdb()
   {
     AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) idInputAssSeqPanel
             .getCmb_assSeq().getSelectedItem();
@@ -663,7 +745,7 @@ public class StructureChooser extends GStructureChooser
       txt_search.setEnabled(true);
       if (isValidPBDEntry)
       {
-        btn_view.setEnabled(true);
+        btn_add.setEnabled(true);
         lbl_pdbManualFetchStatus.setToolTipText("");
         lbl_pdbManualFetchStatus.setIcon(goodImage);
       }
@@ -678,7 +760,7 @@ public class StructureChooser extends GStructureChooser
   /**
    * Validates inputs for the manual PDB file selection options
    */
-  public void validateAssociationFromFile()
+  protected void validateAssociationFromFile()
   {
     AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
             .getCmb_assSeq().getSelectedItem();
@@ -689,7 +771,7 @@ public class StructureChooser extends GStructureChooser
       btn_pdbFromFile.setEnabled(true);
       if (selectedPdbFileName != null && selectedPdbFileName.length() > 0)
       {
-        btn_view.setEnabled(true);
+        btn_add.setEnabled(true);
         lbl_fromFileStatus.setIcon(goodImage);
       }
     }
@@ -701,7 +783,7 @@ public class StructureChooser extends GStructureChooser
   }
 
   @Override
-  public void cmbAssSeqStateChanged()
+  protected void cmbAssSeqStateChanged()
   {
     validateSelections();
   }
@@ -728,16 +810,76 @@ public class StructureChooser extends GStructureChooser
   }
 
   /**
-   * Handles action event for btn_ok
+   * select structures for viewing by their PDB IDs
+   * 
+   * @param pdbids
+   * @return true if structures were found and marked as selected
+   */
+  public boolean selectStructure(String... pdbids)
+  {
+    boolean found = false;
+
+    FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+            .getSelectedItem());
+    String currentView = selectedFilterOpt.getView();
+    JTable restable = (currentView == VIEWS_FILTER) ? getResultTable()
+            : (currentView == VIEWS_LOCAL_PDB) ? tbl_local_pdb : null;
+
+    if (restable == null)
+    {
+      // can't select (enter PDB ID, or load file - need to also select which
+      // sequence to associate with)
+      return false;
+    }
+
+    int pdbIdColIndex = restable.getColumn("PDB Id").getModelIndex();
+    for (int r = 0; r < restable.getRowCount(); r++)
+    {
+      for (int p = 0; p < pdbids.length; p++)
+      {
+        if (String.valueOf(restable.getValueAt(r, pdbIdColIndex))
+                .equalsIgnoreCase(pdbids[p]))
+        {
+          restable.setRowSelectionInterval(r, r);
+          found = true;
+        }
+      }
+    }
+    return found;
+  }
+  
+  /**
+   * Handles the 'New View' action
    */
   @Override
-  public void ok_ActionPerformed()
+  protected void newView_ActionPerformed()
   {
+    targetView.setSelectedItem(null);
+    showStructures(false);
+  }
+
+  /**
+   * Handles the 'Add to existing viewer' action
+   */
+  @Override
+  protected void add_ActionPerformed()
+  {
+    showStructures(false);
+  }
+
+  /**
+   * structure viewer opened by this dialog, or null
+   */
+  private StructureViewer sViewer = null;
+
+  public void showStructures(boolean waitUntilFinished)
+  {
+
     final StructureSelectionManager ssm = ap.getStructureSelectionManager();
 
     final int preferredHeight = pnl_filter.getHeight();
 
-    new Thread(new Runnable()
+    Runnable viewStruc = new Runnable()
     {
       @Override
       public void run()
@@ -745,21 +887,24 @@ public class StructureChooser extends GStructureChooser
         FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
                 .getSelectedItem());
         String currentView = selectedFilterOpt.getView();
+        JTable restable = (currentView == VIEWS_FILTER) ? getResultTable()
+                : tbl_local_pdb;
+
         if (currentView == VIEWS_FILTER)
         {
-          int pdbIdColIndex = getResultTable().getColumn("PDB Id")
+          int pdbIdColIndex = restable.getColumn("PDB Id")
                   .getModelIndex();
-          int refSeqColIndex = getResultTable().getColumn("Ref Sequence")
+          int refSeqColIndex = restable.getColumn("Ref Sequence")
                   .getModelIndex();
-          int[] selectedRows = getResultTable().getSelectedRows();
+          int[] selectedRows = restable.getSelectedRows();
           PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
           int count = 0;
           List<SequenceI> selectedSeqsToView = new ArrayList<>();
           for (int row : selectedRows)
           {
-            String pdbIdStr = getResultTable()
+            String pdbIdStr = restable
                     .getValueAt(row, pdbIdColIndex).toString();
-            SequenceI selectedSeq = (SequenceI) getResultTable()
+            SequenceI selectedSeq = (SequenceI) restable
                     .getValueAt(row, refSeqColIndex);
             selectedSeqsToView.add(selectedSeq);
             PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
@@ -780,7 +925,8 @@ public class StructureChooser extends GStructureChooser
           }
           SequenceI[] selectedSeqs = selectedSeqsToView
                   .toArray(new SequenceI[selectedSeqsToView.size()]);
-          launchStructureViewer(ssm, pdbEntriesToView, ap, selectedSeqs);
+          sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
+                  selectedSeqs);
         }
         else if (currentView == VIEWS_LOCAL_PDB)
         {
@@ -803,7 +949,8 @@ public class StructureChooser extends GStructureChooser
           }
           SequenceI[] selectedSeqs = selectedSeqsToView
                   .toArray(new SequenceI[selectedSeqsToView.size()]);
-          launchStructureViewer(ssm, pdbEntriesToView, ap, selectedSeqs);
+          sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
+                  selectedSeqs);
         }
         else if (currentView == VIEWS_ENTER_ID)
         {
@@ -832,7 +979,7 @@ public class StructureChooser extends GStructureChooser
           }
 
           PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
-          launchStructureViewer(ssm, pdbEntriesToView, ap,
+          sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
                   new SequenceI[]
                   { selectedSequence });
         }
@@ -849,14 +996,40 @@ public class StructureChooser extends GStructureChooser
                           DataSourceType.FILE, selectedSequence, true,
                           Desktop.instance);
 
-          launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap,
+          sViewer = launchStructureViewer(
+                  ssm, new PDBEntry[]
+                  { fileEntry }, ap,
                   new SequenceI[]
                   { selectedSequence });
         }
-        closeAction(preferredHeight);
-        mainFrame.dispose();
+        SwingUtilities.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+            closeAction(preferredHeight);
+            mainFrame.dispose();
+          }
+        });
       }
-    }).start();
+    };
+    Thread runner = new Thread(viewStruc);
+    runner.start();
+    if (waitUntilFinished)
+    {
+      while (sViewer == null ? runner.isAlive()
+              : (sViewer.sview == null ? true
+                      : !sViewer.sview.hasMapping()))
+      {
+        try
+        {
+          Thread.sleep(300);
+        } catch (InterruptedException ie)
+        {
+
+        }
+      }
+    }
   }
 
   private PDBEntry getFindEntry(String id, Vector<PDBEntry> pdbEntries)
@@ -874,16 +1047,49 @@ public class StructureChooser extends GStructureChooser
     return foundEntry;
   }
 
-  private void launchStructureViewer(StructureSelectionManager ssm,
+  /**
+   * Answers a structure viewer (new or existing) configured to superimpose
+   * added structures or not according to the user's choice
+   * 
+   * @param ssm
+   * @return
+   */
+  StructureViewer getTargetedStructureViewer(
+          StructureSelectionManager ssm)
+  {
+    Object sv = targetView.getSelectedItem();
+
+    return sv == null ? new StructureViewer(ssm) : (StructureViewer) sv;
+  }
+
+  /**
+   * Adds PDB structures to a new or existing structure viewer
+   * 
+   * @param ssm
+   * @param pdbEntriesToView
+   * @param alignPanel
+   * @param sequences
+   * @return
+   */
+  private StructureViewer launchStructureViewer(
+          StructureSelectionManager ssm,
           final PDBEntry[] pdbEntriesToView,
           final AlignmentPanel alignPanel, SequenceI[] sequences)
   {
     long progressId = sequences.hashCode();
     setProgressBar(MessageManager
             .getString("status.launching_3d_structure_viewer"), progressId);
-    final StructureViewer sViewer = new StructureViewer(ssm);
-    setProgressBar(null, progressId);
+    final StructureViewer theViewer = getTargetedStructureViewer(ssm);
+    boolean superimpose = chk_superpose.isSelected();
+    theViewer.setSuperpose(superimpose);
 
+    /*
+     * remember user's choice of superimpose or not
+     */
+    Cache.setProperty(AUTOSUPERIMPOSE,
+            Boolean.valueOf(superimpose).toString());
+
+    setProgressBar(null, progressId);
     if (SiftsSettings.isMapWithSifts())
     {
       List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<>();
@@ -908,7 +1114,7 @@ public class StructureChooser extends GStructureChooser
             }
           }
         }
-        if (seq.getPrimaryDBRefs().size() == 0)
+        if (seq.getPrimaryDBRefs().isEmpty())
         {
           seqsWithoutSourceDBRef.add(seq);
           continue;
@@ -920,13 +1126,8 @@ public class StructureChooser extends GStructureChooser
         setProgressBar(MessageManager.formatMessage(
                 "status.fetching_dbrefs_for_sequences_without_valid_refs",
                 y), progressId);
-        SequenceI[] seqWithoutSrcDBRef = new SequenceI[y];
-        int x = 0;
-        for (SequenceI fSeq : seqsWithoutSourceDBRef)
-        {
-          seqWithoutSrcDBRef[x++] = fSeq;
-        }
-
+        SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
+                .toArray(new SequenceI[y]);
         DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
         dbRefFetcher.fetchDBRefs(true);
 
@@ -938,16 +1139,19 @@ public class StructureChooser extends GStructureChooser
       setProgressBar(MessageManager.getString(
               "status.fetching_3d_structures_for_selected_entries"),
               progressId);
-      sViewer.viewStructures(pdbEntriesToView, sequences, alignPanel);
+      theViewer.viewStructures(pdbEntriesToView, sequences, alignPanel);
     }
     else
     {
       setProgressBar(MessageManager.formatMessage(
               "status.fetching_3d_structures_for",
               pdbEntriesToView[0].getId()),progressId);
-      sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
+      theViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
     }
     setProgressBar(null, progressId);
+    // remember the last viewer we used...
+    lastTargetedView = theViewer;
+    return theViewer;
   }
 
   /**
@@ -955,7 +1159,7 @@ public class StructureChooser extends GStructureChooser
    * a unique sequence when more than one sequence selection is made.
    */
   @Override
-  public void populateCmbAssociateSeqOptions(
+  protected void populateCmbAssociateSeqOptions(
           JComboBox<AssociateSeqOptions> cmb_assSeq,
           JLabel lbl_associateSeq)
   {
@@ -980,17 +1184,12 @@ public class StructureChooser extends GStructureChooser
     }
   }
 
-  public boolean isStructuresDiscovered()
+  protected boolean isStructuresDiscovered()
   {
     return discoveredStructuresSet != null
             && !discoveredStructuresSet.isEmpty();
   }
 
-  public Collection<FTSData> getDiscoveredStructuresSet()
-  {
-    return discoveredStructuresSet;
-  }
-
   @Override
   protected void txt_search_ActionPerformed()
   {
@@ -1040,7 +1239,7 @@ public class StructureChooser extends GStructureChooser
   }
 
   @Override
-  public void tabRefresh()
+  protected void tabRefresh()
   {
     if (selectedSequences != null)
     {
@@ -1178,4 +1377,9 @@ public class StructureChooser extends GStructureChooser
   {
     return progressBar.operationInProgress();
   }
+
+  public JalviewStructureDisplayI getOpenedStructureViewer()
+  {
+    return sViewer == null ? null : sViewer.sview;
+  }
 }
index fb37b77..0c8354b 100644 (file)
@@ -49,6 +49,11 @@ public class StructureViewer
 
   StructureSelectionManager ssm;
 
+  /**
+   * decide if new structures are aligned to existing ones
+   */
+  private boolean superposeAdded = true;
+
   public enum ViewerType
   {
     JMOL, CHIMERA
@@ -64,6 +69,27 @@ public class StructureViewer
     ssm = structureSelectionManager;
   }
 
+  /**
+   * Factory to create a proxy for modifying existing structure viewer
+   * 
+   */
+  public static StructureViewer reconfigure(
+          JalviewStructureDisplayI display)
+  {
+    StructureViewer sv = new StructureViewer(display.getBinding().getSsm());
+    sv.sview = display;
+    return sv;
+  }
+
+  @Override
+  public String toString()
+  {
+    if (sview != null)
+    {
+      return sview.toString();
+    }
+    return "New View";
+  }
   public ViewerType getViewerType()
   {
     String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY,
@@ -104,14 +130,40 @@ public class StructureViewer
             new PDBEntry[seqsForPdbs.size()]);
     SequenceI[][] theSeqs = seqsForPdbs.values().toArray(
             new SequenceI[seqsForPdbs.size()][]);
-    JalviewStructureDisplayI sview = null;
+    if (sview != null)
+    {
+      sview.setAlignAddedStructures(superposeAdded);
+      new Thread(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+
+          for (int pdbep = 0; pdbep < pdbsForFile.length; pdbep++)
+          {
+            PDBEntry pdb = pdbsForFile[pdbep];
+            if (!sview.addAlreadyLoadedFile(theSeqs[pdbep], null, ap,
+                    pdb.getId()))
+            {
+              sview.addToExistingViewer(pdb, theSeqs[pdbep], null, ap,
+                      pdb.getId());
+            }
+          }
+
+          sview.updateTitleAndMenus();
+        }
+      }).start();
+      return sview;
+    }
+
     if (viewerType.equals(ViewerType.JMOL))
     {
-      sview = new AppJmol(ap, pdbsForFile, theSeqs);
+      sview = new AppJmol(ap, superposeAdded, pdbsForFile, theSeqs);
     }
     else if (viewerType.equals(ViewerType.CHIMERA))
     {
-      sview = new ChimeraViewFrame(pdbsForFile, theSeqs, ap);
+      sview = new ChimeraViewFrame(pdbsForFile, superposeAdded, theSeqs,
+              ap);
     }
     else
     {
@@ -203,7 +255,7 @@ public class StructureViewer
   private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs,
           SequenceI[] seqsForPdbs, AlignmentPanel ap)
   {
-    List<SequenceI> seqs = new ArrayList<SequenceI>();
+    List<SequenceI> seqs = new ArrayList<>();
     if (pdbs == null || pdbs.length == 0)
     {
       return null;
@@ -227,11 +279,24 @@ public class StructureViewer
             ap);
   }
 
+  JalviewStructureDisplayI sview = null;
+
   public JalviewStructureDisplayI viewStructures(PDBEntry pdb,
           SequenceI[] seqsForPdb, AlignmentPanel ap)
   {
+    if (sview != null)
+    {
+      sview.setAlignAddedStructures(superposeAdded);
+      String pdbId = pdb.getId();
+      if (!sview.addAlreadyLoadedFile(seqsForPdb, null, ap, pdbId))
+      {
+        sview.addToExistingViewer(pdb, seqsForPdb, null, ap, pdbId);
+      }
+      sview.updateTitleAndMenus();
+      sview.raiseViewer();
+      return sview;
+    }
     ViewerType viewerType = getViewerType();
-    JalviewStructureDisplayI sview = null;
     if (viewerType.equals(ViewerType.JMOL))
     {
       sview = new AppJmol(pdb, seqsForPdb, null, ap);
@@ -270,7 +335,6 @@ public class StructureViewer
     final boolean usetoColourbyseq = viewerData.isColourWithAlignPanel();
     final boolean viewerColouring = viewerData.isColourByViewer();
 
-    JalviewStructureDisplayI sview = null;
     switch (type)
     {
     case JMOL:
@@ -287,4 +351,41 @@ public class StructureViewer
     return sview;
   }
 
+  public boolean isBusy()
+  {
+    if (sview != null)
+    {
+      if (!sview.hasMapping())
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * 
+   * @param pDBid
+   * @return true if view is already showing PDBid
+   */
+  public boolean hasPdbId(String pDBid)
+  {
+    if (sview == null)
+    {
+      return false;
+    }
+
+    return sview.getBinding().hasPdbId(pDBid);
+  }
+
+  public boolean isVisible()
+  {
+    return sview != null && sview.isVisible();
+  }
+
+  public void setSuperpose(boolean alignAddedStructures)
+  {
+    superposeAdded = alignAddedStructures;
+  }
+
 }
index 31c20ed..72b0bcc 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.gui;
 
+import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
@@ -34,6 +35,7 @@ import jalview.io.JalviewFileView;
 import jalview.jbgui.GStructureViewer;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemes;
+import jalview.structure.StructureMapping;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.MessageManager;
 
@@ -102,9 +104,9 @@ public abstract class StructureViewerBase extends GStructureViewer
 
   protected boolean alignAddedStructures = false;
 
-  protected boolean _started = false;
+  protected volatile boolean _started = false;
 
-  protected boolean addingStructures = false;
+  protected volatile boolean addingStructures = false;
 
   protected Thread worker = null;
 
@@ -113,6 +115,13 @@ public abstract class StructureViewerBase extends GStructureViewer
   protected JMenu viewSelectionMenu;
 
   /**
+   * set after sequence colouring has been applied for this structure viewer.
+   * used to determine if the final sequence/structure mapping has been
+   * determined
+   */
+  protected volatile boolean seqColoursApplied = false;
+
+  /**
    * Default constructor
    */
   public StructureViewerBase()
@@ -121,6 +130,26 @@ public abstract class StructureViewerBase extends GStructureViewer
   }
 
   /**
+   * @return true if added structures should be aligned to existing one(s)
+   */
+  @Override
+  public boolean isAlignAddedStructures()
+  {
+    return alignAddedStructures;
+  }
+
+  /**
+   * 
+   * @param true
+   *          if added structures should be aligned to existing one(s)
+   */
+  @Override
+  public void setAlignAddedStructures(boolean alignAdded)
+  {
+    alignAddedStructures = alignAdded;
+  }
+
+  /**
    * 
    * @param ap2
    * @return true if this Jmol instance is linked with the given alignPanel
@@ -326,7 +355,7 @@ public abstract class StructureViewerBase extends GStructureViewer
    */
   protected void addStructure(final PDBEntry pdbentry,
           final SequenceI[] seqs, final String[] chains,
-          final boolean align, final IProgressIndicator alignFrame)
+          final IProgressIndicator alignFrame)
   {
     if (pdbentry.getFile() == null)
     {
@@ -350,7 +379,7 @@ public abstract class StructureViewerBase extends GStructureViewer
               }
             }
             // and call ourselves again.
-            addStructure(pdbentry, seqs, chains, align, alignFrame);
+            addStructure(pdbentry, seqs, chains, alignFrame);
           }
         }).start();
         return;
@@ -362,87 +391,42 @@ public abstract class StructureViewerBase extends GStructureViewer
             { seqs }, new String[][] { chains });
     addingStructures = true;
     _started = false;
-    alignAddedStructures = align;
     worker = new Thread(this);
     worker.start();
     return;
   }
 
-  /**
-   * Presents a dialog with the option to add an align a structure to an
-   * existing structure view
-   * 
-   * @param pdbId
-   * @param view
-   * @return YES, NO or CANCEL JvOptionPane code
-   */
-  protected int chooseAlignStructureToViewer(String pdbId,
-          StructureViewerBase view)
-  {
-    int option = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
-            MessageManager.formatMessage("label.add_pdbentry_to_view",
-                    new Object[]
-                    { pdbId, view.getTitle() }),
-            MessageManager
-                    .getString("label.align_to_existing_structure_view"),
-            JvOptionPane.YES_NO_CANCEL_OPTION);
-    return option;
-  }
-
   protected boolean hasPdbId(String pdbId)
   {
     return getBinding().hasPdbId(pdbId);
   }
 
-  protected abstract List<StructureViewerBase> getViewersFor(
-          AlignmentPanel alp);
-
   /**
-   * Check for any existing views involving this alignment and give user the
-   * option to add and align this molecule to one of them
-   * 
-   * @param pdbentry
-   * @param seq
-   * @param chains
-   * @param apanel
-   * @param pdbId
-   * @return true if user adds to a view, or cancels entirely, else false
+   * Returns a list of any viewer of the instantiated type. The list is
+   * restricted to those linked to the given alignment panel if it is not null.
    */
-  protected boolean addToExistingViewer(PDBEntry pdbentry, SequenceI[] seq,
-          String[] chains, final AlignmentPanel apanel, String pdbId)
+  protected List<StructureViewerBase> getViewersFor(AlignmentPanel alp)
   {
-    for (StructureViewerBase view : getViewersFor(apanel))
-    {
-      // TODO: highlight the view somehow
-      /*
-       * JAL-1742 exclude view with this structure already mapped (don't offer
-       * to align chain B to chain A of the same structure)
-       */
-      if (view.hasPdbId(pdbId))
-      {
-        continue;
-      }
-      int option = chooseAlignStructureToViewer(pdbId, view);
-      if (option == JvOptionPane.CANCEL_OPTION)
-      {
-        return true;
-      }
-      else if (option == JvOptionPane.YES_OPTION)
-      {
-        view.useAlignmentPanelForSuperposition(apanel);
-        view.addStructure(pdbentry, seq, chains, true, apanel.alignFrame);
-        return true;
-      }
-      else
-      {
-        // NO_OPTION - offer the next viewer if any
-      }
-    }
+    return Desktop.instance.getStructureViewers(alp, this.getClass());
+  }
 
+  @Override
+  public void addToExistingViewer(PDBEntry pdbentry, SequenceI[] seq,
+          String[] chains, final AlignmentViewPanel apanel, String pdbId)
+  {
     /*
-     * nothing offered and selected
+     * JAL-1742 exclude view with this structure already mapped (don't offer
+     * to align chain B to chain A of the same structure); code may defend
+     * against this possibility before we reach here
      */
-    return false;
+    if (hasPdbId(pdbId))
+    {
+      return;
+    }
+    AlignmentPanel alignPanel = (AlignmentPanel) apanel; // Implementation error if this
+                                                 // cast fails
+    useAlignmentPanelForSuperposition(alignPanel);
+    addStructure(pdbentry, seq, chains, alignPanel.alignFrame);
   }
 
   /**
@@ -454,9 +438,12 @@ public abstract class StructureViewerBase extends GStructureViewer
    * @param apanel
    * @param pdbFilename
    */
-  protected void addSequenceMappingsToStructure(SequenceI[] seq,
-          String[] chains, final AlignmentPanel apanel, String pdbFilename)
+  public void addSequenceMappingsToStructure(SequenceI[] seq,
+          String[] chains, final AlignmentViewPanel alpanel,
+          String pdbFilename)
   {
+    AlignmentPanel apanel = (AlignmentPanel) alpanel;
+
     // TODO : Fix multiple seq to one chain issue here.
     /*
      * create the mappings
@@ -503,47 +490,20 @@ public abstract class StructureViewerBase extends GStructureViewer
     }
   }
 
-  /**
-   * Check if the PDB file is already loaded, if so offer to add it to the
-   * existing viewer
-   * 
-   * @param seq
-   * @param chains
-   * @param apanel
-   * @param pdbId
-   * @return true if the user chooses to add to a viewer, or to cancel entirely
-   */
-  protected boolean addAlreadyLoadedFile(SequenceI[] seq, String[] chains,
-          final AlignmentPanel apanel, String pdbId)
+  @Override
+  public boolean addAlreadyLoadedFile(SequenceI[] seq, String[] chains,
+          final AlignmentViewPanel apanel, String pdbId)
   {
-    boolean finished = false;
     String alreadyMapped = apanel.getStructureSelectionManager()
             .alreadyMappedToFile(pdbId);
 
-    if (alreadyMapped != null)
+    if (alreadyMapped == null)
     {
-      /*
-       * the PDB file is already loaded
-       */
-      int option = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
-              MessageManager.formatMessage(
-                      "label.pdb_entry_is_already_displayed", new Object[]
-                      { pdbId }),
-              MessageManager.formatMessage(
-                      "label.map_sequences_to_visible_window", new Object[]
-                      { pdbId }),
-              JvOptionPane.YES_NO_CANCEL_OPTION);
-      if (option == JvOptionPane.CANCEL_OPTION)
-      {
-        finished = true;
-      }
-      else if (option == JvOptionPane.YES_OPTION)
-      {
-        addSequenceMappingsToStructure(seq, chains, apanel, alreadyMapped);
-        finished = true;
-      }
+      return false;
     }
-    return finished;
+
+    addSequenceMappingsToStructure(seq, chains, apanel, alreadyMapped);
+    return true;
   }
 
   void setChainMenuItems(List<String> chainNames)
@@ -823,11 +783,11 @@ public abstract class StructureViewerBase extends GStructureViewer
       int[] alm = new int[_alignwith.size()];
       int a = 0;
 
-      for (AlignmentPanel ap : _alignwith)
+      for (AlignmentPanel alignPanel : _alignwith)
       {
-        als[a] = ap.av.getAlignment();
+        als[a] = alignPanel.av.getAlignment();
         alm[a] = -1;
-        alc[a++] = ap.av.getAlignment().getHiddenColumns();
+        alc[a++] = alignPanel.av.getAlignment().getHiddenColumns();
       }
       reply = getBinding().superposeStructures(als, alm, alc);
       if (reply != null)
@@ -839,9 +799,9 @@ public abstract class StructureViewerBase extends GStructureViewer
     } catch (Exception e)
     {
       StringBuffer sp = new StringBuffer();
-      for (AlignmentPanel ap : _alignwith)
+      for (AlignmentPanel alignPanel : _alignwith)
       {
-        sp.append("'" + ap.alignFrame.getTitle() + "' ");
+        sp.append("'" + alignPanel.alignFrame.getTitle() + "' ");
       }
       Cache.log.info("Couldn't align structures with the " + sp.toString()
               + "associated alignment panels.", e);
@@ -905,10 +865,11 @@ public abstract class StructureViewerBase extends GStructureViewer
         }
       }
       // Set the colour using the current view for the associated alignframe
-      for (AlignmentPanel ap : _colourwith)
+      for (AlignmentPanel alignPanel : _colourwith)
       {
-        binding.colourBySequence(ap);
+        binding.colourBySequence(alignPanel);
       }
+      seqColoursApplied = true;
     }
   }
 
@@ -988,6 +949,7 @@ public abstract class StructureViewerBase extends GStructureViewer
   /**
    * Configures the title and menu items of the viewer panel.
    */
+  @Override
   public void updateTitleAndMenus()
   {
     AAStructureBindingModel binding = getBinding();
@@ -1028,4 +990,54 @@ public abstract class StructureViewerBase extends GStructureViewer
       seqColour_actionPerformed(null);
     }
   }
+
+  @Override
+  public String toString()
+  {
+    return getTitle();
+  }
+
+  @Override
+  public boolean hasMapping()
+  {
+    if (worker != null && (addingStructures || _started))
+    {
+      return false;
+    }
+    if (getBinding() == null)
+    {
+      if (_aps == null || _aps.size() == 0)
+      {
+        // viewer has been closed, but we did at some point run.
+        return true;
+      }
+      return false;
+    }
+    String[] pdbids = getBinding().getStructureFiles();
+    if (pdbids == null)
+    {
+      return false;
+    }
+    int p=0;
+    for (String pdbid:pdbids) {
+      StructureMapping sm[] = getBinding().getSsm().getMapping(pdbid);
+      if (sm!=null && sm.length>0 && sm[0]!=null) {
+        p++;
+      }
+    }
+    // only return true if there is a mapping for every structure file we have loaded
+    if (p == 0 || p != pdbids.length)
+    {
+      return false;
+    }
+    // and that coloring has been applied
+    return seqColoursApplied;
+  }
+
+  @Override
+  public void raiseViewer()
+  {
+    toFront();
+  }
+
 }
index d2086e0..973cfe8 100644 (file)
@@ -1074,16 +1074,16 @@ public class VamsasApplication implements SelectionSource, VamsasSource
                   }
                   else
                   {
-                    // int[] intervals = colsel.getVisibleContigs(
-                    // seqsel.getStartRes(), seqsel.getEndRes() + 1);
-                    int[] intervals = hidden.getVisibleContigs(
-                            seqsel.getStartRes(), seqsel.getEndRes() + 1);
-                    for (int iv = 0; iv < intervals.length; iv += 2)
+                    Iterator<int[]> intervals = hidden
+                            .getVisContigsIterator(seqsel.getStartRes(),
+                                    seqsel.getEndRes() + 1, false);
+                    while (intervals.hasNext())
                     {
+                      int[] region = intervals.next();
                       Seg s = new Seg();
-                      s.setStart(intervals[iv] + 1); // vamsas indices begin at
-                      // 1, not zero.
-                      s.setEnd(intervals[iv + 1] + 1);
+                      s.setStart(region[0] + 1); // vamsas indices begin at 1,
+                                                 // not zero.
+                      s.setEnd(region[1] + 1);
                       s.setInclusive(true);
                       range.addSeg(s);
                     }
index 2340283..497f0a5 100755 (executable)
@@ -72,7 +72,20 @@ public abstract class AlignFile extends FileParse
 
   long end;
 
-  private boolean parseCalled;
+  /**
+   * true if parse() has been called
+   */
+  private boolean parseCalled = false;
+
+  private boolean parseImmediately = true;
+
+  /**
+   * @return if doParse() was called at construction time
+   */
+  protected boolean isParseImmediately()
+  {
+    return parseImmediately;
+  }
 
   /**
    * Creates a new AlignFile object.
@@ -153,6 +166,11 @@ public abstract class AlignFile extends FileParse
   {
     super(source);
     initData();
+
+    // stash flag in case parse needs to know if it has to autoconfigure or was
+    // configured after construction
+    this.parseImmediately = parseImmediately;
+
     if (parseImmediately)
     {
       doParse();
index 00476d6..e578a45 100755 (executable)
@@ -968,7 +968,7 @@ public class AnnotationFile
             else
             {
               // consider deferring this till after the file has been parsed ?
-              hidden.hideInsertionsFor(sr);
+              hidden.hideList(sr.getInsertions());
             }
           }
           modified = true;
index 7647a16..6d3c18a 100755 (executable)
@@ -212,12 +212,12 @@ public class FormatAdapter extends AppletFormatAdapter
           AlignmentAnnotation na = new AlignmentAnnotation(ala[i]);
           if (selgp != null)
           {
-            hidden.makeVisibleAnnotation(selgp.getStartRes(),
-                    selgp.getEndRes(), na);
+            na.makeVisibleAnnotation(selgp.getStartRes(), selgp.getEndRes(),
+                    hidden);
           }
           else
           {
-            hidden.makeVisibleAnnotation(na);
+            na.makeVisibleAnnotation(hidden);
           }
           alv.addAnnotation(na);
         }
index 9bcaa5a..240e1fd 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,6 +29,7 @@ import jalview.fts.service.pdb.PDBFTSRestClient;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.Desktop;
 import jalview.gui.JvSwingUtils;
+import jalview.gui.StructureViewer;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -36,6 +37,7 @@ import java.awt.CardLayout;
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
+import java.awt.Font;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ItemEvent;
@@ -70,6 +72,8 @@ import javax.swing.event.DocumentListener;
 import javax.swing.event.InternalFrameEvent;
 import javax.swing.table.TableColumn;
 
+import net.miginfocom.swing.MigLayout;
+
 @SuppressWarnings("serial")
 /**
  * GUI layout for structure chooser
@@ -80,12 +84,23 @@ import javax.swing.table.TableColumn;
 public abstract class GStructureChooser extends JPanel
         implements ItemListener
 {
+  private static final Font VERDANA_12 = new Font("Verdana", 0, 12);
+
+  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";
+
+  /*
+   * 'cached' structure view
+   */
+  protected static final String VIEWS_LOCAL_PDB = "VIEWS_LOCAL_PDB";
+
   protected JPanel statusPanel = new JPanel();
 
   public JLabel statusBar = new JLabel();
 
-  private JPanel pnl_actionsAndStatus = new JPanel(new BorderLayout());
-
   protected String frameTitle = MessageManager
           .getString("label.structure_chooser");
 
@@ -97,41 +112,22 @@ public abstract class GStructureChooser extends JPanel
 
   protected StringBuilder errorWarning = new StringBuilder();
 
-  protected JLabel lbl_result = new JLabel(
-          MessageManager.getString("label.select"));
-
-  protected JButton btn_view = new JButton();
+  protected JButton btn_add;
 
-  protected JButton btn_cancel = new JButton();
+  protected JButton btn_newView;
 
   protected JButton btn_pdbFromFile = new JButton();
 
-  protected JTextField txt_search = new JTextField(14);
-
-  private JPanel pnl_actions = new JPanel();
-
-  private JPanel pnl_main = new JPanel();
-
-  private JPanel pnl_idInput = new JPanel(new FlowLayout());
+  protected JCheckBox chk_superpose = new JCheckBox(
+          MessageManager.getString("label.superpose_structures"));
 
-  private JPanel pnl_fileChooser = new JPanel(new FlowLayout());
-
-  private JPanel pnl_idInputBL = new JPanel(new BorderLayout());
-
-  private JPanel pnl_fileChooserBL = new JPanel(new BorderLayout());
-
-  private JPanel pnl_locPDB = new JPanel(new BorderLayout());
+  protected JTextField txt_search = new JTextField(14);
 
   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"));
 
@@ -147,33 +143,20 @@ public abstract class GStructureChooser extends JPanel
   protected ImageIcon warningImage = new ImageIcon(
           getClass().getResource("/images/warning.gif"));
 
-  protected JLabel lbl_warning = new JLabel(warningImage);
-
   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 AssociateSeqPanel idInputAssSeqPanel = new AssociateSeqPanel();
 
-  protected static final String VIEWS_FROM_FILE = "VIEWS_FROM_FILE";
+  protected AssociateSeqPanel fileChooserAssSeqPanel = new AssociateSeqPanel();
 
-  protected static final String VIEWS_ENTER_ID = "VIEWS_ENTER_ID";
-
-  /**
-   * 'cached' structure view
-   */
-  protected static final String VIEWS_LOCAL_PDB = "VIEWS_LOCAL_PDB";
+  protected JComboBox<StructureViewer> targetView = new JComboBox<>();
 
   protected JTable tbl_local_pdb = new JTable();
 
-  protected JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
-
   protected JTabbedPane pnl_filter = new JTabbedPane();
 
   protected FTSDataColumnPreferences pdbDocFieldPrefs = new FTSDataColumnPreferences(
@@ -262,8 +245,6 @@ public abstract class GStructureChooser extends JPanel
     }
   };
 
-  protected JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
-
   public GStructureChooser()
   {
     try
@@ -319,9 +300,9 @@ public abstract class GStructureChooser extends JPanel
           mainFrame.dispose();
           break;
         case KeyEvent.VK_ENTER: // enter key
-          if (btn_view.isEnabled())
+          if (btn_add.isEnabled())
           {
-            ok_ActionPerformed();
+            add_ActionPerformed();
           }
           break;
         case KeyEvent.VK_TAB: // tab key
@@ -331,7 +312,7 @@ public abstract class GStructureChooser extends JPanel
           }
           else
           {
-            btn_view.requestFocus();
+            btn_add.requestFocus();
           }
           evt.consume();
           break;
@@ -340,6 +321,30 @@ public abstract class GStructureChooser extends JPanel
         }
       }
     });
+
+    JButton btn_cancel = new JButton(
+            MessageManager.getString("action.cancel"));
+    btn_cancel.setFont(VERDANA_12);
+    btn_cancel.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        closeAction(pnl_filter.getHeight());
+      }
+    });
+    btn_cancel.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          closeAction(pnl_filter.getHeight());
+        }
+      }
+    });
+
     tbl_local_pdb.setAutoCreateRowSorter(true);
     tbl_local_pdb.getTableHeader().setReorderingAllowed(false);
     tbl_local_pdb.addMouseListener(new MouseAdapter()
@@ -368,9 +373,9 @@ public abstract class GStructureChooser extends JPanel
           mainFrame.dispose();
           break;
         case KeyEvent.VK_ENTER: // enter key
-          if (btn_view.isEnabled())
+          if (btn_add.isEnabled())
           {
-            ok_ActionPerformed();
+            add_ActionPerformed();
           }
           break;
         case KeyEvent.VK_TAB: // tab key
@@ -380,9 +385,9 @@ public abstract class GStructureChooser extends JPanel
           }
           else
           {
-            if (btn_view.isEnabled())
+            if (btn_add.isEnabled())
             {
-              btn_view.requestFocus();
+              btn_add.requestFocus();
             }
             else
             {
@@ -396,51 +401,52 @@ public abstract class GStructureChooser extends JPanel
         }
       }
     });
-    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()
+
+    btn_newView = new JButton(MessageManager.getString("action.new_view"));
+    btn_newView.setFont(VERDANA_12);
+    btn_newView.addActionListener(new java.awt.event.ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        ok_ActionPerformed();
+        newView_ActionPerformed();
       }
     });
-    btn_view.addKeyListener(new KeyAdapter()
+    btn_newView.addKeyListener(new KeyAdapter()
     {
       @Override
       public void keyPressed(KeyEvent evt)
       {
         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
         {
-          ok_ActionPerformed();
+          newView_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()
+    btn_add = new JButton(MessageManager.getString("action.add"));
+    btn_add.setFont(VERDANA_12);
+    btn_add.addActionListener(new java.awt.event.ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        closeAction(pnl_filter.getHeight());
+        add_ActionPerformed();
       }
     });
-    btn_cancel.addKeyListener(new KeyAdapter()
+    btn_add.addKeyListener(new KeyAdapter()
     {
       @Override
       public void keyPressed(KeyEvent evt)
       {
         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
         {
-          closeAction(pnl_filter.getHeight());
+          add_ActionPerformed();
         }
       }
     });
 
-    btn_pdbFromFile.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_pdbFromFile.setFont(VERDANA_12);
     String btn_title = MessageManager.getString("label.select_pdb_file");
     btn_pdbFromFile.setText(btn_title + "              ");
     btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
@@ -463,20 +469,17 @@ public abstract class GStructureChooser extends JPanel
       }
     });
 
+    JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
     scrl_foundStructures.setPreferredSize(new Dimension(width, height));
 
+    JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
     scrl_localPDB.setPreferredSize(new Dimension(width, height));
     scrl_localPDB.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));
-    chk_rememberSettings.setVisible(false);
+    chk_invertFilter.setFont(VERDANA_12);
     txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
             MessageManager.getString("label.enter_pdb_id_tip")));
-    cmb_filterOption.setToolTipText(
-            MessageManager.getString("info.select_filter_option"));
     txt_search.getDocument().addDocumentListener(new DocumentListener()
     {
       @Override
@@ -498,8 +501,10 @@ public abstract class GStructureChooser extends JPanel
       }
     });
 
+    cmb_filterOption.setFont(VERDANA_12);
+    cmb_filterOption.setToolTipText(
+            MessageManager.getString("info.select_filter_option"));
     cmb_filterOption.addItemListener(this);
-
     // add CustomComboSeparatorsRenderer to filter option combo-box
     cmb_filterOption.setRenderer(new CustomComboSeparatorsRenderer(
             (ListCellRenderer<Object>) cmb_filterOption.getRenderer())
@@ -514,23 +519,33 @@ public abstract class GStructureChooser extends JPanel
 
     chk_invertFilter.addItemListener(this);
 
-    pnl_actions.add(chk_rememberSettings);
-    pnl_actions.add(btn_view);
-    pnl_actions.add(btn_cancel);
+    targetView.setVisible(false);
 
-    // pnl_filter.add(lbl_result);
+    JPanel actionsPanel = new JPanel(new MigLayout());
+    actionsPanel.add(targetView, "left");
+    actionsPanel.add(btn_add, "wrap");
+    actionsPanel.add(chk_superpose, "left");
+    actionsPanel.add(btn_newView);
+    actionsPanel.add(btn_cancel, "right");
+
+    JPanel pnl_main = new JPanel();
     pnl_main.add(cmb_filterOption);
     pnl_main.add(lbl_loading);
     pnl_main.add(chk_invertFilter);
     lbl_loading.setVisible(false);
 
+    JPanel pnl_fileChooser = new JPanel(new FlowLayout());
     pnl_fileChooser.add(btn_pdbFromFile);
     pnl_fileChooser.add(lbl_fromFileStatus);
+    JPanel pnl_fileChooserBL = new JPanel(new BorderLayout());
     pnl_fileChooserBL.add(fileChooserAssSeqPanel, BorderLayout.NORTH);
     pnl_fileChooserBL.add(pnl_fileChooser, BorderLayout.CENTER);
 
+    JPanel pnl_idInput = new JPanel(new FlowLayout());
     pnl_idInput.add(txt_search);
     pnl_idInput.add(lbl_pdbManualFetchStatus);
+
+    JPanel pnl_idInputBL = new JPanel(new BorderLayout());
     pnl_idInputBL.add(idInputAssSeqPanel, BorderLayout.NORTH);
     pnl_idInputBL.add(pnl_idInput, BorderLayout.CENTER);
 
@@ -546,13 +561,15 @@ public abstract class GStructureChooser extends JPanel
         JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
                 .getSource();
         int index = sourceTabbedPane.getSelectedIndex();
-        btn_view.setVisible(true);
+        btn_add.setVisible(targetView.isVisible());
+        btn_newView.setVisible(true);
         btn_cancel.setVisible(true);
         if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
         {
-          btn_view.setEnabled(false);
+          btn_add.setEnabled(false);
           btn_cancel.setEnabled(false);
-          btn_view.setVisible(false);
+          btn_add.setVisible(false);
+          btn_newView.setEnabled(false);
           btn_cancel.setVisible(false);
           previousWantedFields = pdbDocFieldPrefs
                   .getStructureSummaryFields()
@@ -578,6 +595,7 @@ public abstract class GStructureChooser extends JPanel
     pnl_filter.add(foundStructureSummary, scrl_foundStructures);
     pnl_filter.add(configureCols, pdbDocFieldPrefs);
 
+    JPanel pnl_locPDB = new JPanel(new BorderLayout());
     pnl_locPDB.add(scrl_localPDB);
 
     pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
@@ -585,12 +603,14 @@ public abstract class GStructureChooser extends JPanel
     pnl_switchableViews.add(pnl_filter, VIEWS_FILTER);
     pnl_switchableViews.add(pnl_locPDB, VIEWS_LOCAL_PDB);
 
-    this.setLayout(mainLayout);
+    this.setLayout(new BorderLayout());
     this.add(pnl_main, java.awt.BorderLayout.NORTH);
     this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
     // this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
     statusPanel.setLayout(new GridLayout());
-    pnl_actionsAndStatus.add(pnl_actions, BorderLayout.CENTER);
+
+    JPanel pnl_actionsAndStatus = new JPanel(new BorderLayout());
+    pnl_actionsAndStatus.add(actionsPanel, BorderLayout.CENTER);
     pnl_actionsAndStatus.add(statusPanel, BorderLayout.SOUTH);
     statusPanel.add(statusBar, null);
     this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH);
@@ -801,13 +821,13 @@ public abstract class GStructureChooser extends JPanel
    * @author tcnofoegbu
    *
    */
-  public class AssciateSeqPanel extends JPanel implements ItemListener
+  public class AssociateSeqPanel extends JPanel implements ItemListener
   {
     private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<>();
 
     private JLabel lbl_associateSeq = new JLabel();
 
-    public AssciateSeqPanel()
+    public AssociateSeqPanel()
     {
       this.setLayout(new FlowLayout());
       this.add(cmb_assSeq);
@@ -901,19 +921,21 @@ public abstract class GStructureChooser extends JPanel
 
   protected abstract void stateChanged(ItemEvent e);
 
-  protected abstract void ok_ActionPerformed();
+  protected abstract void add_ActionPerformed();
+
+  protected abstract void newView_ActionPerformed();
 
   protected abstract void pdbFromFile_actionPerformed();
 
   protected abstract void txt_search_ActionPerformed();
 
-  public abstract void populateCmbAssociateSeqOptions(
+  protected abstract void populateCmbAssociateSeqOptions(
           JComboBox<AssociateSeqOptions> cmb_assSeq,
           JLabel lbl_associateSeq);
 
-  public abstract void cmbAssSeqStateChanged();
+  protected abstract void cmbAssSeqStateChanged();
 
-  public abstract void tabRefresh();
+  protected abstract void tabRefresh();
 
-  public abstract void validateSelections();
+  protected abstract void validateSelections();
 }
\ No newline at end of file
index 8a80d41..adca17e 100644 (file)
@@ -163,7 +163,7 @@ public class AnnotationRenderer
   {
     g.setColor(STEM_COLOUR);
     int sCol = (lastSSX / charWidth)
-            + hiddenColumns.adjustForHiddenColumns(startRes);
+            + hiddenColumns.visibleToAbsoluteColumn(startRes);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
 
@@ -230,7 +230,7 @@ public class AnnotationRenderer
 
     g.setColor(nonCanColor);
     int sCol = (lastSSX / charWidth)
-            + hiddenColumns.adjustForHiddenColumns(startRes);
+            + hiddenColumns.visibleToAbsoluteColumn(startRes);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
 
@@ -602,7 +602,7 @@ public class AnnotationRenderer
         {
           if (hasHiddenColumns)
           {
-            column = hiddenColumns.adjustForHiddenColumns(startRes + x);
+            column = hiddenColumns.visibleToAbsoluteColumn(startRes + x);
             if (column > row_annotations.length - 1)
             {
               break;
@@ -1150,7 +1150,7 @@ public class AnnotationRenderer
     g.setColor(HELIX_COLOUR);
 
     int sCol = (lastSSX / charWidth)
-            + hiddenColumns.adjustForHiddenColumns(startRes);
+            + hiddenColumns.visibleToAbsoluteColumn(startRes);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
 
@@ -1250,7 +1250,7 @@ public class AnnotationRenderer
       column = sRes + x;
       if (hasHiddenColumns)
       {
-        column = hiddenColumns.adjustForHiddenColumns(column);
+        column = hiddenColumns.visibleToAbsoluteColumn(column);
       }
 
       if (column > aaMax)
@@ -1330,7 +1330,7 @@ public class AnnotationRenderer
       column = sRes + x;
       if (hasHiddenColumns)
       {
-        column = hiddenColumns.adjustForHiddenColumns(column);
+        column = hiddenColumns.visibleToAbsoluteColumn(column);
       }
 
       if (column > aaMax)
index 1c50aab..e9b4de4 100644 (file)
@@ -44,8 +44,6 @@ public class OverviewRenderer
   // transparency of hidden cols/seqs overlay
   private final float TRANSPARENCY = 0.5f;
 
-  private final Color HIDDEN_COLOUR = Color.DARK_GRAY.darker();
-
   public static final String UPDATE = "OverviewUpdate";
 
   private static final int MAX_PROGRESS = 100;
@@ -152,7 +150,7 @@ public class OverviewRenderer
         if (pixelCol <= endCol)
         {
           rgbcolor = getColumnColourFromSequence(allGroups, seq,
-                  alignmentCol, finder);
+                  alignmentCol);
     
           // fill in the appropriate number of pixels
           for (int row = pixelRow; row <= endRow; ++row)
@@ -216,27 +214,23 @@ public class OverviewRenderer
   }
 
   /*
-   * Find the colour of a sequence at a specified column position
+   * Find the RGB value of the colour of a sequence at a specified column position
    * 
    * @param seq
    *          sequence to get colour for
    * @param lastcol
    *          column position to get colour for
-   * @param fcfinder
-   *          FeatureColourFinder to use
    * @return colour of sequence at this position, as RGB
    */
-  private int getColumnColourFromSequence(SequenceGroup[] allGroups,
-          jalview.datamodel.SequenceI seq,
-          int lastcol, FeatureColourFinder fcfinder)
+  int getColumnColourFromSequence(SequenceGroup[] allGroups,
+          SequenceI seq, int lastcol)
   {
-    Color color = Color.white;
+    Color color = resColFinder.GAP_COLOUR;
 
     if ((seq != null) && (seq.getLength() > lastcol))
     {
       color = resColFinder.getResidueColour(true, shader, allGroups, seq,
-              lastcol,
-              fcfinder);
+              lastcol, finder);
     }
 
     return color.getRGB();
@@ -359,15 +353,13 @@ public class OverviewRenderer
    *          the graphics object to draw on
    * @param anno
    *          alignment annotation information
-   * @param charWidth
-   *          alignment character width value
    * @param y
    *          y-position for the annotation graph
    * @param cols
    *          the collection of columns used in the overview panel
    */
-  public void drawGraph(Graphics g, AlignmentAnnotation anno, int charWidth,
-          int y, AlignmentColsCollectionI cols)
+  public void drawGraph(Graphics g, AlignmentAnnotation anno, int y,
+          AlignmentColsCollectionI cols)
   {
     Annotation[] annotations = anno.annotations;
     g.setColor(Color.white);
index d92608c..dc3272f 100644 (file)
 package jalview.renderer;
 
 import jalview.api.AlignViewportI;
+import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SequenceI;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -60,27 +62,19 @@ public class ScaleRenderer
       column = col;
       text = txt;
     }
-
-    /**
-     * String representation for inspection when debugging only
-     */
-    @Override
-    public String toString()
-    {
-      return String.format("%s:%d:%s", major ? "major" : "minor", column,
-              text);
-    }
   }
 
   /**
-   * Calculates position markers on the alignment ruler
+   * calculate positions markers on the alignment ruler
    * 
    * @param av
    * @param startx
-   *          left-most column in visible view (0..)
+   *          left-most column in visible view
    * @param endx
-   *          - right-most column in visible view (0..)
-   * @return
+   *          - right-most column in visible view
+   * @return List of ScaleMark holding boolean: true/false for major/minor mark,
+   *         marker position in alignment column coords, a String to be rendered
+   *         at the position (or null)
    */
   public List<ScaleMark> calculateMarks(AlignViewportI av, int startx,
           int endx)
@@ -88,17 +82,27 @@ public class ScaleRenderer
     int scalestartx = (startx / 10) * 10;
 
     SequenceI refSeq = av.getAlignment().getSeqrep();
-    int refSp = 0, refStartI = 0, refEndI = -1;
+    int refSp = 0;
+    int refStartI = 0;
+    int refEndI = -1;
+
+    HiddenColumns hc = av.getAlignment().getHiddenColumns();
+
     if (refSeq != null)
     {
-      // find bounds and set origin appopriately
-      // locate first visible position for this sequence
-      int[] refbounds = av.getAlignment().getHiddenColumns()
-              .locateVisibleBoundsOfSequence(refSeq);
+      // find bounds and set origin appropriately
+      // locate first residue in sequence which is not hidden
+      Iterator<int[]> it = hc.iterator();
+      int index = refSeq.firstResidueOutsideIterator(it);
+      refSp = hc.absoluteToVisibleColumn(index);
+
+      refStartI = refSeq.findIndex(refSeq.getStart()) - 1;
+
+      int seqlength = refSeq.getLength();
+      // get sequence position past the end of the sequence
+      int pastEndPos = refSeq.findPosition(seqlength + 1);
+      refEndI = refSeq.findIndex(pastEndPos - 1) - 1;
 
-      refSp = refbounds[0];
-      refStartI = refbounds[4];
-      refEndI = refbounds[5];
       scalestartx = refSp + ((scalestartx - refSp) / 10) * 10;
     }
 
@@ -106,41 +110,40 @@ public class ScaleRenderer
     {
       scalestartx += 5;
     }
-    List<ScaleMark> marks = new ArrayList<ScaleMark>();
+    List<ScaleMark> marks = new ArrayList<>();
+    String string;
+    int refN, iadj;
     // todo: add a 'reference origin column' to set column number relative to
     for (int i = scalestartx; i <= endx; i += 5)
     {
       if (((i - refSp) % 10) == 0)
       {
-        String text;
         if (refSeq == null)
         {
-          int iadj = av.getAlignment().getHiddenColumns()
-                  .adjustForHiddenColumns(i - 1) + 1;
-          text = String.valueOf(iadj);
+          iadj = hc.visibleToAbsoluteColumn(i - 1) + 1;
+          string = String.valueOf(iadj);
         }
         else
         {
-          int iadj = av.getAlignment().getHiddenColumns()
-                  .adjustForHiddenColumns(i - 1);
-          int refN = refSeq.findPosition(iadj);
+          iadj = hc.visibleToAbsoluteColumn(i - 1);
+          refN = refSeq.findPosition(iadj);
           // TODO show bounds if position is a gap
           // - ie L--R -> "1L|2R" for
           // marker
           if (iadj < refStartI)
           {
-            text = String.valueOf(iadj - refStartI);
+            string = String.valueOf(iadj - refStartI);
           }
           else if (iadj > refEndI)
           {
-            text = "+" + String.valueOf(iadj - refEndI);
+            string = "+" + String.valueOf(iadj - refEndI);
           }
           else
           {
-            text = String.valueOf(refN) + refSeq.getCharAt(iadj);
+            string = String.valueOf(refN) + refSeq.getCharAt(iadj);
           }
         }
-        marks.add(new ScaleMark(true, i - startx - 1, text));
+        marks.add(new ScaleMark(true, i - startx - 1, string));
       }
       else
       {
index 40789ed..4174f5b 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.structure;
 
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Mapping;
 import jalview.datamodel.SequenceI;
 
 import java.util.ArrayList;
@@ -29,6 +30,12 @@ import java.util.List;
 
 public class StructureMapping
 {
+  public static final int UNASSIGNED_VALUE = Integer.MIN_VALUE;
+
+  private static final int PDB_RES_NUM_INDEX = 0;
+
+  private static final int PDB_ATOM_NUM_INDEX = 1;
+
   String mappingDetails;
 
   SequenceI sequence;
@@ -39,16 +46,12 @@ public class StructureMapping
 
   String pdbchain;
 
-  public static final int UNASSIGNED_VALUE = -1;
-
-  private static final int PDB_RES_NUM_INDEX = 0;
-
-  private static final int PDB_ATOM_NUM_INDEX = 1;
-
   // Mapping key is residue index while value is an array containing PDB resNum,
   // and atomNo
   HashMap<Integer, int[]> mapping;
 
+  jalview.datamodel.Mapping seqToPdbMapping = null;
+
   /**
    * Constructor
    * 
@@ -73,6 +76,14 @@ public class StructureMapping
     this.mappingDetails = mappingDetails;
   }
 
+  public StructureMapping(SequenceI seq, String pdbFile2, String pdbId2,
+          String chain, HashMap<Integer, int[]> mapping2,
+          String mappingOutput, Mapping seqToPdbMapping)
+  {
+    this(seq, pdbFile2, pdbId2, chain, mapping2, mappingOutput);
+    this.seqToPdbMapping = seqToPdbMapping;
+  }
+
   public SequenceI getSequence()
   {
     return sequence;
@@ -109,7 +120,8 @@ public class StructureMapping
   /**
    * 
    * @param seqpos
-   * @return 0 or the corresponding residue number for the sequence position
+   * @return UNASSIGNED_VALUE or the corresponding residue number for the
+   *         sequence position
    */
   public int getPDBResNum(int seqpos)
   {
@@ -134,7 +146,7 @@ public class StructureMapping
    */
   public List<int[]> getPDBResNumRanges(int fromSeqPos, int toSeqPos)
   {
-    List<int[]> result = new ArrayList<int[]>();
+    List<int[]> result = new ArrayList<>();
     int startRes = -1;
     int endRes = -1;
 
@@ -247,4 +259,110 @@ public class StructureMapping
   {
     return mapping;
   }
+
+  public Mapping getSeqToPdbMapping()
+  {
+    return seqToPdbMapping;
+  }
+
+  /**
+   * A hash function that satisfies the contract that if two mappings are
+   * equal(), they have the same hashCode
+   */
+  @Override
+  public int hashCode()
+  {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result
+            + ((mappingDetails == null) ? 0 : mappingDetails.hashCode());
+    result = prime * result
+            + ((pdbchain == null) ? 0 : pdbchain.hashCode());
+    result = prime * result + ((pdbfile == null) ? 0 : pdbfile.hashCode());
+    result = prime * result + ((pdbid == null) ? 0 : pdbid.hashCode());
+    result = prime * result
+            + ((seqToPdbMapping == null) ? 0 : seqToPdbMapping.hashCode());
+    result = prime * result
+            + ((sequence == null) ? 0 : sequence.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (this == obj)
+    {
+      return true;
+    }
+    if (obj == null)
+    {
+      return false;
+    }
+    if (getClass() != obj.getClass())
+    {
+      return false;
+    }
+    StructureMapping other = (StructureMapping) obj;
+    if (mappingDetails == null)
+    {
+      if (other.mappingDetails != null)
+      {
+        return false;
+      }
+    }
+    else if (!mappingDetails.equals(other.mappingDetails))
+    {
+      return false;
+    }
+    if (pdbchain == null)
+    {
+      if (other.pdbchain != null)
+      {
+        return false;
+      }
+    }
+    else if (!pdbchain.equals(other.pdbchain))
+    {
+      return false;
+    }
+    if (pdbfile == null)
+    {
+      if (other.pdbfile != null)
+      {
+        return false;
+      }
+    }
+    else if (!pdbfile.equals(other.pdbfile))
+    {
+      return false;
+    }
+    if (pdbid == null)
+    {
+      if (other.pdbid != null)
+      {
+        return false;
+      }
+    }
+    else if (!pdbid.equals(other.pdbid))
+    {
+      return false;
+    }
+    if (seqToPdbMapping == null)
+    {
+      if (other.seqToPdbMapping != null)
+      {
+        return false;
+      }
+    }
+    else if (!seqToPdbMapping.equals(other.seqToPdbMapping))
+    {
+      return false;
+    }
+    if (sequence != other.sequence)
+    {
+      return false;
+    }
+
+    return true;
+  }
 }
index 35e2536..cd986c0 100644 (file)
@@ -74,8 +74,6 @@ public class StructureSelectionManager
 
   private boolean addTempFacAnnot = false;
 
-  private SiftsClient siftsClient = null;
-
   /*
    * Set of any registered mappings between (dataset) sequences.
    */
@@ -287,7 +285,8 @@ public class StructureSelectionManager
   }
 
   /**
-   * Returns the file name for a mapped PDB id (or null if not mapped).
+   * Returns the filename the PDB id is already mapped to if known, or null if
+   * it is not mapped
    * 
    * @param pdbid
    * @return
@@ -296,7 +295,7 @@ public class StructureSelectionManager
   {
     for (StructureMapping sm : mappings)
     {
-      if (sm.getPdbId().equals(pdbid))
+      if (sm.getPdbId().equalsIgnoreCase(pdbid))
       {
         return sm.pdbfile;
       }
@@ -328,22 +327,20 @@ public class StructureSelectionManager
   }
 
   /**
-   * create sequence structure mappings between each sequence and the given
-   * pdbFile (retrieved via the given protocol).
+   * Import a single structure file and register sequence structure mappings for
+   * broadcasting colouring, mouseovers and selection events (convenience
+   * wrapper).
    * 
    * @param forStructureView
    *          when true, record the mapping for use in mouseOvers
-   * 
-   * @param sequenceArray
+   * @param sequence
    *          - one or more sequences to be mapped to pdbFile
-   * @param targetChainIds
+   * @param targetChains
    *          - optional chain specification for mapping each sequence to pdb
-   *          (may be nill, individual elements may be nill) - JBPNote: JAL-2693
-   *          - this should be List<List<String>>, empty lists indicate no
-   *          predefined mappings
+   *          (may be nill, individual elements may be nill)
    * @param pdbFile
    *          - structure data resource
-   * @param sourceType
+   * @param protocol
    *          - how to resolve data from resource
    * @return null or the structure data parsed as a pdb file
    */
@@ -355,49 +352,57 @@ public class StructureSelectionManager
             pdbFile, sourceType, null);
   }
 
+  /**
+   * create sequence structure mappings between each sequence and the given
+   * pdbFile (retrieved via the given protocol). Either constructs a mapping
+   * using NW alignment or derives one from any available SIFTS mapping data.
+   * 
+   * @param forStructureView
+   *          when true, record the mapping for use in mouseOvers
+   * 
+   * @param sequenceArray
+   *          - one or more sequences to be mapped to pdbFile
+   * @param targetChainIds
+   *          - optional chain specification for mapping each sequence to pdb
+   *          (may be nill, individual elements may be nill) - JBPNote: JAL-2693
+   *          - this should be List<List<String>>, empty lists indicate no
+   *          predefined mappings
+   * @param pdbFile
+   *          - structure data resource
+   * @param sourceType
+   *          - how to resolve data from resource
+   * @param IProgressIndicator
+   *          reference to UI component that maintains a progress bar for the
+   *          mapping operation
+   * @return null or the structure data parsed as a pdb file
+   */
   synchronized public StructureFile computeMapping(
           boolean forStructureView, SequenceI[] sequenceArray,
           String[] targetChainIds, String pdbFile, DataSourceType sourceType,
           IProgressIndicator progress)
   {
     long progressSessionId = System.currentTimeMillis() * 3;
-    /*
-     * There will be better ways of doing this in the future, for now we'll use
-     * the tried and tested MCview pdb mapping
+
+    /**
+     * do we extract and transfer annotation from 3D data ?
      */
-    boolean parseSecStr = processSecondaryStructure;
-    if (isPDBFileRegistered(pdbFile))
-    {
-      for (SequenceI sq : sequenceArray)
-      {
-        SequenceI ds = sq;
-        while (ds.getDatasetSequence() != null)
-        {
-          ds = ds.getDatasetSequence();
-        }
-        ;
-        if (ds.getAnnotation() != null)
-        {
-          for (AlignmentAnnotation ala : ds.getAnnotation())
-          {
-            // false if any annotation present from this structure
-            // JBPNote this fails for jmol/chimera view because the *file* is
-            // passed, not the structure data ID -
-            if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
-            {
-              parseSecStr = false;
-            }
-          }
-        }
-      }
-    }
+    // FIXME: possibly should just delete
+
+    boolean parseSecStr = processSecondaryStructure
+            ? isStructureFileProcessed(pdbFile, sequenceArray)
+            : false;
+
     StructureFile pdb = null;
     boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
     try
     {
+      // FIXME if sourceType is not null, we've lost data here
       sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
-      pdb = new JmolParser(pdbFile, sourceType);
-
+      pdb = new JmolParser(false, pdbFile, sourceType);
+      pdb.addSettings(parseSecStr && processSecondaryStructure,
+              parseSecStr && addTempFacAnnot,
+              parseSecStr && secStructServices);
+      pdb.doParse();
       if (pdb.getId() != null && pdb.getId().trim().length() > 0
               && DataSourceType.FILE == sourceType)
       {
@@ -411,7 +416,10 @@ public class StructureSelectionManager
       ex.printStackTrace();
       return null;
     }
-
+    /*
+     * sifts client - non null if SIFTS mappings are to be used 
+     */
+    SiftsClient siftsClient = null;
     try
     {
       if (isMapUsingSIFTs)
@@ -422,6 +430,7 @@ public class StructureSelectionManager
     {
       isMapUsingSIFTs = false;
       e.printStackTrace();
+      siftsClient = null;
     }
 
     String targetChainId;
@@ -524,12 +533,12 @@ public class StructureSelectionManager
           try
           {
             siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
-                    pdb, maxChain, sqmpping, maxAlignseq);
+                    pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
             seqToStrucMapping.add(siftsMapping);
-            maxChain.makeExactMapping(maxAlignseq, seq);
-            maxChain.transferRESNUMFeatures(seq, null);// FIXME: is this
+            maxChain.makeExactMapping(siftsMapping, seq);
+            maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
                                                        // "IEA:SIFTS" ?
-            maxChain.transferResidueAnnotation(siftsMapping, sqmpping);
+            maxChain.transferResidueAnnotation(siftsMapping, null);
             ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
 
           } catch (SiftsException e)
@@ -540,7 +549,8 @@ public class StructureSelectionManager
                     targetChainId, maxChain, pdb, maxAlignseq);
             seqToStrucMapping.add(nwMapping);
             maxChain.makeExactMapping(maxAlignseq, seq);
-            maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
+            maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
+                                                                 // this
                                                         // "IEA:Jalview" ?
             maxChain.transferResidueAnnotation(nwMapping, sqmpping);
             ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
@@ -551,24 +561,32 @@ public class StructureSelectionManager
           List<StructureMapping> foundSiftsMappings = new ArrayList<>();
           for (PDBChain chain : pdb.getChains())
           {
+            StructureMapping siftsMapping = null;
             try
             {
-              StructureMapping siftsMapping = getStructureMapping(seq,
-                      pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq);
+              siftsMapping = getStructureMapping(seq,
+                      pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
+                      siftsClient);
               foundSiftsMappings.add(siftsMapping);
+              chain.makeExactMapping(siftsMapping, seq);
+              chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
+              // "IEA:SIFTS" ?
+              chain.transferResidueAnnotation(siftsMapping, null);
             } catch (SiftsException e)
             {
               System.err.println(e.getMessage());
             }
+            catch (Exception e)
+            {
+              System.err
+                      .println(
+                              "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
+              System.err.println(e.getMessage());
+            }
           }
           if (!foundSiftsMappings.isEmpty())
           {
             seqToStrucMapping.addAll(foundSiftsMappings);
-            maxChain.makeExactMapping(maxAlignseq, seq);
-            maxChain.transferRESNUMFeatures(seq, null);// FIXME: is this
-                                                       // "IEA:SIFTS" ?
-            maxChain.transferResidueAnnotation(foundSiftsMappings.get(0),
-                    sqmpping);
             ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
           }
           else
@@ -598,7 +616,10 @@ public class StructureSelectionManager
       }
       if (forStructureView)
       {
-        mappings.addAll(seqToStrucMapping);
+        for (StructureMapping sm : seqToStrucMapping)
+        {
+          addStructureMapping(sm); // not addAll!
+        }
       }
       if (progress != null)
       {
@@ -608,9 +629,52 @@ public class StructureSelectionManager
     return pdb;
   }
 
+  /**
+   * check if we need to extract secondary structure from given pdbFile and
+   * transfer to sequences
+   * 
+   * @param pdbFile
+   * @param sequenceArray
+   * @return
+   */
+  private boolean isStructureFileProcessed(String pdbFile,
+          SequenceI[] sequenceArray)
+  {
+    boolean parseSecStr = true;
+    if (isPDBFileRegistered(pdbFile))
+    {
+      for (SequenceI sq : sequenceArray)
+      {
+        SequenceI ds = sq;
+        while (ds.getDatasetSequence() != null)
+        {
+          ds = ds.getDatasetSequence();
+        }
+        ;
+        if (ds.getAnnotation() != null)
+        {
+          for (AlignmentAnnotation ala : ds.getAnnotation())
+          {
+            // false if any annotation present from this structure
+            // JBPNote this fails for jmol/chimera view because the *file* is
+            // passed, not the structure data ID -
+            if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
+            {
+              parseSecStr = false;
+            }
+          }
+        }
+      }
+    }
+    return parseSecStr;
+  }
+
   public void addStructureMapping(StructureMapping sm)
   {
-    mappings.add(sm);
+    if (!mappings.contains(sm))
+    {
+      mappings.add(sm);
+    }
   }
 
   /**
@@ -624,13 +688,15 @@ public class StructureSelectionManager
    * @param maxChain
    * @param sqmpping
    * @param maxAlignseq
+   * @param siftsClient
+   *          client for retrieval of SIFTS mappings for this structure
    * @return
    * @throws SiftsException
    */
   private StructureMapping getStructureMapping(SequenceI seq,
           String pdbFile, String targetChainId, StructureFile pdb,
           PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
-          AlignSeq maxAlignseq) throws SiftsException
+          AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
   {
     StructureMapping curChainMapping = siftsClient
             .getSiftsStructureMapping(seq, pdbFile, targetChainId);
@@ -639,7 +705,7 @@ public class StructureSelectionManager
       PDBChain chain = pdb.findChain(targetChainId);
       if (chain != null)
       {
-        chain.transferResidueAnnotation(curChainMapping, sqmpping);
+        chain.transferResidueAnnotation(curChainMapping, null);
       }
     } catch (Exception e)
     {
index 4658724..ae530f9 100644 (file)
@@ -77,8 +77,8 @@ public class MapList
    */
   public MapList()
   {
-    fromShifts = new ArrayList<int[]>();
-    toShifts = new ArrayList<int[]>();
+    fromShifts = new ArrayList<>();
+    toShifts = new ArrayList<>();
   }
 
   /**
@@ -116,8 +116,17 @@ public class MapList
   {
     int hashCode = 31 * fromRatio;
     hashCode = 31 * hashCode + toRatio;
-    hashCode = 31 * hashCode + fromShifts.toArray().hashCode();
-    hashCode = 31 * hashCode + toShifts.toArray().hashCode();
+    for (int[] shift : fromShifts)
+    {
+      hashCode = 31 * hashCode + shift[0];
+      hashCode = 31 * hashCode + shift[1];
+    }
+    for (int[] shift : toShifts)
+    {
+      hashCode = 31 * hashCode + shift[0];
+      hashCode = 31 * hashCode + shift[1];
+    }
+
     return hashCode;
   }
 
@@ -347,7 +356,7 @@ public class MapList
     }
 
     boolean changed = false;
-    List<int[]> merged = new ArrayList<int[]>();
+    List<int[]> merged = new ArrayList<>();
     int[] lastRange = ranges.get(0);
     int lastDirection = lastRange[1] >= lastRange[0] ? 1 : -1;
     lastRange = new int[] { lastRange[0], lastRange[1] };
@@ -803,7 +812,7 @@ public class MapList
     {
       return null;
     }
-    List<int[]> ranges = new ArrayList<int[]>();
+    List<int[]> ranges = new ArrayList<>();
     if (fs <= fe)
     {
       intv = fs;
index 9c5c109..5a26ed6 100644 (file)
@@ -542,9 +542,11 @@ public final class MappingUtils
               toSequences, fromGapChar);
     }
 
-    for (int[] hidden : hiddencols.getHiddenColumnsCopy())
+    Iterator<int[]> regions = hiddencols.iterator();
+    while (regions.hasNext())
     {
-      mapHiddenColumns(hidden, codonFrames, newHidden, fromSequences,
+      mapHiddenColumns(regions.next(), codonFrames, newHidden,
+              fromSequences,
               toSequences, fromGapChar);
     }
     return; // mappedColumns;
index a0cbff4..1366ada 100644 (file)
@@ -67,6 +67,7 @@ import java.util.BitSet;
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -1740,8 +1741,12 @@ public abstract class AlignmentViewport
     if (alignment.getHiddenColumns() != null
             && alignment.getHiddenColumns().hasHiddenColumns())
     {
-      selection = alignment.getHiddenColumns()
-              .getVisibleSequenceStrings(start, end, seqs);
+      for (i = 0; i < iSize; i++)
+      {
+        Iterator<int[]> blocks = alignment.getHiddenColumns()
+                .getVisContigsIterator(start, end + 1, false);
+        selection[i] = seqs[i].getSequenceStringFromIterator(blocks);
+      }
     }
     else
     {
@@ -1768,10 +1773,10 @@ public abstract class AlignmentViewport
       {
         if (start == 0)
         {
-          start = hidden.adjustForHiddenColumns(start);
+          start = hidden.visibleToAbsoluteColumn(start);
         }
 
-        end = hidden.getHiddenBoundaryRight(start);
+        end = hidden.getNextHiddenBoundary(false, start);
         if (start == end)
         {
           end = max;
@@ -1786,8 +1791,8 @@ public abstract class AlignmentViewport
 
       if (hidden != null && hidden.hasHiddenColumns())
       {
-        start = hidden.adjustForHiddenColumns(end);
-        start = hidden.getHiddenBoundaryLeft(start) + 1;
+        start = hidden.visibleToAbsoluteColumn(end);
+        start = hidden.getNextHiddenBoundary(true, start) + 1;
       }
     } while (end < max);
 
@@ -1809,13 +1814,13 @@ public abstract class AlignmentViewport
         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
         if (selectedOnly && selectionGroup != null)
         {
-          alignment.getHiddenColumns().makeVisibleAnnotation(
+          clone.makeVisibleAnnotation(
                   selectionGroup.getStartRes(), selectionGroup.getEndRes(),
-                  clone);
+                  alignment.getHiddenColumns());
         }
         else
         {
-          alignment.getHiddenColumns().makeVisibleAnnotation(clone);
+          clone.makeVisibleAnnotation(alignment.getHiddenColumns());
         }
         ala.add(clone);
       }
@@ -2781,7 +2786,7 @@ public abstract class AlignmentViewport
     int lastSeq = alignment.getHeight() - 1;
     List<AlignedCodonFrame> seqMappings = null;
     for (int seqNo = ranges
-            .getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
+            .getStartSeq(); seqNo <= lastSeq; seqNo++, seqOffset++)
     {
       sequence = getAlignment().getSequenceAt(seqNo);
       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
index 170f4e9..0235081 100644 (file)
@@ -58,6 +58,10 @@ public abstract class OverviewDimensions
 
   protected int alheight;
 
+  protected float widthRatio;
+
+  protected float heightRatio;
+
   /**
    * Create an OverviewDimensions object
    * 
@@ -157,23 +161,25 @@ public abstract class OverviewDimensions
   public float getPixelsPerCol()
   {
     resetAlignmentDims();
-    return (float) width / alwidth;
+    return 1 / widthRatio;
   }
 
   public float getPixelsPerSeq()
   {
     resetAlignmentDims();
-    return (float) sequencesHeight / alheight;
+    return 1 / heightRatio;
   }
 
   public void setWidth(int w)
   {
     width = w;
+    widthRatio = (float) alwidth / width;
   }
 
   public void setHeight(int h)
   {
     sequencesHeight = h - graphHeight;
+    heightRatio = (float) alheight / sequencesHeight;
   }
 
   /**
@@ -273,14 +279,14 @@ public abstract class OverviewDimensions
 
     // boxX, boxY is the x,y location equivalent to startRes, startSeq
     int xPos = Math.min(startRes, alwidth - vpwidth + 1);
-    boxX = Math.round((float) xPos * width / alwidth);
-    boxY = Math.round((float) startSeq * sequencesHeight / alheight);
+    boxX = Math.round(xPos / widthRatio);
+    boxY = Math.round(startSeq / heightRatio);
 
     // boxWidth is the width in residues translated to pixels
-    boxWidth = Math.round((float) vpwidth * width / alwidth);
+    boxWidth = Math.round(vpwidth / widthRatio);
 
     // boxHeight is the height in sequences translated to pixels
-    boxHeight = Math.round((float) vpheight * sequencesHeight / alheight);
+    boxHeight = Math.round(vpheight / heightRatio);
   }
 
   /**
index c158ce7..de90a21 100644 (file)
@@ -50,6 +50,8 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
   public void updateViewportFromMouse(int mousex, int mousey,
           HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
   {
+    resetAlignmentDims();
+
     int xAsRes = getLeftXFromCentreX(mousex, hiddenCols);
     int yAsSeq = getTopYFromCentreY(mousey, hiddenSeqs);
 
@@ -61,24 +63,29 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
   public void adjustViewportFromMouse(int mousex, int mousey,
           HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
   {
+    resetAlignmentDims();
+
     // calculate translation in pixel terms:
     // get mouse location in viewport coords, add translation in viewport
     // coords, and update viewport as usual
-    int vpx = Math.round((float) mousex * alwidth / width);
-    int vpy = Math.round((float) mousey * alheight / sequencesHeight);
+    int vpx = Math.round(mousex * widthRatio);
+    int vpy = Math.round(mousey * heightRatio);
 
     updateViewportFromTopLeft(vpx + xdiff, vpy + ydiff, hiddenSeqs,
             hiddenCols);
 
   }
 
+  /**
+   * {@inheritDoc} Callers should have already called resetAlignmentDims to
+   * refresh alwidth, alheight and width/height ratios
+   */
   @Override
   protected void updateViewportFromTopLeft(int leftx, int topy,
           HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
   {
     int xAsRes = leftx;
     int yAsSeq = topy;
-    resetAlignmentDims();
 
     if (xAsRes < 0)
     {
@@ -147,7 +154,7 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
   public AlignmentColsCollectionI getColumns(AlignmentI al)
   {
     return new VisibleColsCollection(0,
-            ranges.getAbsoluteAlignmentWidth() - 1, al);
+            ranges.getAbsoluteAlignmentWidth() - 1, al.getHiddenColumns());
   }
 
   @Override
@@ -162,19 +169,30 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
   {
     alwidth = ranges.getVisibleAlignmentWidth();
     alheight = ranges.getVisibleAlignmentHeight();
+
+    widthRatio = (float) alwidth / width;
+    heightRatio = (float) alheight / sequencesHeight;
   }
 
+  /**
+   * {@inheritDoc} Callers should have already called resetAlignmentDims to
+   * refresh widthRatio
+   */
   @Override
   protected int getLeftXFromCentreX(int mousex, HiddenColumns hidden)
   {
-    int vpx = Math.round((float) mousex * alwidth / width);
+    int vpx = Math.round(mousex * widthRatio);
     return vpx - ranges.getViewportWidth() / 2;
   }
 
+  /**
+   * {@inheritDoc} Callers should have already called resetAlignmentDims to
+   * refresh heightRatio
+   */
   @Override
   protected int getTopYFromCentreY(int mousey, HiddenSequences hidden)
   {
-    int vpy = Math.round((float) mousey * alheight / sequencesHeight);
+    int vpy = Math.round(mousey * heightRatio);
     return vpy - ranges.getViewportHeight() / 2;
   }
 
@@ -182,10 +200,12 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
   public void setDragPoint(int x, int y, HiddenSequences hiddenSeqs,
           HiddenColumns hiddenCols)
   {
+    resetAlignmentDims();
+
     // get alignment position of x and box (can get directly from vpranges) and
     // calculate difference between the positions
-    int vpx = Math.round((float) x * alwidth / width);
-    int vpy = Math.round((float) y * alheight / sequencesHeight);
+    int vpx = Math.round(x * widthRatio);
+    int vpy = Math.round(y * heightRatio);
 
     xdiff = ranges.getStartRes() - vpx;
     ydiff = ranges.getStartSeq() - vpy;
index 9dde16e..3aa6e1b 100644 (file)
@@ -72,13 +72,15 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
   public void updateViewportFromMouse(int mousex, int mousey,
           HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
   {
+    resetAlignmentDims();
+
     // convert mousex and mousey to alignment units as well as
     // translating to top left corner of viewport - this is an absolute position
     int xAsRes = getLeftXFromCentreX(mousex, hiddenCols);
     int yAsSeq = getTopYFromCentreY(mousey, hiddenSeqs);
 
     // convert to visible positions
-    int visXAsRes = hiddenCols.findColumnPosition(xAsRes);
+    int visXAsRes = hiddenCols.absoluteToVisibleColumn(xAsRes);
     yAsSeq = hiddenSeqs.adjustForHiddenSeqs(
             hiddenSeqs.findIndexWithoutHiddenSeqs(yAsSeq));
     yAsSeq = Math.max(yAsSeq, 0); // -1 if before first visible sequence
@@ -93,27 +95,32 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
   public void adjustViewportFromMouse(int mousex, int mousey,
           HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
   {
+    resetAlignmentDims();
+
     // calculate translation in pixel terms:
     // get mouse location in viewport coords, add translation in viewport
     // coords,
     // convert back to pixel coords
     int vpx = Math.round((float) mousex * alwidth / width);
-    int visXAsRes = hiddenCols.findColumnPosition(vpx) + xdiff;
+    int visXAsRes = hiddenCols.absoluteToVisibleColumn(vpx) + xdiff;
 
-    int vpy = Math.round((float) mousey * alheight / sequencesHeight);
+    int vpy = Math.round(mousey * heightRatio);
     int visYAsRes = hiddenSeqs.findIndexWithoutHiddenSeqs(vpy) + ydiff;
 
     // update viewport accordingly
     updateViewportFromTopLeft(visXAsRes, visYAsRes, hiddenSeqs, hiddenCols);
   }
 
+  /**
+   * {@inheritDoc} Callers should have already called resetAlignmentDims to
+   * refresh alwidth, alheight and width/height ratios
+   */
   @Override
   protected void updateViewportFromTopLeft(int leftx, int topy,
           HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
   {
     int visXAsRes = leftx;
     int visYAsSeq = topy;
-    resetAlignmentDims();
 
     if (visXAsRes < 0)
     {
@@ -136,7 +143,7 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
     int vpwidth = ranges.getViewportWidth();
 
     // check in case we went off the edge of the alignment
-    int visAlignWidth = hiddenCols.findColumnPosition(alwidth - 1);
+    int visAlignWidth = hiddenCols.absoluteToVisibleColumn(alwidth - 1);
     if (visXAsRes + vpwidth - 1 > visAlignWidth)
     {
       // went past the end of the alignment, adjust backwards
@@ -144,8 +151,8 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
       // if last position was before the end of the alignment, need to update
       if (ranges.getEndRes() < visAlignWidth)
       {
-        visXAsRes = hiddenCols.findColumnPosition(hiddenCols
-                .subtractVisibleColumns(vpwidth - 1, alwidth - 1));
+        visXAsRes = hiddenCols.absoluteToVisibleColumn(hiddenCols
+                .offsetByVisibleColumns(-(vpwidth - 1), alwidth - 1));
       }
       else
       {
@@ -195,8 +202,8 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
           HiddenColumns hiddenCols)
   {
     // work with absolute values of startRes and endRes
-    int startRes = hiddenCols.adjustForHiddenColumns(ranges.getStartRes());
-    int endRes = hiddenCols.adjustForHiddenColumns(ranges.getEndRes());
+    int startRes = hiddenCols.visibleToAbsoluteColumn(ranges.getStartRes());
+    int endRes = hiddenCols.visibleToAbsoluteColumn(ranges.getEndRes());
 
     // work with absolute values of startSeq and endSeq
     int startSeq = hiddenSeqs.adjustForHiddenSeqs(ranges.getStartSeq());
@@ -225,20 +232,32 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
   {
     alwidth = ranges.getAbsoluteAlignmentWidth();
     alheight = ranges.getAbsoluteAlignmentHeight();
+
+    widthRatio = (float) alwidth / width;
+    heightRatio = (float) alheight / sequencesHeight;
   }
 
+
+  /**
+   * {@inheritDoc} Callers should have already called resetAlignmentDims to
+   * refresh widthRatio
+   */
   @Override
   protected int getLeftXFromCentreX(int mousex, HiddenColumns hidden)
   {
     int vpx = Math.round((float) mousex * alwidth / width);
-    return hidden.subtractVisibleColumns(ranges.getViewportWidth() / 2,
+    return hidden.offsetByVisibleColumns(-ranges.getViewportWidth() / 2,
             vpx);
   }
 
+  /**
+   * {@inheritDoc} Callers should have already called resetAlignmentDims to
+   * refresh heightRatio
+   */
   @Override
   protected int getTopYFromCentreY(int mousey, HiddenSequences hidden)
   {
-    int vpy = Math.round((float) mousey * alheight / sequencesHeight);
+    int vpy = Math.round(mousey * heightRatio);
     return hidden.subtractVisibleRows(ranges.getViewportHeight() / 2, vpy);
   }
 
@@ -246,12 +265,14 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
   public void setDragPoint(int x, int y, HiddenSequences hiddenSeqs,
           HiddenColumns hiddenCols)
   {
+    resetAlignmentDims();
+
     // get alignment position of x and box (can get directly from vpranges) and
     // calculate difference between the positions
-    int vpx = Math.round((float) x * alwidth / width);
-    int vpy = Math.round((float) y * alheight / sequencesHeight);
+    int vpx = Math.round(x * widthRatio);
+    int vpy = Math.round(y * heightRatio);
 
-    xdiff = ranges.getStartRes() - hiddenCols.findColumnPosition(vpx);
+    xdiff = ranges.getStartRes() - hiddenCols.absoluteToVisibleColumn(vpx);
     ydiff = ranges.getStartSeq()
             - hiddenSeqs.findIndexWithoutHiddenSeqs(vpy);
   }
index c7a3fa1..691e492 100644 (file)
@@ -609,14 +609,14 @@ public class ViewportRanges extends ViewportProperties
     }
     
     HiddenColumns hidden = al.getHiddenColumns();
-    while (x < hidden.adjustForHiddenColumns(startRes))
+    while (x < hidden.visibleToAbsoluteColumn(startRes))
     {
       if (!scrollRight(false))
       {
         break;
       }
     }
-    while (x > hidden.adjustForHiddenColumns(endRes))
+    while (x > hidden.visibleToAbsoluteColumn(endRes))
     {
       if (!scrollRight(true))
       {
@@ -638,7 +638,7 @@ public class ViewportRanges extends ViewportProperties
     boolean changedLocation = false;
 
     // convert the x,y location to visible coordinates
-    int visX = al.getHiddenColumns().findColumnPosition(x);
+    int visX = al.getHiddenColumns().absoluteToVisibleColumn(x);
     int visY = al.getHiddenSequences().findIndexWithoutHiddenSeqs(y);
 
     // if (vis_x,vis_y) is already visible don't do anything
index 2f30e94..43b0550 100644 (file)
@@ -54,9 +54,9 @@ public abstract class FeatureRendererModel
    */
   protected float transparency = 1.0f;
 
-  protected Map<String, FeatureColourI> featureColours = new ConcurrentHashMap<String, FeatureColourI>();
+  protected Map<String, FeatureColourI> featureColours = new ConcurrentHashMap<>();
 
-  protected Map<String, Boolean> featureGroups = new ConcurrentHashMap<String, Boolean>();
+  protected Map<String, Boolean> featureGroups = new ConcurrentHashMap<>();
 
   protected String[] renderOrder;
 
@@ -156,7 +156,7 @@ public abstract class FeatureRendererModel
     {
       av.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
     }
-    List<String> nft = new ArrayList<String>();
+    List<String> nft = new ArrayList<>();
     for (String featureType : featureTypes)
     {
       if (!fdi.isRegistered(featureType))
@@ -192,7 +192,7 @@ public abstract class FeatureRendererModel
     renderOrder = neworder;
   }
 
-  protected Map<String, float[][]> minmax = new Hashtable<String, float[][]>();
+  protected Map<String, float[][]> minmax = new Hashtable<>();
 
   public Map<String, float[][]> getMinMax()
   {
@@ -271,7 +271,7 @@ public abstract class FeatureRendererModel
      * include features at the position provided their feature type is 
      * displayed, and feature group is null or marked for display
      */
-    List<SequenceFeature> result = new ArrayList<SequenceFeature>();
+    List<SequenceFeature> result = new ArrayList<>();
     if (!av.areFeaturesDisplayed() || getFeaturesDisplayed() == null)
     {
       return result;
@@ -320,7 +320,7 @@ public abstract class FeatureRendererModel
     }
     FeaturesDisplayedI featuresDisplayed = av.getFeaturesDisplayed();
 
-    Set<String> oldfeatures = new HashSet<String>();
+    Set<String> oldfeatures = new HashSet<>();
     if (renderOrder != null)
     {
       for (int i = 0; i < renderOrder.length; i++)
@@ -333,7 +333,7 @@ public abstract class FeatureRendererModel
     }
 
     AlignmentI alignment = av.getAlignment();
-    List<String> allfeatures = new ArrayList<String>();
+    List<String> allfeatures = new ArrayList<>();
 
     for (int i = 0; i < alignment.getHeight(); i++)
     {
@@ -413,7 +413,7 @@ public abstract class FeatureRendererModel
      */
     if (minmax == null)
     {
-      minmax = new Hashtable<String, float[][]>();
+      minmax = new Hashtable<>();
     }
     synchronized (minmax)
     {
@@ -450,7 +450,7 @@ public abstract class FeatureRendererModel
    */
   private void updateRenderOrder(List<String> allFeatures)
   {
-    List<String> allfeatures = new ArrayList<String>(allFeatures);
+    List<String> allfeatures = new ArrayList<>(allFeatures);
     String[] oldRender = renderOrder;
     renderOrder = new String[allfeatures.size()];
     boolean initOrders = (featureOrder == null);
@@ -617,7 +617,7 @@ public abstract class FeatureRendererModel
   {
     if (featureOrder == null)
     {
-      featureOrder = new Hashtable<String, Float>();
+      featureOrder = new Hashtable<>();
     }
     featureOrder.put(type, new Float(position));
     return position;
@@ -676,7 +676,7 @@ public abstract class FeatureRendererModel
      * note visible feature ordering and colours before update
      */
     List<String> visibleFeatures = getDisplayedFeatureTypes();
-    Map<String, FeatureColourI> visibleColours = new HashMap<String, FeatureColourI>(
+    Map<String, FeatureColourI> visibleColours = new HashMap<>(
             getFeatureColours());
 
     FeaturesDisplayedI av_featuresdisplayed = null;
@@ -836,7 +836,7 @@ public abstract class FeatureRendererModel
   {
     if (featureGroups != null)
     {
-      List<String> gp = new ArrayList<String>();
+      List<String> gp = new ArrayList<>();
 
       for (String grp : featureGroups.keySet())
       {
@@ -882,7 +882,7 @@ public abstract class FeatureRendererModel
   @Override
   public Map<String, FeatureColourI> getDisplayedFeatureCols()
   {
-    Map<String, FeatureColourI> fcols = new Hashtable<String, FeatureColourI>();
+    Map<String, FeatureColourI> fcols = new Hashtable<>();
     if (getViewport().getFeaturesDisplayed() == null)
     {
       return fcols;
@@ -910,7 +910,7 @@ public abstract class FeatureRendererModel
   public List<String> getDisplayedFeatureTypes()
   {
     List<String> typ = getRenderOrder();
-    List<String> displayed = new ArrayList<String>();
+    List<String> displayed = new ArrayList<>();
     FeaturesDisplayedI feature_disp = av.getFeaturesDisplayed();
     if (feature_disp != null)
     {
@@ -931,7 +931,7 @@ public abstract class FeatureRendererModel
   @Override
   public List<String> getDisplayedFeatureGroups()
   {
-    List<String> _gps = new ArrayList<String>();
+    List<String> _gps = new ArrayList<>();
     for (String gp : getFeatureGroups())
     {
       if (checkGroupVisibility(gp, false))
@@ -966,7 +966,7 @@ public abstract class FeatureRendererModel
   public List<SequenceFeature> findFeaturesAtResidue(SequenceI sequence,
           int resNo)
   {
-    List<SequenceFeature> result = new ArrayList<SequenceFeature>();
+    List<SequenceFeature> result = new ArrayList<>();
     if (!av.areFeaturesDisplayed() || getFeaturesDisplayed() == null)
     {
       return result;
@@ -1011,6 +1011,7 @@ public abstract class FeatureRendererModel
     {
       return;
     }
+
     SequenceFeatures.sortFeatures(features, true);
     boolean simpleColour = fc == null || fc.isSimpleColour();
     SequenceFeature lastFeature = null;
@@ -1030,8 +1031,10 @@ public abstract class FeatureRendererModel
        * same extent as another (so would just redraw the same colour);
        * (checking type and isContactFeature as a fail-safe here, although
        * currently they are guaranteed to match in this context)
+       * don't remove 'redundant' features if transparency is applied 
+       * (as feature count affects depth of feature colour)
        */
-      if (simpleColour)
+      if (simpleColour && transparency == 1f)
       {
         if (lastFeature != null && sf.getBegin() == lastFeature.getBegin()
                 && sf.getEnd() == lastFeature.getEnd()
index fb8864d..1677eca 100644 (file)
@@ -61,6 +61,8 @@ public class DBRefFetcher implements Runnable
 {
   private static final String NEWLINE = System.lineSeparator();
 
+  public static final String TRIM_RETRIEVED_SEQUENCES = "TRIM_FETCHED_DATASET_SEQS";
+
   public interface FetchFinishedListenerI
   {
     void finished();
@@ -139,7 +141,7 @@ public class DBRefFetcher implements Runnable
             .getSequenceFetcherSingleton(progressIndicatorFrame);
     // set default behaviour for transferring excess sequence data to the
     // dataset
-    trimDsSeqs = Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true);
+    trimDsSeqs = Cache.getDefault(TRIM_RETRIEVED_SEQUENCES, true);
     if (sources == null)
     {
       setDatabaseSources(featureSettings, isNucleotide);
index 0227e35..8877c34 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.ws.dbsources;
 
+import jalview.bin.Cache;
 import jalview.datamodel.DBRefSource;
 
 import com.stevesoft.pat.Regex;
@@ -34,6 +35,9 @@ import com.stevesoft.pat.Regex;
  */
 abstract public class Pfam extends Xfam
 {
+  static final String PFAM_BASEURL_KEY = "PFAM_BASEURL";
+
+  private static final String DEFAULT_PFAM_BASEURL = "https://pfam.xfam.org";
 
   public Pfam()
   {
@@ -48,7 +52,6 @@ abstract public class Pfam extends Xfam
   @Override
   public String getAccessionSeparator()
   {
-    // TODO Auto-generated method stub
     return null;
   }
 
@@ -60,7 +63,6 @@ abstract public class Pfam extends Xfam
   @Override
   public Regex getAccessionValidator()
   {
-    // TODO Auto-generated method stub
     return null;
   }
 
@@ -91,17 +93,14 @@ abstract public class Pfam extends Xfam
   @Override
   public String getDbVersion()
   {
-    // TODO Auto-generated method stub
     return null;
   }
 
-  /**
-   * Returns base URL for selected Pfam alignment type
-   * 
-   * @return PFAM URL stub for this DbSource
-   */
   @Override
-  protected abstract String getXFAMURL();
+  protected String getURLPrefix()
+  {
+    return Cache.getDefault(PFAM_BASEURL_KEY, DEFAULT_PFAM_BASEURL);
+  }
 
   /*
    * (non-Javadoc)
index ec9fcbb..0600427 100644 (file)
@@ -31,20 +31,8 @@ public class PfamFull extends Pfam
     super();
   }
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.ws.dbsources.Pfam#getPFAMURL()
-   */
-  @Override
-  protected String getXFAMURL()
-  {
-    return "http://pfam.xfam.org/family/";
-
-  }
-
   @Override
-  public String getXFAMURLSUFFIX()
+  public String getURLSuffix()
   {
     return "/alignment/full";
   }
index 33c39b1..dff8a17 100644 (file)
@@ -33,19 +33,8 @@ public class PfamSeed extends Pfam
     super();
   }
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.ws.dbsources.Pfam#getPFAMURL()
-   */
-  @Override
-  protected String getXFAMURL()
-  {
-    return "http://pfam.xfam.org/family/";
-  }
-
   @Override
-  public String getXFAMURLSUFFIX()
+  public String getURLSuffix()
   {
     return "/alignment/seed";
   }
index 97f73d0..1d9d99a 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.ws.dbsources;
 
+import jalview.bin.Cache;
 import jalview.datamodel.DBRefSource;
 
 import com.stevesoft.pat.Regex;
@@ -31,6 +32,15 @@ import com.stevesoft.pat.Regex;
  */
 abstract public class Rfam extends Xfam
 {
+  static final String RFAM_BASEURL_KEY = "RFAM_BASEURL";
+
+  private static final String DEFAULT_RFAM_BASEURL = "https://rfam.xfam.org";
+
+  @Override
+  protected String getURLPrefix()
+  {
+    return Cache.getDefault(RFAM_BASEURL_KEY, DEFAULT_RFAM_BASEURL);
+  }
 
   public Rfam()
   {
@@ -46,7 +56,6 @@ abstract public class Rfam extends Xfam
   @Override
   public String getAccessionSeparator()
   {
-    // TODO Auto-generated method stub
     return null;
   }
 
@@ -58,7 +67,6 @@ abstract public class Rfam extends Xfam
   @Override
   public Regex getAccessionValidator()
   {
-    // TODO Auto-generated method stub
     return null;
   }
 
@@ -82,18 +90,9 @@ abstract public class Rfam extends Xfam
   @Override
   public String getDbVersion()
   {
-    // TODO Auto-generated method stub
     return null;
   }
 
-  /**
-   * Returns base URL for selected Rfam alignment type
-   * 
-   * @return RFAM URL stub for this DbSource
-   */
-  @Override
-  protected abstract String getXFAMURL();
-
   /*
    * (non-Javadoc)
    * 
index b2ca31a..d815336 100644 (file)
@@ -33,20 +33,8 @@ public class RfamFull extends Rfam
     super();
   }
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.ws.dbsources.Rfam#getXFAMURL()
-   */
-  @Override
-  protected String getXFAMURL()
-  {
-    return "http://rfam.xfam.org/family/";
-
-  }
-
   @Override
-  public String getXFAMURLSUFFIX()
+  public String getURLSuffix()
   {
     return "/alignment/full";
   }
index f714547..a74e829 100644 (file)
@@ -33,19 +33,8 @@ public class RfamSeed extends Rfam
     super();
   }
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.ws.dbsources.Rfam#getRFAMURL()
-   */
-  @Override
-  protected String getXFAMURL()
-  {
-    return "http://rfam.xfam.org/family/";
-  }
-
   @Override
-  public String getXFAMURLSUFFIX()
+  public String getURLSuffix()
   {
     // to download gzipped file add '?gzip=1'
     return "/alignment/stockholm";
index 73775cf..6b09eb6 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.ws.dbsources;
 
+import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
@@ -52,6 +53,8 @@ import com.stevesoft.pat.Regex;
  */
 public class Uniprot extends DbSourceProxyImpl
 {
+  private static final String DEFAULT_UNIPROT_DOMAIN = "https://www.uniprot.org";
+
   private static final String BAR_DELIMITER = "|";
 
   /*
@@ -67,6 +70,11 @@ public class Uniprot extends DbSourceProxyImpl
     super();
   }
 
+  private String getDomain()
+  {
+    return Cache.getDefault("UNIPROT_DOMAIN", DEFAULT_UNIPROT_DOMAIN);
+  }
+
   /*
    * (non-Javadoc)
    * 
@@ -163,7 +171,7 @@ public class Uniprot extends DbSourceProxyImpl
               "(UNIPROT\\|?|UNIPROT_|UNIREF\\d+_|UNIREF\\d+\\|?)", "");
       AlignmentI al = null;
 
-      String downloadstring = "http://www.uniprot.org/uniprot/" + queries
+      String downloadstring = getDomain() + "/uniprot/" + queries
               + ".xml";
       URL url = null;
       URLConnection urlconn = null;
index 26291eb..b83f558 100644 (file)
@@ -42,7 +42,12 @@ public abstract class Xfam extends DbSourceProxyImpl
     super();
   }
 
-  protected abstract String getXFAMURL();
+  /**
+   * the base URL for this Xfam-like service
+   * 
+   * @return
+   */
+  protected abstract String getURLPrefix();
 
   @Override
   public abstract String getDbVersion();
@@ -57,8 +62,7 @@ public abstract class Xfam extends DbSourceProxyImpl
     // retrieved.
     startQuery();
     // TODO: trap HTTP 404 exceptions and return null
-    String xfamUrl = getXFAMURL() + queries.trim().toUpperCase()
-            + getXFAMURLSUFFIX();
+    String xfamUrl = getURL(queries);
 
     if (Cache.log != null)
     {
@@ -83,6 +87,12 @@ public abstract class Xfam extends DbSourceProxyImpl
     return rcds;
   }
 
+  String getURL(String queries)
+  {
+    return getURLPrefix() + "/family/" + queries.trim().toUpperCase()
+            + getURLSuffix();
+  }
+
   /**
    * Pfam and Rfam provide alignments
    */
@@ -97,7 +107,7 @@ public abstract class Xfam extends DbSourceProxyImpl
    * 
    * @return "" for most Xfam sources
    */
-  public String getXFAMURLSUFFIX()
+  public String getURLSuffix()
   {
     return "";
   }
index a239625..23d9eb0 100644 (file)
@@ -235,8 +235,7 @@ class JPredThread extends JWS1Thread implements WSClientI
           {
             // Adjust input view for gaps
             // propagate insertions into profile
-            alhidden = HiddenColumns.propagateInsertions(profileseq, al,
-                    input);
+            alhidden = al.propagateInsertions(profileseq, input);
           }
         }
       }
index 68af7c3..b5f9653 100644 (file)
@@ -92,14 +92,24 @@ public class SiftsClient implements SiftsClientI
 
   private CoordinateSys seqCoordSys = CoordinateSys.UNIPROT;
 
+  /**
+   * PDB sequence position to sequence coordinate mapping as derived from SIFTS
+   * record for the identified SeqCoordSys Used for lift-over from sequence
+   * derived from PDB (with first extracted PDBRESNUM as 'start' to the sequence
+   * being annotated with PDB data
+   */
+  private jalview.datamodel.Mapping seqFromPdbMapping;
+
   private static final int BUFFER_SIZE = 4096;
 
-  public static final int UNASSIGNED = -1;
+  public static final int UNASSIGNED = Integer.MIN_VALUE;
 
   private static final int PDB_RES_POS = 0;
 
   private static final int PDB_ATOM_POS = 1;
 
+  private static final int PDBE_POS = 2;
+
   private static final String NOT_OBSERVED = "Not_Observed";
 
   private static final String SIFTS_FTP_BASE_URL = "http://ftp.ebi.ac.uk/pub/databases/msd/sifts/xml/";
@@ -413,6 +423,11 @@ public class SiftsClient implements SiftsClientI
   public StructureMapping getSiftsStructureMapping(SequenceI seq,
           String pdbFile, String chain) throws SiftsException
   {
+    SequenceI aseq = seq;
+    while (seq.getDatasetSequence() != null)
+    {
+      seq = seq.getDatasetSequence();
+    }
     structId = (chain == null) ? pdbId : pdbId + "|" + chain;
     System.out.println("Getting SIFTS mapping for " + structId + ": seq "
             + seq.getName());
@@ -435,8 +450,9 @@ public class SiftsClient implements SiftsClientI
     HashMap<Integer, int[]> mapping = getGreedyMapping(chain, seq, ps);
 
     String mappingOutput = mappingDetails.toString();
-    StructureMapping siftsMapping = new StructureMapping(seq, pdbFile,
-            pdbId, chain, mapping, mappingOutput);
+    StructureMapping siftsMapping = new StructureMapping(aseq, pdbFile,
+            pdbId, chain, mapping, mappingOutput, seqFromPdbMapping);
+
     return siftsMapping;
   }
 
@@ -444,8 +460,8 @@ public class SiftsClient implements SiftsClientI
   public HashMap<Integer, int[]> getGreedyMapping(String entityId,
           SequenceI seq, java.io.PrintStream os) throws SiftsException
   {
-    List<Integer> omitNonObserved = new ArrayList<Integer>();
-    int nonObservedShiftIndex = 0;
+    List<Integer> omitNonObserved = new ArrayList<>();
+    int nonObservedShiftIndex = 0,pdbeNonObserved=0;
     // System.out.println("Generating mappings for : " + entityId);
     Entity entity = null;
     entity = getEntityById(entityId);
@@ -476,7 +492,7 @@ public class SiftsClient implements SiftsClientI
     TreeMap<Integer, String> resNumMap = new TreeMap<Integer, String>();
     List<Segment> segments = entity.getSegment();
     SegmentHelperPojo shp = new SegmentHelperPojo(seq, mapping, resNumMap,
-            omitNonObserved, nonObservedShiftIndex);
+            omitNonObserved, nonObservedShiftIndex,pdbeNonObserved);
     processSegments(segments, shp);
     try
     {
@@ -498,15 +514,61 @@ public class SiftsClient implements SiftsClientI
     {
       throw new SiftsException("SIFTS mapping failed");
     }
+    // also construct a mapping object between the seq-coord sys and the PDB seq's coord sys
 
     Integer[] keys = mapping.keySet().toArray(new Integer[0]);
     Arrays.sort(keys);
     seqStart = keys[0];
     seqEnd = keys[keys.length - 1];
-
+    List<int[]> from=new ArrayList<>(),to=new ArrayList<>();
+    int[]_cfrom=null,_cto=null;
     String matchedSeq = originalSeq;
-    if (seqStart != UNASSIGNED)
+    if (seqStart != UNASSIGNED) // fixme! seqStart can map to -1 for a pdb sequence that starts <-1
     {
+      for (int seqps:keys)
+      {
+        int pdbpos = mapping.get(seqps)[PDBE_POS];
+        if (pdbpos == UNASSIGNED)
+        {
+          // not correct - pdbpos might be -1, but leave it for now
+          continue;
+        }
+        if (_cfrom==null || seqps!=_cfrom[1]+1)
+        {
+          _cfrom = new int[] { seqps,seqps};
+          from.add(_cfrom);
+          _cto = null; // discontinuity
+        } else {
+          _cfrom[1]= seqps;
+        }
+        if (_cto==null || pdbpos!=1+_cto[1])
+        {
+          _cto = new int[] { pdbpos,pdbpos};
+          to.add(_cto);
+        } else {
+          _cto[1] = pdbpos;
+        }
+      }
+      _cfrom = new int[from.size() * 2];
+      _cto = new int[to.size() * 2];
+      int p = 0;
+      for (int[] range : from)
+      {
+        _cfrom[p++] = range[0];
+        _cfrom[p++] = range[1];
+      }
+      ;
+      p = 0;
+      for (int[] range : to)
+      {
+        _cto[p++] = range[0];
+        _cto[p++] = range[1];
+      }
+      ;
+
+      seqFromPdbMapping = new jalview.datamodel.Mapping(null, _cto, _cfrom,
+              1,
+              1);
       pdbStart = mapping.get(seqStart)[PDB_RES_POS];
       pdbEnd = mapping.get(seqEnd)[PDB_RES_POS];
       int orignalSeqStart = seq.getStart();
@@ -559,6 +621,8 @@ public class SiftsClient implements SiftsClientI
     TreeMap<Integer, String> resNumMap = shp.getResNumMap();
     List<Integer> omitNonObserved = shp.getOmitNonObserved();
     int nonObservedShiftIndex = shp.getNonObservedShiftIndex();
+    int pdbeNonObservedCount = shp.getPdbeNonObserved();
+    int firstPDBResNum = UNASSIGNED;
     for (Segment segment : segments)
     {
       // System.out.println("Mapping segments : " + segment.getSegId() + "\\"s
@@ -566,6 +630,9 @@ public class SiftsClient implements SiftsClientI
       List<Residue> residues = segment.getListResidue().getResidue();
       for (Residue residue : residues)
       {
+        boolean isObserved = isResidueObserved(residue);
+        int pdbeIndex = getLeadingIntegerValue(residue.getDbResNum(),
+                UNASSIGNED);
         int currSeqIndex = UNASSIGNED;
         List<CrossRefDb> cRefDbs = residue.getCrossRefDb();
         CrossRefDb pdbRefDb = null;
@@ -574,6 +641,19 @@ public class SiftsClient implements SiftsClientI
           if (cRefDb.getDbSource().equalsIgnoreCase(DBRefSource.PDB))
           {
             pdbRefDb = cRefDb;
+            if (firstPDBResNum == UNASSIGNED)
+            {
+              firstPDBResNum = getLeadingIntegerValue(cRefDb.getDbResNum(),
+                      UNASSIGNED);
+            }
+            else
+            {
+              if (isObserved)
+              {
+                // after we find the first observed residue we just increment
+                firstPDBResNum++;
+              }
+            }
           }
           if (cRefDb.getDbCoordSys().equalsIgnoreCase(seqCoordSys.getName())
                   && isAccessionMatched(cRefDb.getDbAccessionId()))
@@ -586,11 +666,45 @@ public class SiftsClient implements SiftsClientI
             }
           }
         }
+        if (!isObserved)
+        {
+          ++pdbeNonObservedCount;
+        }
+        if (seqCoordSys == seqCoordSys.PDB) // FIXME: is seqCoordSys ever PDBe
+                                            // ???
+        {
+          // if the sequence has a primary reference to the PDB, then we are
+          // dealing with a sequence extracted directly from the PDB. In that
+          // case, numbering is PDBe - non-observed residues
+          currSeqIndex = seq.getStart() - 1 + pdbeIndex;
+        }
+        if (!isObserved)
+        {
+          if (seqCoordSys != CoordinateSys.UNIPROT) // FIXME: PDB or PDBe only
+                                                    // here
+          {
+            // mapping to PDB or PDBe so we need to bookkeep for the
+            // non-observed
+            // SEQRES positions
+            omitNonObserved.add(currSeqIndex);
+            ++nonObservedShiftIndex;
+          }
+        }
         if (currSeqIndex == UNASSIGNED)
         {
+          // change in logic - unobserved residues with no currSeqIndex
+          // corresponding are still counted in both nonObservedShiftIndex and
+          // pdbeIndex...
           continue;
         }
-        if (currSeqIndex >= seq.getStart() && currSeqIndex <= seq.getEnd())
+        // if (currSeqIndex >= seq.getStart() && currSeqIndex <= seqlength) //
+        // true
+                                                                         // numbering
+                                                                         // is
+                                                                         // not
+                                                                         // up
+                                                                         // to
+                                                                         // seq.getEnd()
         {
 
           int resNum = (pdbRefDb == null)
@@ -599,22 +713,18 @@ public class SiftsClient implements SiftsClientI
                   : getLeadingIntegerValue(pdbRefDb.getDbResNum(),
                           UNASSIGNED);
 
-          if (isResidueObserved(residue)
-                  || seqCoordSys == CoordinateSys.UNIPROT)
+          if (isObserved)
           {
             char resCharCode = ResidueProperties
                     .getSingleCharacterCode(ResidueProperties
                             .getCanonicalAminoAcid(residue.getDbResName()));
             resNumMap.put(currSeqIndex, String.valueOf(resCharCode));
+
+            int[] mappingcols = new int[] { Integer.valueOf(resNum),
+                UNASSIGNED, isObserved ? firstPDBResNum : UNASSIGNED };
+
+            mapping.put(currSeqIndex - nonObservedShiftIndex, mappingcols);
           }
-          else
-          {
-            omitNonObserved.add(currSeqIndex);
-            ++nonObservedShiftIndex;
-          }
-          mapping.put(currSeqIndex - nonObservedShiftIndex,
-                  new int[]
-                  { Integer.valueOf(resNum), UNASSIGNED });
         }
       }
     }
@@ -904,17 +1014,36 @@ public class SiftsClient implements SiftsClientI
 
     private int nonObservedShiftIndex;
 
+    /**
+     * count of number of 'not observed' positions in the PDB record's SEQRES
+     * (total number of residues with coordinates == length(SEQRES) -
+     * pdbeNonObserved
+     */
+    private int pdbeNonObserved;
+
     public SegmentHelperPojo(SequenceI seq, HashMap<Integer, int[]> mapping,
             TreeMap<Integer, String> resNumMap,
-            List<Integer> omitNonObserved, int nonObservedShiftIndex)
+            List<Integer> omitNonObserved, int nonObservedShiftIndex,
+            int pdbeNonObserved)
     {
       setSeq(seq);
       setMapping(mapping);
       setResNumMap(resNumMap);
       setOmitNonObserved(omitNonObserved);
       setNonObservedShiftIndex(nonObservedShiftIndex);
+      setPdbeNonObserved(pdbeNonObserved);
+
     }
 
+    public void setPdbeNonObserved(int pdbeNonObserved2)
+    {
+      this.pdbeNonObserved = pdbeNonObserved2;
+    }
+
+    public int getPdbeNonObserved()
+    {
+      return pdbeNonObserved;
+    }
     public SequenceI getSeq()
     {
       return seq;
@@ -964,6 +1093,7 @@ public class SiftsClient implements SiftsClientI
     {
       this.nonObservedShiftIndex = nonObservedShiftIndex;
     }
+
   }
 
   @Override
index defcdbc..533c0af 100644 (file)
@@ -91,7 +91,7 @@ public class PDBChainTest
     a3.resName = "ASP";
     a3.resNumber = 41;
 
-    Vector<Bond> v = new Vector<Bond>();
+    Vector<Bond> v = new Vector<>();
     v.add(new Bond(a1, a2));
     v.add(new Bond(a2, a3));
     v.add(new Bond(a3, a1));
@@ -234,7 +234,7 @@ public class PDBChainTest
   @Test(groups = { "Functional" })
   public void testMakeResidueList_noAnnotation()
   {
-    Vector<Atom> atoms = new Vector<Atom>();
+    Vector<Atom> atoms = new Vector<>();
     c.atoms = atoms;
     c.isNa = true;
     atoms.add(makeAtom(4, "N", "MET"));
@@ -292,7 +292,7 @@ public class PDBChainTest
   @Test(groups = { "Functional" })
   public void testMakeResidueList_withTempFactor()
   {
-    Vector<Atom> atoms = new Vector<Atom>();
+    Vector<Atom> atoms = new Vector<>();
     c.atoms = atoms;
     atoms.add(makeAtom(4, "N", "MET"));
     atoms.get(atoms.size() - 1).tfactor = 1f;
@@ -307,7 +307,7 @@ public class PDBChainTest
     atoms.add(makeAtom(5, "CA", "LYS"));
     atoms.get(atoms.size() - 1).tfactor = 9f;
     atoms.add(makeAtom(6, "O", "LEU"));
-    atoms.get(atoms.size() - 1).tfactor = 4f;
+    atoms.get(atoms.size() - 1).tfactor = -4f;
     atoms.add(makeAtom(6, "N", "LEU"));
     atoms.get(atoms.size() - 1).tfactor = 5f;
     atoms.add(makeAtom(6, "CA", "LEU"));
@@ -320,7 +320,7 @@ public class PDBChainTest
 
     /*
      * Verify annotations; note the tempFactor is read from the first atom in
-     * each residue i.e. we expect values 1, 7, 4 for the residues
+     * each residue i.e. we expect values 1, 7, -4 for the residues
      */
     AlignmentAnnotation[] ann = c.sequence.getAnnotation();
     assertEquals(1, ann.length);
@@ -328,12 +328,12 @@ public class PDBChainTest
     assertEquals("Temperature Factor for 1gaqA", ann[0].description);
     assertSame(c.sequence, ann[0].sequenceRef);
     assertEquals(AlignmentAnnotation.LINE_GRAPH, ann[0].graph);
-    assertEquals(0f, ann[0].graphMin, 0.001f);
+    assertEquals(-4f, ann[0].graphMin, 0.001f);
     assertEquals(7f, ann[0].graphMax, 0.001f);
     assertEquals(3, ann[0].annotations.length);
     assertEquals(1f, ann[0].annotations[0].value, 0.001f);
     assertEquals(7f, ann[0].annotations[1].value, 0.001f);
-    assertEquals(4f, ann[0].annotations[2].value, 0.001f);
+    assertEquals(-4f, ann[0].annotations[2].value, 0.001f);
   }
 
   /**
@@ -344,7 +344,7 @@ public class PDBChainTest
   public void testMakeCaBondList()
   {
     c.isNa = true;
-    Vector<Atom> atoms = new Vector<Atom>();
+    Vector<Atom> atoms = new Vector<>();
     c.atoms = atoms;
     atoms.add(makeAtom(4, "N", "MET"));
     atoms.add(makeAtom(4, "CA", "MET"));
@@ -375,7 +375,7 @@ public class PDBChainTest
   public void testMakeCaBondList_nucleotide()
   {
     c.isNa = false;
-    Vector<Atom> atoms = new Vector<Atom>();
+    Vector<Atom> atoms = new Vector<>();
     c.atoms = atoms;
     atoms.add(makeAtom(4, "N", "G"));
     atoms.add(makeAtom(4, "P", "G"));
@@ -406,7 +406,7 @@ public class PDBChainTest
   @Test(groups = { "Functional" })
   public void testMakeExactMapping()
   {
-    Vector<Atom> atoms = new Vector<Atom>();
+    Vector<Atom> atoms = new Vector<>();
     c.atoms = atoms;
     atoms.add(makeAtom(4, "N", "MET"));
     atoms.add(makeAtom(4, "CA", "MET"));
index 06b51e6..35196fa 100644 (file)
@@ -47,6 +47,7 @@ import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
 import jalview.io.FormatAdapter;
+import jalview.io.gff.SequenceOntologyI;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
 
@@ -263,14 +264,14 @@ public class AlignmentUtilsTests
   @Test(groups = { "Functional" })
   public void testMapProteinAlignmentToCdna_noXrefs() throws IOException
   {
-    List<SequenceI> protseqs = new ArrayList<SequenceI>();
+    List<SequenceI> protseqs = new ArrayList<>();
     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>();
+    List<SequenceI> dnaseqs = new ArrayList<>();
     dnaseqs.add(new Sequence("EMBL|A11111", "TCAGCACGC")); // = SAR
     dnaseqs.add(new Sequence("EMBL|A22222", "GAGATACAA")); // = EIQ
     dnaseqs.add(new Sequence("EMBL|A33333", "GAAATCCAG")); // = EIQ
@@ -507,7 +508,7 @@ public class AlignmentUtilsTests
     acf.addMap(dna1.getDatasetSequence(), prot1.getDatasetSequence(), map);
     acf.addMap(dna2.getDatasetSequence(), prot2.getDatasetSequence(), map);
     acf.addMap(dna3.getDatasetSequence(), prot3.getDatasetSequence(), map);
-    ArrayList<AlignedCodonFrame> acfs = new ArrayList<AlignedCodonFrame>();
+    ArrayList<AlignedCodonFrame> acfs = new ArrayList<>();
     acfs.add(acf);
     protein.setCodonFrames(acfs);
 
@@ -605,14 +606,14 @@ public class AlignmentUtilsTests
   public void testMapProteinAlignmentToCdna_withStartAndStopCodons()
           throws IOException
   {
-    List<SequenceI> protseqs = new ArrayList<SequenceI>();
+    List<SequenceI> protseqs = new ArrayList<>();
     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>();
+    List<SequenceI> dnaseqs = new ArrayList<>();
     // start + SAR:
     dnaseqs.add(new Sequence("EMBL|A11111", "ATGTCAGCACGC"));
     // = EIQ + stop
@@ -697,14 +698,14 @@ public class AlignmentUtilsTests
   @Test(groups = { "Functional" })
   public void testMapProteinAlignmentToCdna_withXrefs() throws IOException
   {
-    List<SequenceI> protseqs = new ArrayList<SequenceI>();
+    List<SequenceI> protseqs = new ArrayList<>();
     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>();
+    List<SequenceI> dnaseqs = new ArrayList<>();
     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
@@ -774,14 +775,14 @@ public class AlignmentUtilsTests
   public void testMapProteinAlignmentToCdna_prioritiseXrefs()
           throws IOException
   {
-    List<SequenceI> protseqs = new ArrayList<SequenceI>();
+    List<SequenceI> protseqs = new ArrayList<>();
     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>();
+    List<SequenceI> dnaseqs = new ArrayList<>();
     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
@@ -848,8 +849,8 @@ public class AlignmentUtilsTests
     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>();
+    List<String> types = new ArrayList<>();
+    List<SequenceI> scope = new ArrayList<>();
 
     /*
      * Set all sequence related Structure to hidden (ann1, ann2)
@@ -1747,7 +1748,7 @@ public class AlignmentUtilsTests
     map = new MapList(new int[] { 9, 11 }, new int[] { 2, 2 }, 3, 1);
     acf.addMap(dna3.getDatasetSequence(), prot3.getDatasetSequence(), map);
 
-    ArrayList<AlignedCodonFrame> acfs = new ArrayList<AlignedCodonFrame>();
+    ArrayList<AlignedCodonFrame> acfs = new ArrayList<>();
     acfs.add(acf);
     protein.setCodonFrames(acfs);
 
@@ -2030,9 +2031,9 @@ public class AlignmentUtilsTests
     sf6.setValue("ID", "var6");
     sf6.setValue("clinical_significance", "Good");
 
-    List<DnaVariant> codon1Variants = new ArrayList<DnaVariant>();
-    List<DnaVariant> codon2Variants = new ArrayList<DnaVariant>();
-    List<DnaVariant> codon3Variants = new ArrayList<DnaVariant>();
+    List<DnaVariant> codon1Variants = new ArrayList<>();
+    List<DnaVariant> codon2Variants = new ArrayList<>();
+    List<DnaVariant> codon3Variants = new ArrayList<>();
     List<DnaVariant> codonVariants[] = new ArrayList[3];
     codonVariants[0] = codon1Variants;
     codonVariants[1] = codon2Variants;
@@ -2272,7 +2273,7 @@ public class AlignmentUtilsTests
     seq1.createDatasetSequence();
     Mapping mapping = new Mapping(seq1, new MapList(
             new int[] { 3, 6, 9, 10 }, new int[] { 1, 6 }, 1, 1));
-    Map<Integer, Map<SequenceI, Character>> map = new TreeMap<Integer, Map<SequenceI, Character>>();
+    Map<Integer, Map<SequenceI, Character>> map = new TreeMap<>();
     AlignmentUtils.addMappedPositions(seq1, from, mapping, map);
 
     /*
@@ -2304,7 +2305,7 @@ public class AlignmentUtilsTests
     seq1.createDatasetSequence();
     Mapping mapping = new Mapping(seq1, new MapList(
             new int[] { 3, 6, 9, 10 }, new int[] { 1, 6 }, 1, 1));
-    Map<Integer, Map<SequenceI, Character>> map = new TreeMap<Integer, Map<SequenceI, Character>>();
+    Map<Integer, Map<SequenceI, Character>> map = new TreeMap<>();
     AlignmentUtils.addMappedPositions(seq1, from, mapping, map);
 
     /*
@@ -2561,7 +2562,7 @@ public class AlignmentUtilsTests
      * Case 2: CDS 3 times length of peptide + stop codon
      * (note code does not currently check trailing codon is a stop codon)
      */
-    dna = new Sequence("dna", "AACGacgtCTCCTTGA");
+    dna = new Sequence("dna", "AACGacgtCTCCTCCC");
     dna.createDatasetSequence();
     dna.addSequenceFeature(new SequenceFeature("CDS", "", 1, 4, null));
     dna.addSequenceFeature(new SequenceFeature("CDS", "", 9, 16, null));
@@ -2574,17 +2575,42 @@ public class AlignmentUtilsTests
             Arrays.deepToString(ml.getFromRanges().toArray()));
 
     /*
-     * Case 3: CDS not 3 times length of peptide - no mapping is made
+     * Case 3: CDS longer than 3 * peptide + stop codon - no mapping is made
+     */
+    dna = new Sequence("dna", "AACGacgtCTCCTTGATCA");
+    dna.createDatasetSequence();
+    dna.addSequenceFeature(new SequenceFeature("CDS", "", 1, 4, null));
+    dna.addSequenceFeature(new SequenceFeature("CDS", "", 9, 19, null));
+    ml = AlignmentUtils.mapCdsToProtein(dna, peptide);
+    assertNull(ml);
+
+    /*
+     * Case 4: CDS shorter than 3 * peptide - no mapping is made
+     */
+    dna = new Sequence("dna", "AACGacgtCTCC");
+    dna.createDatasetSequence();
+    dna.addSequenceFeature(new SequenceFeature("CDS", "", 1, 4, null));
+    dna.addSequenceFeature(new SequenceFeature("CDS", "", 9, 12, null));
+    ml = AlignmentUtils.mapCdsToProtein(dna, peptide);
+    assertNull(ml);
+
+    /*
+     * Case 5: CDS 3 times length of peptide + part codon - mapping is truncated
      */
     dna = new Sequence("dna", "AACGacgtCTCCTTG");
     dna.createDatasetSequence();
     dna.addSequenceFeature(new SequenceFeature("CDS", "", 1, 4, null));
     dna.addSequenceFeature(new SequenceFeature("CDS", "", 9, 15, null));
     ml = AlignmentUtils.mapCdsToProtein(dna, peptide);
-    assertNull(ml);
+    assertEquals(3, ml.getFromRatio());
+    assertEquals(1, ml.getToRatio());
+    assertEquals("[[1, 3]]",
+            Arrays.deepToString(ml.getToRanges().toArray()));
+    assertEquals("[[1, 4], [9, 13]]",
+            Arrays.deepToString(ml.getFromRanges().toArray()));
 
     /*
-     * Case 4: incomplete start codon corresponding to X in peptide
+     * Case 6: incomplete start codon corresponding to X in peptide
      */
     dna = new Sequence("dna", "ACGacgtCTCCTTGG");
     dna.createDatasetSequence();
@@ -2600,4 +2626,151 @@ public class AlignmentUtilsTests
             Arrays.deepToString(ml.getFromRanges().toArray()));
   }
 
+  /**
+   * Tests for the method that locates the CDS sequence that has a mapping to
+   * the given protein. That is, given a transcript-to-peptide mapping, find the
+   * cds-to-peptide mapping that relates to both, and return the CDS sequence.
+   */
+  @Test
+  public void testFindCdsForProtein()
+  {
+    List<AlignedCodonFrame> mappings = new ArrayList<>();
+    AlignedCodonFrame acf1 = new AlignedCodonFrame();
+    mappings.add(acf1);
+
+    SequenceI dna1 = new Sequence("dna1", "cgatATcgGCTATCTATGacg");
+    dna1.createDatasetSequence();
+
+    // NB we currently exclude STOP codon from CDS sequences
+    // the test would need to change if this changes in future
+    SequenceI cds1 = new Sequence("cds1", "ATGCTATCT");
+    cds1.createDatasetSequence();
+
+    SequenceI pep1 = new Sequence("pep1", "MLS");
+    pep1.createDatasetSequence();
+    List<AlignedCodonFrame> seqMappings = new ArrayList<>();
+    MapList mapList = new MapList(
+            new int[]
+            { 5, 6, 9, 15 }, new int[] { 1, 3 }, 3, 1);
+    Mapping dnaToPeptide = new Mapping(pep1.getDatasetSequence(), mapList);
+    
+    // add dna to peptide mapping
+    seqMappings.add(acf1);
+    acf1.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(),
+            mapList);
+
+    /*
+     * first case - no dna-to-CDS mapping exists - search fails
+     */
+    SequenceI seq = AlignmentUtils.findCdsForProtein(mappings, dna1,
+            seqMappings, dnaToPeptide);
+    assertNull(seq);
+
+    /*
+     * second case - CDS-to-peptide mapping exists but no dna-to-CDS
+     * - search fails
+     */
+    // todo this test fails if the mapping is added to acf1, not acf2
+    // need to tidy up use of lists of mappings in AlignedCodonFrame
+    AlignedCodonFrame acf2 = new AlignedCodonFrame();
+    mappings.add(acf2);
+    MapList cdsToPeptideMapping = new MapList(new int[]
+    { 1, 9 }, new int[] { 1, 3 }, 3, 1);
+    acf2.addMap(cds1.getDatasetSequence(), pep1.getDatasetSequence(),
+            cdsToPeptideMapping);
+    assertNull(AlignmentUtils.findCdsForProtein(mappings, dna1, seqMappings,
+            dnaToPeptide));
+
+    /*
+     * third case - add dna-to-CDS mapping - CDS is now found!
+     */
+    MapList dnaToCdsMapping = new MapList(new int[] { 5, 6, 9, 15 },
+            new int[]
+            { 1, 9 }, 1, 1);
+    acf1.addMap(dna1.getDatasetSequence(), cds1.getDatasetSequence(),
+            dnaToCdsMapping);
+    seq = AlignmentUtils.findCdsForProtein(mappings, dna1, seqMappings,
+            dnaToPeptide);
+    assertSame(seq, cds1.getDatasetSequence());
+  }
+
+  /**
+   * Tests for the method that locates the CDS sequence that has a mapping to
+   * the given protein. That is, given a transcript-to-peptide mapping, find the
+   * cds-to-peptide mapping that relates to both, and return the CDS sequence.
+   * This test is for the case where transcript and CDS are the same length.
+   */
+  @Test
+  public void testFindCdsForProtein_noUTR()
+  {
+    List<AlignedCodonFrame> mappings = new ArrayList<>();
+    AlignedCodonFrame acf1 = new AlignedCodonFrame();
+    mappings.add(acf1);
+  
+    SequenceI dna1 = new Sequence("dna1", "ATGCTATCTTAA");
+    dna1.createDatasetSequence();
+  
+    // NB we currently exclude STOP codon from CDS sequences
+    // the test would need to change if this changes in future
+    SequenceI cds1 = new Sequence("cds1", "ATGCTATCT");
+    cds1.createDatasetSequence();
+  
+    SequenceI pep1 = new Sequence("pep1", "MLS");
+    pep1.createDatasetSequence();
+    List<AlignedCodonFrame> seqMappings = new ArrayList<>();
+    MapList mapList = new MapList(
+            new int[]
+            { 1, 9 }, new int[] { 1, 3 }, 3, 1);
+    Mapping dnaToPeptide = new Mapping(pep1.getDatasetSequence(), mapList);
+    
+    // add dna to peptide mapping
+    seqMappings.add(acf1);
+    acf1.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(),
+            mapList);
+  
+    /*
+     * first case - transcript lacks CDS features - it appears to be
+     * the CDS sequence and is returned
+     */
+    SequenceI seq = AlignmentUtils.findCdsForProtein(mappings, dna1,
+            seqMappings, dnaToPeptide);
+    assertSame(seq, dna1.getDatasetSequence());
+  
+    /*
+     * second case - transcript has CDS feature - this means it is
+     * not returned as a match for CDS (CDS sequences don't have CDS features)
+     */
+    dna1.addSequenceFeature(
+            new SequenceFeature(SequenceOntologyI.CDS, "cds", 1, 12, null));
+    seq = AlignmentUtils.findCdsForProtein(mappings, dna1, seqMappings,
+            dnaToPeptide);
+    assertNull(seq);
+
+    /*
+     * third case - CDS-to-peptide mapping exists but no dna-to-CDS
+     * - search fails
+     */
+    // todo this test fails if the mapping is added to acf1, not acf2
+    // need to tidy up use of lists of mappings in AlignedCodonFrame
+    AlignedCodonFrame acf2 = new AlignedCodonFrame();
+    mappings.add(acf2);
+    MapList cdsToPeptideMapping = new MapList(new int[]
+    { 1, 9 }, new int[] { 1, 3 }, 3, 1);
+    acf2.addMap(cds1.getDatasetSequence(), pep1.getDatasetSequence(),
+            cdsToPeptideMapping);
+    assertNull(AlignmentUtils.findCdsForProtein(mappings, dna1, seqMappings,
+            dnaToPeptide));
+  
+    /*
+     * fourth case - add dna-to-CDS mapping - CDS is now found!
+     */
+    MapList dnaToCdsMapping = new MapList(new int[] { 1, 9 },
+            new int[]
+            { 1, 9 }, 1, 1);
+    acf1.addMap(dna1.getDatasetSequence(), cds1.getDatasetSequence(),
+            dnaToCdsMapping);
+    seq = AlignmentUtils.findCdsForProtein(mappings, dna1, seqMappings,
+            dnaToPeptide);
+    assertSame(seq, cds1.getDatasetSequence());
+  }
 }
index 95be1ff..0265af3 100644 (file)
@@ -106,7 +106,7 @@ public class CrossRefTest
   public void testFindXrefSourcesForSequence_proteinToDna()
   {
     SequenceI seq = new Sequence("Seq1", "MGKYQARLSS");
-    List<String> sources = new ArrayList<String>();
+    List<String> sources = new ArrayList<>();
     AlignmentI al = new Alignment(new SequenceI[] {});
 
     /*
@@ -132,8 +132,9 @@ public class CrossRefTest
     sources = new CrossRef(new SequenceI[] { seq }, al)
             .findXrefSourcesForSequences(false);
     // method is patched to remove EMBL from the sources to match
-    assertEquals(3, sources.size());
-    assertEquals("[EMBLCDS, GENEDB, ENSEMBL]", sources.toString());
+    assertEquals(4, sources.size());
+    assertEquals("[EMBLCDS, GENEDB, ENSEMBL, ENSEMBLGENOMES]",
+            sources.toString());
 
     /*
      * add a sequence to the alignment which has a dbref to UNIPROT|A1234
@@ -270,7 +271,7 @@ public class CrossRefTest
     pep1.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2"));
     AlignmentI al = new Alignment(new SequenceI[] { dna1, pep1 });
 
-    List<SequenceI> result = new ArrayList<SequenceI>();
+    List<SequenceI> result = new ArrayList<>();
 
     /*
      * first search for a dbref nowhere on the alignment:
index d2fa99a..6a31b31 100644 (file)
@@ -38,6 +38,7 @@ import jalview.io.FileFormat;
 import jalview.io.FormatAdapter;
 
 import java.io.IOException;
+import java.util.Iterator;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -135,7 +136,9 @@ public class DnaTest
             FileFormat.Fasta);
     HiddenColumns cs = new HiddenColumns();
     AlignViewportI av = new AlignViewport(alf, cs);
-    Dna dna = new Dna(av, new int[] { 0, alf.getWidth() - 1 });
+    Iterator<int[]> contigs = cs.getVisContigsIterator(0, alf.getWidth(),
+            false);
+    Dna dna = new Dna(av, contigs);
     AlignmentI translated = dna.translateCdna();
     assertNotNull("Couldn't do a full width translation of test data.",
             translated);
@@ -163,7 +166,8 @@ public class DnaTest
         cs.hideColumns(0, ipos - 1);
       }
       cs.hideColumns(ipos + vwidth, alf.getWidth());
-      int[] vcontigs = cs.getVisibleContigs(0, alf.getWidth());
+      Iterator<int[]> vcontigs = cs.getVisContigsIterator(0,
+              alf.getWidth(), false);
       AlignViewportI av = new AlignViewport(alf, cs);
       Dna dna = new Dna(av, vcontigs);
       AlignmentI transAlf = dna.translateCdna();
@@ -190,7 +194,9 @@ public class DnaTest
             DataSourceType.PASTE, FileFormat.Fasta);
     HiddenColumns cs = new HiddenColumns();
     AlignViewportI av = new AlignViewport(alf, cs);
-    Dna dna = new Dna(av, new int[] { 0, alf.getWidth() - 1 });
+    Iterator<int[]> contigs = cs.getVisContigsIterator(0, alf.getWidth(),
+            false);
+    Dna dna = new Dna(av, contigs);
     AlignmentI translated = dna.translateCdna();
     String aa = translated.getSequenceAt(0).getSequenceAsString();
     assertEquals(
@@ -213,7 +219,9 @@ public class DnaTest
     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 });
+    Iterator<int[]> contigs = cs.getVisContigsIterator(0, alf.getWidth(),
+            false);
+    Dna dna = new Dna(av, contigs);
     AlignmentI translated = dna.translateCdna();
     String aa = translated.getSequenceAt(0).getSequenceAsString();
     assertEquals("AACDDGGGGHHIIIKKLLLLLLMNNPPPPQQRRRRRRSSSSSSTTTTVVVVW", aa);
@@ -298,7 +306,9 @@ public class DnaTest
             .generate(12, 8, 97, 5, 5);
     HiddenColumns cs = new HiddenColumns();
     AlignViewportI av = new AlignViewport(cdna, cs);
-    Dna dna = new Dna(av, new int[] { 0, cdna.getWidth() - 1 });
+    Iterator<int[]> contigs = cs.getVisContigsIterator(0, cdna.getWidth(),
+            false);
+    Dna dna = new Dna(av, contigs);
     AlignmentI translated = dna.translateCdna();
 
     /*
@@ -313,7 +323,8 @@ public class DnaTest
     }
     AlignmentI cdnaReordered = new Alignment(sorted);
     av = new AlignViewport(cdnaReordered, cs);
-    dna = new Dna(av, new int[] { 0, cdna.getWidth() - 1 });
+    contigs = cs.getVisContigsIterator(0, cdna.getWidth(), false);
+    dna = new Dna(av, contigs);
     AlignmentI translated2 = dna.translateCdna();
 
     /*
@@ -544,7 +555,9 @@ public class DnaTest
 
     HiddenColumns cs = new HiddenColumns();
     AlignViewportI av = new AlignViewport(al, cs);
-    Dna testee = new Dna(av, new int[] { 0, al.getWidth() - 1 });
+    Iterator<int[]> contigs = cs.getVisContigsIterator(0, al.getWidth(),
+            false);
+    Dna testee = new Dna(av, contigs);
     AlignmentI reversed = testee.reverseCdna(false);
     assertEquals(1, reversed.getHeight());
     assertEquals(seqRev, reversed.getSequenceAt(0).getSequenceAsString());
index e47e9d6..19a725e 100644 (file)
@@ -20,8 +20,8 @@
  */
 package jalview.datamodel;
 
+import static org.testng.Assert.assertNull;
 import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertNull;
 
 import jalview.analysis.AlignSeq;
 import jalview.gui.JvOptionPane;
@@ -335,4 +335,92 @@ public class AlignmentAnnotationTests
     Assert.assertTrue(ann.isQuantitative(),
             "Mixed 'E' annotation set should be quantitative.");
   }
+
+  @Test(groups = "Functional")
+  public void testMakeVisibleAnnotation()
+  {
+    HiddenColumns h = new HiddenColumns();
+    Annotation[] anns = new Annotation[] { null, null, new Annotation(1),
+        new Annotation(2), new Annotation(3), null, null, new Annotation(4),
+        new Annotation(5), new Annotation(6), new Annotation(7),
+        new Annotation(8) };
+    AlignmentAnnotation ann = new AlignmentAnnotation("an", "some an",
+            anns);
+
+    // null annotations
+    AlignmentAnnotation emptyann = new AlignmentAnnotation("an", "some ann",
+            null);
+    emptyann.makeVisibleAnnotation(h);
+    assertNull(emptyann.annotations);
+
+    emptyann.makeVisibleAnnotation(3, 4, h);
+    assertNull(emptyann.annotations);
+
+    // without bounds, does everything
+    ann.makeVisibleAnnotation(h);
+    assertEquals(12, ann.annotations.length);
+    assertNull(ann.annotations[0]);
+    assertNull(ann.annotations[1]);
+    assertEquals(1.0f, ann.annotations[2].value);
+    assertEquals(2.0f, ann.annotations[3].value);
+    assertEquals(3.0f, ann.annotations[4].value);
+    assertNull(ann.annotations[5]);
+    assertNull(ann.annotations[6]);
+    assertEquals(4.0f, ann.annotations[7].value);
+    assertEquals(5.0f, ann.annotations[8].value);
+    assertEquals(6.0f, ann.annotations[9].value);
+    assertEquals(7.0f, ann.annotations[10].value);
+    assertEquals(8.0f, ann.annotations[11].value);
+
+    // without hidden cols, just truncates
+    ann.makeVisibleAnnotation(3, 5, h);
+    assertEquals(3, ann.annotations.length);
+    assertEquals(2.0f, ann.annotations[0].value);
+    assertEquals(3.0f, ann.annotations[1].value);
+    assertNull(ann.annotations[2]);
+
+    anns = new Annotation[] { null, null, new Annotation(1),
+        new Annotation(2), new Annotation(3), null, null, new Annotation(4),
+        new Annotation(5), new Annotation(6), new Annotation(7),
+        new Annotation(8) };
+    ann = new AlignmentAnnotation("an", "some an", anns);
+    h.hideColumns(4, 7);
+    ann.makeVisibleAnnotation(1, 9, h);
+    assertEquals(5, ann.annotations.length);
+    assertNull(ann.annotations[0]);
+    assertEquals(1.0f, ann.annotations[1].value);
+    assertEquals(2.0f, ann.annotations[2].value);
+    assertEquals(5.0f, ann.annotations[3].value);
+    assertEquals(6.0f, ann.annotations[4].value);
+
+    anns = new Annotation[] { null, null, new Annotation(1),
+        new Annotation(2), new Annotation(3), null, null, new Annotation(4),
+        new Annotation(5), new Annotation(6), new Annotation(7),
+        new Annotation(8) };
+    ann = new AlignmentAnnotation("an", "some an", anns);
+    h.hideColumns(1, 2);
+    ann.makeVisibleAnnotation(1, 9, h);
+    assertEquals(3, ann.annotations.length);
+    assertEquals(2.0f, ann.annotations[0].value);
+    assertEquals(5.0f, ann.annotations[1].value);
+    assertEquals(6.0f, ann.annotations[2].value);
+
+    anns = new Annotation[] { null, null, new Annotation(1),
+        new Annotation(2), new Annotation(3), null, null, new Annotation(4),
+        new Annotation(5), new Annotation(6), new Annotation(7),
+        new Annotation(8), new Annotation(9), new Annotation(10),
+        new Annotation(11), new Annotation(12), new Annotation(13),
+        new Annotation(14), new Annotation(15) };
+    ann = new AlignmentAnnotation("an", "some an", anns);
+    h = new HiddenColumns();
+    h.hideColumns(5, 18);
+    h.hideColumns(20, 21);
+    ann.makeVisibleAnnotation(1, 21, h);
+    assertEquals(5, ann.annotations.length);
+    assertEquals(1.0f, ann.annotations[1].value);
+    assertEquals(2.0f, ann.annotations[2].value);
+    assertEquals(3.0f, ann.annotations[3].value);
+    assertNull(ann.annotations[0]);
+    assertNull(ann.annotations[4]);
+  }
 }
index 4b5d096..1d1ebd6 100644 (file)
@@ -34,6 +34,7 @@ import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
 import jalview.io.FormatAdapter;
+import jalview.util.Comparison;
 import jalview.util.MapList;
 
 import java.io.IOException;
@@ -247,7 +248,9 @@ public class AlignmentTest
                   if (raiseAssert)
                   {
                     Assert.fail(message
-                            + " DBRefEntry for sequence in alignment had map to sequence not in dataset");
+                            + " DBRefEntry " + dbr + " for sequence "
+                            + seqds
+                            + " in alignment has map to sequence not in dataset");
                   }
                   return false;
                 }
@@ -668,6 +671,17 @@ public class AlignmentTest
     // third found.. so
     assertFalse(iter.hasNext());
 
+    // search for annotation on one sequence with a particular label - expect
+    // one
+    SequenceI sqfound;
+    anns = al.findAnnotations(sqfound = al.getSequenceAt(1), null,
+            "Secondary Structure");
+    iter = anns.iterator();
+    assertTrue(iter.hasNext());
+    // expect reference to sequence 1 in the alignment
+    assertTrue(sqfound == iter.next().sequenceRef);
+    assertFalse(iter.hasNext());
+
     // null on all parameters == find all annotations
     anns = al.findAnnotations(null, null, null);
     iter = anns.iterator();
@@ -1321,4 +1335,153 @@ public class AlignmentTest
     // todo test coverage for annotations, mappings, groups,
     // hidden sequences, properties
   }
+
+  /**
+   * test that calcId == null on findOrCreate doesn't raise an NPE, and yields
+   * an annotation with a null calcId
+   * 
+   */
+  @Test(groups = "Functional")
+  public void testFindOrCreateForNullCalcId()
+  {
+    SequenceI seq = new Sequence("seq1", "FRMLPSRT-A--L-");
+    AlignmentI alignment = new Alignment(new SequenceI[] { seq });
+
+    AlignmentAnnotation ala = alignment.findOrCreateAnnotation(
+            "Temperature Factor", null, false, seq, null);
+    assertNotNull(ala);
+    assertEquals(seq, ala.sequenceRef);
+    assertEquals("", ala.calcId);
+  }
+
+  @Test(groups = "Functional")
+  public void testPropagateInsertions()
+  {
+    // create an alignment with no gaps - this will be the profile seq and other
+    // JPRED seqs
+    AlignmentGenerator gen = new AlignmentGenerator(false);
+    AlignmentI al = gen.generate(25, 10, 1234, 0, 0);
+
+    // get the profileseq
+    SequenceI profileseq = al.getSequenceAt(0);
+    SequenceI gappedseq = new Sequence(profileseq);
+    gappedseq.insertCharAt(5, al.getGapCharacter());
+    gappedseq.insertCharAt(6, al.getGapCharacter());
+    gappedseq.insertCharAt(7, al.getGapCharacter());
+    gappedseq.insertCharAt(8, al.getGapCharacter());
+
+    // force different kinds of padding
+    al.getSequenceAt(3).deleteChars(2, 23);
+    al.getSequenceAt(4).deleteChars(2, 27);
+    al.getSequenceAt(5).deleteChars(10, 27);
+
+    // create an alignment view with the gapped sequence
+    SequenceI[] seqs = new SequenceI[1];
+    seqs[0] = gappedseq;
+    AlignmentI newal = new Alignment(seqs);
+    HiddenColumns hidden = new HiddenColumns();
+    hidden.hideColumns(15, 17);
+
+    AlignmentView view = new AlignmentView(newal, hidden, null, true, false,
+            false);
+
+    // confirm that original contigs are as expected
+    Iterator<int[]> visible = hidden.getVisContigsIterator(0, 25, false);
+    int[] region = visible.next();
+    assertEquals("[0, 14]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[18, 24]", Arrays.toString(region));
+
+    // propagate insertions
+    HiddenColumns result = al.propagateInsertions(profileseq, view);
+
+    // confirm that the contigs have changed to account for the gaps
+    visible = result.getVisContigsIterator(0, 25, false);
+    region = visible.next();
+    assertEquals("[0, 10]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[14, 24]", Arrays.toString(region));
+
+    // confirm the alignment has been changed so that the other sequences have
+    // gaps inserted where the columns are hidden
+    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[10]));
+    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[11]));
+    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[12]));
+    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[13]));
+    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[14]));
+
+  }
+
+  @Test(groups = "Functional")
+  public void testPropagateInsertionsOverlap()
+  {
+    // test propagateInsertions where gaps and hiddenColumns overlap
+
+    // create an alignment with no gaps - this will be the profile seq and other
+    // JPRED seqs
+    AlignmentGenerator gen = new AlignmentGenerator(false);
+    AlignmentI al = gen.generate(20, 10, 1234, 0, 0);
+
+    // get the profileseq
+    SequenceI profileseq = al.getSequenceAt(0);
+    SequenceI gappedseq = new Sequence(profileseq);
+    gappedseq.insertCharAt(5, al.getGapCharacter());
+    gappedseq.insertCharAt(6, al.getGapCharacter());
+    gappedseq.insertCharAt(7, al.getGapCharacter());
+    gappedseq.insertCharAt(8, al.getGapCharacter());
+
+    // create an alignment view with the gapped sequence
+    SequenceI[] seqs = new SequenceI[1];
+    seqs[0] = gappedseq;
+    AlignmentI newal = new Alignment(seqs);
+
+    // hide columns so that some overlap with the gaps
+    HiddenColumns hidden = new HiddenColumns();
+    hidden.hideColumns(7, 10);
+
+    AlignmentView view = new AlignmentView(newal, hidden, null, true, false,
+            false);
+
+    // confirm that original contigs are as expected
+    Iterator<int[]> visible = hidden.getVisContigsIterator(0, 20, false);
+    int[] region = visible.next();
+    assertEquals("[0, 6]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[11, 19]", Arrays.toString(region));
+    assertFalse(visible.hasNext());
+
+    // propagate insertions
+    HiddenColumns result = al.propagateInsertions(profileseq, view);
+
+    // confirm that the contigs have changed to account for the gaps
+    visible = result.getVisContigsIterator(0, 20, false);
+    region = visible.next();
+    assertEquals("[0, 4]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[7, 19]", Arrays.toString(region));
+    assertFalse(visible.hasNext());
+
+    // confirm the alignment has been changed so that the other sequences have
+    // gaps inserted where the columns are hidden
+    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[4]));
+    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[5]));
+    assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[6]));
+    assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[7]));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testPadGaps()
+  {
+    SequenceI seq1 = new Sequence("seq1", "ABCDEF--");
+    SequenceI seq2 = new Sequence("seq2", "-JKLMNO--");
+    SequenceI seq3 = new Sequence("seq2", "-PQR");
+    AlignmentI a = new Alignment(new SequenceI[] { seq1, seq2, seq3 });
+    a.setGapCharacter('.'); // this replaces existing gaps
+    assertEquals("ABCDEF..", seq1.getSequenceAsString());
+    a.padGaps();
+    // trailing gaps are pruned, short sequences padded with gap character
+    assertEquals("ABCDEF.", seq1.getSequenceAsString());
+    assertEquals(".JKLMNO", seq2.getSequenceAsString());
+    assertEquals(".PQR...", seq3.getSequenceAsString());
+  }
 }
index e99e952..8709961 100644 (file)
@@ -32,6 +32,7 @@ import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.ConcurrentModificationException;
+import java.util.Iterator;
 import java.util.List;
 
 import org.testng.annotations.BeforeClass;
@@ -132,9 +133,9 @@ public class ColumnSelectionTest
     // hide column 5 (and adjacent):
     cs.hideSelectedColumns(5, al.getHiddenColumns());
     // 4,5,6 now hidden:
-    List<int[]> hidden = al.getHiddenColumns().getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
+    Iterator<int[]> regions = al.getHiddenColumns().iterator();
+    assertEquals(1, al.getHiddenColumns().getNumberOfRegions());
+    assertEquals("[4, 6]", Arrays.toString(regions.next()));
     // none now selected:
     assertTrue(cs.getSelected().isEmpty());
 
@@ -145,9 +146,9 @@ public class ColumnSelectionTest
     cs.addElement(5);
     cs.addElement(6);
     cs.hideSelectedColumns(4, al.getHiddenColumns());
-    hidden = al.getHiddenColumns().getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
+    regions = al.getHiddenColumns().iterator();
+    assertEquals(1, al.getHiddenColumns().getNumberOfRegions());
+    assertEquals("[4, 6]", Arrays.toString(regions.next()));
     assertTrue(cs.getSelected().isEmpty());
 
     // repeat, hiding column (4, 5 and) 6
@@ -157,9 +158,9 @@ public class ColumnSelectionTest
     cs.addElement(5);
     cs.addElement(6);
     cs.hideSelectedColumns(6, al.getHiddenColumns());
-    hidden = al.getHiddenColumns().getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
+    regions = al.getHiddenColumns().iterator();
+    assertEquals(1, al.getHiddenColumns().getNumberOfRegions());
+    assertEquals("[4, 6]", Arrays.toString(regions.next()));
     assertTrue(cs.getSelected().isEmpty());
 
     // repeat, with _only_ adjacent columns selected
@@ -168,9 +169,9 @@ public class ColumnSelectionTest
     cs.addElement(4);
     cs.addElement(6);
     cs.hideSelectedColumns(5, al.getHiddenColumns());
-    hidden = al.getHiddenColumns().getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
+    regions = al.getHiddenColumns().iterator();
+    assertEquals(1, al.getHiddenColumns().getNumberOfRegions());
+    assertEquals("[4, 6]", Arrays.toString(regions.next()));
     assertTrue(cs.getSelected().isEmpty());
   }
 
@@ -196,12 +197,12 @@ public class ColumnSelectionTest
 
     cs.hideSelectedColumns(al);
     assertTrue(cs.getSelected().isEmpty());
-    List<int[]> hidden = cols.getHiddenColumnsCopy();
-    assertEquals(4, hidden.size());
-    assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
-    assertEquals("[7, 9]", Arrays.toString(hidden.get(1)));
-    assertEquals("[15, 18]", Arrays.toString(hidden.get(2)));
-    assertEquals("[20, 22]", Arrays.toString(hidden.get(3)));
+    Iterator<int[]> regions = cols.iterator();
+    assertEquals(4, cols.getNumberOfRegions());
+    assertEquals("[2, 4]", Arrays.toString(regions.next()));
+    assertEquals("[7, 9]", Arrays.toString(regions.next()));
+    assertEquals("[15, 18]", Arrays.toString(regions.next()));
+    assertEquals("[20, 22]", Arrays.toString(regions.next()));
   }
 
   /**
diff --git a/test/jalview/datamodel/HiddenColumnsCursorTest.java b/test/jalview/datamodel/HiddenColumnsCursorTest.java
new file mode 100644 (file)
index 0000000..97402b8
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * 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.datamodel;
+
+import static org.testng.Assert.assertNull;
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+public class HiddenColumnsCursorTest
+{
+
+  @Test(groups = { "Functional" })
+  public void testConstructor()
+  {
+    HiddenColumnsCursor cursor = new HiddenColumnsCursor();
+    assertNull(cursor.findRegionForColumn(0, false));
+
+    List<int[]> hlist = new ArrayList<>();
+    cursor = new HiddenColumnsCursor(hlist);
+    assertNull(cursor.findRegionForColumn(0, false));
+
+    cursor = new HiddenColumnsCursor(hlist, 3, 12);
+    assertNull(cursor.findRegionForColumn(0, false));
+
+    hlist.add(new int[] { 3, 7 });
+    hlist.add(new int[] { 15, 25 });
+    cursor = new HiddenColumnsCursor(hlist);
+    HiddenCursorPosition p = cursor.findRegionForColumn(8, false);
+    assertEquals(1, p.getRegionIndex());
+
+    cursor = new HiddenColumnsCursor(hlist, 1, 5);
+    p = cursor.findRegionForColumn(8, false);
+    assertEquals(1, p.getRegionIndex());
+  }
+
+  /**
+   * Test the method which finds the corresponding region given a column
+   */
+  @Test(groups = { "Functional" })
+  public void testFindRegionForColumn()
+  {
+    HiddenColumnsCursor cursor = new HiddenColumnsCursor();
+    
+    HiddenCursorPosition pos = cursor.findRegionForColumn(20, false);
+    assertNull(pos);
+    
+    List<int[]> hidden = new ArrayList<>();
+    hidden.add(new int[] { 53, 76 });
+    hidden.add(new int[] { 104, 125 });
+
+    cursor = new HiddenColumnsCursor(hidden);
+
+    int regionIndex = cursor.findRegionForColumn(126, false).getRegionIndex();
+    assertEquals(2, regionIndex);
+
+    regionIndex = cursor.findRegionForColumn(125, false).getRegionIndex();
+    assertEquals(1, regionIndex);
+
+    regionIndex = cursor.findRegionForColumn(108, false).getRegionIndex();
+    assertEquals(1, regionIndex);
+
+    regionIndex = cursor.findRegionForColumn(104, false).getRegionIndex();
+    assertEquals(1, regionIndex);
+
+    regionIndex = cursor.findRegionForColumn(103, false).getRegionIndex();
+    assertEquals(1, regionIndex);
+
+    regionIndex = cursor.findRegionForColumn(77, false).getRegionIndex();
+    assertEquals(1, regionIndex);
+
+    regionIndex = cursor.findRegionForColumn(76, false).getRegionIndex();
+    assertEquals(0, regionIndex);
+
+    regionIndex = cursor.findRegionForColumn(53, false).getRegionIndex();
+    assertEquals(0, regionIndex);
+
+    regionIndex = cursor.findRegionForColumn(52, false).getRegionIndex();
+    assertEquals(0, regionIndex);
+
+    regionIndex = cursor.findRegionForColumn(0, false).getRegionIndex();
+    assertEquals(0, regionIndex);
+
+    hidden.add(new int[] { 138, 155 });
+
+    cursor = new HiddenColumnsCursor(hidden);
+
+    regionIndex = cursor.findRegionForColumn(160, false).getRegionIndex();
+    assertEquals(3, regionIndex);
+
+    regionIndex = cursor.findRegionForColumn(100, false).getRegionIndex();
+    assertEquals(1, regionIndex);
+  }
+
+  /**
+   * Test the method which counts the number of hidden columns before a column
+   */
+  @Test(groups = { "Functional" })
+  public void testFindRegionForColumn_Visible()
+  {
+    HiddenColumnsCursor cursor = new HiddenColumnsCursor();
+
+    HiddenCursorPosition pos = cursor.findRegionForColumn(20, true);
+    assertNull(pos);
+
+    List<int[]> hidden = new ArrayList<>();
+    hidden.add(new int[] { 53, 76 });
+    hidden.add(new int[] { 104, 125 });
+
+    cursor = new HiddenColumnsCursor(hidden);
+
+    int offset = cursor.findRegionForColumn(80, true).getHiddenSoFar();
+    assertEquals(46, offset);
+
+    offset = cursor.findRegionForColumn(79, true).getHiddenSoFar();
+    assertEquals(24, offset);
+
+    offset = cursor.findRegionForColumn(53, true).getHiddenSoFar();
+    assertEquals(24, offset);
+
+    offset = cursor.findRegionForColumn(52, true).getHiddenSoFar();
+    assertEquals(0, offset);
+
+    offset = cursor.findRegionForColumn(10, true).getHiddenSoFar();
+    assertEquals(0, offset);
+
+    offset = cursor.findRegionForColumn(0, true).getHiddenSoFar();
+    assertEquals(0, offset);
+
+    offset = cursor.findRegionForColumn(79, true).getHiddenSoFar();
+    assertEquals(24, offset);
+
+    offset = cursor.findRegionForColumn(80, true).getHiddenSoFar();
+    assertEquals(46, offset);
+  }
+}
index 7c88d71..2916199 100644 (file)
@@ -26,26 +26,15 @@ import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.analysis.AlignmentGenerator;
-import jalview.gui.JvOptionPane;
 
 import java.util.Arrays;
 import java.util.BitSet;
-import java.util.List;
-import java.util.Random;
+import java.util.Iterator;
 
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 public class HiddenColumnsTest
 {
-
-  @BeforeClass(alwaysRun = true)
-  public void setUpJvOptionPane()
-  {
-    JvOptionPane.setInteractiveMode(false);
-    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
-  }
-
   /**
    * Test the method which counts the number of hidden columns
    */
@@ -77,22 +66,22 @@ public class HiddenColumnsTest
   public void testFindColumnPosition()
   {
     HiddenColumns cs = new HiddenColumns();
-    assertEquals(5, cs.findColumnPosition(5));
+    assertEquals(5, cs.absoluteToVisibleColumn(5));
 
     // hiding column 6 makes no difference
     cs.hideColumns(6, 6);
-    assertEquals(5, cs.findColumnPosition(5));
+    assertEquals(5, cs.absoluteToVisibleColumn(5));
 
     // hiding column 4 moves column 5 to column 4
     cs.hideColumns(4, 4);
-    assertEquals(4, cs.findColumnPosition(5));
+    assertEquals(4, cs.absoluteToVisibleColumn(5));
 
     // hiding column 4 moves column 4 to position 3
-    assertEquals(3, cs.findColumnPosition(4));
+    assertEquals(3, cs.absoluteToVisibleColumn(4));
 
     // hiding columns 1 and 2 moves column 5 to column 2
     cs.hideColumns(1, 2);
-    assertEquals(2, cs.findColumnPosition(5));
+    assertEquals(2, cs.absoluteToVisibleColumn(5));
 
     // check with > 1 hidden column regions
     // where some columns are in the hidden regions
@@ -102,105 +91,82 @@ public class HiddenColumnsTest
     cs2.hideColumns(40, 44);
 
     // hiding columns 5-10 and 20-27 moves column 8 to column 4
-    assertEquals(4, cs2.findColumnPosition(8));
+    assertEquals(4, cs2.absoluteToVisibleColumn(8));
 
     // and moves column 24 to 13
-    assertEquals(13, cs2.findColumnPosition(24));
+    assertEquals(13, cs2.absoluteToVisibleColumn(24));
 
     // and moves column 28 to 14
-    assertEquals(14, cs2.findColumnPosition(28));
+    assertEquals(14, cs2.absoluteToVisibleColumn(28));
 
     // and moves column 40 to 25
-    assertEquals(25, cs2.findColumnPosition(40));
+    assertEquals(25, cs2.absoluteToVisibleColumn(40));
 
     // check when hidden columns start at 0 that the visible column
     // is returned as 0
     HiddenColumns cs3 = new HiddenColumns();
     cs3.hideColumns(0, 4);
-    assertEquals(0, cs3.findColumnPosition(2));
+    assertEquals(0, cs3.absoluteToVisibleColumn(2));
 
+    // check that column after the last hidden region doesn't crash
+    assertEquals(46, cs2.absoluteToVisibleColumn(65));
   }
 
-  /**
-   * Test the method that finds the visible column position a given distance
-   * before another column
-   */
   @Test(groups = { "Functional" })
-  public void testFindColumnNToLeft()
+  public void testVisibleContigsIterator()
   {
     HiddenColumns cs = new HiddenColumns();
 
-    // test that without hidden columns, findColumnNToLeft returns
-    // position n to left of provided position
-    int pos = cs.subtractVisibleColumns(3, 10);
-    assertEquals(7, pos);
-
-    // 0 returns same position
-    pos = cs.subtractVisibleColumns(0, 10);
-    assertEquals(10, pos);
-
-    // overflow to left returns negative number
-    pos = cs.subtractVisibleColumns(3, 0);
-    assertEquals(-3, pos);
-
-    // test that with hidden columns to left of result column
-    // behaviour is the same as above
-    cs.hideColumns(1, 3);
-
-    // position n to left of provided position
-    pos = cs.subtractVisibleColumns(3, 10);
-    assertEquals(7, pos);
-
-    // 0 returns same position
-    pos = cs.subtractVisibleColumns(0, 10);
-    assertEquals(10, pos);
-
-    // test with one set of hidden columns between start and required position
-    cs.hideColumns(12, 15);
-    pos = cs.subtractVisibleColumns(8, 17);
-    assertEquals(5, pos);
-
-    // test with two sets of hidden columns between start and required position
-    cs.hideColumns(20, 21);
-    pos = cs.subtractVisibleColumns(8, 23);
-    assertEquals(9, pos);
-
-    // repeat last 2 tests with no hidden columns to left of required position
-    ColumnSelection colsel = new ColumnSelection();
-    cs.revealAllHiddenColumns(colsel);
-
-    // test with one set of hidden columns between start and required position
-    cs.hideColumns(12, 15);
-    pos = cs.subtractVisibleColumns(8, 17);
-    assertEquals(5, pos);
-
-    // test with two sets of hidden columns between start and required position
-    cs.hideColumns(20, 21);
-    pos = cs.subtractVisibleColumns(8, 23);
-    assertEquals(9, pos);
-
-  }
+    Iterator<int[]> visible = cs.getVisContigsIterator(3, 10, false);
+    int[] region = visible.next();
+    assertEquals("[3, 9]", Arrays.toString(region));
+    assertFalse(visible.hasNext());
 
-  @Test(groups = { "Functional" })
-  public void testGetVisibleContigs()
-  {
-    HiddenColumns cs = new HiddenColumns();
     cs.hideColumns(3, 6);
     cs.hideColumns(8, 9);
     cs.hideColumns(12, 12);
 
-    // start position is inclusive, end position exclusive:
-    int[] visible = cs.getVisibleContigs(1, 13);
-    assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible));
-
-    visible = cs.getVisibleContigs(4, 14);
-    assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible));
-
-    visible = cs.getVisibleContigs(3, 10);
-    assertEquals("[7, 7]", Arrays.toString(visible));
-
-    visible = cs.getVisibleContigs(4, 6);
-    assertEquals("[]", Arrays.toString(visible));
+    // Test both ends visible region
+
+    // start position is inclusive, end position exclusive
+    visible = cs.getVisContigsIterator(1, 13, false);
+    region = visible.next();
+    assertEquals("[1, 2]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[7, 7]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[10, 11]", Arrays.toString(region));
+    assertFalse(visible.hasNext());
+
+    // Test start hidden, end visible
+    visible = cs.getVisContigsIterator(4, 14, false);
+    region = visible.next();
+    assertEquals("[7, 7]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[10, 11]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[13, 13]", Arrays.toString(region));
+    assertFalse(visible.hasNext());
+
+    // Test start hidden, end hidden
+    visible = cs.getVisContigsIterator(3, 10, false);
+    region = visible.next();
+    assertEquals("[7, 7]", Arrays.toString(region));
+    assertFalse(visible.hasNext());
+
+    // Test start visible, end hidden
+    visible = cs.getVisContigsIterator(0, 13, false);
+    region = visible.next();
+    assertEquals("[0, 2]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[7, 7]", Arrays.toString(region));
+    region = visible.next();
+    assertEquals("[10, 11]", Arrays.toString(region));
+    assertFalse(visible.hasNext());
+
+    // Test empty result
+    visible = cs.getVisContigsIterator(4, 6, false);
+    assertFalse(visible.hasNext());
   }
 
   @Test(groups = { "Functional" })
@@ -216,14 +182,31 @@ public class HiddenColumnsTest
     assertFalse(cs.equals(cs2));
     assertFalse(cs2.equals(cs));
 
+    // with the wrong kind of object
+    assertFalse(cs.equals(new HiddenColumnsCursor()));
+
+    // with a different hiddenColumns object - by size
+    HiddenColumns cs3 = new HiddenColumns();
+    cs3.hideColumns(2, 3);
+    assertFalse(cs.equals(cs3));
+
     // with hidden columns added in a different order
     cs2.hideColumns(6, 9);
+    assertFalse(cs.equals(cs2));
+    assertFalse(cs2.equals(cs));
+
     cs2.hideColumns(5, 8);
 
     assertTrue(cs.equals(cs2));
     assertTrue(cs.equals(cs));
     assertTrue(cs2.equals(cs));
     assertTrue(cs2.equals(cs2));
+
+    // different ranges, same size
+    cs.hideColumns(10, 12);
+    cs2.hideColumns(10, 15);
+    assertFalse(cs.equals(cs2));
+
   }
 
   @Test(groups = "Functional")
@@ -232,102 +215,50 @@ public class HiddenColumnsTest
     HiddenColumns cs = new HiddenColumns();
     cs.hideColumns(10, 11);
     cs.hideColumns(5, 7);
+    Iterator<int[]> regions = cs.iterator();
     assertEquals("[5, 7]",
-            Arrays.toString(cs.getHiddenColumnsCopy().get(0)));
+            Arrays.toString(regions.next()));
 
     HiddenColumns cs2 = new HiddenColumns(cs);
+    regions = cs2.iterator();
     assertTrue(cs2.hasHiddenColumns());
-    assertEquals(2, cs2.getHiddenColumnsCopy().size());
+    assertEquals(2, cs2.getNumberOfRegions());
     // hidden columns are held in column order
     assertEquals("[5, 7]",
-            Arrays.toString(cs2.getHiddenColumnsCopy().get(0)));
+            Arrays.toString(regions.next()));
     assertEquals("[10, 11]",
-            Arrays.toString(cs2.getHiddenColumnsCopy().get(1)));
+            Arrays.toString(regions.next()));
   }
 
-  /**
-   * Test the code used to locate the reference sequence ruler origin
-   */
-  @Test(groups = { "Functional" })
-  public void testLocateVisibleBoundsofSequence()
+  @Test(groups = "Functional")
+  public void testCopyConstructor2()
   {
-    // create random alignment
-    AlignmentGenerator gen = new AlignmentGenerator(false);
-    AlignmentI al = gen.generate(50, 20, 123, 5, 5);
+    HiddenColumns cs = new HiddenColumns();
+    cs.hideColumns(10, 11);
+    cs.hideColumns(5, 7);
 
-    HiddenColumns cs = al.getHiddenColumns();
-    ColumnSelection colsel = new ColumnSelection();
+    HiddenColumns cs2 = new HiddenColumns(cs, 3, 9, 1);
+    assertTrue(cs2.hasHiddenColumns());
+    Iterator<int[]> regions = cs2.iterator();
 
-    SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
-    assertEquals(2, seq.findIndex(seq.getStart()));
-
-    // no hidden columns
-    assertEquals(
-            Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
-                seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
-                seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
-                seq.findIndex(seq.getEnd()) - 1 }),
-            Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
-
-    // hidden column on gap after end of sequence - should not affect bounds
-    colsel.hideSelectedColumns(13, al.getHiddenColumns());
-    assertEquals(
-            Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
-                seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
-                seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
-                seq.findIndex(seq.getEnd()) - 1 }),
-            Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
+    // only [5,7] returned, offset by 1
+    assertEquals("[4, 6]",
+            Arrays.toString(regions.next()));
+    assertEquals(3, cs2.getSize());
 
-    cs.revealAllHiddenColumns(colsel);
-    // hidden column on gap before beginning of sequence - should vis bounds by
-    // one
-    colsel.hideSelectedColumns(0, al.getHiddenColumns());
-    assertEquals(
-            Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 2,
-                seq.findIndex(seq.getEnd()) - 2, seq.getStart(),
-                seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
-                seq.findIndex(seq.getEnd()) - 1 }),
-            Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
-
-    cs.revealAllHiddenColumns(colsel);
-    // hide columns around most of sequence - leave one residue remaining
-    cs.hideColumns(1, 3);
-    cs.hideColumns(6, 11);
-    assertEquals("-D",
-            cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]);
-    assertEquals(
-            Arrays.toString(new int[] { 1, 1, 3, 3,
-                seq.findIndex(seq.getStart()) - 1,
-                seq.findIndex(seq.getEnd()) - 1 }),
-            Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
-    cs.revealAllHiddenColumns(colsel);
+    cs2 = new HiddenColumns(cs, 8, 15, 4);
+    regions = cs2.iterator();
+    assertTrue(cs2.hasHiddenColumns());
 
-    // hide whole sequence - should just get location of hidden region
-    // containing sequence
-    cs.hideColumns(1, 11);
-    assertEquals(
-            Arrays.toString(new int[] { 0, 1, 0, 0,
-                seq.findIndex(seq.getStart()) - 1,
-                seq.findIndex(seq.getEnd()) - 1 }),
-            Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
+    // only [10,11] returned, offset by 4
+    assertEquals("[6, 7]",
+            Arrays.toString(regions.next()));
+    assertEquals(2, cs2.getSize());
 
+    cs2 = new HiddenColumns(cs, 6, 10, 4);
+    assertFalse(cs2.hasHiddenColumns());
   }
 
-  @Test(groups = { "Functional" })
-  public void testLocateVisibleBoundsPathologicals()
-  {
-    // test some pathological cases we missed
-    AlignmentI al = new Alignment(new SequenceI[] { new Sequence(
-            "refseqGaptest", "KTDVTI----------NFI-----G----L") });
-    HiddenColumns cs = new HiddenColumns();
-    cs.hideInsertionsFor(al.getSequenceAt(0));
-    assertEquals(
-            "G",
-            ""
-                    + al.getSequenceAt(0).getCharAt(
-                            cs.adjustForHiddenColumns(9)));
-
-  }
 
   @Test(groups = { "Functional" })
   public void testHideColumns()
@@ -339,80 +270,103 @@ public class HiddenColumnsTest
     ColumnSelection colsel = new ColumnSelection();
     HiddenColumns cs = al.getHiddenColumns();
     colsel.hideSelectedColumns(5, al.getHiddenColumns());
-    List<int[]> hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[5, 5]", Arrays.toString(hidden.get(0)));
+    Iterator<int[]> regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals("[5, 5]", Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 1);
 
     colsel.hideSelectedColumns(3, al.getHiddenColumns());
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(2, hidden.size());
+    regions = cs.iterator();
+    assertEquals(2, cs.getNumberOfRegions());
     // two hidden ranges, in order:
-    assertEquals(hidden.size(), cs.getHiddenColumnsCopy().size());
-    assertEquals("[3, 3]", Arrays.toString(hidden.get(0)));
-    assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
+    assertEquals("[3, 3]", Arrays.toString(regions.next()));
+    assertEquals("[5, 5]", Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 2);
 
     // hiding column 4 expands [3, 3] to [3, 4]
     // and merges to [5, 5] to make [3, 5]
     colsel.hideSelectedColumns(4, al.getHiddenColumns());
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[3, 5]", Arrays.toString(hidden.get(0)));
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals("[3, 5]", Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 3);
 
     // clear hidden columns (note they are added to selected)
     cs.revealAllHiddenColumns(colsel);
     // it is now actually null but getter returns an empty list
-    assertTrue(cs.getHiddenColumnsCopy().isEmpty());
+    assertEquals(0, cs.getNumberOfRegions());
+    assertEquals(cs.getSize(), 0);
 
     cs.hideColumns(3, 6);
-    hidden = cs.getHiddenColumnsCopy();
-    int[] firstHiddenRange = hidden.get(0);
+    regions = cs.iterator();
+    int[] firstHiddenRange = regions.next();
     assertEquals("[3, 6]", Arrays.toString(firstHiddenRange));
+    assertEquals(cs.getSize(), 4);
 
     // adding a subrange of already hidden should do nothing
     cs.hideColumns(4, 5);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
     assertEquals("[3, 6]",
-            Arrays.toString(cs.getHiddenColumnsCopy().get(0)));
+            Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 4);
     cs.hideColumns(3, 5);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
     assertEquals("[3, 6]",
-            Arrays.toString(cs.getHiddenColumnsCopy().get(0)));
+            Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 4);
     cs.hideColumns(4, 6);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
     assertEquals("[3, 6]",
-            Arrays.toString(cs.getHiddenColumnsCopy().get(0)));
+            Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 4);
     cs.hideColumns(3, 6);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
     assertEquals("[3, 6]",
-            Arrays.toString(cs.getHiddenColumnsCopy().get(0)));
+            Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 4);
 
     cs.revealAllHiddenColumns(colsel);
     cs.hideColumns(2, 4);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals("[2, 4]", Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 3);
 
     // extend contiguous with 2 positions overlap
     cs.hideColumns(3, 5);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[2, 5]", Arrays.toString(hidden.get(0)));
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals("[2, 5]", Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 4);
 
     // extend contiguous with 1 position overlap
     cs.hideColumns(5, 6);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[2, 6]", Arrays.toString(hidden.get(0)));
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals("[2, 6]", Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 5);
 
     // extend contiguous with overlap both ends:
     cs.hideColumns(1, 7);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[1, 7]", Arrays.toString(hidden.get(0)));
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals("[1, 7]", Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 7);
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(15, 18);
+    cs.hideColumns(2, 4);
+    cs.hideColumns(7, 9);
+    regions = cs.iterator();
+    assertEquals(3, cs.getNumberOfRegions());
+    assertEquals("[2, 4]", Arrays.toString(regions.next()));
+    assertEquals("[7, 9]", Arrays.toString(regions.next()));
+    assertEquals("[15, 18]", Arrays.toString(regions.next()));
+    assertEquals(cs.getSize(), 10);
   }
 
   /**
@@ -424,11 +378,18 @@ public class HiddenColumnsTest
   {
     ColumnSelection colsel = new ColumnSelection();
     HiddenColumns cs = new HiddenColumns();
+
+    // test with null hidden columns
+    cs.revealHiddenColumns(5, colsel);
+    assertTrue(colsel.getSelected().isEmpty());
+
     cs.hideColumns(5, 8);
     colsel.addElement(10);
     cs.revealHiddenColumns(5, colsel);
-    // hidden columns list now null but getter returns empty list:
-    assertTrue(cs.getHiddenColumnsCopy().isEmpty());
+
+    // hiddenColumns now empty
+    assertEquals(0, cs.getSize());
+
     // revealed columns are marked as selected (added to selection):
     assertEquals("[10, 5, 6, 7, 8]", colsel.getSelected().toString());
 
@@ -436,36 +397,67 @@ public class HiddenColumnsTest
     colsel = new ColumnSelection();
     cs = new HiddenColumns();
     cs.hideColumns(5, 8);
-    List<int[]> hidden = cs.getHiddenColumnsCopy();
+
+    int prevSize = cs.getSize();
     cs.revealHiddenColumns(6, colsel);
-    assertEquals(hidden.size(), cs.getHiddenColumnsCopy().size());
+    assertEquals(prevSize, cs.getSize());
+    assertTrue(colsel.getSelected().isEmpty());
+
+    // reveal hidden columns when there is more than one region
+    cs.hideColumns(20, 23);
+    // now there are 2 hidden regions
+    assertEquals(2, cs.getNumberOfRegions());
+
+    cs.revealHiddenColumns(20, colsel);
+
+    // hiddenColumns now has one region
+    assertEquals(1, cs.getNumberOfRegions());
+
+    // revealed columns are marked as selected (added to selection):
+    assertEquals("[20, 21, 22, 23]", colsel.getSelected().toString());
+
+    // call with a column past the end of the hidden column ranges
+    colsel.clear();
+    cs.revealHiddenColumns(20, colsel);
+    // hiddenColumns still has 1 region
+    assertEquals(1, cs.getNumberOfRegions());
     assertTrue(colsel.getSelected().isEmpty());
   }
 
   @Test(groups = { "Functional" })
   public void testRevealAllHiddenColumns()
   {
-    HiddenColumns cs = new HiddenColumns();
+    HiddenColumns hidden = new HiddenColumns();
     ColumnSelection colsel = new ColumnSelection();
-    cs.hideColumns(5, 8);
-    cs.hideColumns(2, 3);
+
+    // test with null hidden columns
+    hidden.revealAllHiddenColumns(colsel);
+    assertTrue(colsel.getSelected().isEmpty());
+
+    hidden.hideColumns(5, 8);
+    hidden.hideColumns(2, 3);
     colsel.addElement(11);
     colsel.addElement(1);
-    cs.revealAllHiddenColumns(colsel);
+    hidden.revealAllHiddenColumns(colsel);
 
     /*
      * revealing hidden columns adds them (in order) to the (unordered)
      * selection list
      */
-    assertTrue(cs.getHiddenColumnsCopy().isEmpty());
-    assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", colsel.getSelected()
-            .toString());
+
+    // hiddenColumns now empty
+    assertEquals(0, hidden.getSize());
+
+    assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]",
+            colsel.getSelected().toString());
   }
 
   @Test(groups = { "Functional" })
   public void testIsVisible()
   {
     HiddenColumns cs = new HiddenColumns();
+    assertTrue(cs.isVisible(5));
+
     cs.hideColumns(2, 4);
     cs.hideColumns(6, 7);
     assertTrue(cs.isVisible(0));
@@ -477,6 +469,7 @@ public class HiddenColumnsTest
     assertTrue(cs.isVisible(5));
     assertFalse(cs.isVisible(6));
     assertFalse(cs.isVisible(7));
+    assertTrue(cs.isVisible(8));
   }
 
   /**
@@ -493,15 +486,17 @@ public class HiddenColumnsTest
     HiddenColumns cs = new HiddenColumns();
     cs.hideColumns(49, 59);
     cs.hideColumns(69, 79);
-    List<int[]> hidden = cs.getHiddenColumnsCopy();
-    assertEquals(2, hidden.size());
-    assertEquals("[49, 59]", Arrays.toString(hidden.get(0)));
-    assertEquals("[69, 79]", Arrays.toString(hidden.get(1)));
+    Iterator<int[]> regions = cs.iterator();
+    assertEquals(2, cs.getNumberOfRegions());
+    assertEquals("[49, 59]", Arrays.toString(regions.next()));
+    assertEquals("[69, 79]", Arrays.toString(regions.next()));
+    assertEquals(22, cs.getSize());
 
     cs.hideColumns(48, 80);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[48, 80]", Arrays.toString(hidden.get(0)));
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals("[48, 80]", Arrays.toString(regions.next()));
+    assertEquals(33, cs.getSize());
 
     /*
      * another...joining hidden ranges
@@ -512,9 +507,10 @@ public class HiddenColumnsTest
     cs.hideColumns(50, 60);
     // hiding 21-49 should merge to one range
     cs.hideColumns(21, 49);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[10, 60]", Arrays.toString(hidden.get(0)));
+    regions = cs.iterator();
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals("[10, 60]", Arrays.toString(regions.next()));
+    assertEquals(51, cs.getSize());
 
     /*
      * another...left overlap, subsumption, right overlap,
@@ -528,14 +524,15 @@ public class HiddenColumnsTest
     cs.hideColumns(60, 70);
 
     cs.hideColumns(15, 45);
-    hidden = cs.getHiddenColumnsCopy();
-    assertEquals(2, hidden.size());
-    assertEquals("[10, 50]", Arrays.toString(hidden.get(0)));
-    assertEquals("[60, 70]", Arrays.toString(hidden.get(1)));
+    regions = cs.iterator();
+    assertEquals(2, cs.getNumberOfRegions());
+    assertEquals("[10, 50]", Arrays.toString(regions.next()));
+    assertEquals("[60, 70]", Arrays.toString(regions.next()));
+    assertEquals(52, cs.getSize());
   }
 
   @Test(groups = { "Functional" })
-  public void testHideBitset()
+  public void testHideColumns_BitSet()
   {
     HiddenColumns cs;
 
@@ -544,80 +541,45 @@ public class HiddenColumnsTest
     // one hidden range
     one.set(1);
     cs = new HiddenColumns();
-    cs.hideMarkedBits(one);
-    assertEquals(1, cs.getHiddenColumnsCopy().size());
+    cs.hideColumns(one);
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals(1, cs.getSize());
 
     one.set(2);
     cs = new HiddenColumns();
-    cs.hideMarkedBits(one);
-    assertEquals(1, cs.getHiddenColumnsCopy().size());
+    cs.hideColumns(one);
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals(2, cs.getSize());
 
     one.set(3);
     cs = new HiddenColumns();
-    cs.hideMarkedBits(one);
-    assertEquals(1, cs.getHiddenColumnsCopy().size());
+    cs.hideColumns(one);
+    assertEquals(1, cs.getNumberOfRegions());
+    assertEquals(3, cs.getSize());
 
     // split
     one.clear(2);
     cs = new HiddenColumns();
-    cs.hideMarkedBits(one);
-    assertEquals(2, cs.getHiddenColumnsCopy().size());
+    cs.hideColumns(one);
+    assertEquals(2, cs.getNumberOfRegions());
+    assertEquals(2, cs.getSize());
 
-    assertEquals(0, cs.adjustForHiddenColumns(0));
-    assertEquals(2, cs.adjustForHiddenColumns(1));
-    assertEquals(4, cs.adjustForHiddenColumns(2));
+    assertEquals(0, cs.visibleToAbsoluteColumn(0));
+    assertEquals(2, cs.visibleToAbsoluteColumn(1));
+    assertEquals(4, cs.visibleToAbsoluteColumn(2));
 
     // one again
     one.clear(1);
     cs = new HiddenColumns();
-    cs.hideMarkedBits(one);
+    cs.hideColumns(one);
+    assertEquals(1, cs.getSize());
 
-    assertEquals(1, cs.getHiddenColumnsCopy().size());
+    assertEquals(1, cs.getNumberOfRegions());
 
-    assertEquals(0, cs.adjustForHiddenColumns(0));
-    assertEquals(1, cs.adjustForHiddenColumns(1));
-    assertEquals(2, cs.adjustForHiddenColumns(2));
-    assertEquals(4, cs.adjustForHiddenColumns(3));
-  }
-
-  @Test(groups = { "Functional" })
-  public void testGetBitset()
-  {
-    BitSet toMark, fromMark;
-    long seed = -3241532;
-    Random number = new Random(seed);
-    for (int n = 0; n < 1000; n++)
-    {
-      // create a random bitfield
-      toMark = BitSet.valueOf(new long[] { number.nextLong(),
-          number.nextLong(), number.nextLong() });
-      toMark.set(n * number.nextInt(10), n * (25 + number.nextInt(25)));
-      HiddenColumns hc = new HiddenColumns();
-      hc.hideMarkedBits(toMark);
-
-      // see if we can recover bitfield
-      hc.markHiddenRegions(fromMark = new BitSet());
-      assertEquals(toMark, fromMark);
-    }
-  }
-
-  @Test(groups = { "Functional" })
-  public void testFindHiddenRegionPositions()
-  {
-    HiddenColumns hc = new HiddenColumns();
-
-    List<Integer> positions = hc.findHiddenRegionPositions();
-    assertTrue(positions.isEmpty());
-
-    hc.hideColumns(3, 7);
-    hc.hideColumns(10, 10);
-    hc.hideColumns(14, 15);
-
-    positions = hc.findHiddenRegionPositions();
-    assertEquals(3, positions.size());
-    assertEquals(3, positions.get(0).intValue());
-    assertEquals(5, positions.get(1).intValue());
-    assertEquals(8, positions.get(2).intValue());
+    assertEquals(0, cs.visibleToAbsoluteColumn(0));
+    assertEquals(1, cs.visibleToAbsoluteColumn(1));
+    assertEquals(2, cs.visibleToAbsoluteColumn(2));
+    assertEquals(4, cs.visibleToAbsoluteColumn(3));
   }
 
   @Test(groups = { "Functional" })
@@ -637,7 +599,7 @@ public class HiddenColumnsTest
   }
 
   @Test(groups = "Functional")
-  public void getVisibleStartAndEndIndexTest()
+  public void testGetVisibleStartAndEndIndex()
   {
     Sequence seq = new Sequence("testSeq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
     AlignmentI align = new Alignment(new SequenceI[] { seq });
@@ -663,6 +625,13 @@ public class HiddenColumnsTest
     System.out.println(startEnd[0] + " : " + startEnd[1]);
     assertEquals(1, startEnd[0]);
     assertEquals(23, startEnd[1]);
+
+    // force lowest range to start of alignment
+    hc = new HiddenColumns();
+    hc.hideColumns(3, 4);
+    startEnd = hc.getVisibleStartAndEndIndex(align.getWidth());
+    assertEquals(0, startEnd[0]);
+    assertEquals(25, startEnd[1]);
   }
 
   @Test(groups = "Functional")
@@ -677,58 +646,683 @@ public class HiddenColumnsTest
     hc.hideColumns(10, 10);
     hc.hideColumns(14, 15);
 
-    result = hc.getRegionWithEdgeAtRes(3);
+    result = hc.getRegionWithEdgeAtRes(2);
     assertEquals(3, result[0]);
     assertEquals(7, result[1]);
 
+    result = hc.getRegionWithEdgeAtRes(4);
+    assertEquals(10, result[0]);
+    assertEquals(10, result[1]);
+
     result = hc.getRegionWithEdgeAtRes(5);
     assertEquals(10, result[0]);
     assertEquals(10, result[1]);
 
     result = hc.getRegionWithEdgeAtRes(6);
     assertNull(result);
+
+    result = hc.getRegionWithEdgeAtRes(0);
+    assertNull(result);
+
+    result = hc.getRegionWithEdgeAtRes(7);
+    assertEquals(14, result[0]);
+    assertEquals(15, result[1]);
+
+    result = hc.getRegionWithEdgeAtRes(8);
+    assertEquals(14, result[0]);
+    assertEquals(15, result[1]);
+
+    result = hc.getRegionWithEdgeAtRes(16);
+    assertNull(result);
   }
 
   @Test(groups = "Functional")
-  public void testPropagateInsertions()
+  public void testHasHiddenColumns()
   {
-    // create an alignment with no gaps - this will be the profile seq and other
-    // JPRED seqs
-    AlignmentGenerator gen = new AlignmentGenerator(false);
-    AlignmentI al = gen.generate(20, 10, 1234, 0, 0);
-
-    // get the profileseq
-    SequenceI profileseq = al.getSequenceAt(0);
-    SequenceI gappedseq = new Sequence(profileseq);
-    gappedseq.insertCharAt(5, al.getGapCharacter());
-    gappedseq.insertCharAt(6, al.getGapCharacter());
-    gappedseq.insertCharAt(7, al.getGapCharacter());
-    gappedseq.insertCharAt(8, al.getGapCharacter());
-    
-    // create an alignment view with the gapped sequence
-    SequenceI[] seqs = new SequenceI[1];
-    seqs[0] = gappedseq;
-    AlignmentI newal = new Alignment(seqs);
-    HiddenColumns hidden = new HiddenColumns();
-    hidden.hideColumns(15, 17);
-
-    AlignmentView view = new AlignmentView(newal, hidden, null, true, false,
-            false);
-
-    // confirm that original contigs are as expected
-    int[] oldcontigs = hidden.getVisibleContigs(0, 20);
-    int[] testcontigs = { 0, 14, 18, 19 };
-    assertTrue(Arrays.equals(oldcontigs, testcontigs));
-            
-    // propagate insertions
-    HiddenColumns result = HiddenColumns.propagateInsertions(profileseq, al,
-            view);
-
-    // confirm that the contigs have changed to account for the gaps
-    int[] newcontigs = result.getVisibleContigs(0, 20);
-    testcontigs[1] = 10;
-    testcontigs[2] = 14;
-    assertTrue(Arrays.equals(newcontigs, testcontigs));
-    
+    HiddenColumns h = new HiddenColumns();
+
+    // new HiddenColumns2 has no hidden cols
+    assertFalse(h.hasHiddenColumns());
+
+    // some columns hidden, returns true
+    h.hideColumns(5, 10);
+    assertTrue(h.hasHiddenColumns());
+
+    // reveal columns, no hidden cols again
+    ColumnSelection sel = new ColumnSelection();
+    h.revealAllHiddenColumns(sel);
+    assertFalse(h.hasHiddenColumns());
+  }
+
+  @Test(groups = "Functional")
+  public void testHasManyHiddenColumns()
+  {
+    HiddenColumns h = new HiddenColumns();
+
+    // h has no hidden cols
+    assertFalse(h.hasMultiHiddenColumnRegions());
+
+    // one set of columns hidden, returns false
+    h.hideColumns(5, 10);
+    assertFalse(h.hasMultiHiddenColumnRegions());
+
+    // two sets hidden, returns true
+    h.hideColumns(15, 17);
+    assertTrue(h.hasMultiHiddenColumnRegions());
+
+    // back to one block, asserts false
+    h.hideColumns(11, 14);
+    assertFalse(h.hasMultiHiddenColumnRegions());
+  }
+
+  @Test(groups = "Functional")
+  public void testAdjustForHiddenColumns()
+  {
+    HiddenColumns h = new HiddenColumns();
+    // returns input value when there are no hidden columns
+    assertEquals(10, h.visibleToAbsoluteColumn(10));
+
+    h.hideColumns(20, 30);
+    assertEquals(10, h.visibleToAbsoluteColumn(10));
+    assertEquals(20 + 11, h.visibleToAbsoluteColumn(20));
+    assertEquals(35 + 11, h.visibleToAbsoluteColumn(35));
+
+    h.hideColumns(5, 7);
+    assertEquals(10 + 3, h.visibleToAbsoluteColumn(10));
+    assertEquals(20 + 14, h.visibleToAbsoluteColumn(20));
+    assertEquals(35 + 14, h.visibleToAbsoluteColumn(35));
+
+    ColumnSelection sel = new ColumnSelection();
+    h.revealAllHiddenColumns(sel);
+    h.hideColumns(0, 1);
+    assertEquals(4, h.visibleToAbsoluteColumn(2));
+  }
+
+  @Test(groups = "Functional")
+  public void testGetNextHiddenBoundary_Left()
+  {
+    HiddenColumns h = new HiddenColumns();
+
+    // returns same value if no hidden cols
+    assertEquals(3, h.getNextHiddenBoundary(true, 3));
+
+    h.hideColumns(5, 10);
+    assertEquals(10, h.getNextHiddenBoundary(true, 15));
+    assertEquals(3, h.getNextHiddenBoundary(true, 3));
+    assertEquals(7, h.getNextHiddenBoundary(true, 7));
+
+    h.hideColumns(15, 20);
+    assertEquals(10, h.getNextHiddenBoundary(true, 15));
+    assertEquals(20, h.getNextHiddenBoundary(true, 21));
+  }
+
+  @Test(groups = "Functional")
+  public void testGetNextHiddenBoundary_Right()
+  {
+    HiddenColumns h = new HiddenColumns();
+
+    // returns same value if no hidden cols
+    assertEquals(3, h.getNextHiddenBoundary(false, 3));
+
+    h.hideColumns(5, 10);
+    assertEquals(5, h.getNextHiddenBoundary(false, 3));
+    assertEquals(15, h.getNextHiddenBoundary(false, 15));
+    assertEquals(7, h.getNextHiddenBoundary(false, 7));
+
+    h.hideColumns(15, 20);
+    assertEquals(15, h.getNextHiddenBoundary(false, 7));
+    assertEquals(15, h.getNextHiddenBoundary(false, 14));
+
+    // returns same value if there is no next hidden column
+    assertEquals(22, h.getNextHiddenBoundary(false, 22));
+  }
+
+  @Test(groups = "Functional")
+  public void testIterator()
+  {
+    HiddenColumns h = new HiddenColumns();
+    Iterator<int[]> result = h.iterator();
+    assertFalse(result.hasNext());
+
+    h.hideColumns(5, 10);
+    result = h.iterator();
+    int[] next = result.next();
+    assertEquals(5, next[0]);
+    assertEquals(10, next[1]);
+    assertFalse(result.hasNext());
+
+    h.hideColumns(22, 23);
+    result = h.iterator();
+    next = result.next();
+    assertEquals(5, next[0]);
+    assertEquals(10, next[1]);
+    next = result.next();
+    assertEquals(22, next[0]);
+    assertEquals(23, next[1]);
+    assertFalse(result.hasNext());
+
+    // test for only one hidden region at start of alignment
+    ColumnSelection sel = new ColumnSelection();
+    h.revealAllHiddenColumns(sel);
+    h.hideColumns(0, 1);
+    result = h.iterator();
+    next = result.next();
+    assertEquals(0, next[0]);
+    assertEquals(1, next[1]);
+    assertFalse(result.hasNext());
+  }
+
+  /* @Test(groups = "Functional")
+  public void testGetVisibleSequenceStrings()
+  {
+    HiddenColumns h = new HiddenColumns();
+    SequenceI seq1 = new Sequence("TEST1", "GALMFWKQESPVICYHRNDT");
+    SequenceI seq2 = new Sequence("TEST2", "VICYHRNDTGA");
+    SequenceI[] seqs = new SequenceI[2];
+    seqs[0] = seq1;
+    seqs[1] = seq2;
+    String[] result = h.getVisibleSequenceStrings(5, 10, seqs);
+    assertEquals(2, result.length);
+    assertEquals("WKQES", result[0]);
+    assertEquals("RNDTG", result[1]);
+  
+    h.hideColumns(6, 8);
+    result = h.getVisibleSequenceStrings(5, 10, seqs);
+    assertEquals(2, result.length);
+    assertEquals("WS", result[0]);
+    assertEquals("RG", result[1]);
+  
+    SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
+    ColumnSelection sel = new ColumnSelection();
+    h.revealAllHiddenColumns(sel);
+    h.hideColumns(1, 3);
+    h.hideColumns(6, 11);
+    assertEquals("-D",
+            h.getVisibleSequenceStrings(0, 5, new SequenceI[]
+    { seq })[0]);
+  }*/
+
+  @Test(groups = "Functional")
+  public void testHideInsertionsFor()
+  {
+    HiddenColumns h = new HiddenColumns();
+    HiddenColumns h2 = new HiddenColumns();
+    SequenceI seq1 = new Sequence("TEST1", "GAL---MFW-KQESPVICY--HRNDT");
+    SequenceI seq2 = new Sequence("TEST1", "GALMFWKQESPVICYHRNDT");
+
+    h.hideList(seq2.getInsertions());
+    assertTrue(h.equals(h2));
+    assertEquals(0, h.getSize());
+
+    h.hideList(seq1.getInsertions());
+    h2.hideColumns(3, 5);
+    h2.hideColumns(9, 9);
+    h2.hideColumns(19, 20);
+    assertTrue(h.equals(h2));
+    assertEquals(6, h.getSize());
+  }
+
+  @Test(groups = "Functional")
+  public void testHideColumns_BitSet_range()
+  {
+    HiddenColumns h = new HiddenColumns();
+    HiddenColumns h2 = new HiddenColumns();
+
+    BitSet tohide = new BitSet(25);
+    h.hideColumns(tohide);
+    assertTrue(h.equals(h2));
+
+    // when setting bitset, first param is inclusive, second exclusive
+    tohide.set(3, 6);
+    tohide.set(9);
+    tohide.set(15, 21);
+    h.clearAndHideColumns(tohide, 5, 23);
+
+    h2.hideColumns(5, 5);
+    h2.hideColumns(9, 9);
+    h2.hideColumns(15, 20);
+    assertTrue(h.equals(h2));
+    assertEquals(h.getSize(), h2.getSize());
+
+    tohide.clear();
+    tohide.set(41);
+    h.clearAndHideColumns(tohide, 23, 30);
+    assertTrue(h.equals(h2));
+    assertEquals(h.getSize(), h2.getSize());
+
+    tohide.set(41);
+    h.clearAndHideColumns(tohide, 30, 45);
+    h2.hideColumns(41, 41);
+    assertTrue(h.equals(h2));
+    assertEquals(h.getSize(), h2.getSize());
+
+    tohide.clear();
+    tohide.set(25, 28);
+    h.clearAndHideColumns(tohide, 17, 50);
+    h2 = new HiddenColumns();
+    h2.hideColumns(5, 5);
+    h2.hideColumns(9, 9);
+    h2.hideColumns(15, 16);
+    h2.hideColumns(25, 27);
+    assertTrue(h.equals(h2));
+    assertEquals(h.getSize(), h2.getSize());
+
+    HiddenColumns hc = new HiddenColumns();
+    hc.hideColumns(3, 5);
+    hc.hideColumns(15, 20);
+    hc.hideColumns(45, 60);
+
+    tohide = new BitSet();
+
+    // all unhidden if tohide is empty and range covers hidden
+    hc.clearAndHideColumns(tohide, 1, 70);
+    assertTrue(!hc.hasHiddenColumns());
+    assertEquals(0, hc.getSize());
+
+    hc.hideColumns(3, 5);
+    hc.hideColumns(15, 20);
+    hc.hideColumns(45, 60);
+    assertEquals(25, hc.getSize());
+
+    // but not if range does not cover hidden
+    hc.clearAndHideColumns(tohide, 23, 40);
+    assertTrue(hc.hasHiddenColumns());
+    assertEquals(25, hc.getSize());
+
+    // and partial unhide if range partially covers
+    hc.clearAndHideColumns(tohide, 1, 17);
+    Iterator<int[]> it = hc.iterator();
+    assertTrue(it.hasNext());
+    int[] region = it.next();
+
+    assertEquals(18, region[0]);
+    assertEquals(20, region[1]);
+
+    assertTrue(it.hasNext());
+    region = it.next();
+
+    assertEquals(45, region[0]);
+    assertEquals(60, region[1]);
+
+    assertFalse(it.hasNext());
+    assertEquals(19, hc.getSize());
+  }
+
+  @Test(groups = "Functional")
+  public void testOffsetByVisibleColumns()
+  {
+    HiddenColumns h = new HiddenColumns();
+    int result = h.offsetByVisibleColumns(-1, 10);
+    assertEquals(9, result);
+
+    h.hideColumns(7, 9);
+    result = h.offsetByVisibleColumns(-4, 10);
+    assertEquals(3, result);
+
+    h.hideColumns(14, 15);
+    result = h.offsetByVisibleColumns(-4, 10);
+    assertEquals(3, result);
+
+    result = h.offsetByVisibleColumns(-10, 17);
+    assertEquals(2, result);
+
+    result = h.offsetByVisibleColumns(-1, 7);
+    assertEquals(5, result);
+
+    result = h.offsetByVisibleColumns(-1, 8);
+    assertEquals(5, result);
+
+    result = h.offsetByVisibleColumns(-3, 15);
+    assertEquals(10, result);
+
+    ColumnSelection sel = new ColumnSelection();
+    h.revealAllHiddenColumns(sel);
+    h.hideColumns(0, 30);
+    result = h.offsetByVisibleColumns(-31, 0);
+    assertEquals(-31, result);
+
+    HiddenColumns cs = new HiddenColumns();
+
+    // test that without hidden columns, offsetByVisibleColumns returns
+    // position n to left of provided position
+    long pos = cs.offsetByVisibleColumns(-3, 10);
+    assertEquals(7, pos);
+
+    // 0 returns same position
+    pos = cs.offsetByVisibleColumns(0, 10);
+    assertEquals(10, pos);
+
+    // overflow to left returns negative number
+    pos = cs.offsetByVisibleColumns(-3, 0);
+    assertEquals(-3, pos);
+
+    // test that with hidden columns to left of result column
+    // behaviour is the same as above
+    cs.hideColumns(1, 3);
+
+    // position n to left of provided position
+    pos = cs.offsetByVisibleColumns(-3, 10);
+    assertEquals(7, pos);
+
+    // 0 returns same position
+    pos = cs.offsetByVisibleColumns(0, 10);
+    assertEquals(10, pos);
+
+    // test with one set of hidden columns between start and required position
+    cs.hideColumns(12, 15);
+    pos = cs.offsetByVisibleColumns(-8, 17);
+    assertEquals(5, pos);
+
+    // test with two sets of hidden columns between start and required position
+    cs.hideColumns(20, 21);
+    pos = cs.offsetByVisibleColumns(-8, 23);
+    assertEquals(9, pos);
+
+    // repeat last 2 tests with no hidden columns to left of required position
+    ColumnSelection colsel = new ColumnSelection();
+    cs.revealAllHiddenColumns(colsel);
+
+    // test with one set of hidden columns between start and required position
+    cs.hideColumns(12, 15);
+    pos = cs.offsetByVisibleColumns(-8, 17);
+    assertEquals(5, pos);
+
+    // test with two sets of hidden columns between start and required position
+    cs.hideColumns(20, 21);
+    pos = cs.offsetByVisibleColumns(-8, 23);
+    assertEquals(9, pos);
+
+    // test with right (positive) offsets
+
+    // test that without hidden columns, offsetByVisibleColumns returns
+    // position n to right of provided position
+    pos = cs.offsetByVisibleColumns(3, 7);
+    assertEquals(10, pos);
+
+    // test that with hidden columns to left of result column
+    // behaviour is the same as above
+    cs.hideColumns(1, 3);
+
+    // test with one set of hidden columns between start and required position
+    cs.hideColumns(12, 15);
+    pos = cs.offsetByVisibleColumns(8, 5);
+    assertEquals(17, pos);
+
+    // test with two sets of hidden columns between start and required position
+    cs.hideColumns(20, 21);
+    pos = cs.offsetByVisibleColumns(8, 9);
+    assertEquals(23, pos);
+
+    // repeat last 2 tests with no hidden columns to left of required position
+    colsel = new ColumnSelection();
+    cs.revealAllHiddenColumns(colsel);
+
+    // test with one set of hidden columns between start and required position
+    cs.hideColumns(12, 15);
+    pos = cs.offsetByVisibleColumns(8, 5);
+    assertEquals(17, pos);
+
+    // test with two sets of hidden columns between start and required position
+    cs.hideColumns(20, 21);
+    pos = cs.offsetByVisibleColumns(8, 9);
+    assertEquals(23, pos);
+  }
+
+  @Test(groups = "Functional")
+  public void testBoundedIterator()
+  {
+    HiddenColumns h = new HiddenColumns();
+    Iterator<int[]> it = h.getBoundedIterator(0, 10);
+
+    // no hidden columns = nothing to iterate over
+    assertFalse(it.hasNext());
+
+    // [start,end] contains all hidden columns
+    // all regions are returned
+    h.hideColumns(3, 10);
+    h.hideColumns(14, 16);
+    it = h.getBoundedIterator(0, 20);
+    assertTrue(it.hasNext());
+    int[] next = it.next();
+    assertEquals(3, next[0]);
+    assertEquals(10, next[1]);
+    next = it.next();
+    assertEquals(14, next[0]);
+    assertEquals(16, next[1]);
+    assertFalse(it.hasNext());
+
+    // [start,end] overlaps a region
+    // 1 region returned
+    it = h.getBoundedIterator(5, 7);
+    assertTrue(it.hasNext());
+    next = it.next();
+    assertEquals(3, next[0]);
+    assertEquals(10, next[1]);
+    assertFalse(it.hasNext());
+
+    // [start,end] fully contains 1 region and start of last
+    // - 2 regions returned
+    it = h.getBoundedIterator(3, 15);
+    assertTrue(it.hasNext());
+    next = it.next();
+    assertEquals(3, next[0]);
+    assertEquals(10, next[1]);
+    next = it.next();
+    assertEquals(14, next[0]);
+    assertEquals(16, next[1]);
+    assertFalse(it.hasNext());
+
+    // [start,end] contains end of first region and whole of last region
+    // - 2 regions returned
+    it = h.getBoundedIterator(4, 20);
+    assertTrue(it.hasNext());
+    next = it.next();
+    assertEquals(3, next[0]);
+    assertEquals(10, next[1]);
+    next = it.next();
+    assertEquals(14, next[0]);
+    assertEquals(16, next[1]);
+    assertFalse(it.hasNext());
+  }
+
+  @Test(groups = "Functional")
+  public void testBoundedStartIterator()
+  {
+    HiddenColumns h = new HiddenColumns();
+    Iterator<Integer> it = h.getStartRegionIterator(0, 10);
+
+    // no hidden columns = nothing to iterate over
+    assertFalse(it.hasNext());
+
+    // [start,end] contains all hidden columns
+    // all regions are returned
+    h.hideColumns(3, 10);
+    h.hideColumns(14, 16);
+    it = h.getStartRegionIterator(0, 20);
+    assertTrue(it.hasNext());
+    int next = it.next();
+    assertEquals(3, next);
+    next = it.next();
+    assertEquals(6, next);
+    assertFalse(it.hasNext());
+
+    // [start,end] does not contain a start of a region
+    // no regions to iterate over
+    it = h.getStartRegionIterator(4, 5);
+    assertFalse(it.hasNext());
+
+    // [start,end] fully contains 1 region and start of last
+    // - 2 regions returned
+    it = h.getStartRegionIterator(3, 7);
+    assertTrue(it.hasNext());
+    next = it.next();
+    assertEquals(3, next);
+    next = it.next();
+    assertEquals(6, next);
+    assertFalse(it.hasNext());
+
+    // [start,end] contains whole of last region
+    // - 1 region returned
+    it = h.getStartRegionIterator(4, 20);
+    assertTrue(it.hasNext());
+    next = it.next();
+    assertEquals(6, next);
+    assertFalse(it.hasNext());
+  }
+
+  @Test(groups = "Functional")
+  public void testVisibleBlocksVisBoundsIterator()
+  {
+    HiddenColumns h = new HiddenColumns();
+    Iterator<int[]> regions = h.getVisContigsIterator(0, 31, true);
+
+    // only 1 visible region spanning 0-30 if nothing is hidden
+    assertTrue(regions.hasNext());
+    int[] region = regions.next();
+    assertEquals(0, region[0]);
+    assertEquals(30, region[1]);
+    assertFalse(regions.hasNext());
+
+    // hide 1 region in middle
+    // 2 regions one on either side
+    // second region boundary accounts for hidden columns
+    h.hideColumns(10, 15);
+    regions = h.getVisContigsIterator(0, 31, true);
+
+    assertTrue(regions.hasNext());
+    region = regions.next();
+    assertEquals(0, region[0]);
+    assertEquals(9, region[1]);
+    region = regions.next();
+    assertEquals(16, region[0]);
+    assertEquals(36, region[1]);
+    assertFalse(regions.hasNext());
+
+    // single hidden region at left
+    h = new HiddenColumns();
+    h.hideColumns(0, 5);
+    regions = h.getVisContigsIterator(0, 31, true);
+
+    assertTrue(regions.hasNext());
+    region = regions.next();
+    assertEquals(6, region[0]);
+    assertEquals(36, region[1]);
+    assertFalse(regions.hasNext());
+
+    // single hidden region at right
+    h = new HiddenColumns();
+    h.hideColumns(27, 30);
+    regions = h.getVisContigsIterator(0, 31, true);
+
+    assertTrue(regions.hasNext());
+    region = regions.next();
+    assertEquals(0, region[0]);
+    assertEquals(26, region[1]);
+    region = regions.next();
+    assertEquals(31, region[0]);
+    assertEquals(34, region[1]);
+    assertFalse(regions.hasNext());
+
+    // hidden region at left + hidden region in middle
+    h = new HiddenColumns();
+    h.hideColumns(0, 5);
+    h.hideColumns(23, 25);
+    regions = h.getVisContigsIterator(0, 31, true);
+
+    assertTrue(regions.hasNext());
+    region = regions.next();
+    assertEquals(6, region[0]);
+    assertEquals(22, region[1]);
+    region = regions.next();
+    assertEquals(26, region[0]);
+    assertEquals(39, region[1]);
+    assertFalse(regions.hasNext());
+
+    // hidden region at right + hidden region in middle
+    h = new HiddenColumns();
+    h.hideColumns(27, 30);
+    h.hideColumns(11, 14);
+    regions = h.getVisContigsIterator(0, 31, true);
+
+    assertTrue(regions.hasNext());
+    region = regions.next();
+    assertEquals(0, region[0]);
+    assertEquals(10, region[1]);
+    region = regions.next();
+    assertEquals(15, region[0]);
+    assertEquals(26, region[1]);
+    region = regions.next();
+    assertEquals(31, region[0]);
+    assertEquals(38, region[1]);
+    assertFalse(regions.hasNext());
+
+    // hidden region at left and right
+    h = new HiddenColumns();
+    h.hideColumns(27, 35);
+    h.hideColumns(0, 4);
+    regions = h.getVisContigsIterator(0, 31, true);
+
+    assertTrue(regions.hasNext());
+    region = regions.next();
+    assertEquals(5, region[0]);
+    assertEquals(26, region[1]);
+    region = regions.next();
+    assertEquals(36, region[0]);
+    assertEquals(44, region[1]);
+    assertFalse(regions.hasNext());
+
+    // multiple hidden regions
+    h = new HiddenColumns();
+    h.hideColumns(1, 1);
+    h.hideColumns(3, 5);
+    h.hideColumns(9, 11);
+    h.hideColumns(22, 26);
+
+    regions = h.getVisContigsIterator(0, 31, true);
+
+    assertTrue(regions.hasNext());
+    region = regions.next();
+    assertEquals(0, region[0]);
+    assertEquals(0, region[1]);
+    region = regions.next();
+    assertEquals(2, region[0]);
+    assertEquals(2, region[1]);
+    region = regions.next();
+    assertEquals(6, region[0]);
+    assertEquals(8, region[1]);
+    region = regions.next();
+    assertEquals(12, region[0]);
+    assertEquals(21, region[1]);
+    region = regions.next();
+    assertEquals(27, region[0]);
+    assertEquals(42, region[1]);
+    assertFalse(regions.hasNext());
+  }
+
+  /*
+   * the VisibleColsIterator is tested elsewhere, this just tests that 
+   * it can be retrieved from HiddenColumns
+   */
+  @Test(groups = "Functional")
+  public void testGetVisibleColsIterator()
+  {
+    HiddenColumns h = new HiddenColumns();
+    Iterator<Integer> it = h.getVisibleColsIterator(0, 10);
+
+    assertTrue(it instanceof RangeElementsIterator);
+  }
+
+  @Test(groups = "Functional")
+  public void testHashCode()
+  {
+    HiddenColumns h = new HiddenColumns();
+    h.hideColumns(0, 25);
+
+    int result = h.hashCode();
+    assertTrue(result > 0);
+
+    h.hideColumns(30, 50);
+    assertTrue(h.hashCode() > 0);
+    assertTrue(result != h.hashCode());
   }
 }
@@ -22,12 +22,13 @@ package jalview.datamodel;
 
 import static org.testng.Assert.assertTrue;
 
+import java.util.Iterator;
 import java.util.NoSuchElementException;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-public class VisibleColsIteratorTest
+public class RangeElementsIteratorTest
 {
   HiddenColumns hiddenCols;
 
@@ -50,11 +51,12 @@ public class VisibleColsIteratorTest
   @Test(groups = { "Functional" })
   public void testHasNextAndNextWithHidden()
   {
-    VisibleColsIterator it = new VisibleColsIterator(0, 6, hiddenCols);
+    Iterator<Integer> it = hiddenCols.getVisibleColsIterator(0, 6);
     int count = 0;
     while (it.hasNext())
     {
-      it.next();
+      int result = it.next();
+      System.out.println(result);
       count++;
     }
     assertTrue(count == 4, "hasNext() is false after 4 iterations");
@@ -67,8 +69,8 @@ public class VisibleColsIteratorTest
   @Test(groups = { "Functional" })
   public void testHasNextAndNextNoHidden()
   {
-    VisibleColsIterator it2 = new VisibleColsIterator(0, 3,
-            new HiddenColumns());
+    HiddenColumns test = new HiddenColumns();
+    Iterator<Integer> it2 = test.getVisibleColsIterator(0, 3);
     int count = 0;
     while (it2.hasNext())
     {
@@ -85,8 +87,7 @@ public class VisibleColsIteratorTest
   @Test(groups = { "Functional" })
   public void testHasNextAndNextStartHidden()
   {
-    VisibleColsIterator it3 = new VisibleColsIterator(0, 6,
-            hiddenColsAtStart);
+    Iterator<Integer> it3 = hiddenColsAtStart.getVisibleColsIterator(0, 6);
     int count = 0;
     while (it3.hasNext())
     {
@@ -103,7 +104,7 @@ public class VisibleColsIteratorTest
   @Test(groups = { "Functional" })
   public void testHasNextAndNextEndHidden()
   {
-    VisibleColsIterator it4 = new VisibleColsIterator(0, 4, hiddenCols);
+    Iterator<Integer> it4 = hiddenCols.getVisibleColsIterator(0, 4);
     int count = 0;
     while (it4.hasNext())
     {
@@ -123,7 +124,7 @@ public class VisibleColsIteratorTest
     expectedExceptions = { NoSuchElementException.class })
   public void testLastNextWithHidden() throws NoSuchElementException
   {
-    VisibleColsIterator it = new VisibleColsIterator(0, 3, hiddenCols);
+    Iterator<Integer> it = hiddenCols.getVisibleColsIterator(0, 3);
     while (it.hasNext())
     {
       it.next();
@@ -140,8 +141,8 @@ public class VisibleColsIteratorTest
     expectedExceptions = { NoSuchElementException.class })
   public void testLastNextNoHidden() throws NoSuchElementException
   {
-    VisibleColsIterator it2 = new VisibleColsIterator(0, 3,
-            new HiddenColumns());
+    HiddenColumns test = new HiddenColumns();
+    Iterator<Integer> it2 = test.getVisibleColsIterator(0, 3);
     while (it2.hasNext())
     {
       it2.next();
@@ -158,8 +159,7 @@ public class VisibleColsIteratorTest
     expectedExceptions = { NoSuchElementException.class })
   public void testLastNextStartHidden() throws NoSuchElementException
   {
-    VisibleColsIterator it3 = new VisibleColsIterator(0, 6,
-            hiddenColsAtStart);
+    Iterator<Integer> it3 = hiddenColsAtStart.getVisibleColsIterator(0, 6);
     while (it3.hasNext())
     {
       it3.next();
@@ -176,7 +176,7 @@ public class VisibleColsIteratorTest
     expectedExceptions = { NoSuchElementException.class })
   public void testLastNextEndHidden() throws NoSuchElementException
   {
-    VisibleColsIterator it4 = new VisibleColsIterator(0, 4, hiddenCols);
+    Iterator<Integer> it4 = hiddenCols.getVisibleColsIterator(0, 4);
     while (it4.hasNext())
     {
       it4.next();
@@ -192,7 +192,7 @@ public class VisibleColsIteratorTest
     expectedExceptions = { UnsupportedOperationException.class })
   public void testRemove() throws UnsupportedOperationException
   {
-    VisibleColsIterator it = new VisibleColsIterator(0, 3, hiddenCols);
+    Iterator<Integer> it = hiddenCols.getVisibleColsIterator(0, 3);
     it.remove();
   }
 }
index a084a8e..5a14514 100644 (file)
@@ -28,6 +28,7 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.analysis.AlignmentGenerator;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.datamodel.PDBEntry.Type;
@@ -38,16 +39,17 @@ import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Vector;
 
-import junit.extensions.PA;
-
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import junit.extensions.PA;
+
 public class SequenceTest
 {
 
@@ -799,7 +801,7 @@ public class SequenceTest
     Assert.assertEquals(pdbe1a,
             sq.getDatasetSequence().getPDBEntry("1PDB"),
             "PDB Entry '1PDB' not found on dataset sequence via getPDBEntry.");
-    ArrayList<Annotation> annotsList = new ArrayList<Annotation>();
+    ArrayList<Annotation> annotsList = new ArrayList<>();
     System.out.println(">>>>>> " + sq.getSequenceAsString().length());
     annotsList.add(new Annotation("A", "A", 'X', 0.1f));
     annotsList.add(new Annotation("A", "A", 'X', 0.1f));
@@ -1674,6 +1676,20 @@ public class SequenceTest
   }
 
   @Test(groups = { "Functional" })
+  public void testGapBitset()
+  {
+    SequenceI sq = new Sequence("test/8-13", "-ABC---DE-F--");
+    BitSet bs = sq.gapBitset();
+    BitSet expected = new BitSet();
+    expected.set(0);
+    expected.set(4, 7);
+    expected.set(9);
+    expected.set(11, 13);
+
+    assertTrue(bs.equals(expected));
+
+  }
+
   public void testFindFeatures_largeEndPos()
   {
     /*
@@ -1881,4 +1897,162 @@ public class SequenceTest
     assertEquals(8, sq.getDatasetSequence().getStart());
     assertEquals(9, sq.getDatasetSequence().getEnd());
   }
+
+  /**
+   * Test the code used to locate the reference sequence ruler origin
+   */
+  @Test(groups = { "Functional" })
+  public void testLocateVisibleStartofSequence()
+  {
+    // create random alignment
+    AlignmentGenerator gen = new AlignmentGenerator(false);
+    AlignmentI al = gen.generate(50, 20, 123, 5, 5);
+
+    HiddenColumns cs = al.getHiddenColumns();
+    ColumnSelection colsel = new ColumnSelection();
+
+    SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
+    assertEquals(2, seq.findIndex(seq.getStart()));
+
+    // no hidden columns
+    assertEquals(seq.findIndex(seq.getStart()) - 1,
+            seq.firstResidueOutsideIterator(cs.iterator()));
+
+    // hidden column on gap after end of sequence - should not affect bounds
+    colsel.hideSelectedColumns(13, al.getHiddenColumns());
+    assertEquals(seq.findIndex(seq.getStart()) - 1,
+            seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    // hidden column on gap before beginning of sequence - should vis bounds by
+    // one
+    colsel.hideSelectedColumns(0, al.getHiddenColumns());
+    assertEquals(seq.findIndex(seq.getStart()) - 2,
+            cs.absoluteToVisibleColumn(
+                    seq.firstResidueOutsideIterator(cs.iterator())));
+
+    cs.revealAllHiddenColumns(colsel);
+    // hide columns around most of sequence - leave one residue remaining
+    cs.hideColumns(1, 3);
+    cs.hideColumns(6, 11);
+
+    Iterator<int[]> it = cs.getVisContigsIterator(0, 6, false);
+
+    assertEquals("-D", seq.getSequenceStringFromIterator(it));
+    // cs.getVisibleSequenceStrings(0, 5, new SequenceI[]
+    // { seq })[0]);
+
+    assertEquals(4, seq.firstResidueOutsideIterator(cs.iterator()));
+    cs.revealAllHiddenColumns(colsel);
+
+    // hide whole sequence - should just get location of hidden region
+    // containing sequence
+    cs.hideColumns(1, 11);
+    assertEquals(0, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 15);
+    assertEquals(0, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    SequenceI seq2 = new Sequence("RefSeq2", "-------A-SD-ASD--E---");
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(7, 17);
+    assertEquals(0, seq2.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(3, 17);
+    assertEquals(0, seq2.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(3, 19);
+    assertEquals(0, seq2.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 0);
+    assertEquals(1, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 1);
+    assertEquals(3, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 2);
+    assertEquals(3, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(1, 1);
+    assertEquals(3, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(1, 2);
+    assertEquals(3, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(1, 3);
+    assertEquals(4, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 2);
+    cs.hideColumns(5, 6);
+    assertEquals(3, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 2);
+    cs.hideColumns(5, 6);
+    cs.hideColumns(9, 10);
+    assertEquals(3, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 2);
+    cs.hideColumns(7, 11);
+    assertEquals(3, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(2, 4);
+    cs.hideColumns(7, 11);
+    assertEquals(1, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(2, 4);
+    cs.hideColumns(7, 12);
+    assertEquals(1, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(1, 11);
+    assertEquals(0, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 12);
+    assertEquals(0, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 4);
+    cs.hideColumns(6, 12);
+    assertEquals(0, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 1);
+    cs.hideColumns(3, 12);
+    assertEquals(0, seq.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(3, 14);
+    cs.hideColumns(17, 19);
+    assertEquals(0, seq2.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(3, 7);
+    cs.hideColumns(9, 14);
+    cs.hideColumns(17, 19);
+    assertEquals(0, seq2.firstResidueOutsideIterator(cs.iterator()));
+
+    cs.revealAllHiddenColumns(colsel);
+    cs.hideColumns(0, 1);
+    cs.hideColumns(3, 4);
+    cs.hideColumns(6, 8);
+    cs.hideColumns(10, 12);
+    assertEquals(0, seq.firstResidueOutsideIterator(cs.iterator()));
+
+  }
 }
diff --git a/test/jalview/datamodel/StartRegionIteratorTest.java b/test/jalview/datamodel/StartRegionIteratorTest.java
new file mode 100644 (file)
index 0000000..23d0b00
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * 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.datamodel;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+public class StartRegionIteratorTest
+{
+  /**
+   * Test the start region iterator
+   */
+  @Test(groups = { "Functional" })
+  public void testBasicBoundsIterator()
+  {
+    List<int[]> hiddenColumns = null;
+
+    // null hidden columns
+    Iterator<Integer> it = new StartRegionIterator(3, 10,
+            hiddenColumns);
+    assertFalse(it.hasNext());
+
+    hiddenColumns = new ArrayList<>();
+
+    // no hidden columns
+    it = new StartRegionIterator(3, 10, hiddenColumns);
+    assertFalse(it.hasNext());
+
+    // add some hidden columns
+    hiddenColumns.add(new int[] { 5, 10 });
+    hiddenColumns.add(new int[] { 25, 40 });
+
+    it = new StartRegionIterator(3, 10, hiddenColumns);
+    assertTrue(it.hasNext());
+    Integer result = it.next();
+    assertEquals(5, (int) result);
+    assertFalse(it.hasNext());
+
+    it = new StartRegionIterator(3, 15, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(5, (int) result);
+    assertFalse(it.hasNext());
+
+    it = new StartRegionIterator(3, 18, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(5, (int) result);
+    assertFalse(it.hasNext());
+
+    it = new StartRegionIterator(3, 19, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(5, (int) result);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(19, (int) result);
+    assertFalse(it.hasNext());
+
+    hiddenColumns.add(new int[] { 47, 50 });
+
+    it = new StartRegionIterator(15, 60, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(19, (int) result);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(25, (int) result);
+    assertFalse(it.hasNext());
+  }
+
+  /**
+   * Test the start region iterator with null cursor
+   */
+  @Test(groups = { "Functional" })
+  public void testBoundsIteratorUsingNullCursor()
+  {
+    List<int[]> hiddenColumns = null;
+    HiddenCursorPosition pos = null;
+
+    // null hidden columns
+    Iterator<Integer> it = new StartRegionIterator(pos, 3, 10,
+            hiddenColumns);
+    assertFalse(it.hasNext());
+
+    hiddenColumns = new ArrayList<>();
+
+    // no hidden columns
+    it = new StartRegionIterator(pos, 3, 10, hiddenColumns);
+    assertFalse(it.hasNext());
+
+    // add some hidden columns
+    hiddenColumns.add(new int[] { 5, 10 });
+    hiddenColumns.add(new int[] { 25, 40 });
+
+    it = new StartRegionIterator(pos, 3, 10, hiddenColumns);
+    assertTrue(it.hasNext());
+    Integer result = it.next();
+    assertEquals(5, (int) result);
+    assertFalse(it.hasNext());
+
+    it = new StartRegionIterator(pos, 3, 15, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(5, (int) result);
+    assertFalse(it.hasNext());
+
+    it = new StartRegionIterator(pos, 3, 18, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(5, (int) result);
+    assertFalse(it.hasNext());
+
+    it = new StartRegionIterator(pos, 3, 19, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(5, (int) result);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(19, (int) result);
+    assertFalse(it.hasNext());
+
+    hiddenColumns.add(new int[] { 47, 50 });
+
+    it = new StartRegionIterator(pos, 15, 60, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(19, (int) result);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(25, (int) result);
+    assertFalse(it.hasNext());
+  }
+
+  /**
+   * Test the start region iterator with nonnull cursor
+   */
+  @Test(groups = { "Functional" })
+  public void testBoundsIteratorUsingCursor()
+  {
+    List<int[]> hiddenColumns = new ArrayList<>();
+
+    // add some hidden columns
+    hiddenColumns.add(new int[] { 5, 10 });
+    hiddenColumns.add(new int[] { 25, 40 });
+
+    HiddenCursorPosition pos = new HiddenCursorPosition(0, 0);
+
+    Iterator<Integer> it = new StartRegionIterator(pos, 3, 10,
+            hiddenColumns);
+    assertTrue(it.hasNext());
+    Integer result = it.next();
+    assertEquals(5, (int) result);
+    assertFalse(it.hasNext());
+
+    it = new StartRegionIterator(pos, 3, 15, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(5, (int) result);
+    assertFalse(it.hasNext());
+
+    it = new StartRegionIterator(pos, 3, 18, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(5, (int) result);
+    assertFalse(it.hasNext());
+
+    it = new StartRegionIterator(pos, 3, 19, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(5, (int) result);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(19, (int) result);
+    assertFalse(it.hasNext());
+
+    pos = new HiddenCursorPosition(1, 6);
+    hiddenColumns.add(new int[] { 47, 50 });
+
+    it = new StartRegionIterator(pos, 15, 60, hiddenColumns);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(19, (int) result);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(25, (int) result);
+    assertFalse(it.hasNext());
+  }
+}
diff --git a/test/jalview/datamodel/VisibleContigsIteratorTest.java b/test/jalview/datamodel/VisibleContigsIteratorTest.java
new file mode 100644 (file)
index 0000000..8f31dae
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * 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.datamodel;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+public class VisibleContigsIteratorTest
+{
+  /**
+   * Test the iterator with single visible regions
+   */
+  @Test(groups = { "Functional" })
+  public void testSimpleVisibleRegions()
+  {
+    List<int[]> hiddenColumns = null;
+
+    // null hidden columns
+    VisibleContigsIterator it = new VisibleContigsIterator(3, 10,
+            hiddenColumns);
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    int[] result = it.next();
+    assertEquals(3, result[0]);
+    assertEquals(9, result[1]);
+    assertFalse(it.hasNext());
+    assertFalse(it.endsAtHidden());
+
+    hiddenColumns = new ArrayList<>();
+
+    // no hidden columns
+    it = new VisibleContigsIterator(3, 10,
+            hiddenColumns);
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    result = it.next();
+    assertEquals(3, result[0]);
+    assertEquals(9, result[1]);
+    assertFalse(it.hasNext());
+    assertFalse(it.endsAtHidden());
+
+    // hidden columns, but not where we are looking
+    hiddenColumns.add(new int[] { 5, 10 });
+    hiddenColumns.add(new int[] { 25, 40 });
+
+    it = new VisibleContigsIterator(2, 3, hiddenColumns);
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    result = it.next();
+    assertEquals(2, result[0]);
+    assertEquals(2, result[1]);
+    assertFalse(it.hasNext());
+    assertFalse(it.endsAtHidden());
+
+    it = new VisibleContigsIterator(5, 7, hiddenColumns);
+    assertFalse(it.hasNext());
+    assertFalse(it.endsAtHidden());
+
+    it = new VisibleContigsIterator(11, 15, hiddenColumns);
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    result = it.next();
+    assertEquals(11, result[0]);
+    assertEquals(14, result[1]);
+    assertFalse(it.hasNext());
+    assertFalse(it.endsAtHidden());
+
+    it = new VisibleContigsIterator(50, 60, hiddenColumns);
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    result = it.next();
+    assertEquals(50, result[0]);
+    assertEquals(59, result[1]);
+    assertFalse(it.hasNext());
+    assertFalse(it.endsAtHidden());
+  }
+
+  /**
+   * Test the iterator with multiple visible regions
+   */
+  @Test(groups = { "Functional" })
+  public void testMultipleVisibleRegions()
+  {
+    List<int[]> hiddenColumns = new ArrayList<>();
+    hiddenColumns.add(new int[] { 5, 10 });
+    hiddenColumns.add(new int[] { 25, 40 });
+
+    // all hidden columns covered
+    VisibleContigsIterator it = new VisibleContigsIterator(3, 50,
+            hiddenColumns);
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    int[] result = it.next();
+    assertEquals(3, result[0]);
+    assertEquals(4, result[1]);
+
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    result = it.next();
+    assertEquals(11, result[0]);
+    assertEquals(24, result[1]);
+
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    result = it.next();
+    assertEquals(41, result[0]);
+    assertEquals(49, result[1]);
+
+    assertFalse(it.hasNext());
+    assertFalse(it.endsAtHidden());
+  }
+
+  /**
+   * Test the iterator with regions which start/end at hidden region edges
+   */
+  @Test(groups = { "Functional" })
+  public void testVisibleRegionsAtHiddenEdges()
+  {
+    List<int[]> hiddenColumns = new ArrayList<>();
+    hiddenColumns.add(new int[] { 5, 10 });
+    hiddenColumns.add(new int[] { 25, 40 });
+
+    VisibleContigsIterator it = new VisibleContigsIterator(0, 10,
+            hiddenColumns);
+    assertTrue(it.hasNext());
+    assertTrue(it.endsAtHidden());
+    int[] result = it.next();
+    assertEquals(0, result[0]);
+    assertEquals(4, result[1]);
+    assertFalse(it.hasNext());
+    assertTrue(it.endsAtHidden());
+
+    it = new VisibleContigsIterator(2, 11, hiddenColumns);
+    assertTrue(it.hasNext());
+    assertTrue(it.endsAtHidden());
+    result = it.next();
+    assertEquals(2, result[0]);
+    assertEquals(4, result[1]);
+    assertFalse(it.hasNext());
+    assertTrue(it.endsAtHidden());
+
+    it = new VisibleContigsIterator(2, 12, hiddenColumns);
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    result = it.next();
+    assertEquals(2, result[0]);
+    assertEquals(4, result[1]);
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    result = it.next();
+    assertEquals(11, result[0]);
+    assertEquals(11, result[1]);
+    assertFalse(it.hasNext());
+    assertFalse(it.endsAtHidden());
+
+    it = new VisibleContigsIterator(13, 25, hiddenColumns);
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    result = it.next();
+    assertEquals(13, result[0]);
+    assertEquals(24, result[1]);
+    assertFalse(it.hasNext());
+
+    it = new VisibleContigsIterator(13, 26, hiddenColumns);
+    assertTrue(it.hasNext());
+    assertTrue(it.endsAtHidden());
+    result = it.next();
+    assertEquals(13, result[0]);
+    assertEquals(24, result[1]);
+    assertFalse(it.hasNext());
+
+    it = new VisibleContigsIterator(13, 27, hiddenColumns);
+    assertTrue(it.hasNext());
+    assertTrue(it.endsAtHidden());
+    result = it.next();
+    assertEquals(13, result[0]);
+    assertEquals(24, result[1]);
+    assertFalse(it.hasNext());
+
+    it = new VisibleContigsIterator(13, 41, hiddenColumns);
+    assertTrue(it.hasNext());
+    assertTrue(it.endsAtHidden());
+    result = it.next();
+    assertEquals(13, result[0]);
+    assertEquals(24, result[1]);
+    assertFalse(it.hasNext());
+
+    it = new VisibleContigsIterator(13, 42, hiddenColumns);
+    assertTrue(it.hasNext());
+    assertFalse(it.endsAtHidden());
+    result = it.next();
+    assertEquals(13, result[0]);
+    assertEquals(24, result[1]);
+    assertTrue(it.hasNext());
+    result = it.next();
+    assertEquals(41, result[0]);
+    assertEquals(41, result[1]);
+  }
+}
index 1b1a2b4..217742d 100644 (file)
@@ -25,6 +25,7 @@ import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.api.FeatureSettingsModelI;
+import jalview.bin.Cache;
 import jalview.datamodel.SequenceDummy;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
@@ -53,6 +54,7 @@ public class EnsemblGeneTest
   @BeforeClass(alwaysRun = true)
   public void setUp()
   {
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
     SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
   }
 
index 3309adf..e42b54f 100644 (file)
@@ -115,7 +115,7 @@ public class JmolCommandsTest
     String chainACommand = commands[0].commands[0];
     // M colour is #82827d == (130, 130, 125) (see strand.html help page)
     assertTrue(chainACommand
-            .contains(";select 21:A/1.1;color[130,130,125]"));
+            .contains("select 21:A/1.1;color[130,130,125]")); // first one
     // H colour is #60609f == (96, 96, 159)
     assertTrue(chainACommand.contains(";select 22:A/1.1;color[96,96,159]"));
     // hidden columns are Gray (128, 128, 128)
@@ -128,7 +128,7 @@ public class JmolCommandsTest
     String chainBCommand = commands[1].commands[0];
     // M colour is #82827d == (130, 130, 125)
     assertTrue(chainBCommand
-            .contains(";select 21:B/2.1;color[130,130,125]"));
+            .contains("select 21:B/2.1;color[130,130,125]"));
     // V colour is #ffff00 == (255, 255, 0)
     assertTrue(chainBCommand
 .contains(";select 22:B/2.1;color[255,255,0]"));
index 792f7ad..e451ed2 100644 (file)
  */
 package jalview.ext.jmol;
 
+import static org.junit.Assert.assertNotNull;
+import static org.testng.Assert.assertEquals;
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.JvOptionPane;
@@ -32,6 +36,10 @@ import jalview.gui.Preferences;
 import jalview.gui.StructureViewer;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileLoader;
+
+import java.lang.reflect.InvocationTargetException;
 
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -54,8 +62,10 @@ public class JmolViewerTest
   @BeforeClass(alwaysRun = true)
   public static void setUpBeforeClass() throws Exception
   {
-    Jalview.main(new String[] { "-noquestionnaire", "-nonews", "-props",
-        "test/jalview/ext/rbvi/chimera/testProps.jvprops" });
+    Jalview.main(
+            new String[]
+            { "-noquestionnaire", "-nonews", "-props",
+                "test/jalview/ext/rbvi/chimera/testProps.jvprops" });
   }
 
   /**
@@ -70,10 +80,11 @@ public class JmolViewerTest
   @Test(groups = { "Functional" })
   public void testSingleSeqViewJMol()
   {
-    Cache.setProperty(Preferences.STRUCTURE_DISPLAY, ViewerType.JMOL.name());
+    Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
+            ViewerType.JMOL.name());
     String inFile = "examples/1gaq.txt";
-    AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
-            inFile, DataSourceType.FILE);
+    AlignFrame af = new jalview.io.FileLoader()
+            .LoadFileWaitTillLoaded(inFile, DataSourceType.FILE);
     assertTrue("Didn't read input file " + inFile, af != null);
     for (SequenceI sq : af.getViewport().getAlignment().getSequences())
     {
@@ -87,13 +98,13 @@ public class JmolViewerTest
       {
         for (int q = 0; q < dsq.getAllPDBEntries().size(); q++)
         {
-          final StructureViewer structureViewer = new StructureViewer(af
-                  .getViewport().getStructureSelectionManager());
+          final StructureViewer structureViewer = new StructureViewer(
+                  af.getViewport().getStructureSelectionManager());
           structureViewer.setViewerType(ViewerType.JMOL);
           JalviewStructureDisplayI jmolViewer = structureViewer
                   .viewStructures(dsq.getAllPDBEntries().elementAt(q),
-                          new SequenceI[] { sq }, af.getCurrentView()
-                                  .getAlignPanel());
+                          new SequenceI[]
+                          { sq }, af.getCurrentView().getAlignPanel());
           /*
            * Wait for viewer load thread to complete
            */
@@ -116,5 +127,86 @@ public class JmolViewerTest
     }
   }
 
+  @Test(groups = { "Functional" })
+  public void testAddStrToSingleSeqViewJMol()
+          throws InvocationTargetException, InterruptedException
+  {
+    Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
+            ViewerType.JMOL.name());
+    String inFile = "examples/1gaq.txt";
+    AlignFrame af = new jalview.io.FileLoader(true)
+            .LoadFileWaitTillLoaded(inFile, DataSourceType.FILE);
+    assertTrue("Didn't read input file " + inFile, af != null);
+    // show a structure for 4th Sequence
+    SequenceI sq1 = af.getViewport().getAlignment().getSequences().get(0);
+    final StructureViewer structureViewer = new StructureViewer(
+            af.getViewport().getStructureSelectionManager());
+    structureViewer.setViewerType(ViewerType.JMOL);
+    JalviewStructureDisplayI jmolViewer = structureViewer.viewStructures(
+            sq1.getDatasetSequence().getAllPDBEntries().elementAt(0),
+            new SequenceI[]
+            { sq1 }, af.getCurrentView().getAlignPanel());
+    /*
+     * Wait for viewer load thread to complete
+     */
+    try
+    {
+      while (!jmolViewer.getBinding().isFinishedInit())
+      {
+        Thread.sleep(500);
+      }
+    } catch (InterruptedException e)
+    {
+    }
+
+    assertTrue(jmolViewer.isVisible());
+
+    // add another pdb file and add it to view
+    final String _inFile = "examples/3W5V.pdb";
+    inFile = _inFile;
+    FileLoader fl = new FileLoader();
+    fl.LoadFile(af.getCurrentView(), _inFile, DataSourceType.FILE,
+            FileFormat.PDB);
+    try
+    {
+      int time = 0;
+      do
+      {
+        Thread.sleep(50); // hope we can avoid race condition
+
+      } while (++time < 30
+              && af.getViewport().getAlignment().getHeight() == 3);
+    } catch (Exception q)
+    {
+    }
+    ;
+    assertTrue("Didn't paste additional structure" + inFile,
+            af.getViewport().getAlignment().getHeight() > 3);
+    SequenceI sq2 = af.getViewport().getAlignment().getSequenceAt(3);
+    PDBEntry pdbe = sq2.getDatasetSequence().getAllPDBEntries().get(0);
+    assertTrue(pdbe.getFile().contains(inFile));
+    structureViewer.viewStructures(pdbe, new SequenceI[] { sq2 },
+            af.alignPanel);
+    /*
+     * Wait for viewer load thread to complete
+     */
+    try
+    {
+      while (structureViewer.isBusy())
+      {
+        Thread.sleep(500);
+      }
+    } catch (InterruptedException e)
+    {
+    }
+    assertEquals(jmolViewer.getBinding().getPdbCount(), 2);
+    String mouseOverTest = "[GLY]293:A.CA/2.1 #2164";
+    ((JalviewJmolBinding) jmolViewer.getBinding()).mouseOverStructure(2164,
+            mouseOverTest);
+    SearchResultsI highlight = af.alignPanel.getSeqPanel()
+            .getLastSearchResults();
+    assertNotNull("Didn't find highlight from second structure mouseover",
+            highlight.getResults(sq2, sq2.getStart(), sq2.getEnd()));
+  }
 
 }
index af9c045..dd1a4de 100644 (file)
@@ -46,7 +46,7 @@ import jalview.schemes.TurnColourScheme;
 import jalview.util.MessageManager;
 
 import java.awt.Color;
-import java.util.List;
+import java.util.Iterator;
 
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
@@ -85,24 +85,20 @@ public class AlignFrameTest
      */
     assertFalse(alignFrame.hideFeatureColumns("exon", true));
     assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty());
-    assertTrue(alignFrame.getViewport().getAlignment().getHiddenColumns()
-            .getHiddenColumnsCopy()
-            .isEmpty());
+    assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns()
+            .getNumberOfRegions(), 0);
     assertFalse(alignFrame.hideFeatureColumns("exon", false));
     assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty());
-    assertTrue(alignFrame.getViewport().getAlignment().getHiddenColumns()
-            .getHiddenColumnsCopy()
-            .isEmpty());
+    assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns()
+            .getNumberOfRegions(), 0);
 
     /*
      * hiding a feature in all columns does nothing
      */
     assertFalse(alignFrame.hideFeatureColumns("Metal", true));
     assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty());
-    List<int[]> hidden = alignFrame.getViewport().getAlignment()
-            .getHiddenColumns()
-            .getHiddenColumnsCopy();
-    assertTrue(hidden.isEmpty());
+    assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns()
+            .getNumberOfRegions(), 0);
 
     /*
      * hide a feature present in some columns
@@ -110,13 +106,16 @@ public class AlignFrameTest
      * [1-3], [6-8] base zero
      */
     assertTrue(alignFrame.hideFeatureColumns("Turn", true));
-    hidden = alignFrame.getViewport().getAlignment().getHiddenColumns()
-            .getHiddenColumnsCopy();
-    assertEquals(hidden.size(), 2);
-    assertEquals(hidden.get(0)[0], 1);
-    assertEquals(hidden.get(0)[1], 3);
-    assertEquals(hidden.get(1)[0], 6);
-    assertEquals(hidden.get(1)[1], 8);
+    Iterator<int[]> regions = alignFrame.getViewport().getAlignment()
+            .getHiddenColumns().iterator();
+    assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns()
+            .getNumberOfRegions(), 2);
+    int[] next = regions.next();
+    assertEquals(next[0], 1);
+    assertEquals(next[1], 3);
+    next = regions.next();
+    assertEquals(next[0], 6);
+    assertEquals(next[1], 8);
   }
 
   @BeforeClass(alwaysRun = true)
index 06478d5..912cd27 100644 (file)
@@ -35,7 +35,7 @@ import jalview.io.FileFormat;
 import jalview.io.FormatAdapter;
 
 import java.io.IOException;
-import java.util.List;
+import java.util.Iterator;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
@@ -141,18 +141,21 @@ public class AnnotationColumnChooserTest
 
     HiddenColumns currentHidden = af.getViewport().getAlignment()
             .getHiddenColumns();
-    List<int[]> regions = currentHidden.getHiddenColumnsCopy();
-    assertEquals(regions.get(0)[0], 0);
-    assertEquals(regions.get(0)[1], 3);
-    assertEquals(regions.get(1)[0], 22);
-    assertEquals(regions.get(1)[1], 25);
+    Iterator<int[]> regions = currentHidden.iterator();
+    int[] next = regions.next();
+    assertEquals(0, next[0]);
+    assertEquals(3, next[1]);
+    next = regions.next();
+    assertEquals(22, next[0]);
+    assertEquals(25, next[1]);
 
     // now reset hidden columns
     acc.reset();
     currentHidden = af.getViewport().getAlignment().getHiddenColumns();
-    regions = currentHidden.getHiddenColumnsCopy();
-    assertEquals(regions.get(0)[0], 10);
-    assertEquals(regions.get(0)[1], 20);
+    regions = currentHidden.iterator();
+    next = regions.next();
+    assertEquals(10, next[0]);
+    assertEquals(20, next[1]);
 
     // check works with empty hidden columns as old columns
     oldhidden = new HiddenColumns();
@@ -169,8 +172,9 @@ public class AnnotationColumnChooserTest
 
     acc.reset();
     currentHidden = af.getViewport().getAlignment().getHiddenColumns();
-    regions = currentHidden.getHiddenColumnsCopy();
-    assertEquals(regions.get(0)[0], 10);
-    assertEquals(regions.get(0)[1], 20);
+    regions = currentHidden.iterator();
+    next = regions.next();
+    assertEquals(10, next[0]);
+    assertEquals(20, next[1]);
   }
 }
index 335240b..8f60021 100644 (file)
@@ -29,9 +29,12 @@ import static org.testng.AssertJUnit.assertTrue;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
+import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
@@ -41,6 +44,7 @@ import jalview.util.MessageManager;
 import java.awt.Component;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 import javax.swing.JMenu;
@@ -100,7 +104,7 @@ public class PopupMenuTest
   public void testConfigureReferenceAnnotationsMenu_noSequenceSelected()
   {
     JMenuItem menu = new JMenuItem();
-    List<SequenceI> seqs = new ArrayList<SequenceI>();
+    List<SequenceI> seqs = new ArrayList<>();
     testee.configureReferenceAnnotationsMenu(menu, seqs);
     assertFalse(menu.isEnabled());
     // now try null list
@@ -469,8 +473,8 @@ public class PopupMenuTest
     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
 
     // create list of links and list of DBRefs
-    List<String> links = new ArrayList<String>();
-    List<DBRefEntry> refs = new ArrayList<DBRefEntry>();
+    List<String> links = new ArrayList<>();
+    List<DBRefEntry> refs = new ArrayList<>();
 
     // links as might be added into Preferences | Connections dialog
     links.add("EMBL-EBI Search | http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$"
@@ -548,7 +552,7 @@ public class PopupMenuTest
     }
 
     // if there are no valid links the Links submenu is disabled
-    List<String> nomatchlinks = new ArrayList<String>();
+    List<String> nomatchlinks = new ArrayList<>();
     nomatchlinks.add("NOMATCH | http://www.uniprot.org/uniprot/$"
             + DB_ACCESSION + "$");
 
@@ -559,4 +563,117 @@ public class PopupMenuTest
     assertFalse(linkMenu.isEnabled());
 
   }
+
+  /**
+   * Test for adding feature links
+   */
+  @Test(groups = { "Functional" })
+  public void testHideInsertions()
+  {
+    // get sequences from the alignment
+    List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
+    
+    // add our own seqs to avoid problems with changes to existing sequences
+    // (gap at end of sequences varies depending on how tests are run!)
+    Sequence seqGap1 = new Sequence("GappySeq",
+            "AAAA----AA-AAAAAAA---AAA-----------AAAAAAAAAA--");
+    seqGap1.createDatasetSequence();
+    seqs.add(seqGap1);
+    Sequence seqGap2 = new Sequence("LessGappySeq",
+            "AAAAAA-AAAAA---AAA--AAAAA--AAAAAAA-AAAAAA");
+    seqGap2.createDatasetSequence();
+    seqs.add(seqGap2);
+    Sequence seqGap3 = new Sequence("AnotherGapSeq",
+            "AAAAAA-AAAAAA--AAAAAA-AAAAAAAAAAA---AAAAAAAA");
+    seqGap3.createDatasetSequence();
+    seqs.add(seqGap3);
+    Sequence seqGap4 = new Sequence("NoGaps",
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+    seqGap4.createDatasetSequence();
+    seqs.add(seqGap4);
+
+    ColumnSelection sel = new ColumnSelection();
+    parentPanel.av.getAlignment().getHiddenColumns()
+            .revealAllHiddenColumns(sel);
+
+    // get the Popup Menu for 7th sequence - no insertions
+    testee = new PopupMenu(parentPanel, (Sequence) seqs.get(7), null);
+    testee.hideInsertions_actionPerformed(null);
+    
+    HiddenColumns hidden = parentPanel.av.getAlignment().getHiddenColumns();
+    Iterator<int[]> it = hidden.iterator();
+    assertFalse(it.hasNext());
+
+    // get the Popup Menu for GappySeq - this time we have insertions
+    testee = new PopupMenu(parentPanel, (Sequence) seqs.get(4), null);
+    testee.hideInsertions_actionPerformed(null);
+    hidden = parentPanel.av.getAlignment().getHiddenColumns();
+    it = hidden.iterator();
+
+    assertTrue(it.hasNext());
+    int[] region = it.next();
+    assertEquals(region[0], 4);
+    assertEquals(region[1], 7);
+
+    assertTrue(it.hasNext());
+    region = it.next();
+    assertEquals(region[0], 10);
+    assertEquals(region[1], 10);
+
+    assertTrue(it.hasNext());
+    region = it.next();
+    assertEquals(region[0], 18);
+    assertEquals(region[1], 20);
+
+    assertTrue(it.hasNext());
+    region = it.next();
+    assertEquals(region[0], 24);
+    assertEquals(region[1], 34);
+
+    assertTrue(it.hasNext());
+    region = it.next();
+    assertEquals(region[0], 45);
+    assertEquals(region[1], 46);
+
+    assertFalse(it.hasNext());
+
+    sel = new ColumnSelection();
+    hidden.revealAllHiddenColumns(sel);
+
+    // make a sequence group and hide insertions within the group
+    SequenceGroup sg = new SequenceGroup();
+    sg.setStartRes(8);
+    sg.setEndRes(42);
+    sg.addSequence(seqGap2, false);
+    sg.addSequence(seqGap3, false);
+    parentPanel.av.setSelectionGroup(sg);
+
+    // hide columns outside and within selection
+    // only hidden columns outside the collection will be retained (unless also
+    // gaps in the selection)
+    hidden.hideColumns(1, 10);
+    hidden.hideColumns(31, 40);
+
+    // get the Popup Menu for LessGappySeq in the sequence group
+    testee = new PopupMenu(parentPanel, (Sequence) seqs.get(5), null);
+    testee.hideInsertions_actionPerformed(null);
+    hidden = parentPanel.av.getAlignment().getHiddenColumns();
+    it = hidden.iterator();
+
+    assertTrue(it.hasNext());
+    region = it.next();
+    assertEquals(region[0], 1);
+    assertEquals(region[1], 7);
+
+    assertTrue(it.hasNext());
+    region = it.next();
+    assertEquals(region[0], 13);
+    assertEquals(region[1], 14);
+
+    assertTrue(it.hasNext());
+    region = it.next();
+    assertEquals(region[0], 34);
+    assertEquals(region[1], 34);
+  }
+
 }
index a27bc3f..ee1270e 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.gui;
 
 import static org.testng.Assert.assertEquals;
@@ -9,11 +29,9 @@ import jalview.io.FileLoader;
 import java.awt.Font;
 import java.awt.FontMetrics;
 
-import junit.extensions.PA;
-
 import org.testng.annotations.Test;
 
-import sun.swing.SwingUtilities2;
+import junit.extensions.PA;
 
 public class SeqCanvasTest
 {
@@ -48,7 +66,7 @@ public class SeqCanvasTest
     av.setScaleAboveWrapped(true);
     av.setScaleLeftWrapped(true);
     av.setScaleRightWrapped(true);
-    FontMetrics fm = SwingUtilities2.getFontMetrics(testee, av.getFont());
+    FontMetrics fm = testee.getFontMetrics(av.getFont());
     int labelWidth = fm.stringWidth("000") + charWidth;
     assertEquals(labelWidth, 39); // 3 x 9 + charWidth
 
@@ -218,7 +236,7 @@ public class SeqCanvasTest
     av.setScaleAboveWrapped(true);
     av.setScaleLeftWrapped(true);
     av.setScaleRightWrapped(true);
-    FontMetrics fm = SwingUtilities2.getFontMetrics(testee, av.getFont());
+    FontMetrics fm = testee.getFontMetrics(av.getFont());
     int labelWidth = fm.stringWidth("000") + charWidth;
     assertEquals(labelWidth, 39); // 3 x 9 + charWidth
     int annotationHeight = testee.getAnnotationHeight();
index 91fe602..f69e6b5 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.gui;
 
 import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.datamodel.DBRefEntry;
@@ -28,8 +29,10 @@ import jalview.datamodel.DBRefSource;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
 import jalview.jbgui.GStructureChooser.FilterOption;
 
+import java.util.Collection;
 import java.util.Vector;
 
 import org.testng.annotations.AfterMethod;
@@ -37,6 +40,8 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import junit.extensions.PA;
+
 public class StructureChooserTest
 {
 
@@ -142,8 +147,10 @@ public class StructureChooserTest
     SequenceI[] selectedSeqs = new SequenceI[] { seq };
     StructureChooser sc = new StructureChooser(selectedSeqs, seq, null);
     sc.fetchStructuresMetaData();
-    assertTrue(sc.getDiscoveredStructuresSet() != null);
-    assertTrue(sc.getDiscoveredStructuresSet().size() > 0);
+    Collection<FTSData> ss = (Collection<FTSData>) PA.getValue(sc,
+            "discoveredStructuresSet");
+    assertNotNull(ss);
+    assertTrue(ss.size() > 0);
 
   }
 
index e14a478..c0038a1 100644 (file)
@@ -39,6 +39,7 @@ import jalview.structure.StructureImportSettings.StructureParser;
 import java.io.File;
 import java.util.List;
 
+import org.junit.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
@@ -101,18 +102,19 @@ public class AnnotatedPDBFileInputTest
     }
   }
 
-  @Test(groups = { "Functional" })
+  @Test(groups = { "Functional" }, enabled = false)
   public void checkPDBannotationSource()
   {
-
+    Assert.fail(
+            "This test is incorrect - does not verify that JmolParser's annotation rows can be recognised as generated by the Jmol parser.");
     for (SequenceI asq : al.getSequences())
     {
       for (AlignmentAnnotation aa : asq.getAnnotation())
       {
 
         System.out.println("CalcId: " + aa.getCalcId());
-        if (StructureImportSettings.getDefaultPDBFileParser().equals(
-                StructureParser.JALVIEW_PARSER))
+        if (StructureImportSettings.getDefaultPDBFileParser()
+                .equals(StructureParser.JALVIEW_PARSER))
         {
           assertTrue(MCview.PDBfile.isCalcIdForFile(aa, pdbId));
         }
index 0715857..ef7615b 100644 (file)
@@ -42,6 +42,7 @@ import java.util.List;
 
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 @Test(singleThreaded = true)
@@ -56,6 +57,14 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
+  @DataProvider(name = "initialAccessions")
+  static Object[][] getAccessions()
+  {
+    return new String[][] { { "UNIPROT", "P00338" },
+        { "UNIPROT", "Q8Z9G6" },
+        { "ENSEMBLGENOMES", "CAD01290" } };
+  }
+
   /**
    * test store and recovery of all reachable cross refs from all reachable
    * crossrefs for one or more fetched db refs. Currently, this test has a known
@@ -63,8 +72,13 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
    * 
    * @throws Exception
    */
-  @Test(groups = { "Operational" }, enabled = true)
-  public void testRetrieveAndShowCrossref() throws Exception
+  @Test(
+    groups =
+    { "Operational" },
+    dataProvider = "initialAccessions",
+    enabled = true)
+  public void testRetrieveAndShowCrossref(String forSource,
+          String forAccession) throws Exception
   {
 
     List<String> failedDBRetr = new ArrayList<>();
@@ -94,8 +108,8 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
     List<String> keyseq = new ArrayList<>();
     HashMap<String, File> savedProjects = new HashMap<>();
 
-    for (String[] did : new String[][] { { "UNIPROT", "P00338" } })
-    {
+//    for (String[] did : new String[][] { { "UNIPROT", "P00338" } })
+//    {
       // pass counters - 0 - first pass, 1 means retrieve project rather than
       // perform action
       int pass1 = 0, pass2 = 0, pass3 = 0;
@@ -105,7 +119,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
       // { pass 2 = 0 { pass 3 = 0 } }
       do
       {
-        String first = did[0] + " " + did[1];
+        String first = forSource + " " + forAccession;//did[0] + " " + did[1];
         AlignFrame af = null;
         boolean dna;
         AlignmentI retral;
@@ -117,7 +131,8 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
           // retrieve dbref
 
           List<AlignFrame> afs = jalview.gui.SequenceFetcher.fetchAndShow(
-                  did[0], did[1]);
+                forSource, forAccession);
+        // did[0], did[1]);
           if (afs.size() == 0)
           {
             failedDBRetr.add("Didn't retrieve " + first);
@@ -411,7 +426,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
           pass1++;
         }
       } while (pass1 < 3);
-    }
+
     if (failedXrefMenuItems.size() > 0)
     {
       for (String s : failedXrefMenuItems)
index 158c901..5e835bf 100644 (file)
@@ -42,6 +42,7 @@ import jalview.schemes.ResidueColourScheme;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -205,7 +206,7 @@ public class JSONFileTest
     TEST_SEQ_HEIGHT = expectedSeqs.size();
     TEST_GRP_HEIGHT = expectedGrps.size();
     TEST_ANOT_HEIGHT = expectedAnnots.size();
-    TEST_CS_HEIGHT = expectedColSel.getHiddenColumnsCopy().size();
+    TEST_CS_HEIGHT = expectedColSel.getNumberOfRegions();
 
     exportSettings = new AlignExportSettingI()
     {
@@ -325,11 +326,12 @@ public class JSONFileTest
   {
     HiddenColumns cs = testJsonFile.getHiddenColumns();
     Assert.assertNotNull(cs);
-    Assert.assertNotNull(cs.getHiddenColumnsCopy());
-    List<int[]> hiddenCols = cs.getHiddenColumnsCopy();
-    Assert.assertEquals(hiddenCols.size(), TEST_CS_HEIGHT);
-    Assert.assertEquals(hiddenCols.get(0), expectedColSel
-            .getHiddenColumnsCopy().get(0),
+
+    Iterator<int[]> it = cs.iterator();
+    Iterator<int[]> colselit = expectedColSel.iterator();
+    Assert.assertTrue(it.hasNext());
+    Assert.assertEquals(cs.getNumberOfRegions(), TEST_CS_HEIGHT);
+    Assert.assertEquals(it.next(), colselit.next(),
             "Mismatched hidden columns!");
   }
 
index 15e18e3..fbdd782 100644 (file)
@@ -76,7 +76,8 @@ public class Jalview2xmlBase
   @BeforeTest(alwaysRun = true)
   public static void clearDesktop()
   {
-    if (Desktop.instance != null && Desktop.getAlignFrames() != null)
+    if (Desktop.instance != null && Desktop.getFrames() != null
+            && Desktop.getFrames().length > 0)
     {
       Desktop.instance.closeAll_actionPerformed(null);
     }
index 6abb7e5..c0eb8c5 100644 (file)
@@ -247,6 +247,31 @@ public class Jalview2xmlTests extends Jalview2xmlBase
 
   }
 
+  /**
+   * Test for JAL-2223 - multiple mappings in View Mapping report
+   * 
+   * @throws Exception
+   */
+  @Test(groups = { "Functional" })
+  public void noDuplicatePdbMappingsMade() throws Exception
+  {
+    StructureImportSettings.setProcessSecondaryStructure(true);
+    StructureImportSettings.setVisibleChainAnnotation(true);
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/exampleFile_2_7.jar", DataSourceType.FILE);
+    assertNotNull(af, "Didn't read in the example file correctly.");
+
+    // locate Jmol viewer
+    // count number of PDB mappings the structure selection manager holds -
+    String pdbFile = af.getCurrentView().getStructureSelectionManager()
+            .findFileForPDBId("1A70");
+    assertEquals(
+            af.getCurrentView().getStructureSelectionManager()
+                    .getMapping(pdbFile).length,
+            2, "Expected only two mappings for 1A70");
+
+  }
+
   @Test(groups = { "Functional" })
   public void viewRefPdbAnnotation() throws Exception
   {
@@ -413,7 +438,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     String afid = af.getViewport().getSequenceSetId();
 
     // remember reference sequence for each panel
-    Map<String, SequenceI> refseqs = new HashMap<String, SequenceI>();
+    Map<String, SequenceI> refseqs = new HashMap<>();
 
     /*
      * mark sequence 2, 3, 4.. in panels 1, 2, 3...
@@ -551,8 +576,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
      * remember representative and hidden sequences marked 
      * on each panel
      */
-    Map<String, SequenceI> repSeqs = new HashMap<String, SequenceI>();
-    Map<String, List<String>> hiddenSeqNames = new HashMap<String, List<String>>();
+    Map<String, SequenceI> repSeqs = new HashMap<>();
+    Map<String, List<String>> hiddenSeqNames = new HashMap<>();
 
     /*
      * mark sequence 2, 3, 4.. in panels 1, 2, 3...
@@ -568,7 +593,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
       repIndex = Math.max(repIndex, 1);
       SequenceI repSeq = alignment.getSequenceAt(repIndex);
       repSeqs.put(ap.getViewName(), repSeq);
-      List<String> hiddenNames = new ArrayList<String>();
+      List<String> hiddenNames = new ArrayList<>();
       hiddenSeqNames.put(ap.getViewName(), hiddenNames);
 
       /*
diff --git a/test/jalview/renderer/OverviewRendererTest.java b/test/jalview/renderer/OverviewRendererTest.java
new file mode 100644 (file)
index 0000000..1d532f7
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.renderer;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignViewport;
+import jalview.renderer.seqfeatures.FeatureRenderer;
+import jalview.schemes.FeatureColour;
+import jalview.schemes.ZappoColourScheme;
+import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.OverviewDimensions;
+import jalview.viewmodel.OverviewDimensionsShowHidden;
+import jalview.viewmodel.ViewportRanges;
+
+import java.awt.Color;
+
+import org.testng.annotations.Test;
+public class OverviewRendererTest
+{
+
+  @Test
+  public void testGetColumnColourFromSequence()
+  {
+    OverviewResColourFinder cf = new OverviewResColourFinder(false,
+            Color.PINK, Color.green); // gapColour, hiddenColour
+    Sequence seq1 = new Sequence("seq1", "PQ-RL-");
+    Sequence seq2 = new Sequence("seq2", "FVE");
+    AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
+    AlignmentViewport av = new AlignViewport(al);
+    OverviewDimensions od = new OverviewDimensionsShowHidden(new ViewportRanges(al), false);
+    ResidueShaderI rs = new ResidueShader(new ZappoColourScheme());
+    FeatureRenderer fr = new FeatureRenderer(av);
+    OverviewRenderer or = new OverviewRenderer(fr, od, al, rs, cf);
+
+    // P is magenta (see ResidueProperties.zappo)
+    assertEquals(or.getColumnColourFromSequence(null, seq1, 0), Color.magenta.getRGB());
+    // Q is green
+    assertEquals(or.getColumnColourFromSequence(null, seq1, 1),
+            Color.green.getRGB());
+    // gap is pink (specified in OverviewResColourFinder constructor above)
+    assertEquals(or.getColumnColourFromSequence(null, seq1, 2),
+            Color.pink.getRGB());
+    // F is orange
+    assertEquals(or.getColumnColourFromSequence(null, seq2, 0),
+            Color.orange.getRGB());
+    // E is red
+    assertEquals(or.getColumnColourFromSequence(null, seq2, 2),
+            Color.red.getRGB());
+    // past end of sequence colour as gap (JAL-2929)
+    assertEquals(or.getColumnColourFromSequence(null, seq2, 3),
+            Color.pink.getRGB());
+
+    /*
+     * now add a feature on seq1
+     */
+    seq1.addSequenceFeature(
+            new SequenceFeature("Pfam", "desc", 1, 4, null));
+    fr.findAllFeatures(true);
+    av.setShowSequenceFeatures(true);
+    fr.setColour("Pfam", new FeatureColour(Color.yellow));
+    assertEquals(or.getColumnColourFromSequence(null, seq1, 0),
+            Color.yellow.getRGB());
+
+    // don't show sequence features
+    av.setShowSequenceFeatures(false);
+    assertEquals(or.getColumnColourFromSequence(null, seq1, 0),
+            Color.magenta.getRGB());
+  }
+}
index d3cddf9..6bb611c 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * 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.renderer.seqfeatures;
 
 import static org.testng.Assert.assertEquals;
@@ -341,10 +361,7 @@ public class FeatureRendererTest
     fr.filterFeaturesForDisplay(features, new FeatureColour(Color.black,
             Color.white, 0f, 1f));
     assertEquals(features.size(), 4);
-    assertTrue(features.contains(sf1));
-    assertTrue(features.contains(sf3));
-    assertTrue(features.contains(sf4));
-    assertTrue(features.contains(sf5));
+    assertFalse(features.contains(sf2));
 
     /*
      * co-located features with colour by label
@@ -359,5 +376,14 @@ public class FeatureRendererTest
     assertTrue(features.contains(sf3));
     assertTrue(features.contains(sf4));
     assertTrue(features.contains(sf5));
+
+    /*
+     * no filtering if transparency is applied
+     */
+    fr.setTransparency(0.5f);
+    features = seq.getSequenceFeatures();
+    fr.setGroupVisibility("group2", true);
+    fr.filterFeaturesForDisplay(features, new FeatureColour(Color.RED));
+    assertEquals(features.size(), 5);
   }
 }
index 85aea40..4bee3f5 100644 (file)
@@ -260,6 +260,8 @@ public class Mapping
   @Test(groups = { "Functional" })
   public void compareTransferredToRefPDBAnnot() throws Exception
   {
+    StructureImportSettings.setProcessSecondaryStructure(true);
+    StructureImportSettings.setVisibleChainAnnotation(true);
     StructureImportSettings.setShowSeqFeatures(true);
     AlignFrame ref = new FileLoader(false)
             .LoadFileWaitTillLoaded("test/jalview/ext/jmol/1QCF.pdb",
index f26c5f1..808e662 100644 (file)
@@ -1,8 +1,34 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.structure;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
+import jalview.datamodel.Mapping;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.util.MapList;
+
 import java.util.HashMap;
 import java.util.List;
 
@@ -11,9 +37,9 @@ import org.testng.annotations.Test;
 public class StructureMappingTest
 {
   @Test(groups = "Functional")
-  public void testgetPDBResNumRanges()
+  public void testGetPDBResNumRanges()
   {
-    HashMap<Integer, int[]> map = new HashMap<Integer, int[]>();
+    HashMap<Integer, int[]> map = new HashMap<>();
 
     StructureMapping mapping = new StructureMapping(null, null, null, null,
             map, null);
@@ -43,4 +69,75 @@ public class StructureMappingTest
     assertEquals(ranges.get(1)[0], 15);
     assertEquals(ranges.get(1)[1], 15);
   }
+
+  @Test(groups = "Functional")
+  public void testEquals()
+  {
+    SequenceI seq1 = new Sequence("seq1", "ABCDE");
+    SequenceI seq2 = new Sequence("seq1", "ABCDE");
+    String pdbFile = "a/b/file1.pdb";
+    String pdbId = "1a70";
+    String chain = "A";
+    String mappingDetails = "these are the mapping details, honest";
+    HashMap<Integer, int[]> map = new HashMap<>();
+
+    Mapping seqToPdbMapping = new Mapping(seq1,
+            new MapList(new int[]
+            { 1, 5 }, new int[] { 2, 6 }, 1, 1));
+    StructureMapping sm1 = new StructureMapping(seq1, pdbFile, pdbId, chain,
+            map, mappingDetails, seqToPdbMapping);
+    assertFalse(sm1.equals(null));
+    assertFalse(sm1.equals("x"));
+
+    StructureMapping sm2 = new StructureMapping(seq1, pdbFile, pdbId, chain,
+            map, mappingDetails, seqToPdbMapping);
+    assertTrue(sm1.equals(sm2));
+    assertTrue(sm2.equals(sm1));
+    assertEquals(sm1.hashCode(), sm2.hashCode());
+
+    // with different sequence
+    sm2 = new StructureMapping(seq2, pdbFile, pdbId, chain, map,
+            mappingDetails, seqToPdbMapping);
+    assertFalse(sm1.equals(sm2));
+    assertFalse(sm2.equals(sm1));
+
+    // with different file
+    sm2 = new StructureMapping(seq1, "a/b/file2.pdb", pdbId, chain, map,
+            mappingDetails, seqToPdbMapping);
+    assertFalse(sm1.equals(sm2));
+    assertFalse(sm2.equals(sm1));
+
+    // with different pdbid (case sensitive)
+    sm2 = new StructureMapping(seq1, pdbFile, "1A70", chain, map,
+            mappingDetails, seqToPdbMapping);
+    assertFalse(sm1.equals(sm2));
+    assertFalse(sm2.equals(sm1));
+
+    // with different chain
+    sm2 = new StructureMapping(seq1, pdbFile, pdbId, "B", map,
+            mappingDetails, seqToPdbMapping);
+    assertFalse(sm1.equals(sm2));
+    assertFalse(sm2.equals(sm1));
+
+    // map is ignore for this test
+    sm2 = new StructureMapping(seq1, pdbFile, pdbId, chain, null,
+            mappingDetails, seqToPdbMapping);
+    assertTrue(sm1.equals(sm2));
+    assertTrue(sm2.equals(sm1));
+
+    // with different mapping details
+    sm2 = new StructureMapping(seq1, pdbFile, pdbId, chain, map,
+            "different details!", seqToPdbMapping);
+    assertFalse(sm1.equals(sm2));
+    assertFalse(sm2.equals(sm1));
+
+    // with different seq to pdb mapping
+    Mapping map2 = new Mapping(seq1,
+            new MapList(new int[]
+            { 1, 5 }, new int[] { 3, 7 }, 1, 1));
+    sm2 = new StructureMapping(seq1, pdbFile, pdbId, chain, map,
+            mappingDetails, map2);
+    assertFalse(sm1.equals(sm2));
+    assertFalse(sm2.equals(sm1));
+  }
 }
index a59fbde..286be1b 100644 (file)
  */
 package jalview.structure;
 
+import static org.junit.Assert.assertArrayEquals;
+import static org.testng.Assert.assertNotNull;
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.analysis.AlignmentUtils;
+import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.bin.Cache;
 import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.ext.jmol.JmolCommands;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
 import jalview.gui.JvOptionPane;
+import jalview.gui.SequenceRenderer;
+import jalview.gui.StructureChooser;
 import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+import jalview.io.Jalview2xmlBase;
 import jalview.io.StructureFile;
 import jalview.util.MapList;
+import jalview.ws.DBRefFetcher;
+import jalview.ws.sifts.SiftsSettings;
 
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
 
+import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-public class StructureSelectionManagerTest
+@Test(singleThreaded = true)
+public class StructureSelectionManagerTest extends Jalview2xmlBase
 {
 
+  @Override
   @BeforeClass(alwaysRun = true)
   public void setUpJvOptionPane()
   {
@@ -160,4 +185,313 @@ public class StructureSelectionManagerTest
     assertEquals("1gaq", sf.getFeatureGroup());
     assertEquals("ALA:   1  1gaqB", sf.getDescription());
   }
+
+  /**
+   * Verify that RESNUM sequence features are present after creating a PDB
+   * mapping from a local file, then that everything stays in the same place
+   * when the file is viewed. The corner case is that 4IM2 is a fragment of a
+   * PDB file, which still includes the 'ID' field - a bug in Jalview 2.10.3
+   * causes features, annotation and positions to be remapped to the wrong place
+   * on viewing the structure
+   */
+  @Test(groups = { "Network" })
+  public void testMapping_EqualsFeatures()
+  {
+    // for some reason 'BeforeMethod' (which should be inherited from
+    // Jalview2XmlBase isn't always called)...
+    Desktop.instance.closeAll_actionPerformed(null);
+    try { 
+      Thread.sleep(200);
+    } catch (Exception foo) {}; 
+    SequenceI seq = new Sequence("4IM2|A",
+            "LDFCIRNIEKTVMGEISDIHTKLLRLSSSQGTIE");
+    String P4IM2_MISSING = "examples/testdata/4IM2_missing.pdb";
+    StructureSelectionManager sm = new StructureSelectionManager();
+    sm.setProcessSecondaryStructure(true);
+    sm.setAddTempFacAnnot(true);
+    StructureFile pmap = sm.setMapping(true, new SequenceI[] { seq },
+            new String[]
+            { null }, P4IM2_MISSING,
+            DataSourceType.FILE);
+    assertTrue(pmap != null);
+
+    assertEquals(1, pmap.getSeqs().size());
+    assertEquals("4IM2|A", pmap.getSeqs().get(0).getName());
+
+    List<int[]> structuremap1 = new ArrayList(
+            sm.getMapping(P4IM2_MISSING)[0]
+                    .getPDBResNumRanges(seq.getStart(), seq.getEnd()));
+
+    /*
+     * Verify a RESNUM sequence feature in the PDBfile sequence
+     * LEU468 - start+0 
+     * VAL479 - start+11
+     * MET486 - start+12
+     * GLY496 - start+13
+     * GLU516 - start+33 (last)
+     * 
+     * Expect features and mapping to resolve to same residues.
+     * Also try creating a view and test again
+     *   
+     */
+    String[] feats = new String[] { "LEU", "468", "VAL", "479", "MET",
+        "486", "GLY", "496", "GLU", "516" };
+    int[] offset = new int[] { 0, 11, 12, 13, 33 };
+
+    List<String> fdesc = new ArrayList<>();
+    for (int f = 0; f < feats.length; f += 2)
+    {
+      fdesc.add(feats[f] + ": " + feats[f + 1] + "  4im2A");
+    }
+    SequenceI pdbseq = pmap.getSeqs().get(0);
+    verifySeqFeats(pdbseq, offset, fdesc);
+
+    /// Now load as a view
+
+    AlignFrame alf = new FileLoader(false).LoadFileWaitTillLoaded(
+            "examples/testdata/4IM2_missing.pdb", DataSourceType.FILE);
+    Desktop.addInternalFrame(alf, "examples/testdata/4IM2_missing.pdb", 800,
+            400);
+    AlignmentI pdbal = alf.getViewport().getAlignment();
+    SequenceI pdb_viewseq = pdbal.getSequenceAt(0);
+    assertEquals(pdb_viewseq.getSequenceAsString(),
+            seq.getSequenceAsString());
+    // verify the feature location on the sequence when pdb imported as an
+    // alignment
+    verifySeqFeats(pdb_viewseq, offset, fdesc);
+
+
+    JalviewStructureDisplayI viewr = openStructureViaChooser(alf,
+            pdb_viewseq, "4IM2");
+
+    // and check all is good with feature location still
+    verifySeqFeats(pdb_viewseq, offset, fdesc);
+
+    // finally check positional mapping for sequence and structure
+    PDBEntry pdbe = seq.getPDBEntry("4IM2");
+    StructureSelectionManager apssm = alf.alignPanel
+            .getStructureSelectionManager();
+    StructureMapping[] smap = apssm
+            .getMapping(pdbe.getFile());
+    assertNotNull(smap);
+    assertNotNull(smap[0]);
+    // find the last position in the alignment sequence - this is not
+    // 'SequenceI.getEnd()' - which gets the last PDBRESNUM rather than
+    // SequenceI.getStart() + number of residues in file...
+    int realSeqEnd = pdb_viewseq.findPosition(pdb_viewseq.getLength());
+    List<int[]> ranges = smap[0].getPDBResNumRanges(pdb_viewseq.getStart(),
+            realSeqEnd);
+    assertEquals(structuremap1.size(), ranges.size());
+    int tot_mapped = 0;
+    for (int p = 0; p < ranges.size(); p++)
+    {
+      assertArrayEquals(structuremap1.get(p), ranges.get(p));
+      tot_mapped += 1 + (structuremap1.get(p)[1] - structuremap1.get(p)[0]);
+    }
+
+    assertEquals(pdb_viewseq.getLength(), tot_mapped);
+
+    int lastmappedp = StructureMapping.UNASSIGNED_VALUE;
+    for (int rp = pdb_viewseq.getStart(), rpEnd = pdb_viewseq
+            .findPosition(pdb_viewseq.getLength() - 1); rp <= rpEnd; rp++)
+    {
+      int mappedp = smap[0].getPDBResNum(rp);
+      if (mappedp != StructureMapping.UNASSIGNED_VALUE)
+      {
+        tot_mapped--;
+        if (lastmappedp == mappedp)
+        {
+          Assert.fail("Duplicate mapped position at " + rp + " (dupe = "
+                  + mappedp + ")");
+        }
+      }
+    }
+
+    Assert.assertEquals(tot_mapped, 0,
+            "Different number of mapped residues compared to ranges of mapped residues");
+
+    // positional mapping to atoms for color by structure is still wrong, even
+    // though panel looks correct.
+
+    StructureMappingcommandSet smcr[] = JmolCommands
+            .getColourBySequenceCommand(apssm,
+            new String[]
+            { pdbe.getFile() },
+            new SequenceI[][]
+            { new SequenceI[] { pdb_viewseq } },
+                    new SequenceRenderer(alf.alignPanel.getAlignViewport()),
+                    alf.alignPanel);
+    // Expected - all residues are white
+    for (StructureMappingcommandSet smm : smcr)
+    {
+      for (String c : smm.commands)
+      {
+        System.out.println(c);
+      }
+    }
+  }
+
+  private void verifySeqFeats(SequenceI pdbseq, int[] offset,
+          List<String> fdesc)
+  {
+    for (int o = 0; o < offset.length; o++)
+    {
+      int res = pdbseq.findPosition(offset[o]);
+      List<SequenceFeature> sf = pdbseq.getFeatures().findFeatures(res, res,
+              "RESNUM");
+      assertEquals("Expected sequence feature at position " + res + "("
+              + offset[o] + ")", 1, sf.size());
+      assertEquals("Wrong description at " + res + "(" + offset[o] + ")",
+              fdesc.get(o), sf.get(0).getDescription());
+    }
+
+  }
+
+  @Test(groups = { "Network" })
+  public void testAssociatedMappingToSubSeq() throws Exception
+  {
+
+    // currently this test fails if trimming is enabled
+    Cache.setProperty(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES,
+            Boolean.FALSE.toString());
+    String TEMP_FACTOR_AA="Temperature Factor";
+    String PDBID = "4IM2";
+    String FullLengthSeq = ">TBK1_HUMAN Serine/threonine-protein kinase TBK1\n" + 
+            "MQSTSNHLWLLSDILGQGATANVFRGRHKKTGDLFAIKVFNNISFLRPVDVQMREFEVLKKLNHKNIVKLFA\n" + 
+            "IEEETTTRHKVLIMEFCPCGSLYTVLEEPSNAYGLPESEFLIVLRDVVGGMNHLRENGIVHRDIKPGNIMRV\n" + 
+            "IGEDGQSVYKLTDFGAARELEDDEQFVSLYGTEEYLHPDMYERAVLRKDHQKKYGATVDLWSIGVTFYHAAT\n" + 
+            "GSLPFRPFEGPRRNKEVMYKIITGKPSGAISGVQKAENGPIDWSGDMPVSCSLSRGLQVLLTPVLANILEAD\n" + 
+            "QEKCWGFDQFFAETSDILHRMVIHVFSLQQMTAHKIYIHSYNTATIFHELVYKQTKIISSNQELIYEGRRLV\n" + 
+            "LEPGRLAQHFPKTTEENPIFVVSREPLNTIGLIYEKISLPKVHPRYDLDGDASMAKAITGVVCYACRIASTL\n" + 
+            "LLYQELMRKGIRWLIELIKDDYNETVHKKTEVVITLDFCIRNIEKTVKVYEKLMKINLEAAELGEISDIHTK\n" + 
+            "LLRLSSSQGTIETSLQDIDSRLSPGGSLADAWAHQEGTHPKDRNVEKLQVLLNCMTEIYYQFKKDKAERRLA\n" + 
+            "YNEEQIHKFDKQKLYYHATKAMTHFTDECVKKYEAFLNKSEEWIRKMLHLRKQLLSLTNQCFDIEEEVSKYQ\n" + 
+            "EYTNELQETLPQKMFTASSGIKHTMTPIYPSSNTLVEMTLGMKKLKEEMEGVVKELAENNHILERFGSLTMD\n" + 
+            "GGLRNVDCL";
+    /*
+     * annotation exported after importing full length sequence to desktop, opening 4IM2 and selecting 'Add Reference Annotation'.
+     * 
+     * Note - tabs must be replaced with \t - Eclipse expands them to spaces otherwise.
+     */
+    String FullLengthAnnot = "JALVIEW_ANNOTATION\n" + 
+            "# Created: Mon Feb 05 15:30:20 GMT 2018\n" + 
+            "# Updated: Fri Feb 09 17:05:17 GMT 2018\n" + 
+            "\n" + 
+            "\n" + 
+            "SEQUENCE_REF\tTBK1_HUMAN\n"
+            + "LINE_GRAPH\tTemperature Factor\tTemperature Factor for 4im2A\t125.22|128.51|120.35|113.12|122.6|114.44|91.49|102.53|98.22|111.41|111.32|116.64|103.55|100.53|95.07|105.55|114.76|128.29|133.55|142.14|121.12|110.36|95.79|95.39|87.14|99.56|93.55|94.21|100.33|110.68|97.85|82.37|75.87|76.53|77.85|82.49|80.92|96.88|122.58|133.31|160.15|180.51|||||242.88|258.97|247.01|227.12|223.24|211.62|184.65|183.51|168.96|160.04|150.88|131.68|130.43|139.87|148.59|136.57|125.7|96.51|74.49|74.08|85.87|70.93|86.47|101.59|97.51|97.39|117.19|114.27|129.5|112.98|147.52|170.26|154.98|168.18|157.51|131.95|105.85|97.78|97.35|76.51|76.31|72.55|71.43|78.82|79.94|75.04|79.54|77.95|83.56|88.5|71.51|71.73|75.96|82.36|81.75|66.51|67.23|69.35|67.92|54.75|71.19|61.85|65.34|67.97|64.51|67.41|62.28|72.85|72.76|70.64|65.23|71.07|67.73|87.72|64.93|75.92|94.02|99.35|93.71|103.59|106.29|115.46|118.69|147.18|130.62|171.64|158.95|164.11||107.42|88.53|83.52|88.06|94.06|80.82|59.01|59.73|78.89|69.21|70.34|81.95|74.53|60.92|64.65|55.79|75.71|68.86|70.95|75.08|87.76|85.43|105.84|||||||||||||||||137.46|151.33|145.17|122.79|111.56|126.72|124.06|161.75|176.84|180.51|198.49|196.75|187.41||195.23|202.27|203.16|226.55|221.75|193.83||||||172.33|177.97|151.47|132.65|99.22|93.7|91.15|88.24|72.35|70.05|70.0|74.92|66.51|68.37|65.76|70.12|74.97|76.89|80.83|70.21|69.48|79.54|82.65|96.54|114.31|140.46|168.51|176.99|205.08|209.27|155.83|139.41|151.3|129.33|111.31|119.62|121.37|102.26|115.39|129.97|128.65|110.38|110.66|116.1|82.53|84.02|82.17|87.63|86.42|77.23|91.23|95.53|102.21|120.73|133.26|109.67|108.49|93.25|92.85|86.39|95.66|94.92|85.82|80.13|76.17|86.61|78.9|77.97|105.6|70.66|69.35|78.94|66.68|63.03|69.91|79.05|75.43|70.73|70.02|80.57|81.74|77.99|84.1|91.66|92.42|94.03|116.47|132.01|154.55|163.99|161.37|155.23|132.78|109.3|90.38|101.83|99.61|91.68|82.77|86.12|82.73|90.13|85.14|79.54|74.27|74.06|72.88|86.34|72.0|69.32|60.9|68.15|52.99|63.53|61.3|66.01|68.28|77.41|71.52|67.18|66.17|71.51|65.47|52.63|65.08|66.37|73.76|77.79|67.58|79.53|84.75|87.42|78.9|79.19|85.57|73.67|80.56|86.19|72.17|66.27|72.8|86.28|78.89|74.5|90.6|80.42|92.5|92.84|96.18|92.08|88.5|87.25|64.6|68.95|65.56|67.55|71.62|78.24|84.95|71.35|86.41|84.73|94.41|95.09|84.74|87.64|88.85|75.1|86.42|79.28|73.14|78.54|80.81|60.66|67.93|71.64|59.85|64.7|61.22|63.84|65.9|62.18|74.95|72.92|93.37|90.47|96.0|93.8|88.46|79.78|83.4|66.55|68.7|73.2|78.76|85.67|84.8|89.59|96.52|79.53|103.51|134.72|126.7|145.31|156.17|149.35|128.48|117.29|118.98|131.59|109.36|90.39|87.68|91.81|78.77|80.11|91.39|75.57|78.98|71.53|76.85|70.9|64.71|73.55|73.45|60.0|69.92|57.89|69.07|66.45|62.85|57.83|57.89|66.4|61.61|60.85|66.47|63.53|63.84|65.96|73.06|70.82|64.51|63.66|73.37|73.59|68.09|78.93|76.99|75.05|71.32|88.4|78.88|93.08|110.61|94.32|99.24|128.99|129.49|132.74|124.21|120.32|142.06|166.41|149.87|153.29|172.19|165.89|181.6|223.11|237.73|176.41|171.09|189.65|188.61|154.84|142.72|154.25|170.99|175.65|||||||110.61||||||||||158.07|170.73|167.93|198.47|212.36|181.71|157.69|163.31|138.96|120.29|131.63|152.26|125.06|136.66|148.97|129.68|120.52|135.31|136.05|119.39|124.18|128.94|123.02|103.37|128.44|134.12|118.88|120.94|130.38|124.67|112.21|113.69|123.65|132.06|114.97|110.75|92.38|101.2|103.25|94.84|85.3|82.19|89.81|98.81|83.03|68.91|65.24|70.31|63.49|86.38|71.07|62.65|63.95|66.98|58.06|68.28|62.11|63.86|67.4|68.69|69.57|68.03|74.23|75.66|70.67|81.08|81.31|82.49|88.15|95.99|92.97|100.01|113.18|122.37|110.99|122.19|159.27|147.74|133.96|111.2|115.64|126.55|107.15|102.85|117.06|116.56|109.55|96.82|98.92|96.53|86.0|88.11|92.76|85.77|79.41|93.06|86.96|76.35|72.37|74.19|68.6|67.46|74.47|76.25|66.73|73.18|75.2|88.21|84.93|75.04|71.09|82.6|80.03|76.22|75.76|83.72|75.85|79.36|90.35|86.9|78.24|95.64|97.38|86.41|85.02|91.87|87.36|77.56|81.25|91.66|83.65|77.67|85.07|89.21|92.66|92.46|89.0|100.83|96.71|94.81|101.37|111.28|124.48|119.73|127.81|134.41|132.4|140.32|140.86|166.52|160.16|168.39|176.74|174.63|172.86|168.55|155.9|132.71|113.44|113.49|123.9|151.11|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"
+            + 
+            "\n" + 
+            "";
+    AlignFrame alf_full=new
+     FileLoader(false).LoadFileWaitTillLoaded(FullLengthSeq,DataSourceType.PASTE);
+    alf_full.loadJalviewDataFile(FullLengthAnnot, DataSourceType.PASTE, null, null);
+    AlignmentI al_full = alf_full.getViewport().getAlignment();
+    AlignmentAnnotation fullseq_tf = al_full.findAnnotations(al_full.getSequences().get(0), null, TEMP_FACTOR_AA).iterator()
+            .next();
+    assertNotNull(fullseq_tf);
+    
+    // getMappingFor
+    // AlignmentI al_full=alf_full.getViewport().getAlignment();
+    //
+    // // load 4IM2 (full length, SIFTS onto full alingnment)
+    // SiftsSettings.setMapWithSifts(true);
+    // StructureChooser schoose = new StructureChooser(selectedSeqs_full,
+    // seq_full,
+    // alf_full.getViewport().getAlignPanel());
+    // schoose.selectStructure(PDBID);
+    // schoose.ok_ActionPerformed();
+
+    AlignFrame alf = new FileLoader(false).LoadFileWaitTillLoaded(
+            ">TBK1_HUMAN/470-502 Serine/threonine-protein kinase TBK1\nFCIRNIEKTVKVYEKLMKINLEAAELGEISDIH",
+            DataSourceType.PASTE);
+    Desktop.addInternalFrame(alf, "Foo", 800, 600);
+    ;
+    AlignmentI al = alf.getViewport().getAlignment();
+    SequenceI seq = al.getSequenceAt(0);
+    assertEquals(470, seq.getStart());
+    // load 4IM2 (full length, SIFTS)
+    SiftsSettings.setMapWithSifts(true);
+    StructureImportSettings.setProcessSecondaryStructure(true);
+    StructureImportSettings.setVisibleChainAnnotation(true);
+    JalviewStructureDisplayI sview = openStructureViaChooser(alf, seq,
+            PDBID);
+
+    AlignmentAnnotation subseq_tf=null;
+    assertTrue(seq.getDBRefs() != null && seq.getDBRefs().length > 0);
+    
+    if (!al.findAnnotations(seq, null, TEMP_FACTOR_AA).iterator().hasNext())
+    {
+      // FIXME JAL-2321 - don't see reference annotation on alignment the first
+      // time
+      // around
+      SortedMap<String, String> tipEntries = new TreeMap<>();
+      final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<>();
+
+      AlignmentUtils.findAddableReferenceAnnotations(al.getSequences(),
+              tipEntries, candidates, al);
+      AlignmentUtils.addReferenceAnnotations(candidates, al, null);
+
+      if (!al.findAnnotations(seq, null, TEMP_FACTOR_AA).iterator()
+              .hasNext())
+      {
+        Assert.fail(
+                "JAL-2321 or worse has occured. No secondary structure added to alignment.");
+      }
+    }
+    subseq_tf = al.findAnnotations(seq, null, TEMP_FACTOR_AA).iterator()
+            .next();
+    // verify against annotation after loading 4IM2 to full length TBK1_HUMAN
+    // verify location of mapped residues
+    // verify location of secondary structure annotation
+    // Specific positions: LYS477 (h),THR478 (no helix), ... GLY496(no helix),
+    // GLU497 (helix),
+    
+    // check there is or is not a tempfactor for each mapped position, and that
+    // values are equal for those positions.
+    for (int p=seq.getStart();p<=seq.getEnd();p++)
+    {
+      Annotation orig,subseq;
+      orig = fullseq_tf.getAnnotationForPosition(p);
+      subseq = subseq_tf.getAnnotationForPosition(p);
+      if (orig == null)
+      {
+        Assert.assertNull(subseq,
+                "Expected no annotation transferred at position " + p);
+      }
+      ;
+      if (orig != null)
+      {
+        Assert.assertNotNull(subseq,
+                "Expected annotation transfer at position " + p);
+        assertEquals(orig.value, subseq.value);
+      }
+      ;
+
+    }
+  }
+
+  private JalviewStructureDisplayI openStructureViaChooser(AlignFrame alf,
+          SequenceI seq,
+          String pDBID)
+  {
+
+    SequenceI[] selectedSeqs = new SequenceI[] { seq };
+
+    StructureChooser schoose = new StructureChooser(selectedSeqs, seq,
+            alf.getViewport().getAlignPanel());
+
+    try
+    {
+      Thread.sleep(5000);
+    } catch (InterruptedException q)
+    {
+    }
+    ;
+    Assert.assertTrue(schoose.selectStructure(pDBID),
+            "Couldn't select structure via structure chooser: " + pDBID);
+    schoose.showStructures(true);
+    return schoose.getOpenedStructureViewer();
+  }
+
 }
index a2f38e2..95d7efe 100644 (file)
@@ -391,7 +391,9 @@ public class MapListTest
     MapList ml7 = new MapList(codons, protein, 3, 1); // toShifts differ
 
     assertTrue(ml.equals(ml));
+    assertEquals(ml.hashCode(), ml.hashCode());
     assertTrue(ml.equals(ml1));
+    assertEquals(ml.hashCode(), ml1.hashCode());
     assertTrue(ml1.equals(ml));
 
     assertFalse(ml.equals(null));
@@ -426,7 +428,7 @@ public class MapListTest
   @Test(groups = { "Functional" })
   public void testGetRanges()
   {
-    List<int[]> ranges = new ArrayList<int[]>();
+    List<int[]> ranges = new ArrayList<>();
     ranges.add(new int[] { 2, 3 });
     ranges.add(new int[] { 5, 6 });
     assertEquals("[2, 3, 5, 6]", Arrays.toString(MapList.getRanges(ranges)));
@@ -603,7 +605,7 @@ public class MapListTest
   public void testAddRange()
   {
     int[] range = { 1, 5 };
-    List<int[]> ranges = new ArrayList<int[]>();
+    List<int[]> ranges = new ArrayList<>();
 
     // add to empty list:
     MapList.addRange(range, ranges);
@@ -702,7 +704,7 @@ public class MapListTest
   public void testCoalesceRanges()
   {
     assertNull(MapList.coalesceRanges(null));
-    List<int[]> ranges = new ArrayList<int[]>();
+    List<int[]> ranges = new ArrayList<>();
     assertSame(ranges, MapList.coalesceRanges(ranges));
     ranges.add(new int[] { 1, 3 });
     assertSame(ranges, MapList.coalesceRanges(ranges));
@@ -763,7 +765,7 @@ public class MapListTest
   @Test(groups = { "Functional" })
   public void testCoalesceRanges_withOverlap()
   {
-    List<int[]> ranges = new ArrayList<int[]>();
+    List<int[]> ranges = new ArrayList<>();
     ranges.add(new int[] { 1, 3 });
     ranges.add(new int[] { 2, 5 });
 
index 5226819..022e2d6 100644 (file)
@@ -50,6 +50,7 @@ import java.awt.Color;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
 
 import org.testng.annotations.BeforeClass;
@@ -913,9 +914,9 @@ public class MappingUtilsTest
     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
             proteinView, dnaView, dnaSelection, dnaHidden);
     assertEquals("[]", dnaSelection.getSelected().toString());
-    List<int[]> hidden = dnaHidden.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[0, 4]", Arrays.toString(hidden.get(0)));
+    Iterator<int[]> regions = dnaHidden.iterator();
+    assertEquals(1, dnaHidden.getNumberOfRegions());
+    assertEquals("[0, 4]", Arrays.toString(regions.next()));
 
     /*
      * Column 1 in protein picks up Seq1/K which maps to cols 0-3 in dna
@@ -930,9 +931,9 @@ public class MappingUtilsTest
     proteinSelection.hideSelectedColumns(1, hiddenCols);
     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
             proteinView, dnaView, dnaSelection, dnaHidden);
-    hidden = dnaHidden.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[0, 3]", Arrays.toString(hidden.get(0)));
+    regions = dnaHidden.iterator();
+    assertEquals(1, dnaHidden.getNumberOfRegions());
+    assertEquals("[0, 3]", Arrays.toString(regions.next()));
 
     /*
      * Column 2 in protein picks up gaps only - no mapping
@@ -944,7 +945,7 @@ public class MappingUtilsTest
     proteinSelection.hideSelectedColumns(2, hiddenCols);
     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
             proteinView, dnaView, dnaSelection, dnaHidden);
-    assertTrue(dnaHidden.getHiddenColumnsCopy().isEmpty());
+    assertEquals(0, dnaHidden.getNumberOfRegions());
 
     /*
      * Column 3 in protein picks up Seq1/P, Seq2/Q, Seq3/S which map to columns
@@ -959,9 +960,9 @@ public class MappingUtilsTest
     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
             proteinView, dnaView, dnaSelection, dnaHidden);
     assertEquals("[0, 1, 2, 3]", dnaSelection.getSelected().toString());
-    hidden = dnaHidden.getHiddenColumnsCopy();
-    assertEquals(1, hidden.size());
-    assertEquals("[5, 10]", Arrays.toString(hidden.get(0)));
+    regions = dnaHidden.iterator();
+    assertEquals(1, dnaHidden.getNumberOfRegions());
+    assertEquals("[5, 10]", Arrays.toString(regions.next()));
 
     /*
      * Combine hiding columns 1 and 3 to get discontiguous hidden columns
@@ -974,10 +975,10 @@ public class MappingUtilsTest
     proteinSelection.hideSelectedColumns(3, hiddenCols);
     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
             proteinView, dnaView, dnaSelection, dnaHidden);
-    hidden = dnaHidden.getHiddenColumnsCopy();
-    assertEquals(2, hidden.size());
-    assertEquals("[0, 3]", Arrays.toString(hidden.get(0)));
-    assertEquals("[5, 10]", Arrays.toString(hidden.get(1)));
+    regions = dnaHidden.iterator();
+    assertEquals(2, dnaHidden.getNumberOfRegions());
+    assertEquals("[0, 3]", Arrays.toString(regions.next()));
+    assertEquals("[5, 10]", Arrays.toString(regions.next()));
   }
 
   @Test(groups = { "Functional" })
index 95863e7..cc3dca8 100644 (file)
  */
 package jalview.ws;
 
+import static org.testng.Assert.assertEquals;
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.JvOptionPane;
 import jalview.structure.StructureImportSettings;
 import jalview.structure.StructureImportSettings.StructureParser;
 import jalview.ws.seqfetcher.DbSourceProxy;
 
+import java.util.Arrays;
 import java.util.List;
 
+import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -106,23 +110,76 @@ public class PDBSequenceFetcherTest
     testRetrieveProteinSeqFromPDB();
   }
 
+  private class TestRetrieveObject
+  {
+    String id;
+
+    int expectedHeight;
+
+    public TestRetrieveObject(String id, int expectedHeight)
+    {
+      super();
+      this.id = id;
+      this.expectedHeight = expectedHeight;
+    }
+
+  }
+
+  private List<TestRetrieveObject> toRetrieve = Arrays.asList(
+          new TestRetrieveObject("1QIP", 4),
+          new TestRetrieveObject("4IM2", 1));
+
   private void testRetrieveProteinSeqFromPDB() throws Exception
   {
     List<DbSourceProxy> sps = sf.getSourceProxy("PDB");
-    AlignmentI response = sps.get(0).getSequenceRecords("1QIP");
-    assertTrue(response != null);
-    assertTrue(response.getHeight() == 4);
-    for (SequenceI sq : response.getSequences())
+    StringBuilder errors = new StringBuilder();
+    for (TestRetrieveObject str : toRetrieve)
     {
-      assertTrue("No annotation transfered to sequence.",
-              sq.getAnnotation().length > 0);
-      assertTrue("No PDBEntry on sequence.",
-              sq.getAllPDBEntries().size() > 0);
-      org.testng.Assert
-              .assertEquals(sq.getEnd() - sq.getStart() + 1,
-                      sq.getLength(),
-                      "Sequence start/end doesn't match number of residues in sequence");
+      AlignmentI response = sps.get(0).getSequenceRecords(str.id);
+      assertTrue("No aligment for " + str.id, response != null);
+      assertEquals(response.getHeight(), str.expectedHeight,
+              "Number of chains for " + str.id);
+      for (SequenceI sq : response.getSequences())
+      {
+        assertTrue("No annotation transfered to sequence " + sq.getName(),
+                sq.getAnnotation().length > 0);
+        assertTrue("No PDBEntry on sequence " + sq.getName(),
+                sq.getAllPDBEntries().size() > 0);
+        // FIXME: should test that all residues extracted as sequences from
+        // chains in structure have a mapping to data in the structure
+        List<SequenceFeature> prev = null;
+        int lastp = -1;
+        for (int col = 1; col <= sq.getLength(); col++)
+        {
+          List<SequenceFeature> sf = sq.findFeatures(col, col, "RESNUM");
+          if (sf.size() != 1)
+          {
+            errors.append(
+                    str.id + ": " +
+                            "Expected one feature at column (position): "
+                            + (col - 1)
+                            + " (" + sq.findPosition(col - 1) + ")"
+                            + ": saw "
+                            + sf.size());
+            errors.append("\n");
+            if (prev != null)
+            {
+              errors.append("Last Feature was at position " + lastp + ": "
+                      + prev.get(0).toString());
+              errors.append("\n");
+            }
+          }
+          else
+          {
+            prev = sf;
+            lastp = sq.findPosition(col - 1);
+          }
+        }
+      }
+    }
+    if (errors.length() > 0)
+    {
+      Assert.fail(errors.toString());
     }
   }
-
 }
diff --git a/test/jalview/ws/dbsources/PfamFullTest.java b/test/jalview/ws/dbsources/PfamFullTest.java
new file mode 100644 (file)
index 0000000..f5cc640
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.dbsources;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.bin.Cache;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class PfamFullTest
+{
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+  }
+
+  @Test(groups = "Functional")
+  public void testGetURL()
+  {
+    String path = "pfam.xfam.org/family/ABC/alignment/full";
+
+    // with default value for domain
+    String url = new PfamFull().getURL(" abc ");
+    assertEquals(url, "https://" + path);
+
+    // with override in properties
+    Cache.setProperty(Pfam.PFAM_BASEURL_KEY, "http://pfam.xfam.org");
+    url = new PfamFull().getURL(" abc ");
+    assertEquals(url, "http://" + path);
+  }
+}
diff --git a/test/jalview/ws/dbsources/PfamSeedTest.java b/test/jalview/ws/dbsources/PfamSeedTest.java
new file mode 100644 (file)
index 0000000..355ef0c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.dbsources;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.bin.Cache;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class PfamSeedTest
+{
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+  }
+
+  @Test(groups = "Functional")
+  public void testGetURL()
+  {
+    String path = "pfam.xfam.org/family/ABC/alignment/seed";
+
+    // with default value for domain
+    String url = new PfamSeed().getURL(" abc ");
+    assertEquals(url, "https://" + path);
+
+    // with override in properties
+    Cache.setProperty(Pfam.PFAM_BASEURL_KEY, "http://pfam.xfam.org");
+    url = new PfamSeed().getURL(" abc ");
+    assertEquals(url, "http://" + path);
+  }
+}
diff --git a/test/jalview/ws/dbsources/RfamFullTest.java b/test/jalview/ws/dbsources/RfamFullTest.java
new file mode 100644 (file)
index 0000000..2d1497f
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.dbsources;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.bin.Cache;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class RfamFullTest
+{
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+  }
+
+  @Test(groups = "Functional")
+  public void testGetURL()
+  {
+    String path = "rfam.xfam.org/family/ABC/alignment/full";
+
+    // with default value for domain
+    String url = new RfamFull().getURL(" abc ");
+    assertEquals(url, "https://" + path);
+
+    // with override in properties
+    Cache.setProperty(Rfam.RFAM_BASEURL_KEY, "http://rfam.xfam.org");
+    url = new RfamFull().getURL(" abc ");
+    assertEquals(url, "http://" + path);
+  }
+}
diff --git a/test/jalview/ws/dbsources/RfamSeedTest.java b/test/jalview/ws/dbsources/RfamSeedTest.java
new file mode 100644 (file)
index 0000000..745ba2e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.dbsources;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.bin.Cache;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class RfamSeedTest
+{
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+  }
+
+  @Test(groups = "Functional")
+  public void testGetURL()
+  {
+    String path = "rfam.xfam.org/family/ABC/alignment/stockholm";
+
+    // with default value for domain
+    String url = new RfamSeed().getURL(" abc ");
+    assertEquals(url, "https://" + path);
+
+    // with override in properties
+    Cache.setProperty(Rfam.RFAM_BASEURL_KEY, "http://rfam.xfam.org");
+    url = new RfamSeed().getURL(" abc ");
+    assertEquals(url, "http://" + path);
+  }
+}
index 573022d..51cff6e 100644 (file)
@@ -58,7 +58,7 @@ public class XfamFetcherTest
   @Test(groups = { "External" })
   public void testPfamFullAndSeed() throws Exception
   {
-    PfamFull pff = new PfamFull();
+    Pfam pff = new PfamFull();
     PfamSeed pfseed = new PfamSeed();
 
     AlignmentI fullpf = pff.getSequenceRecords(pff.getTestQuery());
index b92766e..75c1413 100644 (file)
@@ -83,103 +83,103 @@ public class SiftsClientTest
   @BeforeTest(alwaysRun = true)
   public void populateExpectedMapping() throws SiftsException
   {
-    expectedMapping.put(51, new int[] { 1, 2 });
-    expectedMapping.put(52, new int[] { 2, 7 });
-    expectedMapping.put(53, new int[] { 3, 12 });
-    expectedMapping.put(54, new int[] { 4, 24 });
-    expectedMapping.put(55, new int[] { 5, 33 });
-    expectedMapping.put(56, new int[] { 6, 40 });
-    expectedMapping.put(57, new int[] { 7, 47 });
-    expectedMapping.put(58, new int[] { 8, 55 });
-    expectedMapping.put(59, new int[] { 9, 62 });
-    expectedMapping.put(60, new int[] { 10, 69 });
-    expectedMapping.put(61, new int[] { 11, 76 });
-    expectedMapping.put(62, new int[] { 12, 83 });
-    expectedMapping.put(63, new int[] { 13, 87 });
-    expectedMapping.put(64, new int[] { 14, 95 });
-    expectedMapping.put(65, new int[] { 15, 102 });
-    expectedMapping.put(66, new int[] { 16, 111 });
-    expectedMapping.put(67, new int[] { 17, 122 });
-    expectedMapping.put(68, new int[] { 18, 131 });
-    expectedMapping.put(69, new int[] { 19, 137 });
-    expectedMapping.put(70, new int[] { 20, 144 });
-    expectedMapping.put(71, new int[] { 21, 152 });
-    expectedMapping.put(72, new int[] { 22, 160 });
-    expectedMapping.put(73, new int[] { 23, 167 });
-    expectedMapping.put(74, new int[] { 24, 179 });
-    expectedMapping.put(75, new int[] { 25, 187 });
-    expectedMapping.put(76, new int[] { 26, 195 });
-    expectedMapping.put(77, new int[] { 27, 203 });
-    expectedMapping.put(78, new int[] { 28, 208 });
-    expectedMapping.put(79, new int[] { 29, 213 });
-    expectedMapping.put(80, new int[] { 30, 222 });
-    expectedMapping.put(81, new int[] { 31, 231 });
-    expectedMapping.put(82, new int[] { 32, 240 });
-    expectedMapping.put(83, new int[] { 33, 244 });
-    expectedMapping.put(84, new int[] { 34, 252 });
-    expectedMapping.put(85, new int[] { 35, 260 });
-    expectedMapping.put(86, new int[] { 36, 268 });
-    expectedMapping.put(87, new int[] { 37, 275 });
-    expectedMapping.put(88, new int[] { 38, 287 });
-    expectedMapping.put(89, new int[] { 39, 293 });
-    expectedMapping.put(90, new int[] { 40, 299 });
-    expectedMapping.put(91, new int[] { 41, 310 });
-    expectedMapping.put(92, new int[] { 42, 315 });
-    expectedMapping.put(93, new int[] { 43, 319 });
-    expectedMapping.put(94, new int[] { 44, 325 });
-    expectedMapping.put(95, new int[] { 45, 331 });
-    expectedMapping.put(96, new int[] { 46, 337 });
-    expectedMapping.put(97, new int[] { 47, 343 });
-    expectedMapping.put(98, new int[] { 48, 349 });
-    expectedMapping.put(99, new int[] { 49, 354 });
-    expectedMapping.put(100, new int[] { 50, 358 });
-    expectedMapping.put(101, new int[] { 51, 367 });
-    expectedMapping.put(102, new int[] { 52, 375 });
-    expectedMapping.put(103, new int[] { 53, 384 });
-    expectedMapping.put(104, new int[] { 54, 391 });
-    expectedMapping.put(105, new int[] { 55, 395 });
-    expectedMapping.put(106, new int[] { 56, 401 });
-    expectedMapping.put(107, new int[] { 57, 409 });
-    expectedMapping.put(108, new int[] { 58, 417 });
-    expectedMapping.put(109, new int[] { 59, 426 });
-    expectedMapping.put(110, new int[] { 60, 434 });
-    expectedMapping.put(111, new int[] { 61, 442 });
-    expectedMapping.put(112, new int[] { 62, 451 });
-    expectedMapping.put(113, new int[] { 63, 457 });
-    expectedMapping.put(114, new int[] { 64, 468 });
-    expectedMapping.put(115, new int[] { 65, 476 });
-    expectedMapping.put(116, new int[] { 66, 484 });
-    expectedMapping.put(117, new int[] { 67, 492 });
-    expectedMapping.put(118, new int[] { 68, 500 });
-    expectedMapping.put(119, new int[] { 69, 509 });
-    expectedMapping.put(120, new int[] { 70, 517 });
-    expectedMapping.put(121, new int[] { 71, 525 });
-    expectedMapping.put(122, new int[] { 72, 534 });
-    expectedMapping.put(123, new int[] { 73, 538 });
-    expectedMapping.put(124, new int[] { 74, 552 });
-    expectedMapping.put(125, new int[] { 75, 559 });
-    expectedMapping.put(126, new int[] { 76, 567 });
-    expectedMapping.put(127, new int[] { 77, 574 });
-    expectedMapping.put(128, new int[] { 78, 580 });
-    expectedMapping.put(129, new int[] { 79, 585 });
-    expectedMapping.put(130, new int[] { 80, 590 });
-    expectedMapping.put(131, new int[] { 81, 602 });
-    expectedMapping.put(132, new int[] { 82, 609 });
-    expectedMapping.put(133, new int[] { 83, 616 });
-    expectedMapping.put(134, new int[] { 84, 622 });
-    expectedMapping.put(135, new int[] { 85, 630 });
-    expectedMapping.put(136, new int[] { 86, 637 });
-    expectedMapping.put(137, new int[] { 87, 644 });
-    expectedMapping.put(138, new int[] { 88, 652 });
-    expectedMapping.put(139, new int[] { 89, 661 });
-    expectedMapping.put(140, new int[] { 90, 668 });
-    expectedMapping.put(141, new int[] { 91, 678 });
-    expectedMapping.put(142, new int[] { 92, 687 });
-    expectedMapping.put(143, new int[] { 93, 696 });
-    expectedMapping.put(144, new int[] { 94, 705 });
-    expectedMapping.put(145, new int[] { 95, 714 });
-    expectedMapping.put(146, new int[] { 96, 722 });
-    expectedMapping.put(147, new int[] { 97, 729 });
+    expectedMapping.put(51, new int[] { 1, 2, 1 });
+    expectedMapping.put(52, new int[] { 2, 7, 2 });
+    expectedMapping.put(53, new int[] { 3, 12, 3 });
+    expectedMapping.put(54, new int[] { 4, 24, 4 });
+    expectedMapping.put(55, new int[] { 5, 33, 5 });
+    expectedMapping.put(56, new int[] { 6, 40, 6 });
+    expectedMapping.put(57, new int[] { 7, 47, 7 });
+    expectedMapping.put(58, new int[] { 8, 55, 8 });
+    expectedMapping.put(59, new int[] { 9, 62, 9 });
+    expectedMapping.put(60, new int[] { 10, 69, 10 });
+    expectedMapping.put(61, new int[] { 11, 76, 11 });
+    expectedMapping.put(62, new int[] { 12, 83, 12 });
+    expectedMapping.put(63, new int[] { 13, 87, 13 });
+    expectedMapping.put(64, new int[] { 14, 95, 14 });
+    expectedMapping.put(65, new int[] { 15, 102, 15 });
+    expectedMapping.put(66, new int[] { 16, 111, 16 });
+    expectedMapping.put(67, new int[] { 17, 122, 17 });
+    expectedMapping.put(68, new int[] { 18, 131, 18 });
+    expectedMapping.put(69, new int[] { 19, 137, 19 });
+    expectedMapping.put(70, new int[] { 20, 144, 20 });
+    expectedMapping.put(71, new int[] { 21, 152, 21 });
+    expectedMapping.put(72, new int[] { 22, 160, 22 });
+    expectedMapping.put(73, new int[] { 23, 167, 23 });
+    expectedMapping.put(74, new int[] { 24, 179, 24 });
+    expectedMapping.put(75, new int[] { 25, 187, 25 });
+    expectedMapping.put(76, new int[] { 26, 195, 26 });
+    expectedMapping.put(77, new int[] { 27, 203, 27 });
+    expectedMapping.put(78, new int[] { 28, 208, 28 });
+    expectedMapping.put(79, new int[] { 29, 213, 29 });
+    expectedMapping.put(80, new int[] { 30, 222, 30 });
+    expectedMapping.put(81, new int[] { 31, 231, 31 });
+    expectedMapping.put(82, new int[] { 32, 240, 32 });
+    expectedMapping.put(83, new int[] { 33, 244, 33 });
+    expectedMapping.put(84, new int[] { 34, 252, 34 });
+    expectedMapping.put(85, new int[] { 35, 260, 35 });
+    expectedMapping.put(86, new int[] { 36, 268, 36 });
+    expectedMapping.put(87, new int[] { 37, 275, 37 });
+    expectedMapping.put(88, new int[] { 38, 287, 38 });
+    expectedMapping.put(89, new int[] { 39, 293, 39 });
+    expectedMapping.put(90, new int[] { 40, 299, 40 });
+    expectedMapping.put(91, new int[] { 41, 310, 41 });
+    expectedMapping.put(92, new int[] { 42, 315, 42 });
+    expectedMapping.put(93, new int[] { 43, 319, 43 });
+    expectedMapping.put(94, new int[] { 44, 325, 44 });
+    expectedMapping.put(95, new int[] { 45, 331, 45 });
+    expectedMapping.put(96, new int[] { 46, 337, 46 });
+    expectedMapping.put(97, new int[] { 47, 343, 47 });
+    expectedMapping.put(98, new int[] { 48, 349, 48 });
+    expectedMapping.put(99, new int[] { 49, 354, 49 });
+    expectedMapping.put(100, new int[] { 50, 358, 50 });
+    expectedMapping.put(101, new int[] { 51, 367, 51 });
+    expectedMapping.put(102, new int[] { 52, 375, 52 });
+    expectedMapping.put(103, new int[] { 53, 384, 53 });
+    expectedMapping.put(104, new int[] { 54, 391, 54 });
+    expectedMapping.put(105, new int[] { 55, 395, 55 });
+    expectedMapping.put(106, new int[] { 56, 401, 56 });
+    expectedMapping.put(107, new int[] { 57, 409, 57 });
+    expectedMapping.put(108, new int[] { 58, 417, 58 });
+    expectedMapping.put(109, new int[] { 59, 426, 59 });
+    expectedMapping.put(110, new int[] { 60, 434, 60 });
+    expectedMapping.put(111, new int[] { 61, 442, 61 });
+    expectedMapping.put(112, new int[] { 62, 451, 62 });
+    expectedMapping.put(113, new int[] { 63, 457, 63 });
+    expectedMapping.put(114, new int[] { 64, 468, 64 });
+    expectedMapping.put(115, new int[] { 65, 476, 65 });
+    expectedMapping.put(116, new int[] { 66, 484, 66 });
+    expectedMapping.put(117, new int[] { 67, 492, 67 });
+    expectedMapping.put(118, new int[] { 68, 500, 68 });
+    expectedMapping.put(119, new int[] { 69, 509, 69 });
+    expectedMapping.put(120, new int[] { 70, 517, 70 });
+    expectedMapping.put(121, new int[] { 71, 525, 71 });
+    expectedMapping.put(122, new int[] { 72, 534, 72 });
+    expectedMapping.put(123, new int[] { 73, 538, 73 });
+    expectedMapping.put(124, new int[] { 74, 552, 74 });
+    expectedMapping.put(125, new int[] { 75, 559, 75 });
+    expectedMapping.put(126, new int[] { 76, 567, 76 });
+    expectedMapping.put(127, new int[] { 77, 574, 77 });
+    expectedMapping.put(128, new int[] { 78, 580, 78 });
+    expectedMapping.put(129, new int[] { 79, 585, 79 });
+    expectedMapping.put(130, new int[] { 80, 590, 80 });
+    expectedMapping.put(131, new int[] { 81, 602, 81 });
+    expectedMapping.put(132, new int[] { 82, 609, 82 });
+    expectedMapping.put(133, new int[] { 83, 616, 83 });
+    expectedMapping.put(134, new int[] { 84, 622, 84 });
+    expectedMapping.put(135, new int[] { 85, 630, 85 });
+    expectedMapping.put(136, new int[] { 86, 637, 86 });
+    expectedMapping.put(137, new int[] { 87, 644, 87 });
+    expectedMapping.put(138, new int[] { 88, 652, 88 });
+    expectedMapping.put(139, new int[] { 89, 661, 89 });
+    expectedMapping.put(140, new int[] { 90, 668, 90 });
+    expectedMapping.put(141, new int[] { 91, 678, 91 });
+    expectedMapping.put(142, new int[] { 92, 687, 92 });
+    expectedMapping.put(143, new int[] { 93, 696, 93 });
+    expectedMapping.put(144, new int[] { 94, 705, 94 });
+    expectedMapping.put(145, new int[] { 95, 714, 95 });
+    expectedMapping.put(146, new int[] { 96, 722, 96 });
+    expectedMapping.put(147, new int[] { 97, 729, 97 });
   }
 
   @BeforeTest(alwaysRun = true)
@@ -311,7 +311,7 @@ public class SiftsClientTest
     atom.atomIndex = 7;
     atoms.add(atom);
     int actualAtomIndex = siftsClient.getAtomIndex(1, atoms);
-    Assert.assertEquals(actualAtomIndex, -1);
+    Assert.assertEquals(actualAtomIndex, siftsClient.UNASSIGNED);
     actualAtomIndex = siftsClient.getAtomIndex(43, atoms);
     Assert.assertEquals(actualAtomIndex, 7);
   }
index a649cb4..ced9ac0 100755 (executable)
@@ -314,13 +314,13 @@ and any path to a file to save to the file]]></string>
                                                                <string><![CDATA[logo.gif]]></string>
                                                        </property>
                                                        <property name="smallIconPath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/resources/images/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/resources/images/]]></string>
                                                        </property>
                                                        <property name="largeIconPath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/resources/images/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/resources/images/]]></string>
                                                        </property>
                                                        <property name="macOSXIconPath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/utils/InstallAnywhere/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/utils/InstallAnywhere/]]></string>
                                                        </property>
                                                        <property name="macOSXIconName">
                                                                <string><![CDATA[mac_logo.icns]]></string>
@@ -367,7 +367,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/dist/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/dist/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -419,7 +419,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -471,7 +471,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -523,7 +523,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -575,7 +575,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -627,7 +627,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -679,7 +679,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -731,7 +731,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -783,7 +783,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -835,7 +835,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -887,7 +887,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -939,7 +939,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -991,7 +991,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1043,7 +1043,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1095,7 +1095,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1147,7 +1147,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1199,7 +1199,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1251,7 +1251,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1303,7 +1303,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1355,7 +1355,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1407,7 +1407,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1459,7 +1459,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1511,7 +1511,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1563,7 +1563,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1615,7 +1615,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1667,7 +1667,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1719,7 +1719,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1771,7 +1771,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1823,7 +1823,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1875,7 +1875,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1927,7 +1927,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -1979,7 +1979,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2031,7 +2031,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2083,7 +2083,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2135,7 +2135,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2187,7 +2187,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2239,7 +2239,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2291,7 +2291,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2320,6 +2320,58 @@ and any path to a file to save to the file]]></string>
                                                </object>
                                        </method>
                                        <method name="addElement">
+            <object class="com.zerog.ia.installer.actions.InstallZipfile" objectID="9a1f46efeef911a">
+              <property name="belongsToUninstallPhase">
+                <boolean>false</boolean>
+              </property>
+              <property name="rollbackEnabledCancel">
+                <boolean>true</boolean>
+              </property>
+              <property name="rollbackEnabledError">
+                <boolean>true</boolean>
+              </property>
+              <property name="ruleExpression">
+                <string><![CDATA[]]></string>
+              </property>
+              <property name="unixPermissions">
+                <string><![CDATA[664]]></string>
+              </property>
+              <property name="sourceName">
+                <string><![CDATA[VAqua4.jar]]></string>
+              </property>
+              <property name="overrideUnixPermissions">
+                <boolean>false</boolean>
+              </property>
+              <property name="sourcePath">
+                <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
+              </property>
+              <property name="shouldUninstall">
+                <boolean>true</boolean>
+              </property>
+              <property name="rollbackEnabledCancel">
+                <boolean>true</boolean>
+              </property>
+              <property name="rollbackEnabledError">
+                <boolean>true</boolean>
+              </property>
+              <property name="destinationName">
+                <string><![CDATA[VAqua4.jar]]></string>
+              </property>
+              <property name="fileSize">
+                <long>1355141</long>
+              </property>
+              <property name="macBinary">
+                <boolean>false</boolean>
+              </property>
+              <property name="targetCheckKind">
+                <int>0</int>
+              </property>
+              <property name="ruleExpression">
+                <string><![CDATA[]]></string>
+              </property>
+            </object>
+          </method>
+          <method name="addElement">
                                                <object class="com.zerog.ia.installer.actions.InstallZipfile" objectID="1936efeefab93">
                                                        <property name="belongsToUninstallPhase">
                                                                <boolean>false</boolean>
@@ -2343,7 +2395,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2395,7 +2447,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2447,7 +2499,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2499,7 +2551,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2551,7 +2603,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2603,7 +2655,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2655,7 +2707,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2707,7 +2759,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2759,7 +2811,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2811,7 +2863,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2863,7 +2915,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2915,7 +2967,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>false</boolean>
                                                        </property>
                                                        <property name="sourcePath">
-                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib/]]></string>
                                                        </property>
                                                        <property name="shouldUninstall">
                                                                <boolean>true</boolean>
@@ -2949,7 +3001,7 @@ and any path to a file to save to the file]]></string>
                                <string><![CDATA[The installer cannot run on your configuration. It will now quit.]]></string>
                        </property>
                        <property name="userSplashPath">
-                               <string><![CDATA[/home/cruisecontrol/jalview/utils/InstallAnywhere/]]></string>
+                               <string><![CDATA[/homes/cruisecontrol/jalview/utils/InstallAnywhere/]]></string>
                        </property>
                        <property name="userSplashName">
                                <string><![CDATA[jalview.gif]]></string>
@@ -4242,7 +4294,7 @@ Press "Done" to quit the installer.]]></string>
                                                                <boolean>true</boolean>
                                                        </property>
                                                        <property name="buildOutputLocation">
-                                                               <string><![CDATA[/opt/homes/cruisecontrol/live/cruisecontrol/checkout/next-release-jalview/utils/InstallAnywhere/Jalview_Build_Output]]></string>
+                                                               <string><![CDATA[/homes/cruisecontrol/jalview/utils/InstallAnywhere/Jalview_Build_Output]]></string>
                                                        </property>
                                                        <property name="relatedProjectSettings">
                                                                <object class="com.zerog.ia.installer.RelatedProjectSettings" objectID="97f2363da6ac">
@@ -5406,7 +5458,7 @@ Press "Done" to quit the installer.]]></string>
                                                <boolean>true</boolean>
                                        </property>
                                        <property name="backgroundImagePath">
-                                               <string><![CDATA[/home/cruisecontrol/jalview/utils/InstallAnywhere/]]></string>
+                                               <string><![CDATA[/homes/cruisecontrol/jalview/utils/InstallAnywhere/]]></string>
                                        </property>
                                        <property name="backgroundImageName">
                                                <string><![CDATA[align.gif]]></string>
@@ -7230,6 +7282,7 @@ and any path to a file to read from that file]]></string>
                                                                                <object refID="24485f8ca673"/>
                                                                                <object refID="24485f8ba674"/>
                                                                                <object refID="24485f8ca674"/>
+                                                                               <object refID="9a1f46efeef911a"/>
                                                                                <object class="com.zerog.ia.installer.actions.CreateShortcut" objectID="3cd8e2ffa672">
                                                                                        <property name="belongsToUninstallPhase">
                                                                                                <boolean>false</boolean>
@@ -7291,7 +7344,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7332,7 +7385,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/utils/InstallAnywhere/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/utils/InstallAnywhere/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7373,7 +7426,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/doc/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/doc/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7445,7 +7498,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7486,7 +7539,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7527,7 +7580,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7568,7 +7621,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7609,7 +7662,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7659,7 +7712,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7700,7 +7753,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7741,7 +7794,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7782,7 +7835,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7823,7 +7876,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7864,7 +7917,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7905,7 +7958,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7946,7 +7999,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/lib]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -7981,6 +8034,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <object refID="24485f8ca673"/>
                                                                                                <object refID="24485f8ba674"/>
                                                                                                <object refID="24485f8ca674"/>
+                                                                                               <object refID="9a1f46efeef911a"/>
                                                                                                <object refID="b1a16838a449"/>
                                                                                                <object refID="b1a16839a449"/>
                                                                                                <object refID="495aeddb8b3d"/>
@@ -8036,7 +8090,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples/groovy]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples/groovy]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -8077,7 +8131,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/home/cruisecontrol/jalview/examples]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/examples]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>false</boolean>
@@ -8133,7 +8187,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/opt/homes/cruisecontrol/live/cruisecontrol/checkout/next-release-jalview/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -8174,7 +8228,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <boolean>false</boolean>
                                                                                        </property>
                                                                                        <property name="sourcePath">
-                                                                                               <string><![CDATA[/opt/homes/cruisecontrol/live/cruisecontrol/checkout/next-release-jalview/]]></string>
+                                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/]]></string>
                                                                                        </property>
                                                                                        <property name="shouldUninstall">
                                                                                                <boolean>true</boolean>
@@ -8359,7 +8413,7 @@ and any path to a file to read from that file]]></string>
                                                                                <string><![CDATA[]]></string>
                                                                        </property>
                                                                        <property name="imagePath">
-                                                                               <string><![CDATA[/home/cruisecontrol/jalview/utils/InstallAnywhere/]]></string>
+                                                                               <string><![CDATA[/homes/cruisecontrol/jalview/utils/InstallAnywhere/]]></string>
                                                                        </property>
                                                                        <property name="imageName">
                                                                                <string><![CDATA[bartonGroup.gif]]></string>
index 6310934..6f0115e 100644 (file)
@@ -20,7 +20,7 @@
 <project name="jalviewInstallAnywhere" default="build" basedir=".">
   <property name="IA_LOCATION" value="/home/cruisecontrol/InstallAnywhere 2013/"/>
   <property name="IA_PROJECT" location="Jalview.iap_xml"/>
-  <property name="ABS_PATH" value="/home/cruisecontrol/jalview"/> <!-- \/utils\/InstallAnywhere"/> --> <!--/home/cruisecontrol/jalview"/> -->
+  <property name="ABS_PATH" value="/homes/cruisecontrol/jalview"/> <!-- \/utils\/InstallAnywhere"/> --> <!--/home/cruisecontrol/jalview"/> -->
   <!-- location of top level of jalview distribution directory -->
   <property name="CUR_PATH" location="../../." />
   <property name="USER_HOME" location="~" />
diff --git a/utils/proguard.jar b/utils/proguard.jar
deleted file mode 100755 (executable)
index dfb7f29..0000000
Binary files a/utils/proguard.jar and /dev/null differ
diff --git a/utils/proguard_5.3.3.jar b/utils/proguard_5.3.3.jar
new file mode 100755 (executable)
index 0000000..08f4a4c
Binary files /dev/null and b/utils/proguard_5.3.3.jar differ