Merge remote-tracking branch 'origin/features/JAL-2609fastPaintWrapped'
authorkiramt <k.mourao@dundee.ac.uk>
Tue, 10 Oct 2017 13:34:50 +0000 (14:34 +0100)
committerkiramt <k.mourao@dundee.ac.uk>
Tue, 10 Oct 2017 13:34:50 +0000 (14:34 +0100)
into bug/JAL-2690attemptedmerge

Conflicts:
src/jalview/gui/SeqCanvas.java

91 files changed:
.ant-targets-build.xml [new file with mode: 0644]
.gitignore
RELEASE
benchmarking/.classpath [new file with mode: 0644]
benchmarking/.gitignore [new file with mode: 0644]
benchmarking/.settings/org.eclipse.core.resources.prefs [new file with mode: 0644]
benchmarking/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
benchmarking/.settings/org.eclipse.m2e.core.prefs [new file with mode: 0644]
benchmarking/README [new file with mode: 0644]
benchmarking/pom.xml [new file with mode: 0644]
benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java [new file with mode: 0644]
build.xml
help/help.jhm
help/helpTOC.xml
help/html/features/overview.gif [deleted file]
help/html/features/overview.html
help/html/features/overview.png [new file with mode: 0644]
help/html/features/preferences.html
help/html/releases.html
help/html/whatsNew.html
lib/jabaws-min-client-2.2.0.jar
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/api/AlignmentColsCollectionI.java
src/jalview/api/AlignmentRowsCollectionI.java
src/jalview/api/RendererListenerI.java [new file with mode: 0644]
src/jalview/appletgui/OverviewCanvas.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/SequenceRenderer.java
src/jalview/commands/RemoveGapColCommand.java
src/jalview/commands/RemoveGapsCommand.java
src/jalview/datamodel/AlignmentI.java
src/jalview/datamodel/AllColsCollection.java
src/jalview/datamodel/AllRowsCollection.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/datamodel/VisibleColsCollection.java
src/jalview/datamodel/VisibleRowsCollection.java
src/jalview/datamodel/features/SequenceFeatures.java
src/jalview/datamodel/xdb/embl/EmblEntry.java
src/jalview/ext/ensembl/EnsemblGene.java
src/jalview/ext/ensembl/EnsemblSeqProxy.java
src/jalview/fts/service/pdb/PDBFTSRestClient.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/AnnotationColourChooser.java
src/jalview/gui/CrossRefAction.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/FontChooser.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/ProgressPanel.java [new file with mode: 0644]
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SequenceRenderer.java
src/jalview/gui/SplitFrame.java
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GStructureChooser.java
src/jalview/renderer/OverviewRenderer.java
src/jalview/renderer/OverviewResColourFinder.java [new file with mode: 0644]
src/jalview/renderer/ResidueColourFinder.java [new file with mode: 0644]
src/jalview/renderer/ResidueShader.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/schemes/ColourSchemeI.java
src/jalview/schemes/ResidueColourScheme.java
src/jalview/schemes/UserColourScheme.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/structures/models/AAStructureBindingModel.java
src/jalview/util/Platform.java
src/jalview/util/UrlConstants.java
src/jalview/ws/dbsources/EmblXmlSource.java
src/jalview/ws/dbsources/Uniprot.java
src/jalview/ws/ebi/EBIFetchClient.java
src/jalview/ws/seqfetcher/ASequenceFetcher.java
test/jalview/datamodel/SequenceTest.java
test/jalview/datamodel/features/SequenceFeaturesTest.java
test/jalview/gui/SequenceRendererTest.java
test/jalview/io/CrossRef2xmlTests.java
test/jalview/io/FeaturesFileTest.java
test/jalview/renderer/OverviewResColourFinderTest.java [new file with mode: 0644]
test/jalview/renderer/ResidueColourFinderTest.java [new file with mode: 0644]
test/jalview/renderer/ResidueShaderTest.java
test/jalview/structures/models/AAStructureBindingModelTest.java
test/jalview/ws/ebi/EBIFetchClientTest.java
utils/InstallAnywhere/Jalview.iap_xml
utils/checkstyle/import-control.xml

diff --git a/.ant-targets-build.xml b/.ant-targets-build.xml
new file mode 100644 (file)
index 0000000..15432a1
--- /dev/null
@@ -0,0 +1,31 @@
+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 bfd5da9..2a55560 100644 (file)
@@ -11,3 +11,5 @@
 .gitattributes
 TESTNG
 /jalviewApplet.jar
+/benchmarking/lib
+*.class
\ No newline at end of file
diff --git a/RELEASE b/RELEASE
index 0229909..cecefec 100644 (file)
--- a/RELEASE
+++ b/RELEASE
@@ -1,2 +1,2 @@
-jalview.release=releases/Release_2_10_2_Branch
-jalview.version=2.10.2b1
+jalview.release=releases/Release_2_10_2b1_Branch
+jalview.version=2.10.2b2
diff --git a/benchmarking/.classpath b/benchmarking/.classpath
new file mode 100644 (file)
index 0000000..131ff24
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java">
+               <attributes>
+                       <attribute name="optional" value="true"/>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/java">
+               <attributes>
+                       <attribute name="optional" value="true"/>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+               <attributes>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+               <attributes>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry combineaccessrules="false" kind="src" path="/Jalview Release 2.7"/>
+       <classpathentry kind="lib" path="/Jalview Release 2.7/jalviewApplet.jar"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/benchmarking/.gitignore b/benchmarking/.gitignore
new file mode 100644 (file)
index 0000000..f4da43d
--- /dev/null
@@ -0,0 +1,6 @@
+/target/
+/bin
+/results
+*.log
+*.json
+*~
diff --git a/benchmarking/.settings/org.eclipse.core.resources.prefs b/benchmarking/.settings/org.eclipse.core.resources.prefs
new file mode 100644 (file)
index 0000000..e9441bb
--- /dev/null
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/benchmarking/.settings/org.eclipse.jdt.core.prefs b/benchmarking/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..714351a
--- /dev/null
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/benchmarking/.settings/org.eclipse.m2e.core.prefs b/benchmarking/.settings/org.eclipse.m2e.core.prefs
new file mode 100644 (file)
index 0000000..f897a7f
--- /dev/null
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/benchmarking/README b/benchmarking/README
new file mode 100644 (file)
index 0000000..60b94a9
--- /dev/null
@@ -0,0 +1,25 @@
+To set up benchmarking:
+
+1. In the jalview directory run 
+  ant makedist
+
+This builds a jalview.jar file and puts it into dist/
+
+2. Make a lib directory in benchmarking/ if not already present.
+
+3. Purge any previous maven dependencies:
+   mvn dependency:purge-local-repository -DactTransitively=false -DreResolve=false
+
+4. Run
+  mvn install:install-file -Dfile=../dist/jalview.jar -DgroupId=jalview.org -DartifactId=jalview -Dversion=1.0 -Dpackaging=jar -DlocalRepositoryPath=lib
+
+to install the jalview.jar file in the local maven repository. The pom.xml in the benchmarking references this installation, so if you change the names the pom.xml file will also need to be updated.
+
+5. Build and run jmh benchmarking. In the benchmarking directory:
+  mvn clean install
+  java -jar target/benchmarks.jar
+  
+  To get JSON output instead use:
+  java -jar target/benchmarks.jar -rf json
+  
+  JSON output can be viewed quickly by drag-dropping on http://jmh.morethan.io/
\ No newline at end of file
diff --git a/benchmarking/pom.xml b/benchmarking/pom.xml
new file mode 100644 (file)
index 0000000..3bced18
--- /dev/null
@@ -0,0 +1,183 @@
+<!--
+Copyright (c) 2014, Oracle America, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Oracle nor the names of its contributors may be used
+   to endorse or promote products derived from this software without
+   specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.jalview</groupId>
+    <artifactId>benchmarking</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>JMH benchmark sample: Java</name>
+
+    <!--
+       This is the demo/sample template build script for building Java benchmarks with JMH.
+       Edit as needed.
+    -->
+
+       <repositories>
+               <repository>
+               <id>lib</id>
+               <url>file://${project.basedir}/lib</url>
+               </repository>
+       </repositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-core</artifactId>
+            <version>${jmh.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-generator-annprocess</artifactId>
+            <version>${jmh.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+               <groupId>jalview.org</groupId>
+               <artifactId>jalview</artifactId>
+               <version>1.0</version>
+        </dependency>
+    </dependencies>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+        <!--
+            JMH version to use with this project.
+          -->
+        <jmh.version>1.19</jmh.version>
+
+        <!--
+            Java source/target to use for compilation.
+          -->
+        <javac.target>1.8</javac.target>
+
+        <!--
+            Name of the benchmark Uber-JAR to generate.
+          -->
+        <uberjar.name>benchmarks</uberjar.name>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.1</version>
+                <configuration>
+                    <compilerVersion>${javac.target}</compilerVersion>
+                    <source>${javac.target}</source>
+                    <target>${javac.target}</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>2.2</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <finalName>${uberjar.name}</finalName>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>org.openjdk.jmh.Main</mainClass>
+                                </transformer>
+                            </transformers>
+                            <filters>
+                                <filter>
+                                    <!--
+                                        Shading signed JARs will fail without this.
+                                        http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
+                                    -->
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA</exclude>
+                                        <exclude>META-INF/*.RSA</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <artifactId>maven-clean-plugin</artifactId>
+                    <version>2.5</version>
+                </plugin>
+                <plugin>
+                    <artifactId>maven-deploy-plugin</artifactId>
+                    <version>2.8.1</version>
+                </plugin>
+                <plugin>
+                    <artifactId>maven-install-plugin</artifactId>
+                    <version>2.5.1</version>
+                </plugin>
+                <plugin>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>2.4</version>
+                </plugin>
+                <plugin>
+                    <artifactId>maven-javadoc-plugin</artifactId>
+                    <version>2.9.1</version>
+                </plugin>
+                <plugin>
+                    <artifactId>maven-resources-plugin</artifactId>
+                    <version>2.6</version>
+                </plugin>
+                <plugin>
+                    <artifactId>maven-site-plugin</artifactId>
+                    <version>3.3</version>
+                </plugin>
+                <plugin>
+                    <artifactId>maven-source-plugin</artifactId>
+                    <version>2.2.1</version>
+                </plugin>
+                <plugin>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>2.17</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+</project>
diff --git a/benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java b/benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java
new file mode 100644 (file)
index 0000000..eb35e3b
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package 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.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.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 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()
+               {
+                       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.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;
+    }
+       
+       
+}
\ No newline at end of file
index 9e21593..f39fdf3 100755 (executable)
--- a/build.xml
+++ b/build.xml
           <offline_allowed />
         </information>
         <resources>
-          <j2se version="9+" />
+          <j2se version="1.7+" />
           <jar main="true" href="jalview.jar"/>
           <fileset dir="${packageDir}">
             <exclude name="jalview.jar" />
     </presetdef>
 
     <jnlpf toFile="${jnlpFile}" />
-    <!-- add a j2se entry for java 9 -->
-    <replace file="${jnlpFile}" value="j2se version=&quot;7+&quot; initial-heap-size=&quot;${inih}&quot; max-heap-size=&quot;${maxh}&quot; java-vm-args=&quot;--add-modules=java.se.ee&quot;">
-          <replacetoken>j2se version="9+"</replacetoken>
+    <!-- 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&quot;">
+          <replacetoken>j2se version="1.7+"</replacetoken>
            
         </replace>
   </target>
index c12436f..010bca8 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.2b1"/>
+   <mapID target="release" url="html/releases.html#Jalview.2.10.3"/>
    <mapID target="alannotation" url="html/features/annotation.html"/>
    <mapID target="keys" url="html/keys.html"/>
    <mapID target="newkeys" url="html/features/newkeystrokes.html"/>
    <mapID target="printSetupIcon" url="icons/setup.png" />
    
    <mapID target="overview" url="html/features/overview.html" />
+   <mapID target="overviewprefs" url="html/features/preferences.html#overview" />
 </map>
index f3311a7..4636ea3 100755 (executable)
@@ -28,6 +28,7 @@
                                <tocitem text="Groovy Features Counter example" target="groovy.featurescounter"/>
                                <tocitem text="Custom Colourschemes in Groovy" target="groovy.colours"/>
                                <tocitem text="Omit hidden regions in Overview" target="overview"/>
+                               <tocitem text="Show gaps as grey in overview" target="overviewprefs"/>
                                <tocitem text="identifers.org for URL Links" target="linksprefs" />
                                <tocitem text="New features in Split Frame View" target="splitframe.mirrorfonts" />
                </tocitem>
diff --git a/help/html/features/overview.gif b/help/html/features/overview.gif
deleted file mode 100755 (executable)
index b5b1bc5..0000000
Binary files a/help/html/features/overview.gif and /dev/null differ
index ac5aecb..6401fdd 100755 (executable)
     <strong>View&#8594;Overview window</strong>
   </p>
   <p>Select the overview window menu item to get a navigable image
-    of the whole alignment.</p>
+    of the whole alignment. By default, gaps are shown as dark grey, in
+    the overview (since 2.10.2b1).</p>
   <p>The red box indicates the currently viewed region of the
     alignment, this may be moved by clicking and dragging with the
     mouse.</p>
   <p>Click anywhere else in the overview to centre the view on that
     position</p>
-    <p></p>
+  <p></p>
   <p>
     <strong>Hiding hidden regions in the overview</strong>
   </p>
     Hidden sequences and columns are by default shown as dark-grey rows
     and columns in the overview. Hidden regions can also be omitted
     entirely (to make the Overview 'WYSIWIG') by <strong>Right-clicking</strong>
-    (or CMD-Clicking) to open the overview's popup menu.<br />
-    <br /> <em>The option to include/exclude hidden regions in the
+    (or CMD-Clicking) to open the overview's popup menu.<br /> <br />
+    <em>The option to include/exclude hidden regions in the
       overview was introduced in Jalview 2.10.2</em>.
   
   <p>
-    <img src="overview.gif" width="407" height="137">
+    <img src="overview.png" width="800" height="196" alt="1hdc.pfam example alignment">
   </p>
   <p>&nbsp;</p>
+  <em>Previous to 2.10.2b1, gaps were white, and sequences shown as
+    dark grey unless coloured.</em>
 </body>
 </html>
diff --git a/help/html/features/overview.png b/help/html/features/overview.png
new file mode 100644 (file)
index 0000000..d7aaa19
Binary files /dev/null and b/help/html/features/overview.png differ
index acd7ba6..b29b66b 100755 (executable)
@@ -41,6 +41,9 @@
         Preferences</a> tab allows you to configure default colourschemes
       for a new alignment window.
     </li>
+    <li>The <a href="#overview"><strong>&quot;Overview&quot;</strong>
+        Preferences</a> tab configures defaults for the overview window.
+    </li>
     <li>The <a href="#structure"><strong>&quot;Structure&quot;</strong>
         Preferences</a> tab allows you to configure options for obtaining
       and displaying structure information.
       by Annotation...</a> is selected from the alignment window's colours
     menu.
   </p>
+   <p>
+    <a name="overview"><strong>&quot;Overview&quot;
+        Preferences tab</strong>
+  </p>
+  <p>
+    <em>Use legacy gap colouring (gaps are white)</em> - when enabled,
+    Jalview's overview shows gaps as white, and sequences with no
+    colourscheme applied as grey.
+  </p>
+  <p>
+    <em>Show Hidden regions when opening overview</em> - default setting
+    for inclusion of hidden regions.
+  </p>
+  <p>
+    <em>Gap Colour</em> - When legacy gap colouring is not enabled, this
+    configures the default colour for gaps in the overview.
+  </p>
+  <p>
+    <em>Hidden Colour</em> - colour used to highlight regions in the
+    overview that are hidden in the alignment.
+  </p>
+  <p>
+    <em>Gap Colour</em> - The default colour scheme for a new alignment
+    window. If the chosen option is &quot;User Defined&quot; then the
+    last User Defined Colour loaded or saved via the User Defined
+    Colours panel will be loaded.
+  </p>
   <p>
     <a name="structure"><strong>&quot;Structure&quot;
         Preferences tab</strong></a><em> added in Jalview 2.8.2</em>
index c331ae1..8e55ca1 100755 (executable)
@@ -70,21 +70,128 @@ li:before {
     <tr>
       <td width="60" nowrap>
         <div align="center">
+          <strong><a name="Jalview.2.10.3">2.10.3</a><br />
+            <em>10/10/2017</em></strong>
+        </div>
+      </td>
+      <td><div align="left">
+          <em></em>
+          <ul>
+            <li>
+              <!-- JAL-2446 -->Faster and more efficient management and
+              rendering of sequence features
+            </li>
+          </ul>
+      </td>
+      <td><div align="left">
+          <em></em>
+          <ul>
+            <li><!-- JAL-2728 -->Protein annotation panel too high in CDS/Protein view
+            </li> 
+          </ul>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" nowrap>
+        <div align="center">
+          <strong><a name="Jalview.2.10.2b2">2.10.2b2</a><br />
+            <em>2/10/2017</em></strong>
+        </div>
+      </td>
+      <td><div align="left">
+          <em>New features in Jalview Desktop</em>
+          <ul>
+            <li>
+              <!-- JAL-2748 -->Uniprot Sequence Fetcher now uses web API at uniprot.org 
+            </li>
+            <li>  <!-- JAL-2745 -->HTTPS used for all connections to ebi.ac.uk 
+            </li>
+          </ul>
+        </div></td>
+      <td><div align="left">
+        </div></td>
+    </tr>
+    <tr>
+      <td width="60" nowrap>
+        <div align="center">
           <strong><a name="Jalview.2.10.2b1">2.10.2b1</a><br />
-            <em>TBA</em></strong>
+            <em>7/9/2017</em></strong>
         </div>
       </td>
       <td><div align="left">
           <em></em>
           <ul>
+            <li>
+              <!-- JAL-2588 -->Show gaps in overview window by colouring
+              in grey (sequences used to be coloured grey, and gaps were
+              white)
+            </li>
+            <li>
+              <!-- JAL-2588,JAL-2527 -->Overview tab in Jalview Desktop
+              Preferences
+            </li>
+            <li>
+              <!-- JAL-2587 -->Overview updates immediately on increase
+              in size and progress bar shown as higher resolution
+              overview is recalculated
+            </li>
+
           </ul>
         </div></td>
       <td><div align="left">
           <em></em>
           <ul>
+            <li>
+              <!-- JAL-2664 -->Overview window redraws every hidden
+              column region row by row
+            </li>
+            <li>
+              <!-- JAL-2681 -->duplicate protein sequences shown after
+              retrieving Ensembl crossrefs for sequences from Uniprot
+            </li>
+            <li>
+              <!-- JAL-2603 -->Overview window throws NPE if show boxes
+              format setting is unticked
+            </li>
+            <li>
+              <!-- JAL-2610 -->Groups are coloured wrongly in overview
+              if group has show boxes format setting unticked
+            </li>
+            <li>
+              <!-- JAL-2672,JAL-2665 -->Redraw problems when
+              autoscrolling whilst dragging current selection group to
+              include sequences and columns not currently displayed
+            </li>
+            <li>
+              <!-- JAL-2691 -->Not all chains are mapped when multimeric
+              assemblies are imported via CIF file
+            </li>
+            <li>
+              <!-- JAL-2704 -->Gap colour in custom colourscheme is not
+              displayed when threshold or conservation colouring is also
+              enabled.
+            </li>
+            <li>
+              <!-- JAL-2549 -->JABAWS 2.2 services report wrong JABAWS
+              server version
+            </li>
+            <li>
+              <!-- JAL-2673 -->Jalview continues to scroll after
+              dragging a selected region off the visible region of the
+              alignment
+            </li>
+            <li>
+              <!-- JAL-2724 -->Cannot apply annotation based
+              colourscheme to all groups in a view
+            </li>
+            <li>
+              <!-- JAL-2511 -->IDs don't line up with sequences
+              initially after font size change using the Font chooser or
+              middle-mouse zoom
+            </li>
           </ul>
         </div></td>
-    
+    </tr>
     <tr>
       <td width="60" nowrap>
         <div align="center">
index ff0fe35..3475012 100755 (executable)
 </head>
 <body>
   <p>
-  <strong>Jalview 2.10.2b1 bugfix release</strong>
+    <strong>What's new in Jalview 2.10.3 ?</strong>
   </p>
   <p>
-    This is patch release for 2.10.2. See the <a
-      href="releases.html#Jalview2.10.2b1">release notes</a>.
-  </p>
-  <p>
-    <strong>What's new in Jalview 2.10.2 ?</strong>
-  </p>
-  <p>
-    Version 2.10.2 was released in August 2017, and introduced new user
-    interface features, improved and more extensible tree and PCA
-    analysis, more robust 3D structure viewing with UCSF Chimera and an
-    updated service client for JABAWS. The full list of bug fixes and
-    new features can be found in the <a
-      href="releases.html#Jalview.2.10.2"> 2.10.2 Release Notes</a>, but
+    Version 2.10.3 is due for release in October 2017. The full list of
+    bug fixes and new features can be found in the <a
+      href="releases.html#Jalview.2.10.3"> 2.10.3 Release Notes</a>, but
     the highlights are below.
   </p>
-  <ul>
-    <li><strong>New dialog and faster and more
-        configurable Tree and PCA calculations</strong><br> Menu entries for
-      calculating PCA and different types of tree have been replaced by
-      a single <a href="calculations/calculations.html"><em>Calculations</em>
-        dialog box</a>. The underlying implementation for the PCA and tree
-      calculations have been made faster and more memory efficient.</li>
-    <li><strong>Extensible score models</strong><br />A new
-      framework has also been created for the score models used to
-      calculate distances between sequences and shade alignments. This
-      framework allows import of substitution matrices in NCBI and
-      AAIndex format.<br /> <strong>PCA Bug Fixes</strong>. Jalview's
-      implementation of PCA differed in its treatment of gaps and
-      non-standard residues. The BLOSUM62 matrix also included a typo
-      that affected results. See the <a
-      href="releases.html#2102scoremodelbugs">2.10.2 release note
-        about score model bugs</a> for details and how to reinstate legacy
-      behaviour.</li>
-    <li><strong>Update to JABAWS 2.2</strong><br />Jalview's
-      alignment, protein conservation analysis, and protein disorder and
-      RNA secondary structure prediction services are now provided by <a
-      href="http://www.compbio.dundee.ac.uk/jabaws">JABAWS 2.2</a>.
-      Several of the programs provided as JABAWS 2.2 services have been
-      updated, so their options and parameters have changed.</li>
-    <li><strong>URL linkouts to other bioinformatics
-        databases</strong><br />New preferences for <a
-      href="webServices/urllinks.html">opening web pages for
-        database cross-references</a> via the UK Elixir's EMBL-EBI's MIRIAM
-      database and identifiers.org services.</li>
-    <li><strong>Showing and hiding regions</strong> <br /> <a
-      href="menus/popupMenu.html#hideinserts">Hide insertions</a> in the
-      PopUp menu has changed its behaviour. Prior to 2.10.2, columns
-      were only shown or hidden according to gaps in the sequence under
-      the popup menu. Now, only columns that are gapped in all selected
-      sequences as well as the sequence under the popup menu are hidden,
-      and column visibility outside the selected region is left as is.
-      This makes it easy to filter insertions from the alignment view
-      (just select the region containing insertions to remove) without
-      affecting the rest of the hidden columns.</li>
-    <li><strong>Gap count - a.k.a. the Occupancy
-        Annotation Row</strong><br /> Another way to filter columns according to
-      the presence of gaps is to enable the <strong>Occupancy
-        Annotation</strong> row via Jalview's Preferences. This annotation row
-      shows a histogram of the number of aligned residues at each
-      column. The <a href="features/columnFilterByAnnotation.html">Select
-        By Annotation</a> dialog now also includes a percentage threshold
-      mode, to make it easy to filter alignments to show only those
-      columns with a particular fraction of aligned sequences.</li>
-    <li><strong>Recent search history for Find, PDBe and
-        Uniprot</strong><br />Easily repeat a previous search for <a
-      href="features/search.html#queryhistory">Find</a> and the free
-      text search system (for querying Uniprot and the PDBe).</li>
-    <li><strong>Improved Overview Window</strong><br />The <a
-      href="features/overview.html">alignment overview</a> is now easier
-      to use when working with alignments of more than 5000 rows and
-      columns, and features a new pop-up menu that allows hidden regions
-      to be excluded from the overview. It also works with CDS/Protein
-      alignments and MSA views in wrapped mode.</li>
-    <li><strong>3D Structure</strong><br />Jalview's communication
-      with UCSF Chimera has been made more robust, particularly when
-      working with many structures and long sequences. Regions in
-      structures that correspond to hidden regions in an alignment view
-      are now left un-coloured, making it easier to highlight specific
-      features in 3D. See below for <a href="#experimental">experimental
-        features for exchanging annotation between Chimera and Jalview.</a></li>
-  </ul>
-  <p>
-    <strong>Scripting</strong><br />New <a
-      href="http://www.jalview.org/examples/groovy">groovy examples</a>
-    demonstrate Jalview 2.10.2 APIs for creation of data-driven
-    colourschemes, and custom alignment file handlers. The <a
-      href="groovy/featuresCounter.html">FeatureAnnotationWorker</a>
-    introduced in Jalview 2.10 has also been refactored to allow
-    efficient counting across multiple feature types. Please be aware
-    that feature counter scripts created for earlier versions will not
-    execute in Jalview 2.10.2.
-  </p>
   <p>
     <strong><a name="experimental">Experimental Features</a></strong>
   </p>
index bf1e8b1..37426c3 100644 (file)
Binary files a/lib/jabaws-min-client-2.2.0.jar and b/lib/jabaws-min-client-2.2.0.jar differ
index c9d2e63..5d9bdff 100644 (file)
@@ -1295,7 +1295,6 @@ label.database = Database
 label.urltooltip = Only one url, which must use a sequence id, can be selected for the 'On Click' option
 label.edit_sequence_url_link = Edit sequence URL link
 warn.name_cannot_be_duplicate = User-defined URL names must be unique and cannot be MIRIAM ids
-label.invalid_name = Invalid Name !
 label.output_seq_details = Output Sequence Details to list all database references
 label.urllinks = Links
 label.default_cache_size = Default Cache Size
@@ -1310,3 +1309,13 @@ label.occupancy_descr = Number of aligned positions
 label.show_experimental = Enable experimental features
 label.show_experimental_tip = Enable any new and currently 'experimental' features (see Latest Release Notes for details)
 label.warning_hidden = Warning: {0} {1} is currently hidden
+label.overview_settings = Overview settings
+label.ov_legacy_gap = Use legacy gap colouring (gaps are white)
+label.gap_colour = Gap colour:
+label.ov_show_hide_default = Show hidden regions when opening overview
+label.hidden_colour = Hidden colour:
+label.select_gap_colour = Select gap colour
+label.select_hidden_colour = Select hidden colour
+label.overview = Overview
+label.reset_to_defaults = Reset to defaults
+label.oview_calc = Recalculating overview...
index 64f06c8..e8fd411 100644 (file)
@@ -1295,7 +1295,6 @@ label.database = Base de datos
 label.urltooltip = Sólo una url, que debe usar una id de secuencia, puede ser seleccionada en la opción 'On Click'
 label.edit_sequence_url_link = Editar link de secuencia URL
 warn.name_cannot_be_duplicate = Los nombres URL definidos por el usuario deben ser Ãºnicos y no pueden ser ids de MIRIAM
-label.invalid_name = Nombre inválido !
 label.output_seq_details = Seleccionar Detalles de la secuencia para ver todas
 label.urllinks = Enlaces
 label.default_cache_size = Tamaño del caché por defecto
@@ -1310,3 +1309,13 @@ label.togglehidden = Mostrar regiones ocultas
 label.show_experimental = Habilitar funciones experimentales
 label.show_experimental_tip = Habilitar funciones nuevas y experimentales (ver Latest Release Notes para más detalles)
 label.warning_hidden = Advertencia: {0} {1} está actualmente oculto
+label.overview_settings = Ajustes para la ventana resumen
+label.ov_legacy_gap = <html>Utilizar el color heredado de huecos<br>(los huecos son blancos)
+label.gap_colour = Color de huecos:
+label.ov_show_hide_default = Mostrar regiones ocultas al abrir el resumen
+label.hidden_colour = Color de las regiones ocultas:
+label.select_gap_colour = Seleccionar color de huecos
+label.select_hidden_colour = Seleccionar color de las regiones ocultas 
+label.overview = Resumen
+label.reset_to_defaults = Restablecen a los predeterminados
+label.oview_calc = Recalculando resumen
index 3a3f660..06b1675 100644 (file)
@@ -30,4 +30,11 @@ public interface AlignmentColsCollectionI extends Iterable<Integer>
    * @return true if the column at the position is hidden
    */
   public boolean isHidden(int c);
+
+  /**
+   * Answers if any column in this collection is hidden
+   * 
+   * @return true if there is at least 1 hidden column
+   */
+  public boolean hasHidden();
 }
index 24affc5..fbf6ceb 100644 (file)
@@ -34,6 +34,13 @@ public interface AlignmentRowsCollectionI extends Iterable<Integer>
   public boolean isHidden(int r);
 
   /**
+   * Answers if any row in this collection is hidden
+   * 
+   * @return true if there is at least 1 hidden row
+   */
+  public boolean hasHidden();
+
+  /**
    * Answers the sequence at the given position in the alignment
    * 
    * @param r
diff --git a/src/jalview/api/RendererListenerI.java b/src/jalview/api/RendererListenerI.java
new file mode 100644 (file)
index 0000000..0ce4116
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.api;
+
+import java.beans.PropertyChangeListener;
+
+public interface RendererListenerI extends PropertyChangeListener
+{
+
+}
index a0466d3..9597b44 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.appletgui;
 
 import jalview.renderer.OverviewRenderer;
+import jalview.renderer.OverviewResColourFinder;
 import jalview.viewmodel.OverviewDimensions;
 
 import java.awt.Color;
@@ -48,10 +49,6 @@ public class OverviewCanvas extends Component
 
   private AlignViewport av;
 
-  // Can set different properties in this seqCanvas than
-  // main visible SeqCanvas
-  private SequenceRenderer sr;
-
   private jalview.renderer.seqfeatures.FeatureRenderer fr;
 
   private Frame nullFrame;
@@ -65,10 +62,6 @@ public class OverviewCanvas extends Component
     nullFrame = new Frame();
     nullFrame.addNotify();
 
-    sr = new SequenceRenderer(av);
-    sr.graphics = nullFrame.getGraphics();
-    sr.renderGaps = false;
-    sr.forOverview = true;
     fr = new jalview.renderer.seqfeatures.FeatureRenderer(av);
   }
 
@@ -119,7 +112,8 @@ public class OverviewCanvas extends Component
 
     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
 
-    or = new OverviewRenderer(sr, fr, od);
+    or = new OverviewRenderer(fr, od, av.getAlignment(),
+            av.getResidueShading(), new OverviewResColourFinder());
     miniMe = nullFrame.createImage(od.getWidth(), od.getHeight());
     offscreen = nullFrame.createImage(od.getWidth(), od.getHeight());
 
index be552c0..e74e1cd 100755 (executable)
@@ -148,11 +148,12 @@ public class OverviewPanel extends Panel implements Runnable,
     }
     else
     {
+      // don't do anything if the mouse press is in the overview's box
+      // (wait to see if it's a drag instead)
+      // otherwise update the viewport
       if (!od.isPositionInBox(evt.getX(), evt.getY()))
-      {
-        // don't do anything if the mouse press is in the overview's box
-        // (wait to see if it's a drag instead)
-        // otherwise update the viewport
+      { 
+       draggingBox = false;
         od.updateViewportFromMouse(evt.getX(), evt.getY(),
                 av.getAlignment().getHiddenSequences(),
                 av.getAlignment().getHiddenColumns());
@@ -170,10 +171,6 @@ public class OverviewPanel extends Panel implements Runnable,
   @Override
   public void mouseReleased(MouseEvent evt)
   {
-    if (draggingBox)
-    {
-      draggingBox = false;
-    }
   }
 
   @Override
index fc7c46e..f36a8e2 100644 (file)
@@ -1402,7 +1402,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   {
     if (scrollThread != null)
     {
-      scrollThread.running = false;
+      scrollThread.threadRunning = false;
       scrollThread = null;
     }
 
@@ -1649,7 +1649,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       mouseExited(evt);
     }
 
-    if (scrollThread != null)
+    if ((scrollThread != null) && (scrollThread.isRunning()))
     {
       scrollThread.setEvent(evt);
     }
@@ -1665,9 +1665,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       oldSeq = 0;
     }
 
-    if (scrollThread != null)
+    if ((scrollThread != null) && (scrollThread.isRunning()))
     {
-      scrollThread.running = false;
+      scrollThread.stopScrolling();
       scrollThread = null;
     }
   }
@@ -1690,9 +1690,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   {
     if (evt == null)
     {
-      if (scrollThread != null)
+      if ((scrollThread != null) && (scrollThread.isRunning()))
       {
-        scrollThread.running = false;
+        scrollThread.stopScrolling();
         scrollThread = null;
       }
       mouseDragging = false;
@@ -1715,7 +1715,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   {
     MouseEvent evt;
 
-    boolean running = false;
+    private volatile boolean threadRunning = true;
 
     public ScrollThread()
     {
@@ -1729,14 +1729,18 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
     public void stopScrolling()
     {
-      running = false;
+      threadRunning = false;
+    }
+
+    public boolean isRunning()
+    {
+      return threadRunning;
     }
 
     @Override
     public void run()
     {
-      running = true;
-      while (running)
+      while (threadRunning)
       {
 
         if (evt != null)
@@ -1745,23 +1749,23 @@ public class SeqPanel extends Panel implements MouseMotionListener,
           if (mouseDragging && evt.getY() < 0
                   && av.getRanges().getStartSeq() > 0)
           {
-            running = av.getRanges().scrollUp(true);
+            av.getRanges().scrollUp(true);
           }
 
           if (mouseDragging && evt.getY() >= getSize().height && av
                   .getAlignment().getHeight() > av.getRanges().getEndSeq())
           {
-            running = av.getRanges().scrollUp(false);
+            av.getRanges().scrollUp(false);
           }
 
           if (mouseDragging && evt.getX() < 0)
           {
-            running = av.getRanges().scrollRight(false);
+            av.getRanges().scrollRight(false);
           }
 
           else if (mouseDragging && evt.getX() >= getSize().width)
           {
-            running = av.getRanges().scrollRight(true);
+            av.getRanges().scrollRight(true);
           }
         }
 
index a382c60..e55f2a5 100755 (executable)
@@ -22,7 +22,7 @@ package jalview.appletgui;
 
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
-import jalview.renderer.ResidueShaderI;
+import jalview.renderer.ResidueColourFinder;
 import jalview.renderer.seqfeatures.FeatureColourFinder;
 
 import java.awt.Color;
@@ -40,19 +40,18 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
   boolean renderGaps = true;
 
-  SequenceGroup currentSequenceGroup = null;
-
   SequenceGroup[] allGroups = null;
 
   Color resBoxColour;
 
   Graphics graphics;
 
-  boolean forOverview = false;
+  ResidueColourFinder resColourFinder;
 
   public SequenceRenderer(AlignViewport av)
   {
     this.av = av;
+    resColourFinder = new ResidueColourFinder();
   }
 
   /**
@@ -69,25 +68,6 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     this.renderGaps = renderGaps;
   }
 
-  protected Color getResidueBoxColour(SequenceI seq, int i)
-  {
-    allGroups = av.getAlignment().findAllGroups(seq);
-
-    if (inCurrentSequenceGroup(i))
-    {
-      if (currentSequenceGroup.getDisplayBoxes())
-      {
-        getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, i);
-      }
-    }
-    else if (av.getShowBoxes())
-    {
-      getBoxColour(av.getResidueShading(), seq, i);
-    }
-
-    return resBoxColour;
-  }
-
   /**
    * Get the residue colour at the given sequence position - as determined by
    * the sequence group colour (if any), else the colour scheme, possibly
@@ -104,31 +84,9 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   {
     // TODO replace 8 or so code duplications with calls to this method
     // (refactored as needed)
-    Color col = getResidueBoxColour(seq, position);
-
-    if (finder != null)
-    {
-      col = finder.findFeatureColour(col, seq, position);
-    }
-    return col;
-  }
-
-  void getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
-  {
-    if (shader.getColourScheme() != null)
-    {
-      resBoxColour = shader.findColour(seq.getCharAt(i), i, seq);
-    }
-    else if (forOverview
-            && !jalview.util.Comparison.isGap(seq.getCharAt(i)))
-    {
-      resBoxColour = Color.lightGray;
-    }
-    else
-    {
-      resBoxColour = Color.white;
-    }
-
+    return resColourFinder.getResidueColour(av.getShowBoxes(),
+            av.getResidueShading(),
+            allGroups, seq, position, finder);
   }
 
   public Color findSequenceColour(SequenceI seq, int i)
@@ -165,23 +123,28 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     int curWidth = av.getCharWidth(), avCharWidth = av.getCharWidth(),
             avCharHeight = av.getCharHeight();
 
+    Color resBoxColour = Color.white;
     Color tempColour = null;
     while (i <= end)
     {
       resBoxColour = Color.white;
       if (i < length)
       {
-        if (inCurrentSequenceGroup(i))
+        SequenceGroup currentSequenceGroup = resColourFinder
+                .getCurrentSequenceGroup(allGroups, i);
+        if (currentSequenceGroup != null)
         {
           if (currentSequenceGroup.getDisplayBoxes())
           {
-            getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq,
+            resBoxColour = resColourFinder.getBoxColour(
+                    currentSequenceGroup.getGroupColourScheme(), seq,
                     i);
           }
         }
         else if (av.getShowBoxes())
         {
-          getBoxColour(av.getResidueShading(), seq, i);
+          resBoxColour = resColourFinder
+                  .getBoxColour(av.getResidueShading(), seq, i);
         }
       }
 
@@ -245,7 +208,9 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
         continue;
       }
 
-      if (inCurrentSequenceGroup(i))
+      SequenceGroup currentSequenceGroup = resColourFinder
+              .getCurrentSequenceGroup(allGroups, i);
+      if (currentSequenceGroup != null)
       {
         if (!currentSequenceGroup.getDisplayText())
         {
@@ -254,7 +219,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
         if (currentSequenceGroup.getColourText())
         {
-          getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, i);
+          resBoxColour = resColourFinder.getBoxColour(
+                  currentSequenceGroup.getGroupColourScheme(), seq, i);
           graphics.setColor(resBoxColour.darker());
         }
         if (currentSequenceGroup.getShowNonconserved())
@@ -271,7 +237,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
         if (av.getColourText())
         {
-          getBoxColour(av.getResidueShading(), seq, i);
+          resBoxColour = resColourFinder
+                  .getBoxColour(av.getResidueShading(), seq, i);
           if (av.getShowBoxes())
           {
             graphics.setColor(resBoxColour.darker());
@@ -358,28 +325,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     return sequenceChar;
   }
 
-  boolean inCurrentSequenceGroup(int res)
-  {
-    if (allGroups == null)
-    {
-      return false;
-    }
-
-    for (int i = 0; i < allGroups.length; i++)
-    {
-      if (allGroups[i].getStartRes() <= res
-              && allGroups[i].getEndRes() >= res)
-      {
-        currentSequenceGroup = allGroups[i];
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-  public void drawHighlightedText(SequenceI seq, int start, int end, int x1,
-          int y1)
+  public void drawHighlightedText(SequenceI seq, int start, int end,
+          int x1, int y1)
   {
     int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
     int pady = avCharHeight / 5;
index 3266874..32e5fb4 100644 (file)
  */
 package jalview.commands;
 
-/*
- * Jalview - A Sequence Alignment Editor and Viewer Copyright (C) 2007 AM
- * Waterhouse, J Procter, G Barton, M Clamp, S Searle
- * 
- * This program 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 2 of the License, or (at your option) any later
- * version.
- * 
- * This program 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
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 
@@ -110,6 +91,7 @@ public class RemoveGapColCommand extends EditCommand
     performEdit(0, null);
   }
 
+  @Override
   public int getSize()
   {
     // We're interested in the number of columns deleted,
index c5c35f1..27831dd 100644 (file)
  */
 package jalview.commands;
 
-/*
- * Jalview - A Sequence Alignment Editor and Viewer Copyright (C) 2007 AM
- * Waterhouse, J Procter, G Barton, M Clamp, S Searle
- * 
- * This program 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 2 of the License, or (at your option) any later
- * version.
- * 
- * This program 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
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 
index 1b5207f..084b80e 100755 (executable)
@@ -5,16 +5,16 @@
  * This file is part of Jalview.
  * 
  * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General License 
+ * 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 License for more details.
+ * PURPOSE.  See the GNU General Public License for more details.
  * 
- * You should have received a copy of the GNU General License
+ * 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.
  */
index 4ac4f83..e216c46 100644 (file)
@@ -50,4 +50,10 @@ public class AllColsCollection implements AlignmentColsCollectionI
   {
     return !hidden.isVisible(c);
   }
+
+  @Override
+  public boolean hasHidden()
+  {
+    return hidden.hasHiddenColumns();
+  }
 }
index 9a33094..10c47f0 100644 (file)
@@ -59,4 +59,10 @@ public class AllRowsCollection implements AlignmentRowsCollectionI
   {
     return alignment.getSequenceAtAbsoluteIndex(seq);
   }
+
+  @Override
+  public boolean hasHidden()
+  {
+    return (hidden.getSize() > 0);
+  }
 }
index 2f1da7f..796937d 100755 (executable)
@@ -89,7 +89,7 @@ public class Sequence extends ASequence implements SequenceI
    */
   int index = -1;
 
-  private SequenceFeatures sequenceFeatureStore;
+  private SequenceFeaturesI sequenceFeatureStore;
 
   /*
    * A cursor holding the approximate current view position to the sequence,
index 6964b53..e2f15e1 100755 (executable)
@@ -27,6 +27,8 @@ import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ColourSchemeI;
 
 import java.awt.Color;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -39,6 +41,26 @@ import java.util.Map;
  */
 public class SequenceGroup implements AnnotatedCollectionI
 {
+  // TODO ideally this event notification functionality should be separated into
+  // a
+  // subclass of ViewportProperties similarly to ViewportRanges. Done here as
+  // quick fix for JAL-2665
+  public static final String SEQ_GROUP_CHANGED = "Sequence group changed";
+
+  protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+          this);
+
+  public void addPropertyChangeListener(PropertyChangeListener listener)
+  {
+    changeSupport.addPropertyChangeListener(listener);
+  }
+
+  public void removePropertyChangeListener(PropertyChangeListener listener)
+  {
+    changeSupport.removePropertyChangeListener(listener);
+  }
+  // end of event notification functionality initialisation
+
   String groupName;
 
   String description;
@@ -496,6 +518,8 @@ public class SequenceGroup implements AnnotatedCollectionI
       if (s != null && !sequences.contains(s))
       {
         sequences.add(s);
+        changeSupport.firePropertyChange(SEQ_GROUP_CHANGED,
+                sequences.size() - 1, sequences.size());
       }
 
       if (recalc)
@@ -693,6 +717,8 @@ public class SequenceGroup implements AnnotatedCollectionI
     synchronized (sequences)
     {
       sequences.remove(s);
+      changeSupport.firePropertyChange(SEQ_GROUP_CHANGED,
+              sequences.size() + 1, sequences.size());
 
       if (recalc)
       {
@@ -729,7 +755,9 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public void setStartRes(int i)
   {
+    int before = startRes;
     startRes = i;
+    changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before, startRes);
   }
 
   /**
@@ -739,7 +767,9 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public void setEndRes(int i)
   {
+    int before = endRes;
     endRes = i;
+    changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before, endRes);
   }
 
   /**
@@ -1349,7 +1379,10 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     synchronized (sequences)
     {
+      int before = sequences.size();
       sequences.clear();
+      changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before,
+              sequences.size());
     }
   }
 
index bc32fac..e9437a7 100644 (file)
@@ -51,4 +51,10 @@ public class VisibleColsCollection implements AlignmentColsCollectionI
     return false;
   }
 
+  @Override
+  public boolean hasHidden()
+  {
+    return false;
+  }
+
 }
index ee0557f..fd7cf47 100644 (file)
@@ -56,4 +56,10 @@ public class VisibleRowsCollection implements AlignmentRowsCollectionI
   {
     return alignment.getSequenceAtAbsoluteIndex(seq);
   }
+
+  @Override
+  public boolean hasHidden()
+  {
+    return false;
+  }
 }
index 52da8c7..8d5ba58 100644 (file)
@@ -149,6 +149,14 @@ public class SequenceFeatures implements SequenceFeaturesI
     }
 
     Set<String> featureTypes = getFeatureTypes(ontologyTerm);
+    if (featureTypes.isEmpty())
+    {
+      /*
+       * no features of the specified type or any sub-type
+       */
+      return new ArrayList<>();
+    }
+
     return getAllFeatures(featureTypes.toArray(new String[featureTypes
             .size()]));
   }
index 8cc81d9..bbe6a20 100644 (file)
@@ -279,7 +279,7 @@ public class EmblEntry
     String translation = null;
     String proteinName = "";
     String proteinId = null;
-    Map<String, String> vals = new Hashtable<String, String>();
+    Map<String, String> vals = new Hashtable<>();
 
     /*
      * codon_start 1/2/3 in EMBL corresponds to phase 0/1/2 in CDS
index 365c1c2..ad01324 100644 (file)
@@ -428,6 +428,12 @@ public class EnsemblGene extends EnsemblSeqProxy
   /**
    * Returns a list of the transcript features on the sequence whose Parent is
    * the gene for the accession id.
+   * <p>
+   * Transcript features are those of type "transcript", or any of its sub-types
+   * in the Sequence Ontology e.g. "mRNA", "processed_transcript". We also
+   * include "NMD_transcript_variant", because this type behaves like a
+   * transcript identifier in Ensembl, although strictly speaking it is not in
+   * the SO.
    * 
    * @param accId
    * @param geneSequence
@@ -439,19 +445,18 @@ public class EnsemblGene extends EnsemblSeqProxy
     List<SequenceFeature> transcriptFeatures = new ArrayList<SequenceFeature>();
 
     String parentIdentifier = GENE_PREFIX + accId;
-    // todo optimise here by transcript type!
+
     List<SequenceFeature> sfs = geneSequence.getFeatures()
-            .getPositionalFeatures();
+            .getFeaturesByOntology(SequenceOntologyI.TRANSCRIPT);
+    sfs.addAll(geneSequence.getFeatures().getPositionalFeatures(
+            SequenceOntologyI.NMD_TRANSCRIPT_VARIANT));
 
     for (SequenceFeature sf : sfs)
     {
-      if (isTranscript(sf.getType()))
+      String parent = (String) sf.getValue(PARENT);
+      if (parentIdentifier.equals(parent))
       {
-        String parent = (String) sf.getValue(PARENT);
-        if (parentIdentifier.equals(parent))
-        {
-          transcriptFeatures.add(sf);
-        }
+        transcriptFeatures.add(sf);
       }
     }
 
index 4da4f15..577111e 100644 (file)
@@ -792,8 +792,7 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
       return false;
     }
 
-    long start = System.currentTimeMillis();
-    // SequenceFeature[] sfs = sourceSequence.getSequenceFeatures();
+//    long start = System.currentTimeMillis();
     List<SequenceFeature> sfs = sourceSequence.getFeatures()
             .getPositionalFeatures();
     MapList mapping = getGenomicRangesFromFeatures(sourceSequence,
@@ -805,10 +804,10 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
 
     boolean result = transferFeatures(sfs, targetSequence, mapping,
             accessionId);
-    System.out.println("transferFeatures (" + (sfs.size()) + " --> "
-            + targetSequence.getFeatures().getFeatureCount(true) + ") to "
-            + targetSequence.getName() + " took "
-            + (System.currentTimeMillis() - start) + "ms");
+//    System.out.println("transferFeatures (" + (sfs.size()) + " --> "
+//            + targetSequence.getFeatures().getFeatureCount(true) + ") to "
+//            + targetSequence.getName() + " took "
+//            + (System.currentTimeMillis() - start) + "ms");
     return result;
   }
 
index a483f44..cbeaff1 100644 (file)
@@ -59,7 +59,7 @@ public class PDBFTSRestClient extends FTSRestClient
 
   private static FTSRestClientI instance = null;
 
-  public static final String PDB_SEARCH_ENDPOINT = "http://www.ebi.ac.uk/pdbe/search/pdb/select?";
+  public static final String PDB_SEARCH_ENDPOINT = "https://www.ebi.ac.uk/pdbe/search/pdb/select?";
 
   protected PDBFTSRestClient()
   {
index 1bd1541..13b715e 100644 (file)
@@ -4386,7 +4386,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // Java's Transferable for native dnd
     evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
     Transferable t = evt.getTransferable();
-    List<String> files = new ArrayList<>();
+    final AlignFrame thisaf = this;
+    final List<String> files = new ArrayList<>();
     List<DataSourceType> protocols = new ArrayList<>();
 
     try
@@ -4398,133 +4399,144 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     if (files != null)
     {
-      try
+      new Thread(new Runnable()
       {
-        // check to see if any of these files have names matching sequences in
-        // the alignment
-        SequenceIdMatcher idm = new SequenceIdMatcher(
-                viewport.getAlignment().getSequencesArray());
-        /**
-         * Object[] { String,SequenceI}
-         */
-        ArrayList<Object[]> filesmatched = new ArrayList<>();
-        ArrayList<String> filesnotmatched = new ArrayList<>();
-        for (int i = 0; i < files.size(); i++)
+        @Override
+        public void run()
         {
-          String file = files.get(i).toString();
-          String pdbfn = "";
-          DataSourceType protocol = FormatAdapter.checkProtocol(file);
-          if (protocol == DataSourceType.FILE)
-          {
-            File fl = new File(file);
-            pdbfn = fl.getName();
-          }
-          else if (protocol == DataSourceType.URL)
-          {
-            URL url = new URL(file);
-            pdbfn = url.getFile();
-          }
-          if (pdbfn.length() > 0)
+          try
           {
-            // attempt to find a match in the alignment
-            SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
-            int l = 0, c = pdbfn.indexOf(".");
-            while (mtch == null && c != -1)
+            // check to see if any of these files have names matching sequences
+            // in
+            // the alignment
+            SequenceIdMatcher idm = new SequenceIdMatcher(
+                    viewport.getAlignment().getSequencesArray());
+            /**
+             * Object[] { String,SequenceI}
+             */
+            ArrayList<Object[]> filesmatched = new ArrayList<>();
+            ArrayList<String> filesnotmatched = new ArrayList<>();
+            for (int i = 0; i < files.size(); i++)
             {
-              do
+              String file = files.get(i).toString();
+              String pdbfn = "";
+              DataSourceType protocol = FormatAdapter.checkProtocol(file);
+              if (protocol == DataSourceType.FILE)
               {
-                l = c;
-              } while ((c = pdbfn.indexOf(".", l)) > l);
-              if (l > -1)
-              {
-                pdbfn = pdbfn.substring(0, l);
+                File fl = new File(file);
+                pdbfn = fl.getName();
               }
-              mtch = idm.findAllIdMatches(pdbfn);
-            }
-            if (mtch != null)
-            {
-              FileFormatI type = null;
-              try
+              else if (protocol == DataSourceType.URL)
               {
-                type = new IdentifyFile().identify(file, protocol);
-              } catch (Exception ex)
-              {
-                type = null;
+                URL url = new URL(file);
+                pdbfn = url.getFile();
               }
-              if (type != null && type.isStructureFile())
+              if (pdbfn.length() > 0)
               {
-                filesmatched.add(new Object[] { file, protocol, mtch });
-                continue;
+                // attempt to find a match in the alignment
+                SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
+                int l = 0, c = pdbfn.indexOf(".");
+                while (mtch == null && c != -1)
+                {
+                  do
+                  {
+                    l = c;
+                  } while ((c = pdbfn.indexOf(".", l)) > l);
+                  if (l > -1)
+                  {
+                    pdbfn = pdbfn.substring(0, l);
+                  }
+                  mtch = idm.findAllIdMatches(pdbfn);
+                }
+                if (mtch != null)
+                {
+                  FileFormatI type = null;
+                  try
+                  {
+                    type = new IdentifyFile().identify(file, protocol);
+                  } catch (Exception ex)
+                  {
+                    type = null;
+                  }
+                  if (type != null && type.isStructureFile())
+                  {
+                    filesmatched.add(new Object[] { file, protocol, mtch });
+                    continue;
+                  }
+                }
+                // File wasn't named like one of the sequences or wasn't a PDB
+                // file.
+                filesnotmatched.add(file);
               }
             }
-            // File wasn't named like one of the sequences or wasn't a PDB file.
-            filesnotmatched.add(file);
-          }
-        }
-        int assocfiles = 0;
-        if (filesmatched.size() > 0)
-        {
-          if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
-                  || JvOptionPane.showConfirmDialog(this,
-                          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)
-
-          {
-            for (Object[] fm : filesmatched)
+            int assocfiles = 0;
+            if (filesmatched.size() > 0)
             {
-              // try and associate
-              // TODO: may want to set a standard ID naming formalism for
-              // associating PDB files which have no IDs.
-              for (SequenceI toassoc : (SequenceI[]) fm[2])
+              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)
+
               {
-                PDBEntry pe = new AssociatePdbFileWithSeq()
-                        .associatePdbWithSeq((String) fm[0],
-                                (DataSourceType) fm[1], toassoc, false,
-                                Desktop.instance);
-                if (pe != null)
+                for (Object[] fm : filesmatched)
                 {
-                  System.err.println("Associated file : " + ((String) fm[0])
-                          + " with " + toassoc.getDisplayId(true));
-                  assocfiles++;
+                  // try and associate
+                  // TODO: may want to set a standard ID naming formalism for
+                  // associating PDB files which have no IDs.
+                  for (SequenceI toassoc : (SequenceI[]) fm[2])
+                  {
+                    PDBEntry pe = new AssociatePdbFileWithSeq()
+                            .associatePdbWithSeq((String) fm[0],
+                                    (DataSourceType) fm[1], toassoc, false,
+                                    Desktop.instance);
+                    if (pe != null)
+                    {
+                      System.err.println("Associated file : "
+                              + ((String) fm[0]) + " with "
+                              + toassoc.getDisplayId(true));
+                      assocfiles++;
+                    }
+                  }
+                  alignPanel.paintAlignment(true);
                 }
               }
-              alignPanel.paintAlignment(true);
             }
-          }
-        }
-        if (filesnotmatched.size() > 0)
-        {
-          if (assocfiles > 0 && (Cache.getDefault(
-                  "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false)
-                  || JvOptionPane.showConfirmDialog(this,
-                          "<html>" + MessageManager.formatMessage(
-                                  "label.ignore_unmatched_dropped_files_info",
-                                  new Object[]
-                                  { Integer.valueOf(filesnotmatched.size())
-                                          .toString() })
-                                  + "</html>",
-                          MessageManager.getString(
-                                  "label.ignore_unmatched_dropped_files"),
-                          JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION))
-          {
-            return;
-          }
-          for (String fn : filesnotmatched)
+            if (filesnotmatched.size() > 0)
+            {
+              if (assocfiles > 0 && (Cache.getDefault(
+                      "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false)
+                      || JvOptionPane.showConfirmDialog(thisaf,
+                              "<html>" + MessageManager.formatMessage(
+                                      "label.ignore_unmatched_dropped_files_info",
+                                      new Object[]
+                                      { Integer.valueOf(
+                                              filesnotmatched.size())
+                                              .toString() })
+                                      + "</html>",
+                              MessageManager.getString(
+                                      "label.ignore_unmatched_dropped_files"),
+                              JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION))
+              {
+                return;
+              }
+              for (String fn : filesnotmatched)
+              {
+                loadJalviewDataFile(fn, null, null, null);
+              }
+
+            }
+          } catch (Exception ex)
           {
-            loadJalviewDataFile(fn, null, null, null);
+            ex.printStackTrace();
           }
-
         }
-      } catch (Exception ex)
-      {
-        ex.printStackTrace();
-      }
+      }).start();
     }
   }
 
index 25f44c5..c700635 100644 (file)
@@ -231,7 +231,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
             new Dimension(10, av.getCharHeight() + fm.getDescent()));
     idSpaceFillerPanel1.setPreferredSize(
             new Dimension(10, av.getCharHeight() + fm.getDescent()));
-
+    idwidthAdjuster.invalidate();
+    scalePanelHolder.invalidate();
     getIdPanel().getIdCanvas().gg = null;
     getSeqPanel().seqCanvas.img = null;
     getAnnotationPanel().adjustPanelHeight();
@@ -242,11 +243,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
     getIdPanel().getIdCanvas().setPreferredSize(d);
     hscrollFillerPanel.setPreferredSize(d);
 
-    if (this.alignFrame.getSplitViewContainer() != null)
-    {
-      ((SplitFrame) this.alignFrame.getSplitViewContainer()).adjustLayout();
-    }
-
     repaint();
   }
 
@@ -1100,8 +1096,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * single graphics context), then reset to (0, scale height)
      */
     alignmentGraphics.translate(alignmentGraphicsOffset, scaleHeight);
-    getSeqPanel().seqCanvas.drawPanel(alignmentGraphics, startRes, endRes,
-            startSeq, endSeq, 0);
+    getSeqPanel().seqCanvas.drawPanelForPrinting(alignmentGraphics, startRes,
+            endRes, startSeq, endSeq);
     alignmentGraphics.translate(-alignmentGraphicsOffset, 0);
 
     if (av.isShowAnnotation() && (endSeq == alignmentHeight))
@@ -1228,7 +1224,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     pg.translate(idWidth, 0);
 
-    getSeqPanel().seqCanvas.drawWrappedPanel(pg, pwidth - idWidth,
+    getSeqPanel().seqCanvas.drawWrappedPanelForPrinting(pg, pwidth - idWidth,
             totalHeight, 0);
 
     if ((pi * pheight) < totalHeight)
index f088791..8d123bb 100644 (file)
@@ -456,22 +456,8 @@ public class AnnotationColourChooser extends AnnotationRowFilter
         {
           continue;
         }
-
-        if (useOriginalColours.isSelected())
-        {
-          sg.setColourScheme(new AnnotationColourGradient(currentAnn,
-                  sg.getColourScheme(), selectedThresholdOption));
-          ((AnnotationColourGradient) sg.cs)
-                  .setSeqAssociated(seqAssociated.isSelected());
-        }
-        else
-        {
-          sg.setColourScheme(new AnnotationColourGradient(currentAnn,
-                  minColour.getBackground(), maxColour.getBackground(),
-                  selectedThresholdOption));
-          ((AnnotationColourGradient) sg.cs)
-                  .setSeqAssociated(seqAssociated.isSelected());
-        }
+        sg.setColourScheme(
+                acg.getInstance(sg, ap.av.getHiddenRepSequences()));
       }
     }
     return false;
index 01ee1ff..2d1dfd4 100644 (file)
@@ -242,7 +242,7 @@ public class CrossRefAction implements Runnable
       String linkedTitle = MessageManager
               .getString("label.linked_view_title");
       Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
-      sf.adjustDivider();
+      sf.adjustInitialLayout();
 
       // finally add the top, then bottom frame to the view list
       xrefViews.add(dna ? copyThis.alignPanel : newFrame.alignPanel);
index c8d900d..1f8983f 100644 (file)
@@ -269,12 +269,14 @@ public class Desktop extends jalview.jbgui.GDesktop
     public void endDraggingFrame(JComponent f)
     {
       delegate.endDraggingFrame(f);
+      desktop.repaint();
     }
 
     @Override
     public void endResizingFrame(JComponent f)
     {
       delegate.endResizingFrame(f);
+      desktop.repaint();
     }
 
     @Override
@@ -344,10 +346,6 @@ public class Desktop extends jalview.jbgui.GDesktop
     boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
             false);
     desktop = new MyDesktopPane(selmemusage);
-    if (Platform.isAMac())
-    {
-      desktop.setDoubleBuffered(false);
-    }
     showMemusage.setSelected(selmemusage);
     desktop.setBackground(Color.white);
     getContentPane().setLayout(new BorderLayout());
@@ -361,7 +359,9 @@ public class Desktop extends jalview.jbgui.GDesktop
     // This line prevents Windows Look&Feel resizing all new windows to maximum
     // if previous window was maximised
     desktop.setDesktopManager(
-            new MyDesktopManager(new DefaultDesktopManager()));
+            new MyDesktopManager(
+                    (Platform.isWindows() ? new DefaultDesktopManager()
+                            : desktop.getDesktopManager())));
 
     Rectangle dims = getLastKnownDimensions("");
     if (dims != null)
@@ -866,13 +866,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     frame.setResizable(resizable);
     frame.setMaximizable(resizable);
     frame.setIconifiable(resizable);
-    if (Platform.isAMac())
-    {
-      frame.setIconifiable(false);
-      frame.setFrameIcon(null);
-      // frame.setDesktopIcon(null);
-      frame.setDoubleBuffered(false);
-    }
+
     if (frame.getX() < 1 && frame.getY() < 1)
     {
       frame.setLocation(xOffset * openFrameCount,
@@ -1011,8 +1005,8 @@ public class Desktop extends jalview.jbgui.GDesktop
     // Java's Transferable for native dnd
     evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
     Transferable t = evt.getTransferable();
-    List<String> files = new ArrayList<String>();
-    List<DataSourceType> protocols = new ArrayList<DataSourceType>();
+    List<String> files = new ArrayList<>();
+    List<DataSourceType> protocols = new ArrayList<>();
 
     try
     {
@@ -1720,7 +1714,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   JPanel progressPanel;
 
-  ArrayList<JPanel> fileLoadingPanels = new ArrayList<JPanel>();
+  ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
 
   public void startLoading(final String fileName)
   {
@@ -1812,7 +1806,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       // TODO: verify that frames are recoverable when in headless mode
       return null;
     }
-    List<AlignmentPanel> aps = new ArrayList<AlignmentPanel>();
+    List<AlignmentPanel> aps = new ArrayList<>();
     AlignFrame[] frames = getAlignFrames();
     if (frames == null)
     {
@@ -1847,7 +1841,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    */
   public static AlignmentViewport[] getViewports(String sequenceSetId)
   {
-    List<AlignmentViewport> viewp = new ArrayList<AlignmentViewport>();
+    List<AlignmentViewport> viewp = new ArrayList<>();
     if (desktop != null)
     {
       AlignFrame[] frames = Desktop.getAlignFrames();
@@ -2360,7 +2354,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           // SEQUENCE_ID which is not the default EMBL_EBI link
           ListIterator<String> li = links.listIterator();
           boolean check = false;
-          List<JLabel> urls = new ArrayList<JLabel>();
+          List<JLabel> urls = new ArrayList<>();
           while (li.hasNext())
           {
             String link = li.next();
@@ -2454,6 +2448,7 @@ public class Desktop extends jalview.jbgui.GDesktop
         Thread worker = new Thread(this);
         worker.start();
       }
+      repaint();
     }
 
     public boolean isShowMemoryUsage()
@@ -2545,7 +2540,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       return null;
     }
-    List<AlignFrame> avp = new ArrayList<AlignFrame>();
+    List<AlignFrame> avp = new ArrayList<>();
     // REVERSE ORDER
     for (int i = frames.length - 1; i > -1; i--)
     {
@@ -2590,7 +2585,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       return null;
     }
-    List<GStructureViewer> avp = new ArrayList<GStructureViewer>();
+    List<GStructureViewer> avp = new ArrayList<>();
     // REVERSE ORDER
     for (int i = frames.length - 1; i > -1; i--)
     {
@@ -2733,8 +2728,8 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     if (progressBars == null)
     {
-      progressBars = new Hashtable<Long, JPanel>();
-      progressBarHandlers = new Hashtable<Long, IProgressIndicatorHandler>();
+      progressBars = new Hashtable<>();
+      progressBarHandlers = new Hashtable<>();
     }
 
     if (progressBars.get(new Long(id)) != null)
index bd74db5..0963b31 100644 (file)
@@ -528,6 +528,7 @@ public class FeatureSettings extends JPanel
     final String grp = group;
     final JCheckBox check = new JCheckBox(group, visible);
     check.setFont(new Font("Serif", Font.BOLD, 12));
+    check.setToolTipText(group);
     check.addItemListener(new ItemListener()
     {
       @Override
index 5271e4f..6cddcca 100755 (executable)
@@ -327,22 +327,17 @@ public class FontChooser extends GFontChooser
       ap.fontChanged();
 
       /*
-       * adjust other half of split frame if any, if either same
-       * font, or proportionate scaling, is selected
+       * adjust other half of split frame if present, whether or not same font or
+       * scale to cDNA is selected, because a font change may affect character
+       * width, and this is kept the same in both panels
        */
-      if (fontAsCdna.isEnabled())
+      if (fontAsCdna.isVisible())
       {
         if (fontAsCdna.isSelected())
         {
-          /*
-           * copy the font
-           */
           ap.av.getCodingComplement().setFont(newFont, true);
         }
 
-        /*
-         * adjust layout for font change / reset / sizing
-         */
         SplitFrame splitFrame = (SplitFrame) ap.alignFrame
                 .getSplitViewContainer();
         splitFrame.adjustLayout();
index 1658f0f..b357234 100644 (file)
@@ -2295,6 +2295,7 @@ public class Jalview2XML
 
       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
       af = loadJalviewAlign(jprovider);
+      af.setMenusForViewport();
 
     } catch (MalformedURLException e)
     {
index 27f9c3f..7371eb5 100644 (file)
@@ -21,7 +21,9 @@
 package jalview.gui;
 
 import jalview.api.AlignViewportI;
+import jalview.bin.Cache;
 import jalview.renderer.OverviewRenderer;
+import jalview.renderer.OverviewResColourFinder;
 import jalview.viewmodel.OverviewDimensions;
 
 import java.awt.Color;
@@ -41,6 +43,8 @@ public class OverviewCanvas extends JComponent
 
   private volatile boolean updaterunning = false;
 
+  private boolean dispose = false;
+
   private BufferedImage miniMe;
 
   private BufferedImage lastMiniMe = null;
@@ -57,16 +61,29 @@ public class OverviewCanvas extends JComponent
 
   private AlignViewportI av;
 
+  private OverviewResColourFinder cf;
+
+  private ProgressPanel progressPanel;
+
   public OverviewCanvas(OverviewDimensions overviewDims,
-          AlignViewportI alignvp)
+          AlignViewportI alignvp, ProgressPanel pp)
   {
     od = overviewDims;
     av = alignvp;
+    progressPanel = pp;
 
     sr = new SequenceRenderer(av);
     sr.renderGaps = false;
-    sr.forOverview = true;
     fr = new jalview.renderer.seqfeatures.FeatureRenderer(av);
+
+    boolean useLegacy = Cache.getDefault(Preferences.USE_LEGACY_GAP, false);
+    Color gapCol = Cache.getDefaultColour(Preferences.GAP_COLOUR,
+            jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP);
+    Color hiddenCol = Cache.getDefaultColour(Preferences.HIDDEN_COLOUR,
+            jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN);
+    cf = new OverviewResColourFinder(useLegacy, gapCol, hiddenCol);
+
+    setSize(od.getWidth(), od.getHeight());
   }
 
   /**
@@ -126,7 +143,11 @@ public class OverviewCanvas extends JComponent
 
     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
 
-    or = new OverviewRenderer(sr, fr, od);
+    or = new OverviewRenderer(fr, od, av.getAlignment(),
+            av.getResidueShading(), cf);
+
+    or.addPropertyChangeListener(progressPanel);
+
     miniMe = or.draw(od.getRows(av.getAlignment()),
             od.getColumns(av.getAlignment()));
 
@@ -142,10 +163,15 @@ public class OverviewCanvas extends JComponent
     }
     System.gc();
 
+    or.removePropertyChangeListener(progressPanel);
+    or = null;
     if (restart)
     {
       restart = false;
-      draw(showSequenceFeatures, showAnnotation, transferRenderer);
+      if (!dispose)
+      {
+        draw(showSequenceFeatures, showAnnotation, transferRenderer);
+      }
     }
     else
     {
@@ -173,16 +199,71 @@ public class OverviewCanvas extends JComponent
     }
     else if (lastMiniMe != null)
     {
-      g.drawImage(lastMiniMe, 0, 0, this);
-      if (lastMiniMe != miniMe)
+      // is this a resize?
+      if ((getWidth() > 0) && (getHeight() > 0)
+              && ((getWidth() != od.getWidth())
+                      || (getHeight() != od.getHeight())))
       {
-        g.setColor(TRANS_GREY);
-        g.fillRect(0, 0, getWidth(), getHeight());
+        // if there is annotation, scale the alignment and annotation separately
+        if (od.getGraphHeight() > 0)
+        {
+          BufferedImage topImage = lastMiniMe.getSubimage(0, 0,
+                  od.getWidth(), od.getSequencesHeight());
+          BufferedImage bottomImage = lastMiniMe.getSubimage(0,
+                  od.getSequencesHeight(), od.getWidth(),
+                  od.getGraphHeight());
+
+          // must be done at this point as we rely on using old width/height
+          // above, and new width/height below
+          od.setWidth(getWidth());
+          od.setHeight(getHeight());
+
+          // stick the images back together so lastMiniMe is consistent in the
+          // event of a repaint - BUT probably not thread safe
+          lastMiniMe = new BufferedImage(od.getWidth(), od.getHeight(),
+                  BufferedImage.TYPE_INT_RGB);
+          Graphics lg = lastMiniMe.getGraphics();
+          lg.drawImage(topImage, 0, 0, od.getWidth(),
+                  od.getSequencesHeight(), null);
+          lg.drawImage(bottomImage, 0, od.getSequencesHeight(),
+                  od.getWidth(), od.getGraphHeight(), this);
+          lg.dispose();
+        }
+        else
+        {
+          od.setWidth(getWidth());
+          od.setHeight(getHeight());
+        }
+
+        // scale lastMiniMe to the new size
+        g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
+
+        // make sure the box is in the right place
+        od.setBoxPosition(av.getAlignment().getHiddenSequences(),
+                av.getAlignment().getHiddenColumns());
+      }
+      else // not a resize
+      {
+        // fall back to normal behaviour
+        g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
       }
     }
 
+    // draw the box
     g.setColor(Color.red);
     od.drawBox(g);
   }
 
+  public void dispose()
+  {
+    dispose = true;
+    synchronized (this)
+    {
+      restart = true;
+      if (or != null)
+      {
+        or.setRedraw(true);
+      }
+    }
+  }
 }
index 28de801..51d7a84 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import jalview.bin.Cache;
+import jalview.renderer.OverviewRenderer;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.viewmodel.OverviewDimensions;
@@ -68,6 +70,8 @@ public class OverviewPanel extends JPanel
 
   private boolean draggingBox = false;
 
+  private ProgressPanel progressPanel;
+
   /**
    * Creates a new OverviewPanel object.
    * 
@@ -79,30 +83,62 @@ public class OverviewPanel extends JPanel
     this.av = alPanel.av;
     this.ap = alPanel;
 
-    od = new OverviewDimensionsShowHidden(av.getRanges(),
+    showHidden = Cache.getDefault(Preferences.SHOW_OV_HIDDEN_AT_START,
+            true);
+    if (showHidden)
+    {
+      od = new OverviewDimensionsShowHidden(av.getRanges(),
             (av.isShowAnnotation()
                     && av.getAlignmentConservationAnnotation() != null));
+    }
+    else
+    {
+      od = new OverviewDimensionsHideHidden(av.getRanges(),
+              (av.isShowAnnotation()
+                      && av.getAlignmentConservationAnnotation() != null));
+    }
 
-    setSize(od.getWidth(), od.getHeight());
-
-    oviewCanvas = new OverviewCanvas(od, av);
     setLayout(new BorderLayout());
+    progressPanel = new ProgressPanel(OverviewRenderer.UPDATE,
+            MessageManager.getString("label.oview_calc"), getWidth());
+    this.add(progressPanel, BorderLayout.SOUTH);
+    oviewCanvas = new OverviewCanvas(od, av, progressPanel);
+
     add(oviewCanvas, BorderLayout.CENTER);
 
     av.getRanges().addPropertyChangeListener(this);
 
+    // without this the overview window does not size to fit the overview canvas
+    setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
+
     addComponentListener(new ComponentAdapter()
     {
       @Override
       public void componentResized(ComponentEvent evt)
       {
-        if ((getWidth() != od.getWidth())
-                || (getHeight() != (od.getHeight())))
+        // Resize is called on the initial display of the overview.
+        // This code adjusts sizes to account for the progress bar if it has not
+        // already been accounted for, which triggers another resize call for
+        // the correct sizing, at which point the overview image is updated.
+        // (This avoids a double recalculation of the image.)
+        if (getWidth() == od.getWidth() && getHeight() == od.getHeight()
+                + progressPanel.getHeight())
         {
           updateOverviewImage();
-          setBoxPosition();
+        }
+        else
+        {
+          if ((getWidth() > 0) && (getHeight() > 0))
+          {
+            od.setWidth(getWidth());
+            od.setHeight(getHeight() - progressPanel.getHeight());
+          }
+
+          setPreferredSize(new Dimension(od.getWidth(),
+                  od.getHeight() + progressPanel.getHeight()));
         }
       }
+
     });
 
     addMouseMotionListener(new MouseMotionAdapter()
@@ -158,13 +194,13 @@ public class OverviewPanel extends JPanel
           }
         }
         else
-        // if (!av.getWrapAlignment())
         {
+          // don't do anything if the mouse press is in the overview's box
+          // (wait to see if it's a drag instead)
+          // otherwise update the viewport
           if (!od.isPositionInBox(evt.getX(), evt.getY()))
           {
-            // don't do anything if the mouse press is in the overview's box
-            // (wait to see if it's a drag instead)
-            // otherwise update the viewport
+            draggingBox = false;
             od.updateViewportFromMouse(evt.getX(), evt.getY(),
                     av.getAlignment().getHiddenSequences(),
                     av.getAlignment().getHiddenColumns());
@@ -180,15 +216,6 @@ public class OverviewPanel extends JPanel
       }
 
       @Override
-      public void mouseReleased(MouseEvent evt)
-      {
-        if (draggingBox)
-        {
-          draggingBox = false;
-        }
-      }
-
-      @Override
       public void mouseClicked(MouseEvent evt)
       {
         if (SwingUtilities.isRightMouseButton(evt))
@@ -197,8 +224,6 @@ public class OverviewPanel extends JPanel
         }
       }
     });
-
-    updateOverviewImage();
   }
 
   /*
@@ -266,10 +291,11 @@ public class OverviewPanel extends JPanel
     if ((getWidth() > 0) && (getHeight() > 0))
     {
       od.setWidth(getWidth());
-      od.setHeight(getHeight());
+      od.setHeight(getHeight() - progressPanel.getHeight());
     }
-
-    setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
+    
+    setPreferredSize(new Dimension(od.getWidth(),
+            od.getHeight() + progressPanel.getHeight()));
 
     if (oviewCanvas.restartDraw())
     {
@@ -279,16 +305,21 @@ public class OverviewPanel extends JPanel
     Thread thread = new Thread(this);
     thread.start();
     repaint();
+
+    
   }
 
   @Override
   public void run()
   {
-    oviewCanvas.draw(av.isShowSequenceFeatures(),
-            (av.isShowAnnotation()
-                    && av.getAlignmentConservationAnnotation() != null),
-            ap.getSeqPanel().seqCanvas.getFeatureRenderer());
-    setBoxPosition();
+    if (oviewCanvas != null)
+    {
+      oviewCanvas.draw(av.isShowSequenceFeatures(),
+              (av.isShowAnnotation()
+                      && av.getAlignmentConservationAnnotation() != null),
+              ap.getSeqPanel().seqCanvas.getFeatureRenderer());
+      setBoxPosition();
+    }
   }
 
   /**
@@ -298,9 +329,12 @@ public class OverviewPanel extends JPanel
    */
   private void setBoxPosition()
   {
-    od.setBoxPosition(av.getAlignment().getHiddenSequences(),
-            av.getAlignment().getHiddenColumns());
-    repaint();
+    if (od != null)
+    {
+      od.setBoxPosition(av.getAlignment().getHiddenSequences(),
+              av.getAlignment().getHiddenColumns());
+      repaint();
+    }
   }
 
   @Override
@@ -317,8 +351,10 @@ public class OverviewPanel extends JPanel
     try
     {
       av.getRanges().removePropertyChangeListener(this);
+      oviewCanvas.dispose();
     } finally
     {
+      progressPanel = null;
       av = null;
       oviewCanvas = null;
       ap = null;
index 2473ba9..846ba64 100644 (file)
@@ -455,8 +455,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         buildGroupURLMenu(sg, groupLinks);
       }
       // Add a 'show all structures' for the current selection
-      Hashtable<String, PDBEntry> pdbe = new Hashtable<String, PDBEntry>(),
-              reppdb = new Hashtable<String, PDBEntry>();
+      Hashtable<String, PDBEntry> pdbe = new Hashtable<>(), reppdb = new Hashtable<>();
+
       SequenceI sqass = null;
       for (SequenceI sq : ap.av.getSequenceSelection())
       {
@@ -523,7 +523,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   void addFeatureLinks(final SequenceI seq, List<String> links)
   {
     JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
-    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
+    Map<String, List<String>> linkset = new LinkedHashMap<>();
 
     for (String link : links)
     {
@@ -609,8 +609,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
      * the insertion order, which is the order of the annotations on the
      * alignment.
      */
-    Map<String, List<List<String>>> shownTypes = new LinkedHashMap<String, List<List<String>>>();
-    Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<String, List<List<String>>>();
+    Map<String, List<List<String>>> shownTypes = new LinkedHashMap<>();
+    Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<>();
     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
             AlignmentAnnotationUtils.asList(annotations), forSequences);
 
@@ -716,7 +716,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
     SequenceI[] seqs = ap.av.getSelectionAsNewSequence();
     String[][] idandseqs = GroupUrlLink.formStrings(seqs);
-    Hashtable<String, Object[]> commonDbrefs = new Hashtable<String, Object[]>();
+    Hashtable<String, Object[]> commonDbrefs = new Hashtable<>();
     for (int sq = 0; sq < seqs.length; sq++)
     {
 
@@ -1377,8 +1377,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
      * Temporary store to hold distinct calcId / type pairs for the tooltip.
      * Using TreeMap means calcIds are shown in alphabetical order.
      */
-    SortedMap<String, String> tipEntries = new TreeMap<String, String>();
-    final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<SequenceI, List<AlignmentAnnotation>>();
+    SortedMap<String, String> tipEntries = new TreeMap<>();
+    final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<>();
     AlignmentI al = this.ap.av.getAlignment();
     AlignmentUtils.findAddableReferenceAnnotations(forSequences, tipEntries,
             candidates, al);
@@ -1561,7 +1561,9 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   void refresh()
   {
     ap.updateAnnotation();
-    ap.paintAlignment(true);
+    // removed paintAlignment(true) here:
+    // updateAnnotation calls paintAlignment already, so don't need to call
+    // again
 
     PaintRefresher.Refresh(this, ap.av.getSequenceSetId());
   }
@@ -1929,8 +1931,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       return;
     }
 
-    List<SequenceI> seqs = new ArrayList<SequenceI>();
-    List<SequenceFeature> features = new ArrayList<SequenceFeature>();
+    List<SequenceI> seqs = new ArrayList<>();
+    List<SequenceFeature> features = new ArrayList<>();
 
     /*
      * assemble dataset sequences, and template new sequence features,
index c3c9239..6635dbe 100755 (executable)
@@ -109,6 +109,14 @@ public class Preferences extends GPreferences
 
   public static final String SHOW_OCCUPANCY = "SHOW_OCCUPANCY";
 
+  public static final String SHOW_OV_HIDDEN_AT_START = "SHOW_OV_HIDDEN_AT_START";
+
+  public static final String USE_LEGACY_GAP = "USE_LEGACY_GAP";
+
+  public static final String GAP_COLOUR = "GAP_COLOUR";
+
+  public static final String HIDDEN_COLOUR = "HIDDEN_COLOUR";
+
   private static final int MIN_FONT_SIZE = 1;
 
   private static final int MAX_FONT_SIZE = 30;
@@ -155,7 +163,7 @@ public class Preferences extends GPreferences
      * .properties file as '|' separated strings
      */
 
-    groupURLLinks = new ArrayList<String>();
+    groupURLLinks = new ArrayList<>();
   }
 
   JInternalFrame frame;
@@ -309,6 +317,21 @@ public class Preferences extends GPreferences
             Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX", Color.red));
 
     /*
+     * Set overview panel defaults
+     */
+    gapColour.setBackground(
+            Cache.getDefaultColour(GAP_COLOUR,
+                    jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP));
+    hiddenColour.setBackground(
+            Cache.getDefaultColour(HIDDEN_COLOUR,
+                    jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN));
+    useLegacyGap.setSelected(Cache.getDefault(USE_LEGACY_GAP, false));
+    gapLabel.setEnabled(!useLegacyGap.isSelected());
+    gapColour.setEnabled(!useLegacyGap.isSelected());
+    showHiddenAtStart
+            .setSelected(Cache.getDefault(SHOW_OV_HIDDEN_AT_START, true));
+
+    /*
      * Set Structure tab defaults.
      */
     final boolean structSelected = Cache.getDefault(STRUCT_FROM_PDB, false);
@@ -631,6 +654,16 @@ public class Preferences extends GPreferences
             maxColour.getBackground());
 
     /*
+     * Save Overview settings
+     */
+    Cache.setColourProperty(GAP_COLOUR, gapColour.getBackground());
+    Cache.setColourProperty(HIDDEN_COLOUR, hiddenColour.getBackground());
+    Cache.applicationProperties.setProperty(USE_LEGACY_GAP,
+            Boolean.toString(useLegacyGap.isSelected()));
+    Cache.applicationProperties.setProperty(SHOW_OV_HIDDEN_AT_START,
+            Boolean.toString(showHiddenAtStart.isSelected()));
+
+    /*
      * Save Structure settings
      */
     Cache.applicationProperties.setProperty(ADD_TEMPFACT_ANN,
@@ -1035,6 +1068,63 @@ public class Preferences extends GPreferences
   }
 
   @Override
+  public void gapColour_actionPerformed(JPanel gap)
+  {
+    if (!useLegacyGap.isSelected())
+    {
+      Color col = JColorChooser.showDialog(this,
+              MessageManager.getString("label.select_gap_colour"),
+              gapColour.getBackground());
+      if (col != null)
+      {
+        gap.setBackground(col);
+      }
+      gap.repaint();
+    }
+  }
+
+  @Override
+  public void hiddenColour_actionPerformed(JPanel hidden)
+  {
+    Color col = JColorChooser.showDialog(this,
+            MessageManager.getString("label.select_hidden_colour"),
+            hiddenColour.getBackground());
+    if (col != null)
+    {
+      hidden.setBackground(col);
+    }
+    hidden.repaint();
+  }
+
+  @Override
+  protected void useLegacyGaps_actionPerformed(ActionEvent e)
+  {
+    boolean enabled = useLegacyGap.isSelected();
+    if (enabled)
+    {
+      gapColour.setBackground(
+              jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_LEGACY_GAP);
+    }
+    else
+    {
+      gapColour.setBackground(
+              jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP);
+    }
+    gapColour.setEnabled(!enabled);
+    gapLabel.setEnabled(!enabled);
+  }
+
+  @Override
+  protected void resetOvDefaults_actionPerformed(ActionEvent e)
+  {
+    useLegacyGap.setSelected(false);
+    useLegacyGaps_actionPerformed(null);
+    showHiddenAtStart.setSelected(true);
+    hiddenColour.setBackground(
+            jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN);
+  }
+
+  @Override
   protected void userIdWidth_actionPerformed()
   {
     try
diff --git a/src/jalview/gui/ProgressPanel.java b/src/jalview/gui/ProgressPanel.java
new file mode 100644 (file)
index 0000000..170e9eb
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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 jalview.api.RendererListenerI;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.beans.PropertyChangeEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * A class to manage a panel containing a label and progress bar updated by an
+ * event firing
+ * 
+ * @author kmourao
+ *
+ */
+public class ProgressPanel extends JPanel implements RendererListenerI
+{
+  // max value of progress bar: values expected to be %s
+  private final int MAXVALUE = 100;
+
+  private final String VISIBLE = "VISIBLE";
+
+  private final String INVISIBLE = "INVISIBLE";
+
+  // name of event property which updates the progress bar
+  private String eventName;
+
+  private JProgressBar progressBar;
+
+  private JLabel progressLabel;
+
+  private JPanel labelPanel = new JPanel();
+
+  private CardLayout labelLayout = new CardLayout();
+
+  private JPanel barPanel = new JPanel();
+
+  private CardLayout barLayout = new CardLayout();
+
+  /**
+   * Construct a JPanel containing a progress bar and a label.
+   * 
+   * @param eventPropertyName
+   *          The name of the event property to update the progress bar
+   * @param label
+   *          The label to place next to the progress bar
+   */
+  public ProgressPanel(String eventPropertyName, String label, int maxwidth)
+  {
+    super(new BorderLayout(10, 0));
+    setBorder(new EmptyBorder(0, 3, 0, 0));
+
+    eventName = eventPropertyName;
+    String labelText = label;
+
+    final int w = maxwidth;
+
+    progressBar = new JProgressBar()
+    {
+      @Override
+      public Dimension getMaximumSize()
+      {
+        return new Dimension(w, 1);
+      }
+    };
+    progressBar.setMinimum(0);
+    progressBar.setPreferredSize(progressBar.getMaximumSize());
+    progressLabel = new JLabel(labelText);
+    progressLabel.setFont(new java.awt.Font("Verdana", 0, 11));
+
+    // Use a CardLayout to stop the progress bar panel moving around when
+    // changing visibility
+    labelPanel.setLayout(labelLayout);
+    barPanel.setLayout(barLayout);
+  
+    labelPanel.add(progressLabel, VISIBLE);
+    labelPanel.add(new JPanel(), INVISIBLE);
+    barPanel.add(progressBar, VISIBLE);
+    barPanel.add(new JPanel(), INVISIBLE);
+
+    labelLayout.show(labelPanel, VISIBLE);
+    barLayout.show(barPanel, VISIBLE);
+
+    add(labelPanel, BorderLayout.WEST);
+    add(barPanel, BorderLayout.CENTER);
+    add(new JLabel(" "), BorderLayout.EAST);
+
+    setBorder(BorderFactory.createLineBorder(Color.black));
+    // setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
+  }
+
+  @Override
+  /**
+   * Update the progress bar in response to the event. Expects the value
+   * supplied by the event to be in the range 0-100 i.e. a percentage
+   */
+  public void propertyChange(PropertyChangeEvent evt)
+  {
+    if (evt.getPropertyName().equals(eventName))
+    {
+      int progress = (int) evt.getNewValue();
+      progressBar.setValue(progress);
+
+      // switch progress bar to visible if it is not visible and current
+      // progress is less than MAXVALUE
+      // switch progress bar to invisible if it is visible and we reached
+      // MAXVALUE
+      if (progress < MAXVALUE && !progressBar.isVisible())
+      {
+        labelLayout.show(labelPanel, VISIBLE);
+        barLayout.show(barPanel, VISIBLE);
+      }
+      if (progress >= MAXVALUE)
+      {
+        labelLayout.show(labelPanel, INVISIBLE);
+        barLayout.show(barPanel, INVISIBLE);
+      }
+    }
+  }
+}
index 22e2fe4..2eb8c4d 100755 (executable)
@@ -31,6 +31,7 @@ import jalview.util.Comparison;
 import jalview.viewmodel.ViewportListenerI;
 import jalview.viewmodel.ViewportRanges;
 
+import java.awt.AlphaComposite; // ok
 import java.awt.BasicStroke;
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -57,7 +58,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
   final FeatureRenderer fr;
 
-  final SequenceRenderer sr;
+  final SequenceRenderer seqRdr; //ok
 
   BufferedImage img;
 
@@ -102,7 +103,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
   {
     this.av = ap.av;
     fr = new FeatureRenderer(ap);
-    sr = new SequenceRenderer(av);
+    seqRdr = new SequenceRenderer(av); //ok
     setLayout(new BorderLayout());
     PaintRefresher.Register(this, av.getSequenceSetId());
     setBackground(Color.white);
@@ -112,7 +113,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
   public SequenceRenderer getSequenceRenderer()
   {
-    return sr;
+    return seqRdr; //ok
   }
 
   public FeatureRenderer getFeatureRenderer()
@@ -258,6 +259,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     }
   }
 
+  //ok
   /**
    * Does a fast paint of an alignment in response to a scroll. Most of the
    * visible region is simply copied and shifted, and then any newly visible
@@ -278,7 +280,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
    */
   public void fastPaint(int horizontal, int vertical)
   {
-    if (fastpainting || gg == null)
+    if (fastpainting || gg == null || img == null) //ok
     {
       return;
     }
@@ -289,7 +291,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     {
       int charHeight = av.getCharHeight();
       int charWidth = av.getCharWidth();
-
+    
       ViewportRanges ranges = av.getRanges();
       int startRes = ranges.getStartRes();
       int endRes = ranges.getEndRes();
@@ -298,123 +300,262 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       int transX = 0;
       int transY = 0;
 
-      gg.copyArea(horizontal * charWidth, vertical * charHeight, imgWidth,
-              imgHeight, -horizontal * charWidth, -vertical * charHeight);
+    gg.copyArea(horizontal * charWidth, vertical * charHeight,
+            img.getWidth(), img.getHeight(), -horizontal * charWidth,
+            -vertical * charHeight);
 
-      if (horizontal > 0) // scrollbar pulled right, image to the left
-      {
-        transX = (endRes - startRes - horizontal) * charWidth;
-        startRes = endRes - horizontal;
+    if (horizontal > 0) // scrollbar pulled right, image to the left
+    {
+      transX = (endRes - startRes - horizontal) * charWidth;
+      startRes = endRes - horizontal;
+    }
+    else if (horizontal < 0)
+    {
+      endRes = startRes - horizontal;
+    }
+    else if (vertical > 0) // scroll down
+    {
+      startSeq = endSeq - vertical;
+
+      if (startSeq < ranges.getStartSeq())
+      { // ie scrolling too fast, more than a page at a time
+        startSeq = ranges.getStartSeq();
       }
-      else if (horizontal < 0)
+      else
       {
-        endRes = startRes - horizontal;
+        transY = img.getHeight() - ((vertical + 1) * charHeight);
       }
-      else if (vertical > 0) // scroll down
-      {
-        startSeq = endSeq - vertical;
+    }
+    else if (vertical < 0)
+    {
+      endSeq = startSeq - vertical;
 
-        if (startSeq < ranges.getStartSeq())
-        { // ie scrolling too fast, more than a page at a time
-          startSeq = ranges.getStartSeq();
-        }
-        else
-        {
-          transY = imgHeight - ((vertical + 1) * charHeight);
-        }
-      }
-      else if (vertical < 0)
+      if (endSeq > ranges.getEndSeq())
       {
-        endSeq = startSeq - vertical;
-
-        if (endSeq > ranges.getEndSeq())
-        {
-          endSeq = ranges.getEndSeq();
-        }
+        endSeq = ranges.getEndSeq();
       }
+    }
 
-      gg.translate(transX, transY);
-      drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
-      gg.translate(-transX, -transY);
+    gg.translate(transX, transY);
+    drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
+    gg.translate(-transX, -transY);
 
-      repaint();
+    repaint();
     } finally
     {
       fastpainting = false;
     }
   }
+  //ok
 
+  //ok
   @Override
   public void paintComponent(Graphics g)
   {
+    super.paintComponent(g);    
+    
     int charHeight = av.getCharHeight();
     int charWidth = av.getCharWidth();
-    BufferedImage lcimg = img; // take reference since other threads may null
-    // img and call later.
-    super.paintComponent(g);
 
-    if (lcimg != null && (fastPaint
+    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)))
     {
+      BufferedImage lcimg = buildLocalImage(selectImage);
       g.drawImage(lcimg, 0, 0, this);
       fastPaint = false;
-      return;
     }
+    else if ((width > 0) && (height > 0))
+    {
+      // img is a cached version of the last view we drew, if any
+      // if we have no img or the size has changed, make a new one
+      if (img == null || width != img.getWidth()
+              || height != img.getHeight())
+      {
+        img = setupImage();
+        if (img == null)
+        {
+          return;
+        }
+        gg = (Graphics2D) img.getGraphics();
+        gg.setFont(av.getFont());
+      }
 
-    // this draws the whole of the alignment
-    imgWidth = getWidth();
-    imgHeight = getHeight();
+      if (av.antiAlias)
+      {
+        gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                RenderingHints.VALUE_ANTIALIAS_ON);
+      }
 
-    imgWidth -= (imgWidth % charWidth);
-    imgHeight -= (imgHeight % charHeight);
+      gg.setColor(Color.white);
+      gg.fillRect(0, 0, img.getWidth(), img.getHeight());
+
+      if (av.getWrapAlignment())
+      {
+        drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
+      }
+      else
+      {
+        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);
+    }
+  }
+//ok
+  
+  /**
+   * Draw an alignment panel for printing
+   * 
+   * @param g1
+   *          Graphics object to draw with
+   * @param startRes
+   *          start residue of print area
+   * @param endRes
+   *          end residue of print area
+   * @param startSeq
+   *          start sequence of print area
+   * @param endSeq
+   *          end sequence of print area
+   */
+  public void drawPanelForPrinting(Graphics g1, int startRes, int endRes,
+          int startSeq, int endSeq)
+  {
+    drawPanel(g1, startRes, endRes, startSeq, endSeq, 0);
 
-    if ((imgWidth < 1) || (imgHeight < 1))
+    BufferedImage selectImage = drawSelectionGroup(startRes, endRes,
+            startSeq, endSeq);
+    if (selectImage != null)
     {
-      return;
+      ((Graphics2D) g1).setComposite(AlphaComposite
+              .getInstance(AlphaComposite.SRC_OVER));
+      g1.drawImage(selectImage, 0, 0, this);
     }
+  }
+
+  /**
+   * Draw a wrapped alignment panel for printing
+   * 
+   * @param g
+   *          Graphics object to draw with
+   * @param canvasWidth
+   *          width of drawing area
+   * @param canvasHeight
+   *          height of drawing area
+   * @param startRes
+   *          start residue of print area
+   */
+  public void drawWrappedPanelForPrinting(Graphics g, int canvasWidth,
+          int canvasHeight, int startRes)
+  {
+    SequenceGroup group = av.getSelectionGroup();
+
+    drawWrappedPanel(g, canvasWidth, canvasHeight, startRes);
 
-    if (lcimg == null || imgWidth != lcimg.getWidth()
-            || imgHeight != lcimg.getHeight())
+    if (group != null)
     {
+      BufferedImage selectImage = null;
       try
       {
-        lcimg = img = new BufferedImage(imgWidth, imgHeight,
-                BufferedImage.TYPE_INT_RGB);
-        gg = (Graphics2D) img.getGraphics();
-        gg.setFont(av.getFont());
+        selectImage = new BufferedImage(canvasWidth, canvasHeight,
+                BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
       } catch (OutOfMemoryError er)
       {
         System.gc();
-        System.err.println("SeqCanvas OutOfMemory Redraw Error.\n" + er);
-        new OOMWarning("Creating alignment image for display", er);
-
-        return;
+        System.err.println("Print image OutOfMemory Error.\n" + er);
+        new OOMWarning("Creating wrapped alignment image for printing", er);
+      }
+      if (selectImage != null)
+      {
+        Graphics2D g2 = selectImage.createGraphics();
+        setupSelectionGroup(g2, selectImage);
+        drawWrappedSelection(g2, group, canvasWidth, canvasHeight,
+                startRes);
+
+        g2.setComposite(
+                AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
+        g.drawImage(selectImage, 0, 0, this);
+        g2.dispose();
       }
     }
+  }
 
-    if (av.antiAlias)
+  /*
+   * Make a local image by combining the cached image img
+   * with any selection
+   */
+  private BufferedImage buildLocalImage(BufferedImage selectImage)
+  {
+    // clone the cached image
+    BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
+            img.getType());
+    Graphics2D g2d = lcimg.createGraphics();
+    g2d.drawImage(img, 0, 0, null);
+
+    // overlay selection group on lcimg
+    if (selectImage != null)
     {
-      gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-              RenderingHints.VALUE_ANTIALIAS_ON);
+      g2d.setComposite(
+              AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
+      g2d.drawImage(selectImage, 0, 0, this);
     }
+    g2d.dispose();
 
-    gg.setColor(Color.white);
-    gg.fillRect(0, 0, imgWidth, imgHeight);
+    return lcimg;
+  }
 
-    ViewportRanges ranges = av.getRanges();
-    if (av.getWrapAlignment())
+  /*
+   * Set up a buffered image of the correct height and size for the sequence canvas
+   */
+  private BufferedImage setupImage()
+  {
+    BufferedImage lcimg = null;
+
+    int charWidth = av.getCharWidth();
+    int charHeight = av.getCharHeight();
+    
+    int width = getWidth();
+    int height = getHeight();
+
+    width -= (width % charWidth);
+    height -= (height % charHeight);
+
+    if ((width < 1) || (height < 1))
     {
-      drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
+      return null;
     }
-    else
+
+    try
     {
-      drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
-              ranges.getStartSeq(), ranges.getEndSeq(), 0);
-    }
+      lcimg = new BufferedImage(width, height,
+              BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
+    } catch (OutOfMemoryError er)
+    {
+      System.gc();
+      System.err.println(
+              "Group image OutOfMemory Redraw Error.\n" + er);
+      new OOMWarning("Creating alignment image for display", er);
 
-    g.drawImage(lcimg, 0, 0, this);
+      return null;
+    }
 
+    return lcimg;
   }
 
   /**
@@ -567,7 +708,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
      */
     wrappedRepeatHeightPx = wrappedSpaceAboveAlignment;
     // add sequences
-    wrappedRepeatHeightPx += av.getRanges().getViewportHeight() * charHeight;
+    wrappedRepeatHeightPx += av.getRanges().getViewportHeight()
+            * charHeight;
     // add annotations panel height if shown
     wrappedRepeatHeightPx += getAnnotationHeight();
 
@@ -576,8 +718,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
      * this is just canvas width less scale left and right (if shown), 
      * as a whole multiple of character widths 
      */
-    int wrappedWidthInResidues = (canvasWidth - labelWidthEast - labelWidthWest)
-            / charWidth;
+    int wrappedWidthInResidues = (canvasWidth - labelWidthEast
+            - labelWidthWest) / charWidth;
 
     /*
      * number of visible widths (the last one may be part height),
@@ -615,8 +757,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
    * @param endColumn
    * @param canvasHeight
    */
-  protected void drawWrappedWidth(Graphics g, int ypos,
-          int startColumn, int endColumn, int canvasHeight)
+  protected void drawWrappedWidth(Graphics g, int ypos, int startColumn,
+          int endColumn, int canvasHeight)
   {
     int charHeight = av.getCharHeight();
     int charWidth = av.getCharWidth();
@@ -781,6 +923,66 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     }
   }
 
+  /*
+   * Draw a selection group over a wrapped alignment
+   */
+  private void drawWrappedSelection(Graphics2D g, SequenceGroup group,
+          int canvasWidth,
+          int canvasHeight, int startRes)
+  {
+       int charHeight = av.getCharHeight();
+       int charWidth = av.getCharWidth();
+         
+    // height gap above each panel
+    int hgap = charHeight;
+    if (av.getScaleAboveWrapped())
+    {
+      hgap += charHeight;
+    }
+
+    int cWidth = (canvasWidth - labelWidthEast - labelWidthWest)
+            / charWidth;
+    int cHeight = av.getAlignment().getHeight() * charHeight;
+
+    int startx = startRes;
+    int endx;
+    int ypos = hgap; // vertical offset
+    int maxwidth = av.getAlignment().getWidth();
+
+    if (av.hasHiddenColumns())
+    {
+      maxwidth = av.getAlignment().getHiddenColumns()
+              .findColumnPosition(maxwidth);
+    }
+
+    // chop the wrapped alignment extent up into panel-sized blocks and treat
+    // each block as if it were a block from an unwrapped alignment
+    while ((ypos <= canvasHeight) && (startx < maxwidth))
+    {
+      // set end value to be start + width, or maxwidth, whichever is smaller
+      endx = startx + cWidth - 1;
+
+      if (endx > maxwidth)
+      {
+        endx = maxwidth;
+      }
+
+      g.translate(labelWidthWest, 0);
+
+      drawUnwrappedSelection(g, group, startx, endx, 0,
+              av.getAlignment().getHeight() - 1,
+              ypos);
+
+      g.translate(-labelWidthWest, 0);
+
+      // update vertical offset
+      ypos += cHeight + getAnnotationHeight() + hgap;
+
+      // update horizontal offset
+      startx += cWidth;
+    }
+  }
+
   int getAnnotationHeight()
   {
     if (!av.isShowAnnotation())
@@ -914,7 +1116,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     int charWidth = av.getCharWidth();
 
     g.setFont(av.getFont());
-    sr.prepare(g, av.isRenderGaps());
+    seqRdr.prepare(g, av.isRenderGaps());
 
     SequenceI nextSeq;
 
@@ -929,7 +1131,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
         // empty
         continue;
       }
-      sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
+      seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
               startRes, endRes, offset + ((i - startSeq) * charHeight));
 
       if (av.isShowSequenceFeatures())
@@ -950,10 +1152,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
         {
           for (int r = 0; r < visibleResults.length; r += 2)
           {
-            sr.drawHighlightedText(nextSeq, visibleResults[r],
-                    visibleResults[r + 1],
-                    (visibleResults[r] - startRes) * charWidth,
-                    offset + ((i - startSeq) * charHeight));
+            seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
+                    visibleResults[r + 1], (visibleResults[r] - startRes)
+                            * charWidth, offset
+                            + ((i - startSeq) * charHeight));
           }
         }
       }
@@ -961,7 +1163,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       if (av.cursorMode && cursorY == i && cursorX >= startRes
               && cursorX <= endRes)
       {
-        sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
+        seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
                 offset + ((i - startSeq) * charHeight));
       }
     }
@@ -977,23 +1179,16 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
   void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
           int startSeq, int endSeq, int offset)
   {
-    int charHeight = av.getCharHeight();
-    int charWidth = av.getCharWidth();
-
     Graphics2D g = (Graphics2D) g1;
     //
     // ///////////////////////////////////
     // Now outline any areas if necessary
     // ///////////////////////////////////
-    SequenceGroup group = av.getSelectionGroup();
 
-    int sx = -1;
-    int sy = -1;
-    int ex = -1;
+    SequenceGroup group = null;
     int groupIndex = -1;
-    int visWidth = (endRes - startRes + 1) * charWidth;
 
-    if ((group == null) && (av.getAlignment().getGroups().size() > 0))
+    if (av.getAlignment().getGroups().size() > 0)
     {
       group = av.getAlignment().getGroups().get(0);
       groupIndex = 0;
@@ -1001,168 +1196,314 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
     if (group != null)
     {
+      g.setStroke(new BasicStroke());
+      g.setColor(group.getOutlineColour());
+      
       do
       {
-        int oldY = -1;
-        int i = 0;
-        boolean inGroup = false;
-        int top = -1;
-        int bottom = -1;
+        drawPartialGroupOutline(g, group, startRes, endRes, startSeq,
+                endSeq, offset);
+
+        groupIndex++;
+
+        g.setStroke(new BasicStroke());
 
-        for (i = startSeq; i <= endSeq; i++)
+        if (groupIndex >= av.getAlignment().getGroups().size())
         {
-          sx = (group.getStartRes() - startRes) * charWidth;
-          sy = offset + ((i - startSeq) * charHeight);
-          ex = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
-                  - 1;
+          break;
+        }
 
-          if (sx + ex < 0 || sx > visWidth)
-          {
-            continue;
-          }
+        group = av.getAlignment().getGroups().get(groupIndex);
 
-          if ((sx <= (endRes - startRes) * charWidth)
-                  && group.getSequences(null)
-                          .contains(av.getAlignment().getSequenceAt(i)))
-          {
-            if ((bottom == -1) && !group.getSequences(null)
-                    .contains(av.getAlignment().getSequenceAt(i + 1)))
-            {
-              bottom = sy + charHeight;
-            }
+      } while (groupIndex < av.getAlignment().getGroups().size());
 
-            if (!inGroup)
-            {
-              if (((top == -1) && (i == 0)) || !group.getSequences(null)
-                      .contains(av.getAlignment().getSequenceAt(i - 1)))
-              {
-                top = sy;
-              }
+    }
 
-              oldY = sy;
-              inGroup = true;
+  }
 
-              if (group == av.getSelectionGroup())
-              {
-                g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
-                        BasicStroke.JOIN_ROUND, 3f, new float[]
-                        { 5f, 3f }, 0f));
-                g.setColor(Color.RED);
-              }
-              else
-              {
-                g.setStroke(new BasicStroke());
-                g.setColor(group.getOutlineColour());
-              }
-            }
-          }
-          else
-          {
-            if (inGroup)
-            {
-              if (sx >= 0 && sx < visWidth)
-              {
-                g.drawLine(sx, oldY, sx, sy);
-              }
 
-              if (sx + ex < visWidth)
-              {
-                g.drawLine(sx + ex, oldY, sx + ex, sy);
-              }
+  /*
+   * Draw the selection group as a separate image and overlay
+   */
+  private BufferedImage drawSelectionGroup(int startRes, int endRes,
+          int startSeq, int endSeq)
+  {
+    // get a new image of the correct size
+    BufferedImage selectionImage = setupImage();
 
-              if (sx < 0)
-              {
-                ex += sx;
-                sx = 0;
-              }
+    if (selectionImage == null)
+    {
+      return null;
+    }
 
-              if (sx + ex > visWidth)
-              {
-                ex = visWidth;
-              }
+    SequenceGroup group = av.getSelectionGroup();
+    if (group == null)
+    {
+      // nothing to draw
+      return null;
+    }
 
-              else if (sx + ex >= (endRes - startRes + 1) * charWidth)
-              {
-                ex = (endRes - startRes + 1) * charWidth;
-              }
+    // set up drawing colour
+    Graphics2D g = (Graphics2D) selectionImage.getGraphics();
 
-              if (top != -1)
-              {
-                g.drawLine(sx, top, sx + ex, top);
-                top = -1;
-              }
+    setupSelectionGroup(g, selectionImage);
 
-              if (bottom != -1)
-              {
-                g.drawLine(sx, bottom, sx + ex, bottom);
-                bottom = -1;
-              }
+    if (!av.getWrapAlignment())
+    {
+      drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq,
+              0);
+    }
+    else
+    {
+      drawWrappedSelection(g, group, getWidth(), getHeight(),
+              av.getRanges().getStartRes());
+    }
 
-              inGroup = false;
-            }
-          }
+    g.dispose();
+    return selectionImage;
+  }
+
+  /*
+   * Set up graphics for selection group
+   */
+  private void setupSelectionGroup(Graphics2D g,
+          BufferedImage selectionImage)
+  {
+    // set background to transparent
+    g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
+    g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight());
+
+    // set up foreground to draw red dashed line
+    g.setComposite(AlphaComposite.Src);
+    g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
+            BasicStroke.JOIN_ROUND, 3f, new float[]
+    { 5f, 3f }, 0f));
+    g.setColor(Color.RED);
+  }
+
+  /*
+   * Draw a selection group over an unwrapped alignment
+   * @param g graphics object to draw with
+   * @param group selection group
+   * @param startRes start residue of area to draw
+   * @param endRes end residue of area to draw
+   * @param startSeq start sequence of area to draw
+   * @param endSeq end sequence of area to draw
+   * @param offset vertical offset (used when called from wrapped alignment code)
+   */
+  private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group,
+          int startRes, int endRes, int startSeq, int endSeq, int offset)
+  {
+       int charWidth = av.getCharWidth();
+         
+    if (!av.hasHiddenColumns())
+    {
+      drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
+              offset);
+    }
+    else
+    {
+      // package into blocks of visible columns
+      int screenY = 0;
+      int blockStart = startRes;
+      int blockEnd = endRes;
+
+      for (int[] region : av.getAlignment().getHiddenColumns()
+              .getHiddenColumnsCopy())
+      {
+        int hideStart = region[0];
+        int hideEnd = region[1];
+
+        if (hideStart <= blockStart)
+        {
+          blockStart += (hideEnd - hideStart) + 1;
+          continue;
+        }
+
+        blockEnd = hideStart - 1;
+
+        g.translate(screenY * charWidth, 0);
+        drawPartialGroupOutline(g, group,
+                blockStart, blockEnd, startSeq, endSeq, offset);
+
+        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);
+      }
+    }
+  }
 
+  /*
+   * Draw the selection group as a separate image and overlay
+   */
+  private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group,
+          int startRes, int endRes, int startSeq, int endSeq,
+          int verticalOffset)
+  {
+       int charHeight = av.getCharHeight();
+       int charWidth = av.getCharWidth();
+         
+    int visWidth = (endRes - startRes + 1) * charWidth;
+
+    int oldY = -1;
+    int i = 0;
+    boolean inGroup = false;
+    int top = -1;
+    int bottom = -1;
+
+    int sx = -1;
+    int sy = -1;
+    int xwidth = -1;
+
+    for (i = startSeq; i <= endSeq; i++)
+    {
+      // position of start residue of group relative to startRes, in pixels
+      sx = (group.getStartRes() - startRes) * charWidth;
+
+      // width of group in pixels
+      xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
+              - 1;
+
+      sy = verticalOffset + (i - startSeq) * charHeight;
+
+      if (sx + xwidth < 0 || sx > visWidth)
+      {
+        continue;
+      }
+
+      if ((sx <= (endRes - startRes) * charWidth)
+              && group.getSequences(null)
+                      .contains(av.getAlignment().getSequenceAt(i)))
+      {
+        if ((bottom == -1) && !group.getSequences(null)
+                .contains(av.getAlignment().getSequenceAt(i + 1)))
+        {
+          bottom = sy + charHeight;
+        }
+
+        if (!inGroup)
+        {
+          if (((top == -1) && (i == 0)) || !group.getSequences(null)
+                  .contains(av.getAlignment().getSequenceAt(i - 1)))
+          {
+            top = sy;
+          }
+
+          oldY = sy;
+          inGroup = true;
+        }
+      }
+      else
+      {
         if (inGroup)
         {
-          sy = offset + ((i - startSeq) * charHeight);
+          // if start position is visible, draw vertical line to left of
+          // group
           if (sx >= 0 && sx < visWidth)
           {
             g.drawLine(sx, oldY, sx, sy);
           }
 
-          if (sx + ex < visWidth)
+          // if end position is visible, draw vertical line to right of
+          // group
+          if (sx + xwidth < visWidth)
           {
-            g.drawLine(sx + ex, oldY, sx + ex, sy);
+            g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
           }
 
           if (sx < 0)
           {
-            ex += sx;
+            xwidth += sx;
             sx = 0;
           }
 
-          if (sx + ex > visWidth)
+          // don't let width extend beyond current block, or group extent
+          // fixes JAL-2672
+          if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
           {
-            ex = visWidth;
+            xwidth = (endRes - startRes + 1) * charWidth - sx;
           }
-          else if (sx + ex >= (endRes - startRes + 1) * charWidth)
-          {
-            ex = (endRes - startRes + 1) * charWidth;
-          }
-
+          
+          // draw horizontal line at top of group
           if (top != -1)
           {
-            g.drawLine(sx, top, sx + ex, top);
+            g.drawLine(sx, top, sx + xwidth, top);
             top = -1;
           }
 
+          // draw horizontal line at bottom of group
           if (bottom != -1)
           {
-            g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
+            g.drawLine(sx, bottom, sx + xwidth, bottom);
             bottom = -1;
           }
 
           inGroup = false;
         }
+      }
+    }
 
-        groupIndex++;
+    if (inGroup)
+    {
+      sy = verticalOffset + ((i - startSeq) * charHeight);
+      if (sx >= 0 && sx < visWidth)
+      {
+        g.drawLine(sx, oldY, sx, sy);
+      }
 
-        g.setStroke(new BasicStroke());
+      if (sx + xwidth < visWidth)
+      {
+        g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
+      }
 
-        if (groupIndex >= av.getAlignment().getGroups().size())
-        {
-          break;
-        }
+      if (sx < 0)
+      {
+        xwidth += sx;
+        sx = 0;
+      }
 
-        group = av.getAlignment().getGroups().get(groupIndex);
+      if (sx + xwidth > visWidth)
+      {
+        xwidth = visWidth;
+      }
+      else if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
+      {
+        xwidth = (endRes - startRes + 1) * charWidth;
+      }
 
-      } while (groupIndex < av.getAlignment().getGroups().size());
+      if (top != -1)
+      {
+        g.drawLine(sx, top, sx + xwidth, top);
+        top = -1;
+      }
 
-    }
+      if (bottom != -1)
+      {
+        g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1);
+        bottom = -1;
+      }
 
+      inGroup = false;
+    }
   }
-
+  
   /**
    * Highlights search results in the visible region by rendering as white text
    * on a black background. Any previous highlighting is removed. Answers true
@@ -1331,45 +1672,52 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
   {
     String eventName = evt.getPropertyName();
 
-    int scrollX = 0;
-    if (eventName.equals(ViewportRanges.STARTRES))
+    if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
     {
-      // Make sure we're not trying to draw a panel
-      // larger than the visible window
-      ViewportRanges vpRanges = av.getRanges();
-      scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
-      int range = vpRanges.getEndRes() - vpRanges.getStartRes();
-      if (scrollX > range)
-      {
-        scrollX = range;
-      }
-      else if (scrollX < -range)
-      {
-        scrollX = -range;
-      }
+      fastPaint = true;
+      repaint();
     }
-
-    // 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))
+    else if (eventName.equals(ViewportRanges.STARTRES))
     {
-      // scroll - startres and endres both change
-      if (av.getWrapAlignment())
+      int scrollX = 0;
+      if (eventName.equals(ViewportRanges.STARTRES))
       {
-        fastPaintWrapped(scrollX);
+        // Make sure we're not trying to draw a panel
+        // larger than the visible window
+        ViewportRanges vpRanges = av.getRanges();
+        scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
+        int range = vpRanges.getEndRes() - vpRanges.getStartRes();
+        if (scrollX > range)
+        {
+          scrollX = range;
+        }
+        else if (scrollX < -range)
+        {
+          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.
+        
+        // scroll - startres and endres both change
+         if (av.getWrapAlignment())
+        {
+          fastPaintWrapped(scrollX);
+        }
+        else
+        {
+          fastPaint(scrollX, 0);
+        }
       }
-      else
+      else if (eventName.equals(ViewportRanges.STARTSEQ))
       {
-        fastPaint(scrollX, 0);
+        // scroll
+        fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
       }
     }
-    else if (eventName.equals(ViewportRanges.STARTSEQ))
-    {
-      fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
-    }
   }
 
   /**
@@ -1622,6 +1970,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     }
   }
 
+  
   /**
    * Redraws any positions in the search results in the visible region of a
    * wrapped alignment. Any highlights are drawn depending on the search results
index 1e1af79..2d30c90 100644 (file)
@@ -1111,7 +1111,7 @@ public class SeqPanel extends JPanel
     }
 
     mouseDragging = true;
-    if (scrollThread != null)
+    if ((scrollThread != null) && (scrollThread.isRunning()))
     {
       scrollThread.setEvent(evt);
     }
@@ -1537,9 +1537,9 @@ public class SeqPanel extends JPanel
       oldSeq = 0;
     }
 
-    if (scrollThread != null)
+    if ((scrollThread != null) && (scrollThread.isRunning()))
     {
-      scrollThread.running = false;
+      scrollThread.stopScrolling();
       scrollThread = null;
     }
   }
@@ -1558,7 +1558,7 @@ public class SeqPanel extends JPanel
       return;
     }
 
-    if (mouseDragging)
+    if (mouseDragging && scrollThread == null)
     {
       scrollThread = new ScrollThread();
     }
@@ -1721,43 +1721,53 @@ public class SeqPanel extends JPanel
 
     if (stretchGroup == null)
     {
-      // Only if left mouse button do we want to change group sizes
+      createStretchGroup(res, sequence);
+    }
 
-      // define a new group here
-      SequenceGroup sg = new SequenceGroup();
-      sg.setStartRes(res);
-      sg.setEndRes(res);
-      sg.addSequence(sequence, false);
-      av.setSelectionGroup(sg);
-      stretchGroup = sg;
+    if (stretchGroup != null)
+    {
+      stretchGroup.addPropertyChangeListener(seqCanvas);
+    }
 
-      if (av.getConservationSelected())
-      {
-        SliderPanel.setConservationSlider(ap, av.getResidueShading(),
-                ap.getViewName());
-      }
+    seqCanvas.repaint();
+  }
 
-      if (av.getAbovePIDThreshold())
-      {
-        SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
-                ap.getViewName());
-      }
-      // TODO: stretchGroup will always be not null. Is this a merge error ?
-      if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
-      {
-        // Edit end res position of selected group
-        changeEndRes = true;
-      }
-      else if ((stretchGroup != null)
-              && (stretchGroup.getStartRes() == res))
-      {
-        // Edit end res position of selected group
-        changeStartRes = true;
-      }
-      stretchGroup.getWidth();
+  private void createStretchGroup(int res, SequenceI sequence)
+  {
+    // Only if left mouse button do we want to change group sizes
+    // define a new group here
+    SequenceGroup sg = new SequenceGroup();
+    sg.setStartRes(res);
+    sg.setEndRes(res);
+    sg.addSequence(sequence, false);
+    av.setSelectionGroup(sg);
+    stretchGroup = sg;
+
+    if (av.getConservationSelected())
+    {
+      SliderPanel.setConservationSlider(ap, av.getResidueShading(),
+              ap.getViewName());
     }
 
-    seqCanvas.repaint();
+    if (av.getAbovePIDThreshold())
+    {
+      SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
+              ap.getViewName());
+    }
+    // TODO: stretchGroup will always be not null. Is this a merge error ?
+    // or is there a threading issue here?
+    if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
+    {
+      // Edit end res position of selected group
+      changeEndRes = true;
+    }
+    else if ((stretchGroup != null) && (stretchGroup.getStartRes() == res))
+    {
+      // Edit end res position of selected group
+      changeStartRes = true;
+    }
+    stretchGroup.getWidth();
+
   }
 
   /**
@@ -1805,6 +1815,9 @@ public class SeqPanel extends JPanel
     {
       return;
     }
+
+    stretchGroup.removePropertyChangeListener(seqCanvas);
+
     // always do this - annotation has own state
     // but defer colourscheme update until hidden sequences are passed in
     boolean vischange = stretchGroup.recalcConservation(true);
@@ -1946,21 +1959,19 @@ public class SeqPanel extends JPanel
 
     mouseDragging = true;
 
-    if (scrollThread != null)
+    if ((scrollThread != null) && (scrollThread.isRunning()))
     {
       scrollThread.setEvent(evt);
     }
-
-    seqCanvas.repaint();
   }
 
   void scrollCanvas(MouseEvent evt)
   {
     if (evt == null)
     {
-      if (scrollThread != null)
+      if ((scrollThread != null) && (scrollThread.isRunning()))
       {
-        scrollThread.running = false;
+        scrollThread.stopScrolling();
         scrollThread = null;
       }
       mouseDragging = false;
@@ -1983,7 +1994,7 @@ public class SeqPanel extends JPanel
   {
     MouseEvent evt;
 
-    boolean running = false;
+    private volatile boolean threadRunning = true;
 
     public ScrollThread()
     {
@@ -1997,37 +2008,40 @@ public class SeqPanel extends JPanel
 
     public void stopScrolling()
     {
-      running = false;
+      threadRunning = false;
+    }
+
+    public boolean isRunning()
+    {
+      return threadRunning;
     }
 
     @Override
     public void run()
     {
-      running = true;
-
-      while (running)
+      while (threadRunning)
       {
         if (evt != null)
         {
           if (mouseDragging && (evt.getY() < 0)
                   && (av.getRanges().getStartSeq() > 0))
           {
-            running = av.getRanges().scrollUp(true);
+            av.getRanges().scrollUp(true);
           }
 
           if (mouseDragging && (evt.getY() >= getHeight()) && (av
                   .getAlignment().getHeight() > av.getRanges().getEndSeq()))
           {
-            running = av.getRanges().scrollUp(false);
+            av.getRanges().scrollUp(false);
           }
 
           if (mouseDragging && (evt.getX() < 0))
           {
-            running = av.getRanges().scrollRight(false);
+            av.getRanges().scrollRight(false);
           }
           else if (mouseDragging && (evt.getX() >= getWidth()))
           {
-            running = av.getRanges().scrollRight(true);
+            av.getRanges().scrollRight(true);
           }
         }
 
index 4498f88..0a1e8ef 100755 (executable)
@@ -23,9 +23,8 @@ package jalview.gui;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
-import jalview.renderer.ResidueShaderI;
+import jalview.renderer.ResidueColourFinder;
 import jalview.renderer.seqfeatures.FeatureColourFinder;
-import jalview.util.Comparison;
 
 import java.awt.Color;
 import java.awt.FontMetrics;
@@ -41,17 +40,15 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
   boolean renderGaps = true;
 
-  SequenceGroup currentSequenceGroup = null;
-
   SequenceGroup[] allGroups = null;
 
-  Color resBoxColour;
+  // Color resBoxColour;
 
   Graphics graphics;
 
   boolean monospacedFont;
 
-  boolean forOverview = false;
+  ResidueColourFinder resColourFinder;
 
   /**
    * Creates a new SequenceRenderer object
@@ -61,6 +58,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   public SequenceRenderer(AlignViewportI viewport)
   {
     this.av = viewport;
+    resColourFinder = new ResidueColourFinder();
   }
 
   /**
@@ -83,26 +81,6 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     this.renderGaps = renderGaps;
   }
 
-  protected Color getResidueBoxColour(SequenceI seq, int i)
-  {
-    // rate limiting step when rendering overview for lots of groups
-    allGroups = av.getAlignment().findAllGroups(seq);
-
-    if (inCurrentSequenceGroup(i))
-    {
-      if (currentSequenceGroup.getDisplayBoxes())
-      {
-        getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, i);
-      }
-    }
-    else if (av.getShowBoxes())
-    {
-      getBoxColour(av.getResidueShading(), seq, i);
-    }
-
-    return resBoxColour;
-  }
-
   /**
    * Get the residue colour at the given sequence position - as determined by
    * the sequence group colour (if any), else the colour scheme, possibly
@@ -117,39 +95,11 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   public Color getResidueColour(final SequenceI seq, int position,
           FeatureColourFinder finder)
   {
-    Color col = getResidueBoxColour(seq, position);
-
-    if (finder != null)
-    {
-      col = finder.findFeatureColour(col, seq, position);
-    }
-    return col;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param shader
-   *          DOCUMENT ME!
-   * @param seq
-   *          DOCUMENT ME!
-   * @param i
-   *          DOCUMENT ME!
-   */
-  void getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
-  {
-    if (shader.getColourScheme() != null)
-    {
-      resBoxColour = shader.findColour(seq.getCharAt(i), i, seq);
-    }
-    else if (forOverview && !Comparison.isGap(seq.getCharAt(i)))
-    {
-      resBoxColour = Color.lightGray;
-    }
-    else
-    {
-      resBoxColour = Color.white;
-    }
+    allGroups = av.getAlignment().findAllGroups(seq);
+    return resColourFinder.getResidueColour(av.getShowBoxes(),
+            av.getResidueShading(),
+            allGroups, seq, position,
+            finder);
   }
 
   /**
@@ -208,6 +158,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   public synchronized void drawBoxes(SequenceI seq, int start, int end,
           int y1)
   {
+    Color resBoxColour = Color.white;
+
     if (seq == null)
     {
       return; // fix for racecondition
@@ -227,17 +179,22 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
       if (i < length)
       {
-        if (inCurrentSequenceGroup(i))
+        SequenceGroup currentSequenceGroup = resColourFinder
+                .getCurrentSequenceGroup(
+                allGroups, i);
+        if (currentSequenceGroup != null)
         {
           if (currentSequenceGroup.getDisplayBoxes())
           {
-            getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq,
+            resBoxColour = resColourFinder.getBoxColour(
+                    currentSequenceGroup.getGroupColourScheme(), seq,
                     i);
           }
         }
         else if (av.getShowBoxes())
         {
-          getBoxColour(av.getResidueShading(), seq, i);
+          resBoxColour = resColourFinder
+                  .getBoxColour(av.getResidueShading(), seq, i);
         }
       }
 
@@ -317,10 +274,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
       boolean srep = av.isDisplayReferenceSeq();
       boolean getboxColour = false;
       boolean isarep = av.getAlignment().getSeqrep() == seq;
-      boolean isgrep = currentSequenceGroup != null
-              ? currentSequenceGroup.getSeqrep() == seq
-              : false;
-      char sr_c;
+      Color resBoxColour = Color.white;
+
       for (int i = start; i <= end; i++)
       {
 
@@ -333,7 +288,10 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           continue;
         }
 
-        if (inCurrentSequenceGroup(i))
+        SequenceGroup currentSequenceGroup = resColourFinder
+                .getCurrentSequenceGroup(
+                allGroups, i);
+        if (currentSequenceGroup != null)
         {
           if (!currentSequenceGroup.getDisplayText())
           {
@@ -344,7 +302,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
                   || currentSequenceGroup.getColourText())
           {
             getboxColour = true;
-            getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq,
+            resBoxColour = resColourFinder.getBoxColour(
+                    currentSequenceGroup.getGroupColourScheme(), seq,
                     i);
 
             if (currentSequenceGroup.getColourText())
@@ -366,6 +325,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           {
             graphics.setColor(currentSequenceGroup.textColour);
           }
+          boolean isgrep = currentSequenceGroup != null
+                  ? currentSequenceGroup.getSeqrep() == seq : false;
           if (!isarep && !isgrep
                   && currentSequenceGroup.getShowNonconserved()) // todo
                                                                  // optimize
@@ -386,7 +347,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           if (av.getColourText())
           {
             getboxColour = true;
-            getBoxColour(av.getResidueShading(), seq, i);
+            resBoxColour = resColourFinder
+                    .getBoxColour(av.getResidueShading(), seq, i);
 
             if (av.getShowBoxes())
             {
@@ -402,7 +364,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           {
             if (!getboxColour)
             {
-              getBoxColour(av.getResidueShading(), seq, i);
+              resBoxColour = resColourFinder
+                      .getBoxColour(av.getResidueShading(), seq, i);
             }
 
             if (resBoxColour.getRed() + resBoxColour.getBlue()
@@ -473,35 +436,6 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   /**
    * DOCUMENT ME!
    * 
-   * @param res
-   *          DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  boolean inCurrentSequenceGroup(int res)
-  {
-    if (allGroups == null)
-    {
-      return false;
-    }
-
-    for (int i = 0; i < allGroups.length; i++)
-    {
-      if ((allGroups[i].getStartRes() <= res)
-              && (allGroups[i].getEndRes() >= res))
-      {
-        currentSequenceGroup = allGroups[i];
-
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
    * @param seq
    *          DOCUMENT ME!
    * @param start
index beb2d62..5bff407 100644 (file)
@@ -194,15 +194,29 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
   }
 
   /**
-   * Adjust the divider for a sensible split of the real estate (for example,
+   * Adjusts the divider for a sensible split of the real estate (for example,
    * when many transcripts are shown with a single protein). This should only be
    * called after the split pane has been laid out (made visible) so it has a
-   * height.
+   * height. The aim is to avoid unnecessary vertical scroll bars, while
+   * ensuring that at least 2 sequences are visible in each panel.
+   * <p>
+   * Once laid out, the user may choose to customise as they wish, so this
+   * method is not called again after the initial layout.
    */
-  protected void adjustDivider()
+  protected void adjustInitialLayout()
   {
-    final AlignViewport topViewport = ((AlignFrame) getTopFrame()).viewport;
-    final AlignViewport bottomViewport = ((AlignFrame) getBottomFrame()).viewport;
+    AlignFrame topFrame = (AlignFrame) getTopFrame();
+    AlignFrame bottomFrame = (AlignFrame) getBottomFrame();
+
+    /*
+     * recompute layout of top and bottom panels to reflect their
+     * actual (rather than requested) height
+     */
+    topFrame.alignPanel.adjustAnnotationHeight();
+    bottomFrame.alignPanel.adjustAnnotationHeight();
+
+    final AlignViewport topViewport = topFrame.viewport;
+    final AlignViewport bottomViewport = bottomFrame.viewport;
     final AlignmentI topAlignment = topViewport.getAlignment();
     final AlignmentI bottomAlignment = bottomViewport.getAlignment();
     boolean topAnnotations = topViewport.isShowAnnotation();
@@ -214,6 +228,29 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
     int bottomCharHeight = bottomViewport.getViewStyle().getCharHeight();
 
     /*
+     * calculate the minimum ratio that leaves at least the height 
+     * of two sequences (after rounding) visible in the top panel
+     */
+    int topPanelHeight = topFrame.getHeight();
+    int bottomPanelHeight = bottomFrame.getHeight();
+    int topSequencesHeight = topFrame.alignPanel.getSeqPanel().seqCanvas
+            .getHeight();
+    int topPanelMinHeight = topPanelHeight
+            - Math.max(0, topSequencesHeight - 3 * topCharHeight);
+    double totalHeight = (double) topPanelHeight + bottomPanelHeight;
+    double minRatio = topPanelMinHeight / totalHeight;
+
+    /*
+     * calculate the maximum ratio that leaves at least the height 
+     * of two sequences (after rounding) visible in the bottom panel
+     */
+    int bottomSequencesHeight = bottomFrame.alignPanel.getSeqPanel().seqCanvas
+            .getHeight();
+    int bottomPanelMinHeight = bottomPanelHeight
+            - Math.max(0, bottomSequencesHeight - 3 * bottomCharHeight);
+    double maxRatio = (totalHeight - bottomPanelMinHeight) / totalHeight;
+
+    /*
      * estimate ratio of (topFrameContent / bottomFrameContent)
      */
     int insets = Platform.isAMac() ? MAC_INSETS_HEIGHT
@@ -223,13 +260,14 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
             + (topAnnotations ? topViewport.calcPanelHeight() : 0);
     int bottomHeight = insets + (3 + bottomCount) * bottomCharHeight
             + (bottomAnnotations ? bottomViewport.calcPanelHeight() : 0);
-    double ratio = ((double) topHeight) / (topHeight + bottomHeight);
+    double ratio = ((double) topHeight)
+            / (double) (topHeight + bottomHeight);
 
     /*
-     * limit to 0.2 <= ratio <= 0.8 to avoid concealing all sequences
+     * limit ratio to avoid concealing all sequences
      */
-    ratio = Math.min(ratio, 0.8d);
-    ratio = Math.max(ratio, 0.2d);
+    ratio = Math.min(ratio, maxRatio);
+    ratio = Math.max(ratio, minRatio);
     setRelativeDividerLocation(ratio);
   }
 
index 633d2b8..1ca0802 100755 (executable)
@@ -95,11 +95,11 @@ public class GPreferences extends JPanel
 
   protected JCheckBox rightAlign = new JCheckBox();
 
-  protected JComboBox<String> fontSizeCB = new JComboBox<String>();
+  protected JComboBox<String> fontSizeCB = new JComboBox<>();
 
-  protected JComboBox<String> fontStyleCB = new JComboBox<String>();
+  protected JComboBox<String> fontStyleCB = new JComboBox<>();
 
-  protected JComboBox<String> fontNameCB = new JComboBox<String>();
+  protected JComboBox<String> fontNameCB = new JComboBox<>();
 
   protected JCheckBox showOccupancy = new JCheckBox();
 
@@ -111,15 +111,15 @@ public class GPreferences extends JPanel
 
   protected JCheckBox scaleProteinToCdna = new JCheckBox();
 
-  protected JComboBox<String> gapSymbolCB = new JComboBox<String>();
+  protected JComboBox<String> gapSymbolCB = new JComboBox<>();
 
   protected JCheckBox wrap = new JCheckBox();
 
-  protected JComboBox<String> sortby = new JComboBox<String>();
+  protected JComboBox<String> sortby = new JComboBox<>();
 
-  protected JComboBox<String> sortAnnBy = new JComboBox<String>();
+  protected JComboBox<String> sortAnnBy = new JComboBox<>();
 
-  protected JComboBox<String> sortAutocalc = new JComboBox<String>();
+  protected JComboBox<String> sortAutocalc = new JComboBox<>();
 
   protected JCheckBox startupCheckbox = new JCheckBox();
 
@@ -159,7 +159,7 @@ public class GPreferences extends JPanel
 
   protected JCheckBox addTempFactor = new JCheckBox();
 
-  protected JComboBox<String> structViewer = new JComboBox<String>();
+  protected JComboBox<String> structViewer = new JComboBox<>();
 
   protected JTextField chimeraPath = new JTextField();
 
@@ -176,9 +176,22 @@ public class GPreferences extends JPanel
 
   protected JPanel maxColour = new JPanel();
 
-  protected JComboBox<String> protColour = new JComboBox<String>();
+  protected JComboBox<String> protColour = new JComboBox<>();
 
-  protected JComboBox<String> nucColour = new JComboBox<String>();
+  protected JComboBox<String> nucColour = new JComboBox<>();
+
+  /*
+   * Overview tab components
+   */
+  protected JPanel gapColour = new JPanel();
+
+  protected JPanel hiddenColour = new JPanel();
+
+  protected JCheckBox useLegacyGap;
+
+  protected JCheckBox showHiddenAtStart;
+
+  protected JLabel gapLabel;
 
   /*
    * Connections tab components
@@ -216,7 +229,7 @@ public class GPreferences extends JPanel
   /*
    * Output tab components
    */
-  protected JComboBox<Object> epsRendering = new JComboBox<Object>();
+  protected JComboBox<Object> epsRendering = new JComboBox<>();
 
   protected JLabel userIdWidthlabel = new JLabel();
 
@@ -294,6 +307,9 @@ public class GPreferences extends JPanel
     tabbedPane.add(initColoursTab(),
             MessageManager.getString("label.colours"));
 
+    tabbedPane.add(initOverviewTab(),
+            MessageManager.getString("label.overview"));
+
     tabbedPane.add(initStructureTab(),
             MessageManager.getString("label.structure"));
 
@@ -963,6 +979,151 @@ public class GPreferences extends JPanel
   }
 
   /**
+   * Initialises the Overview tabbed panel.
+   * 
+   * @return
+   */
+  private JPanel initOverviewTab()
+  {
+    JPanel overviewPanel = new JPanel();
+    overviewPanel.setBorder(new TitledBorder(
+            MessageManager.getString("label.overview_settings")));
+
+    gapColour.setFont(LABEL_FONT);
+    // fixing the border colours stops apparent colour bleed from the panel
+    gapColour.setBorder(
+            BorderFactory.createEtchedBorder(Color.white, Color.lightGray));
+    gapColour.setPreferredSize(new Dimension(40, 20));
+    gapColour.addMouseListener(new MouseAdapter()
+    {
+      @Override
+      public void mousePressed(MouseEvent e)
+      {
+        gapColour_actionPerformed(gapColour);
+      }
+    });
+
+    hiddenColour.setFont(LABEL_FONT);
+    // fixing the border colours stops apparent colour bleed from the panel
+    hiddenColour.setBorder(
+            BorderFactory.createEtchedBorder(Color.white, Color.lightGray));
+    hiddenColour.setPreferredSize(new Dimension(40, 20));
+    hiddenColour.addMouseListener(new MouseAdapter()
+    {
+      @Override
+      public void mousePressed(MouseEvent e)
+      {
+        hiddenColour_actionPerformed(hiddenColour);
+      }
+    });
+    
+    useLegacyGap = new JCheckBox(
+            MessageManager.getString("label.ov_legacy_gap"));
+    useLegacyGap.setFont(LABEL_FONT);
+    useLegacyGap.setHorizontalAlignment(SwingConstants.LEFT);
+    useLegacyGap.setVerticalTextPosition(SwingConstants.TOP);
+    gapLabel = new JLabel(
+            MessageManager.getString("label.gap_colour"));
+    gapLabel.setFont(LABEL_FONT);
+    gapLabel.setHorizontalAlignment(SwingConstants.LEFT);
+    gapLabel.setVerticalTextPosition(SwingConstants.TOP);
+    showHiddenAtStart = new JCheckBox(
+            MessageManager.getString("label.ov_show_hide_default"));
+    showHiddenAtStart.setFont(LABEL_FONT);
+    showHiddenAtStart.setHorizontalAlignment(SwingConstants.LEFT);
+    showHiddenAtStart.setVerticalTextPosition(SwingConstants.TOP);
+    JLabel hiddenLabel = new JLabel(
+            MessageManager.getString("label.hidden_colour"));
+    hiddenLabel.setFont(LABEL_FONT);
+    hiddenLabel.setHorizontalAlignment(SwingConstants.LEFT);
+    hiddenLabel.setVerticalTextPosition(SwingConstants.TOP);
+
+    useLegacyGap.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        useLegacyGaps_actionPerformed(e);
+      }
+    });
+
+    overviewPanel.setLayout(new GridBagLayout());
+    GridBagConstraints c1 = new GridBagConstraints();
+
+    c1.fill = GridBagConstraints.HORIZONTAL;
+    c1.gridx = 0;
+    c1.gridy = 0;
+    c1.weightx = 1;
+    c1.ipady = 20;
+    c1.anchor = GridBagConstraints.FIRST_LINE_START;
+    overviewPanel.add(useLegacyGap, c1);
+
+    GridBagConstraints c2 = new GridBagConstraints();
+    c2.fill = GridBagConstraints.HORIZONTAL;
+    c2.gridx = 1;
+    c2.gridy = 0;
+    c2.insets = new Insets(0, 15, 0, 10);
+    overviewPanel.add(gapLabel, c2);
+
+    GridBagConstraints c3 = new GridBagConstraints();
+    c3.fill = GridBagConstraints.HORIZONTAL;
+    c3.gridx = 2;
+    c3.gridy = 0;
+    c3.insets = new Insets(0, 0, 0, 15);
+    overviewPanel.add(gapColour, c3);
+
+    GridBagConstraints c4 = new GridBagConstraints();
+    c4.fill = GridBagConstraints.HORIZONTAL;
+    c4.gridx = 0;
+    c4.gridy = 1;
+    c4.weightx = 1;
+    overviewPanel.add(showHiddenAtStart, c4);
+
+    GridBagConstraints c5 = new GridBagConstraints();
+    c5.fill = GridBagConstraints.HORIZONTAL;
+    c5.gridx = 1;
+    c5.gridy = 1;
+    c5.insets = new Insets(0, 15, 0, 10);
+    overviewPanel.add(hiddenLabel, c5);
+
+    GridBagConstraints c6 = new GridBagConstraints();
+    c6.fill = GridBagConstraints.HORIZONTAL;
+    c6.gridx = 2;
+    c6.gridy = 1;
+    c6.insets = new Insets(0, 0, 0, 15);
+    overviewPanel.add(hiddenColour, c6);
+
+    JButton resetButton = new JButton(
+            MessageManager.getString("label.reset_to_defaults"));
+
+    resetButton.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        resetOvDefaults_actionPerformed(e);
+      }
+    });
+
+    GridBagConstraints c7 = new GridBagConstraints();
+    c7.fill = GridBagConstraints.NONE;
+    c7.gridx = 0;
+    c7.gridy = 2;
+    c7.insets = new Insets(10, 0, 0, 0);
+    c7.anchor = GridBagConstraints.WEST;
+    overviewPanel.add(resetButton, c7);
+
+    // Add padding so the panel doesn't look ridiculous
+    JPanel spacePanel = new JPanel();
+    overviewPanel.add(spacePanel,
+            new GridBagConstraints(0, 3, 1, 1, 1.0, 1.0,
+                    GridBagConstraints.WEST, GridBagConstraints.BOTH,
+                    new Insets(0, 0, 0, 5), 0, 0));
+
+    return overviewPanel;
+  }
+
+  /**
    * Initialises the Structure tabbed panel.
    * 
    * @return
@@ -1494,12 +1655,28 @@ public class GPreferences extends JPanel
   {
   }
 
+  protected void gapColour_actionPerformed(JPanel panel)
+  {
+  }
+
+  protected void hiddenColour_actionPerformed(JPanel panel)
+  {
+  }
+
   protected void showunconserved_actionPerformed(ActionEvent e)
   {
     // TODO Auto-generated method stub
 
   }
 
+  protected void useLegacyGaps_actionPerformed(ActionEvent e)
+  {
+  }
+
+  protected void resetOvDefaults_actionPerformed(ActionEvent e)
+  {
+  }
+
   /**
    * DOCUMENT ME!
    * 
index 3e6cd2a..7c4672a 100644 (file)
@@ -165,6 +165,9 @@ public abstract class GStructureChooser extends JPanel
 
   protected static final String VIEWS_ENTER_ID = "VIEWS_ENTER_ID";
 
+  /**
+   * 'cached' structure view
+   */
   protected static final String VIEWS_LOCAL_PDB = "VIEWS_LOCAL_PDB";
 
   protected JTable tbl_local_pdb = new JTable();
index 0f8cda6..1c50aab 100644 (file)
@@ -22,22 +22,38 @@ package jalview.renderer;
 
 import jalview.api.AlignmentColsCollectionI;
 import jalview.api.AlignmentRowsCollectionI;
+import jalview.api.RendererListenerI;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.renderer.seqfeatures.FeatureRenderer;
 import jalview.viewmodel.OverviewDimensions;
 
+import java.awt.AlphaComposite;
 import java.awt.Color;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeSupport;
 
 public class OverviewRenderer
 {
-  private FeatureColourFinder finder;
+  // 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;
 
-  private jalview.api.SequenceRenderer sr;
+  private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+          this);
+
+  private FeatureColourFinder finder;
 
   // image to render on
   private BufferedImage miniMe;
@@ -48,17 +64,32 @@ public class OverviewRenderer
   // raw number of pixels to allocate to each row
   private float pixelsPerSeq;
 
+  // height in pixels of graph
+  private int graphHeight;
+
   // flag to indicate whether to halt drawing
   private volatile boolean redraw = false;
 
-  public OverviewRenderer(jalview.api.SequenceRenderer seqRenderer,
-          FeatureRenderer fr, OverviewDimensions od)
+  // reference to alignment, needed to get sequence groups
+  private AlignmentI al;
+
+  private ResidueShaderI shader;
+
+  private OverviewResColourFinder resColFinder;
+
+  public OverviewRenderer(FeatureRenderer fr, OverviewDimensions od,
+          AlignmentI alignment,
+          ResidueShaderI resshader, OverviewResColourFinder colFinder)
   {
-    sr = seqRenderer;
     finder = new FeatureColourFinder(fr);
+    resColFinder = colFinder;
+
+    al = alignment;
+    shader = resshader;
 
     pixelsPerCol = od.getPixelsPerCol();
     pixelsPerSeq = od.getPixelsPerSeq();
+    graphHeight = od.getGraphHeight();
     miniMe = new BufferedImage(od.getWidth(), od.getHeight(),
             BufferedImage.TYPE_INT_RGB);
   }
@@ -78,6 +109,12 @@ public class OverviewRenderer
     int rgbcolor = Color.white.getRGB();
     int seqIndex = 0;
     int pixelRow = 0;
+    int alignmentHeight = miniMe.getHeight() - graphHeight;
+    int totalPixels = miniMe.getWidth() * alignmentHeight;
+
+    int lastRowUpdate = 0;
+    int lastUpdate = 0;
+    changeSupport.firePropertyChange(UPDATE, -1, 0);
 
     for (int alignmentRow : rows)
     {
@@ -85,15 +122,17 @@ public class OverviewRenderer
       {
         break;
       }
-
+    
       // get details of this alignment row
-      boolean hidden = rows.isHidden(alignmentRow);
       SequenceI seq = rows.getSequence(alignmentRow);
 
+      // rate limiting step when rendering overview for lots of groups
+      SequenceGroup[] allGroups = al.findAllGroups(seq);
+
       // calculate where this row extends to in pixels
       int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1,
               miniMe.getHeight() - 1);
-
+    
       int colIndex = 0;
       int pixelCol = 0;
       for (int alignmentCol : cols)
@@ -102,21 +141,19 @@ public class OverviewRenderer
         {
           break;
         }
-
+    
         // calculate where this column extends to in pixels
         int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
                 miniMe.getWidth() - 1);
-
+    
         // don't do expensive colour determination if we're not going to use it
         // NB this is important to avoid performance issues in the overview
         // panel
         if (pixelCol <= endCol)
         {
-          // determine the colour based on the sequence and column position
-          rgbcolor = getColumnColourFromSequence(seq,
-                  hidden || cols.isHidden(alignmentCol), alignmentCol,
-                  finder);
-
+          rgbcolor = getColumnColourFromSequence(allGroups, seq,
+                  alignmentCol, finder);
+    
           // fill in the appropriate number of pixels
           for (int row = pixelRow; row <= endRow; ++row)
           {
@@ -126,35 +163,193 @@ public class OverviewRenderer
             }
           }
 
+          // store last update value
+          lastUpdate = sendProgressUpdate(
+                  (pixelCol + 1) * (endRow - pixelRow), totalPixels,
+                  lastRowUpdate, lastUpdate);
+
           pixelCol = endCol + 1;
         }
         colIndex++;
       }
-      pixelRow = endRow + 1;
+
+      if (pixelRow != endRow + 1)
+      {
+        // store row offset and last update value
+        lastRowUpdate = sendProgressUpdate(endRow + 1, alignmentHeight, 0,
+                lastUpdate);
+        lastUpdate = lastRowUpdate;
+        pixelRow = endRow + 1;
+      }
       seqIndex++;
     }
+
+    overlayHiddenRegions(rows, cols);
+    // final update to progress bar if present
+    if (redraw)
+    {
+      sendProgressUpdate(pixelRow - 1, alignmentHeight, 0, 0);
+    }
+    else
+    {
+      sendProgressUpdate(alignmentHeight, miniMe.getHeight(), 0, 0);
+    }
     return miniMe;
   }
 
   /*
+   * Calculate progress update value and fire event
+   * @param rowOffset number of rows to offset calculation by
+   * @return new rowOffset - return value only to be used when at end of a row
+   */
+  private int sendProgressUpdate(int position, int maximum, int rowOffset,
+          int lastUpdate)
+  {
+    int newUpdate = rowOffset
+            + Math.round(MAX_PROGRESS * ((float) position / maximum));
+    if (newUpdate > lastUpdate)
+    {
+      changeSupport.firePropertyChange(UPDATE, rowOffset, newUpdate);
+      return newUpdate;
+    }
+    return newUpdate;
+  }
+
+  /*
    * Find 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(jalview.datamodel.SequenceI seq,
-          boolean isHidden, int lastcol, FeatureColourFinder fcfinder)
+  private int getColumnColourFromSequence(SequenceGroup[] allGroups,
+          jalview.datamodel.SequenceI seq,
+          int lastcol, FeatureColourFinder fcfinder)
   {
     Color color = Color.white;
 
     if ((seq != null) && (seq.getLength() > lastcol))
     {
-      color = sr.getResidueColour(seq, lastcol, fcfinder);
+      color = resColFinder.getResidueColour(true, shader, allGroups, seq,
+              lastcol,
+              fcfinder);
     }
 
-    if (isHidden)
+    return color.getRGB();
+  }
+
+  /**
+   * Overlay the hidden regions on the overview image
+   * 
+   * @param rows
+   *          collection of rows the overview is built over
+   * @param cols
+   *          collection of columns the overview is built over
+   */
+  private void overlayHiddenRegions(AlignmentRowsCollectionI rows,
+          AlignmentColsCollectionI cols)
+  {
+    if (cols.hasHidden() || rows.hasHidden())
     {
-      color = color.darker().darker();
+      BufferedImage mask = buildHiddenImage(rows, cols, miniMe.getWidth(),
+              miniMe.getHeight());
+
+      Graphics2D g = (Graphics2D) miniMe.getGraphics();
+      g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+              TRANSPARENCY));
+      g.drawImage(mask, 0, 0, miniMe.getWidth(), miniMe.getHeight(), null);
     }
+  }
 
-    return color.getRGB();
+  /**
+   * Build a masking image of hidden columns and rows to be applied on top of
+   * the main overview image.
+   * 
+   * @param rows
+   *          collection of rows the overview is built over
+   * @param cols
+   *          collection of columns the overview is built over
+   * @param width
+   *          width of overview in pixels
+   * @param height
+   *          height of overview in pixels
+   * @return BufferedImage containing mask of hidden regions
+   */
+  private BufferedImage buildHiddenImage(AlignmentRowsCollectionI rows,
+          AlignmentColsCollectionI cols, int width, int height)
+  {
+    // new masking image
+    BufferedImage hiddenImage = new BufferedImage(width, height,
+            BufferedImage.TYPE_INT_ARGB);
+
+    int colIndex = 0;
+    int pixelCol = 0;
+
+    Color hidden = resColFinder.getHiddenColour();
+
+    Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics();
+
+    // set background to transparent
+    g2d.setComposite(AlphaComposite.Clear);
+    g2d.fillRect(0, 0, width, height);
+
+    // set next colour to opaque
+    g2d.setComposite(AlphaComposite.Src);
+
+    for (int alignmentCol : cols)
+    {
+      if (redraw)
+      {
+        break;
+      }
+
+      // calculate where this column extends to in pixels
+      int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
+              hiddenImage.getWidth() - 1);
+
+      if (pixelCol <= endCol)
+      {
+        // determine the colour based on the sequence and column position
+        if (cols.isHidden(alignmentCol))
+        {
+          g2d.setColor(hidden);
+          g2d.fillRect(pixelCol, 0, endCol - pixelCol + 1, height);
+        }
+
+        pixelCol = endCol + 1;
+      }
+      colIndex++;
+
+    }
+
+    int seqIndex = 0;
+    int pixelRow = 0;
+    for (int alignmentRow : rows)
+    {
+      if (redraw)
+      {
+        break;
+      }
+
+      // calculate where this row extends to in pixels
+      int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1,
+              miniMe.getHeight() - 1);
+
+      // get details of this alignment row
+      if (rows.isHidden(alignmentRow))
+      {
+        g2d.setColor(hidden);
+        g2d.fillRect(0, pixelRow, width, endRow - pixelRow + 1);
+      }
+      pixelRow = endRow + 1;
+      seqIndex++;
+    }
+
+    return hiddenImage;
   }
 
   /**
@@ -185,8 +380,10 @@ public class OverviewRenderer
     {
       if (redraw)
       {
+        changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1, 0);
         break;
       }
+
       if (alignmentCol >= annotations.length)
       {
         break; // no more annotations to draw here
@@ -216,12 +413,22 @@ public class OverviewRenderer
 
           g.fillRect(pixelCol, y - height, endCol - pixelCol + 1, height);
         }
+
         pixelCol = endCol + 1;
         colIndex++;
       }
     }
+    changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1,
+            MAX_PROGRESS);
   }
 
+  /**
+   * Allows redraw flag to be set
+   * 
+   * @param b
+   *          value to set redraw to: true = redraw is occurring, false = no
+   *          redraw
+   */
   public void setRedraw(boolean b)
   {
     synchronized (this)
@@ -229,4 +436,14 @@ public class OverviewRenderer
       redraw = b;
     }
   }
+
+  public void addPropertyChangeListener(RendererListenerI listener)
+  {
+    changeSupport.addPropertyChangeListener(listener);
+  }
+
+  public void removePropertyChangeListener(RendererListenerI listener)
+  {
+    changeSupport.removePropertyChangeListener(listener);
+  }
 }
diff --git a/src/jalview/renderer/OverviewResColourFinder.java b/src/jalview/renderer/OverviewResColourFinder.java
new file mode 100644 (file)
index 0000000..a497d92
--- /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.renderer;
+
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
+
+import java.awt.Color;
+
+public class OverviewResColourFinder extends ResidueColourFinder
+{
+  final Color GAP_COLOUR; // default colour to use at gaps
+
+  final Color RESIDUE_COLOUR; // default colour to use at residues
+
+  final Color HIDDEN_COLOUR; // colour for hidden regions
+
+  boolean useLegacy = false;
+
+  public static final Color OVERVIEW_DEFAULT_GAP = Color.lightGray;
+
+  public static final Color OVERVIEW_DEFAULT_LEGACY_GAP = Color.white;
+
+  public static final Color OVERVIEW_DEFAULT_HIDDEN = Color.darkGray
+          .darker();
+
+  /**
+   * Constructor without colour settings (used by applet)
+   */
+  public OverviewResColourFinder()
+  {
+    this(false, OVERVIEW_DEFAULT_GAP, OVERVIEW_DEFAULT_HIDDEN);
+  }
+
+  /**
+   * Constructor with colour settings
+   * 
+   * @param useLegacyColouring
+   *          whether to use legacy gap colouring (white gaps, grey residues)
+   * @param gapCol
+   *          gap colour if not legacy
+   * @param hiddenCol
+   *          hidden region colour (transparency applied by rendering code)
+   */
+  public OverviewResColourFinder(boolean useLegacyColouring, Color gapCol,
+          Color hiddenCol)
+  {
+    if (useLegacyColouring)
+    {
+      GAP_COLOUR = Color.white;
+      RESIDUE_COLOUR = Color.lightGray;
+      HIDDEN_COLOUR = hiddenCol;
+    }
+    else
+    {
+      GAP_COLOUR = gapCol;
+      RESIDUE_COLOUR = Color.white;
+      HIDDEN_COLOUR = hiddenCol;
+    }
+  }
+
+  @Override
+  public Color getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
+  {
+    Color resBoxColour = RESIDUE_COLOUR;
+    char currentChar = seq.getCharAt(i);
+
+    // In the overview window, gaps are coloured grey, unless the colour scheme
+    // specifies a gap colour, in which case gaps honour the colour scheme
+    // settings
+    if (shader.getColourScheme() != null)
+    {
+      if (Comparison.isGap(currentChar)
+              && (!shader.getColourScheme().hasGapColour()))
+      {
+        resBoxColour = GAP_COLOUR;
+      }
+      else
+      {
+        resBoxColour = shader.findColour(currentChar, i, seq);
+      }
+    }
+    else if (Comparison.isGap(currentChar))
+    {
+      resBoxColour = GAP_COLOUR;
+    }
+
+    return resBoxColour;
+  }
+
+  /**
+   * {@inheritDoc} In the overview, the showBoxes setting is ignored, as the
+   * overview displays the colours regardless.
+   */
+  @Override
+  protected Color getResidueBoxColour(boolean showBoxes,
+          ResidueShaderI shader,
+          SequenceGroup[] allGroups, SequenceI seq, int i)
+  {
+    ResidueShaderI currentShader;
+    SequenceGroup currentSequenceGroup = getCurrentSequenceGroup(allGroups,
+            i);
+    if (currentSequenceGroup != null)
+    {
+      currentShader = currentSequenceGroup.getGroupColourScheme();
+    }
+    else
+    {
+      currentShader = shader;
+    }
+
+    return getBoxColour(currentShader, seq, i);
+  }
+
+  /**
+   * Supply hidden colour
+   * 
+   * @return colour of hidden regions
+   */
+  protected Color getHiddenColour()
+  {
+    return HIDDEN_COLOUR;
+  }
+}
diff --git a/src/jalview/renderer/ResidueColourFinder.java b/src/jalview/renderer/ResidueColourFinder.java
new file mode 100644 (file)
index 0000000..2da7233
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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 jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
+
+import java.awt.Color;
+
+public class ResidueColourFinder
+{
+  public ResidueColourFinder()
+  {
+  }
+
+  /**
+   * Get the colour of a residue in a sequence
+   * 
+   * @param showBoxes
+   *          true if the viewport's Show Boxes setting is true
+   * @param shader
+   *          the viewport's colour scheme
+   * @param allGroups
+   *          all the groups which seq participates in
+   * @param seq
+   *          the sequence containing the residue
+   * @param position
+   *          the position of the residue in the sequence
+   * @param finder
+   *          FeatureColourFinder for the viewport
+   * @return colour of the residue
+   */
+  public Color getResidueColour(boolean showBoxes, ResidueShaderI shader,
+          SequenceGroup[] allGroups,
+          final SequenceI seq, int position, FeatureColourFinder finder)
+  {
+    Color col = getResidueBoxColour(showBoxes, shader, allGroups, seq,
+            position);
+
+    // if there's a FeatureColourFinder we might override the residue colour
+    // here with feature colouring
+    if (finder != null)
+    {
+      col = finder.findFeatureColour(col, seq, position);
+    }
+    return col;
+  }
+
+  /**
+   * Get the residue colour without accounting for any features
+   * 
+   * @param showBoxes
+   *          true if the viewport's Show Boxes setting is true
+   * @param shader
+   *          the viewport's colour scheme
+   * @param allGroups
+   *          all the groups which seq participates in
+   * @param seq
+   *          the sequence containing the residue
+   * @param i
+   *          the position of the residue in the sequence
+   * @return
+   */
+  protected Color getResidueBoxColour(boolean showBoxes,
+          ResidueShaderI shader,
+          SequenceGroup[] allGroups,
+          SequenceI seq, int i)
+  {
+    SequenceGroup currentSequenceGroup = getCurrentSequenceGroup(allGroups,
+            i);
+    if (currentSequenceGroup != null)
+    {
+      if (currentSequenceGroup.getDisplayBoxes())
+      {
+        return getBoxColour(currentSequenceGroup.getGroupColourScheme(),
+                seq, i);
+      }
+    }
+    else if (showBoxes)
+    {
+      return getBoxColour(shader, seq, i);
+    }
+  
+    return Color.white;
+  }
+
+  /**
+   * Search all the groups for a sequence to find the one which a given res
+   * falls into
+   * 
+   * @param allGroups
+   *          all the groups a sequence participates in
+   * @param res
+   *          the residue to search for
+   * @return a sequence group for res, or null if no sequence group applies
+   */
+  public SequenceGroup getCurrentSequenceGroup(SequenceGroup[] allGroups,
+          int res)
+  {
+    if (allGroups == null)
+    {
+      return null;
+    }
+
+    for (int i = 0; i < allGroups.length; i++)
+    {
+      if ((allGroups[i].getStartRes() <= res)
+              && (allGroups[i].getEndRes() >= res))
+      {
+        return (allGroups[i]);
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param shader
+   *          the viewport's colour scheme
+   * @param seq
+   *          the sequence containing the residue
+   * @param i
+   *          the position of the residue in the sequence
+   */
+  public Color getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
+  {
+    Color resBoxColour = Color.white;
+    if (shader.getColourScheme() != null)
+    {
+      resBoxColour = shader.findColour(seq.getCharAt(i), i, seq);
+    }
+    return resBoxColour;
+  }
+
+}
index 7e4f211..c031170 100644 (file)
@@ -235,6 +235,11 @@ public class ResidueShader implements ResidueShaderI
   @Override
   public Color findColour(char symbol, int position, SequenceI seq)
   {
+    if (colourScheme == null)
+    {
+      return Color.white; // Colour is 'None'
+    }
+
     /*
      * get 'base' colour
      */
@@ -243,14 +248,16 @@ public class ResidueShader implements ResidueShaderI
             : profile.getModalResidue();
     float pid = profile == null ? 0f
             : profile.getPercentageIdentity(ignoreGaps);
-    Color colour = colourScheme == null ? Color.white
-            : colourScheme.findColour(symbol, position, seq, modalResidue,
-                    pid);
+    Color colour = colourScheme.findColour(symbol, position, seq,
+            modalResidue, pid);
 
     /*
      * apply PID threshold and consensus fading if in force
      */
-    colour = adjustColour(symbol, position, colour);
+    if (!Comparison.isGap(symbol))
+    {
+      colour = adjustColour(symbol, position, colour);
+    }
 
     return colour;
   }
index e81e519..f16522f 100644 (file)
@@ -401,27 +401,6 @@ public class FeatureRenderer extends FeatureRendererModel
   }
 
   /**
-<<<<<<< HEAD
-=======
-   * Answers true if the feature belongs to a feature group which is not
-   * currently displayed, else false
-   * 
-   * @param sequenceFeature
-   * @return
-   */
-  @Override
-  protected boolean featureGroupNotShown(
-          final SequenceFeature sequenceFeature)
-  {
-    return featureGroups != null && sequenceFeature.featureGroup != null
-            && sequenceFeature.featureGroup.length() != 0
-            && featureGroups.containsKey(sequenceFeature.featureGroup)
-            && !featureGroups.get(sequenceFeature.featureGroup)
-                    .booleanValue();
-  }
-
-  /**
->>>>>>> refs/heads/develop
    * Called when alignment in associated view has new/modified features to
    * discover and display.
    * 
index f16ca21..d70b4e2 100755 (executable)
@@ -98,4 +98,11 @@ public interface ColourSchemeI
    * @return
    */
   boolean isSimple();
+
+  /**
+   * Answers true if the colour scheme has a colour specified for gaps.
+   * 
+   * @return
+   */
+  boolean hasGapColour();
 }
index 34a5daa..2f7a5e0 100755 (executable)
@@ -208,4 +208,14 @@ public abstract class ResidueColourScheme implements ColourSchemeI
   {
     return true;
   }
+
+  /**
+   * Default method returns false. Override this to return true in colour
+   * schemes that have a colour associated with gap residues.
+   */
+  @Override
+  public boolean hasGapColour()
+  {
+    return false;
+  }
 }
index b86250a..bf62e45 100755 (executable)
@@ -287,7 +287,7 @@ public class UserColourScheme extends ResidueColourScheme
     /*
      * step 1: build a map from colours to the symbol(s) that have the colour
      */
-    Map<Color, List<String>> colours = new HashMap<Color, List<String>>();
+    Map<Color, List<String>> colours = new HashMap<>();
 
     for (char symbol = 'A'; symbol <= 'Z'; symbol++)
     {
@@ -320,7 +320,7 @@ public class UserColourScheme extends ResidueColourScheme
     /*
      * step 2: make a list of { A,G,R=12f9d6 } residues/colour specs
      */
-    List<String> residueColours = new ArrayList<String>();
+    List<String> residueColours = new ArrayList<>();
     for (Entry<Color, List<String>> cols : colours.entrySet())
     {
       boolean first = true;
@@ -350,4 +350,10 @@ public class UserColourScheme extends ResidueColourScheme
     Collections.sort(residueColours);
     return StringUtils.listToDelimitedString(residueColours, ";");
   }
+
+  @Override
+  public boolean hasGapColour()
+  {
+    return (findColour(' ') != null);
+  }
 }
index fbfa486..b973f45 100644 (file)
@@ -340,7 +340,9 @@ public class StructureSelectionManager
    *          - 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)
+   *          (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
index 9f4cea0..2528286 100644 (file)
@@ -142,7 +142,6 @@ public abstract class AAStructureBindingModel
    * @param ssm
    * @param pdbentry
    * @param sequenceIs
-   * @param chains
    * @param protocol
    */
   public AAStructureBindingModel(StructureSelectionManager ssm,
@@ -154,8 +153,60 @@ public abstract class AAStructureBindingModel
     this.nucleotide = Comparison.isNucleotide(sequenceIs);
     this.pdbEntry = pdbentry;
     this.protocol = protocol;
+    resolveChains();
   }
 
+  private boolean resolveChains()
+  {
+    /**
+     * final count of chain mappings discovered
+     */
+    int chainmaps = 0;
+    // JBPNote: JAL-2693 - this should be a list of chain mappings per
+    // [pdbentry][sequence]
+    String[][] newchains = new String[pdbEntry.length][];
+    int pe = 0;
+    for (PDBEntry pdb : pdbEntry)
+    {
+      SequenceI[] seqsForPdb = sequence[pe];
+      if (seqsForPdb != null)
+      {
+        newchains[pe] = new String[seqsForPdb.length];
+        int se = 0;
+        for (SequenceI asq : seqsForPdb)
+        {
+          String chain = (chains != null && chains[pe] != null)
+                  ? chains[pe][se]
+                  : null;
+          SequenceI sq = (asq.getDatasetSequence() == null) ? asq
+                  : asq.getDatasetSequence();
+          if (sq.getAllPDBEntries() != null)
+          {
+            for (PDBEntry pdbentry : sq.getAllPDBEntries())
+            {
+              if (pdb.getFile() != null && pdbentry.getFile() != null
+                      && pdb.getFile().equals(pdbentry.getFile()))
+              {
+                String chaincode = pdbentry.getChainCode();
+                if (chaincode != null && chaincode.length() > 0)
+                {
+                  chain = chaincode;
+                  chainmaps++;
+                  break;
+                }
+              }
+            }
+          }
+          newchains[pe][se] = chain;
+          se++;
+        }
+        pe++;
+      }
+    }
+
+    chains = newchains;
+    return chainmaps > 0;
+  }
   public StructureSelectionManager getSsm()
   {
     return ssm;
index d9f8bea..2c74609 100644 (file)
@@ -30,7 +30,7 @@ import java.awt.event.MouseEvent;
  */
 public class Platform
 {
-  private static Boolean isAMac = null;
+  private static Boolean isAMac = null, isWindows = null;
 
   private static Boolean isHeadless = null;
 
@@ -45,10 +45,29 @@ public class Platform
     {
       isAMac = System.getProperty("os.name").indexOf("Mac") > -1;
     }
+
     return isAMac.booleanValue();
 
   }
 
+  /**
+   * Check if we are on a Microsoft plaform...
+   * 
+   * @return true if we have to cope with another platform variation
+   */
+  public static boolean isWindows()
+  {
+    if (isWindows == null)
+    {
+      isWindows = System.getProperty("os.name").indexOf("Win") > -1;
+    }
+    return isWindows.booleanValue();
+  }
+
+  /**
+   * 
+   * @return true if we are running in non-interactive no UI mode
+   */
   public static boolean isHeadless()
   {
     if (isHeadless == null)
index 3347cc7..d6ece8d 100644 (file)
@@ -55,7 +55,7 @@ public class UrlConstants
    * Default sequence URL link string for EMBL-EBI search
    */
   public static final String DEFAULT_STRING = DEFAULT_LABEL
-          + "|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$";
+          + "|https://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$";
 
   /*
    * not instantiable
index 21b226b..ca90d60 100644 (file)
@@ -91,7 +91,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
           File reply) throws Exception
   {
     EmblFile efile = null;
-    List<SequenceI> seqs = new ArrayList<SequenceI>();
+    List<SequenceI> seqs = new ArrayList<>();
 
     if (reply != null && reply.exists())
     {
@@ -107,7 +107,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
      * EmbFile reads something like (e.g.) this ungrammatical phrase
      * Entry: <acc> display type is either not supported or entry is not found.
      */
-    List<SequenceI> peptides = new ArrayList<SequenceI>();
+    List<SequenceI> peptides = new ArrayList<>();
     if (efile != null && efile.getEntries() != null)
     {
       for (EmblEntry entry : efile.getEntries())
index c868576..c9beb8e 100644 (file)
@@ -31,13 +31,13 @@ import jalview.datamodel.SequenceI;
 import jalview.datamodel.xdb.uniprot.UniprotEntry;
 import jalview.datamodel.xdb.uniprot.UniprotFeature;
 import jalview.datamodel.xdb.uniprot.UniprotFile;
-import jalview.ws.ebi.EBIFetchClient;
 import jalview.ws.seqfetcher.DbSourceProxyImpl;
 
-import java.io.File;
-import java.io.FileReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.Reader;
 import java.net.URL;
+import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Vector;
 
@@ -162,17 +162,21 @@ public class Uniprot extends DbSourceProxyImpl
       queries = queries.toUpperCase().replaceAll(
               "(UNIPROT\\|?|UNIPROT_|UNIREF\\d+_|UNIREF\\d+\\|?)", "");
       AlignmentI al = null;
-      EBIFetchClient ebi = new EBIFetchClient();
-      // uniprotxml parameter required since december 2007
-      // uniprotkb dbname changed introduced december 2008
-      File file = ebi.fetchDataAsFile("uniprotkb:" + queries, "uniprotxml",
-              "xml");
+
+      String downloadstring = "http://www.uniprot.org/uniprot/" + queries
+              + ".xml";
+      URL url = null;
+      URLConnection urlconn = null;
+
+      url = new URL(downloadstring);
+      urlconn = url.openConnection();
+      InputStream istr = urlconn.getInputStream();
       Vector<UniprotEntry> entries = getUniprotEntries(
-              new FileReader(file));
+              new InputStreamReader(istr, "UTF-8"));
 
       if (entries != null)
       {
-        ArrayList<SequenceI> seqs = new ArrayList<SequenceI>();
+        ArrayList<SequenceI> seqs = new ArrayList<>();
         for (UniprotEntry entry : entries)
         {
           seqs.add(uniprotEntryToSequenceI(entry));
@@ -184,8 +188,10 @@ public class Uniprot extends DbSourceProxyImpl
       return al;
     } catch (Exception e)
     {
-      stopQuery();
       throw (e);
+    } finally
+    {
+      stopQuery();
     }
   }
 
@@ -203,7 +209,7 @@ public class Uniprot extends DbSourceProxyImpl
     sequence.setDescription(getUniprotEntryDescription(entry));
 
     final String dbVersion = getDbVersion();
-    ArrayList<DBRefEntry> dbRefs = new ArrayList<DBRefEntry>();
+    ArrayList<DBRefEntry> dbRefs = new ArrayList<>();
     for (String accessionId : entry.getAccession())
     {
       DBRefEntry dbRef = new DBRefEntry(DBRefSource.UNIPROT, dbVersion,
@@ -213,7 +219,7 @@ public class Uniprot extends DbSourceProxyImpl
       dbRefs.add(dbRef);
     }
 
-    Vector<PDBEntry> onlyPdbEntries = new Vector<PDBEntry>();
+    Vector<PDBEntry> onlyPdbEntries = new Vector<>();
     for (PDBEntry pdb : entry.getDbReference())
     {
       DBRefEntry dbr = new DBRefEntry();
index 3e8c55e..07a9df4 100644 (file)
@@ -29,6 +29,7 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
@@ -205,7 +206,14 @@ public class EBIFetchClient
     {
       URL rcall = new URL(url);
 
-      InputStream is = new BufferedInputStream(rcall.openStream());
+      HttpURLConnection conn = (HttpURLConnection) rcall.openConnection();
+      int responseCode = conn.getResponseCode();
+      if (responseCode != 200)
+      {
+        System.err.println("Warning: response code " + responseCode
+                + " for " + url);
+      }
+      InputStream is = new BufferedInputStream(conn.getInputStream());
       if (outFile != null)
       {
         FileOutputStream fio = new FileOutputStream(outFile);
@@ -267,12 +275,12 @@ public class EBIFetchClient
     if (database.equalsIgnoreCase(DBRefSource.EMBL)
             || database.equalsIgnoreCase(DBRefSource.EMBLCDS))
     {
-      url = "http://www.ebi.ac.uk/ena/data/view/" + ids.toLowerCase()
+      url = "https://www.ebi.ac.uk/ena/data/view/" + ids.toLowerCase()
               + (format != null ? "&" + format : "");
     }
     else
     {
-      url = "http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/"
+      url = "https://www.ebi.ac.uk/Tools/dbfetch/dbfetch/"
               + database.toLowerCase() + "/" + ids.toLowerCase()
               + (format != null ? "/" + format : "");
     }
index 977f9da..9284f82 100644 (file)
@@ -126,15 +126,16 @@ public class ASequenceFetcher
    */
   public SequenceI[] getSequences(List<DBRefEntry> refs, boolean dna)
   {
-    Vector<SequenceI> rseqs = new Vector<SequenceI>();
-    Hashtable<String, List<String>> queries = new Hashtable<String, List<String>>();
+    Vector<SequenceI> rseqs = new Vector<>();
+    Hashtable<String, List<String>> queries = new Hashtable<>();
     for (DBRefEntry ref : refs)
     {
-      if (!queries.containsKey(ref.getSource()))
+      String canonical = DBRefUtils.getCanonicalName(ref.getSource());
+      if (!queries.containsKey(canonical))
       {
-        queries.put(ref.getSource(), new ArrayList<String>());
+        queries.put(canonical, new ArrayList<String>());
       }
-      List<String> qset = queries.get(ref.getSource());
+      List<String> qset = queries.get(canonical);
       if (!qset.contains(ref.getAccessionId()))
       {
         qset.add(ref.getAccessionId());
@@ -154,14 +155,14 @@ public class ASequenceFetcher
         continue;
       }
 
-      Stack<String> queriesLeft = new Stack<String>();
+      Stack<String> queriesLeft = new Stack<>();
       queriesLeft.addAll(query);
 
       List<DbSourceProxy> proxies = getSourceProxy(db);
       for (DbSourceProxy fetcher : proxies)
       {
-        List<String> queriesMade = new ArrayList<String>();
-        HashSet<String> queriesFound = new HashSet<String>();
+        List<String> queriesMade = new ArrayList<>();
+        HashSet<String> queriesFound = new HashSet<>();
         try
         {
           if (fetcher.isDnaCoding() != dna)
@@ -306,13 +307,13 @@ public class ASequenceFetcher
     Map<String, DbSourceProxy> dblist = fetchableDbs.get(db);
     if (dblist == null)
     {
-      return new ArrayList<DbSourceProxy>();
+      return new ArrayList<>();
     }
 
     /*
      * sort so that primary sources precede secondary
      */
-    List<DbSourceProxy> dbs = new ArrayList<DbSourceProxy>(dblist.values());
+    List<DbSourceProxy> dbs = new ArrayList<>(dblist.values());
     Collections.sort(dbs, proxyComparator);
     return dbs;
   }
@@ -357,14 +358,14 @@ public class ASequenceFetcher
     {
       if (fetchableDbs == null)
       {
-        fetchableDbs = new Hashtable<String, Map<String, DbSourceProxy>>();
+        fetchableDbs = new Hashtable<>();
       }
       Map<String, DbSourceProxy> slist = fetchableDbs
               .get(proxy.getDbSource());
       if (slist == null)
       {
         fetchableDbs.put(proxy.getDbSource(),
-                slist = new Hashtable<String, DbSourceProxy>());
+                slist = new Hashtable<>());
       }
       slist.put(proxy.getDbName(), proxy);
     }
@@ -391,7 +392,7 @@ public class ASequenceFetcher
       return null;
     }
     String[] sources = null;
-    Vector<String> src = new Vector<String>();
+    Vector<String> src = new Vector<>();
     Enumeration<String> dbs = fetchableDbs.keys();
     while (dbs.hasMoreElements())
     {
@@ -413,7 +414,7 @@ public class ASequenceFetcher
 
   public DbSourceProxy[] getDbSourceProxyInstances(Class class1)
   {
-    List<DbSourceProxy> prlist = new ArrayList<DbSourceProxy>();
+    List<DbSourceProxy> prlist = new ArrayList<>();
     for (String fetchable : getSupportedDb())
     {
       for (DbSourceProxy pr : getSourceProxy(fetchable))
index 2f824ca..6844072 100644 (file)
@@ -1358,6 +1358,10 @@ public class SequenceTest
     SequenceFeature sfContactFG = new SequenceFeature("Disulfide Bond",
             "desc", 13, 14, 2f, null);
     sq.addSequenceFeature(sfContactFG);
+    // add single position feature at [I]
+    SequenceFeature sfI = new SequenceFeature("Disulfide Bond",
+            "desc", 16, 16, null);
+    sq.addSequenceFeature(sfI);
 
     // no features in columns 1-2 (-A)
     List<SequenceFeature> found = sq.findFeatures(1, 2);
@@ -1384,6 +1388,11 @@ public class SequenceTest
     // columns 10-11 (--) should find nothing
     found = sq.findFeatures(10, 11);
     assertEquals(0, found.size());
+
+    // columns 14-14 (I) should find variant feature
+    found = sq.findFeatures(14, 14);
+    assertEquals(1, found.size());
+    assertTrue(found.contains(sfI));
   }
 
   @Test(groups = { "Functional" })
index a144f03..39d6dce 100644 (file)
@@ -1032,6 +1032,9 @@ public class SequenceFeaturesTest
     assertEquals(features.size(), 2);
     assertTrue(features.contains(sf2));
     assertTrue(features.contains(sf3));
+
+    features = store.getFeaturesByOntology("sequence_variant");
+    assertTrue(features.isEmpty());
   }
 
   @Test(groups = "Functional")
index c80b830..359377a 100644 (file)
  */
 package jalview.gui;
 
-import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.Assert.assertEquals;
 
+import jalview.bin.Jalview;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.renderer.ResidueShader;
+import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ZappoColourScheme;
 
 import java.awt.Color;
 
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 public class SequenceRendererTest
 {
+  AlignmentI al;
+  AlignViewport av;
+
+  SequenceI seq1;
 
   @BeforeClass(alwaysRun = true)
-  public void setUpJvOptionPane()
+  public static void setUpBeforeClass() throws Exception
   {
-    JvOptionPane.setInteractiveMode(false);
-    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+    Jalview.main(
+            new String[]
+    { "-nonews", "-props", "test/jalview/testProps.jvprops" });
   }
 
-  @Test(groups = { "Functional" })
-  public void testGetResidueBoxColour_zappo()
+  @BeforeMethod(alwaysRun = true)
+  public void setUp()
   {
-    SequenceI seq = new Sequence("name", "MATVLGSPRAPAFF"); // FER1_MAIZE...
-    AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
-    SequenceRenderer sr = new SequenceRenderer(av);
-    av.setGlobalColourScheme(new ZappoColourScheme());
-
-    // @see ResidueProperties.zappo
-    assertEquals(Color.pink, sr.getResidueBoxColour(seq, 0)); // M
-    assertEquals(Color.green, sr.getResidueBoxColour(seq, 2)); // T
-    assertEquals(Color.magenta, sr.getResidueBoxColour(seq, 5)); // G
-    assertEquals(Color.orange, sr.getResidueBoxColour(seq, 12)); // F
+    seq1 = new Sequence("Seq1", "ABCEEEABCABC");
+    SequenceI seq2 = new Sequence("Seq2", "ABCABCABCABC");
+    SequenceI seq3 = new Sequence("Seq3", "ABCABCABCABC");
+    SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3 };
+    al = new Alignment(seqs);
+    al.setDataset(null);
+    av = new AlignViewport(al);
   }
 
-  @Test(groups = { "Functional" })
-  public void testGetResidueBoxColour_none()
+  @Test(groups = "Functional")
+  public void testGetResidueColour_WithGroup()
   {
-    SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
-    AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
     SequenceRenderer sr = new SequenceRenderer(av);
+    SequenceGroup sg = new SequenceGroup();
+    sg.addSequence(seq1, false);
+    sg.setStartRes(3);
+    sg.setEndRes(5);
 
-    assertEquals(Color.white, sr.getResidueBoxColour(seq, 0));
-    assertEquals(Color.white, sr.getResidueBoxColour(seq, 2));
+    ResidueShaderI rs = new ResidueShader();
+    rs.setColourScheme(new ZappoColourScheme());
+    sg.setGroupColourScheme(rs);
 
-    // set for overview
-    sr.forOverview = true;
-    assertEquals(Color.lightGray, sr.getResidueBoxColour(seq, 0));
-    assertEquals(Color.white, sr.getResidueBoxColour(seq, 2));
-  }
+    av.getAlignment().addGroup(sg);
 
-  // TODO more tests for getResidueBoxColour covering groups, feature rendering,
-  // gaps, overview...
+    // outside group residues are white
+    assertEquals(Color.white, sr.getResidueColour(seq1, 1, null));
 
+    // within group use Zappo scheme - E = red
+    assertEquals(Color.red, sr.getResidueColour(seq1, 3, null));
+  }
 }
index ec5855f..0715857 100644 (file)
@@ -31,10 +31,12 @@ import jalview.gui.CrossRefAction;
 import jalview.gui.Desktop;
 import jalview.gui.Jalview2XML;
 import jalview.gui.JvOptionPane;
+import jalview.util.DBRefUtils;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 
@@ -65,10 +67,13 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
   public void testRetrieveAndShowCrossref() throws Exception
   {
 
-    List<String> failedDBRetr = new ArrayList<String>();
-    List<String> failedXrefMenuItems = new ArrayList<String>();
-    List<String> failedProjectRecoveries = new ArrayList<String>();
-
+    List<String> failedDBRetr = new ArrayList<>();
+    List<String> failedXrefMenuItems = new ArrayList<>();
+    List<String> failedProjectRecoveries = new ArrayList<>();
+    // only search for ensembl or Uniprot crossrefs
+    List<String> limit=Arrays.asList(new String[] {
+        DBRefUtils.getCanonicalName("ENSEMBL"), 
+        DBRefUtils.getCanonicalName("Uniprot")});
     // for every set of db queries
     // retrieve db query
     // verify presence of expected xrefs
@@ -85,12 +90,11 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
     // . codonframes
     //
     //
-    HashMap<String, String> dbtoviewBit = new HashMap<String, String>();
-    List<String> keyseq = new ArrayList<String>();
-    HashMap<String, File> savedProjects = new HashMap<String, File>();
+    HashMap<String, String> dbtoviewBit = new HashMap<>();
+    List<String> keyseq = new ArrayList<>();
+    HashMap<String, File> savedProjects = new HashMap<>();
 
-    for (String[] did : new String[][] { { "ENSEMBL", "ENSG00000157764" },
-    { "UNIPROT", "P01731" } })
+    for (String[] did : new String[][] { { "UNIPROT", "P00338" } })
     {
       // pass counters - 0 - first pass, 1 means retrieve project rather than
       // perform action
@@ -163,7 +167,8 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
 
         ptypes = (seqs == null || seqs.length == 0) ? null : new CrossRef(
                 seqs, dataset).findXrefSourcesForSequences(dna);
-
+        filterDbRefs(ptypes, limit);
+        
         // start of pass2: retrieve each cross-ref for fetched or restored
         // project.
         do // first cross ref and recover crossref loop
@@ -176,7 +181,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
             // build next key so we an retrieve all views
             String nextxref = first + " -> " + db + "{" + firstcr_ap + "}";
             // perform crossref action, or retrieve stored project
-            List<AlignmentViewPanel> cra_views = new ArrayList<AlignmentViewPanel>();
+            List<AlignmentViewPanel> cra_views = new ArrayList<>();
             CrossRefAction cra = null;
 
             if (pass2 == 0)
@@ -237,7 +242,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
 
               }
             }
-            HashMap<String, List<String>> xrptypes = new HashMap<String, List<String>>();
+            HashMap<String, List<String>> xrptypes = new HashMap<>();
             // first save/verify views.
             for (AlignmentViewPanel avp : cra_views)
             {
@@ -274,7 +279,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
                 nextxref = first + " -> " + db + "{" + firstcr_ap++ + "}";
                 for (String xrefdb : xrptypes.get(nextxref))
                 {
-                  List<AlignmentViewPanel> cra_views2 = new ArrayList<AlignmentViewPanel>();
+                  List<AlignmentViewPanel> cra_views2 = new ArrayList<>();
                   int q = 0;
                   String nextnextxref = nextxref + " -> " + xrefdb + "{"
                           + q + "}";
@@ -437,6 +442,25 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
     }
   }
 
+  private void filterDbRefs(List<String> ptypes, List<String> limit)
+  {
+    if (limit != null)
+    {
+      int p = 0;
+      while (ptypes.size() > p)
+      {
+        if (!limit.contains(ptypes.get(p)))
+        {
+          ptypes.remove(p);
+        }
+        else
+        {
+          p++;
+        }
+      }
+    }
+  }
+
   /**
    * wrapper to trap known defect for AH002001 testcase
    * 
@@ -480,7 +504,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
   private void assertType(boolean expectProtein,
           AlignmentViewPanel alignmentViewPanel, String message)
   {
-    List<SequenceI> nonType = new ArrayList<SequenceI>();
+    List<SequenceI> nonType = new ArrayList<>();
     for (SequenceI sq : alignmentViewPanel.getAlignViewport()
             .getAlignment().getSequences())
     {
index 3b688db..152ab84 100644 (file)
@@ -34,7 +34,9 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.features.SequenceFeatures;
 import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
 import jalview.gui.JvOptionPane;
+import jalview.structure.StructureSelectionManager;
 
 import java.awt.Color;
 import java.io.File;
@@ -45,11 +47,24 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 public class FeaturesFileTest
 {
+  private static String simpleGffFile = "examples/testdata/simpleGff3.gff";
+
+  @AfterClass(alwaysRun = true)
+  public void tearDownAfterClass()
+  {
+    /*
+     * remove any sequence mappings created so they don't pollute other tests
+     */
+    StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
+    ssm.resetAll();
+  }
 
   @BeforeClass(alwaysRun = true)
   public void setUpJvOptionPane()
@@ -58,8 +73,6 @@ public class FeaturesFileTest
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
-  private static String simpleGffFile = "examples/testdata/simpleGff3.gff";
-
   @Test(groups = { "Functional" })
   public void testParse() throws Exception
   {
diff --git a/test/jalview/renderer/OverviewResColourFinderTest.java b/test/jalview/renderer/OverviewResColourFinderTest.java
new file mode 100644 (file)
index 0000000..1687516
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * 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.AssertJUnit.assertEquals;
+
+import jalview.bin.Cache;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignViewport;
+import jalview.gui.JvOptionPane;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.UserColourScheme;
+import jalview.schemes.ZappoColourScheme;
+
+import java.awt.Color;
+import java.util.ArrayList;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class OverviewResColourFinderTest
+{
+
+  @BeforeClass(alwaysRun = true)
+  public void setUpJvOptionPane()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueBoxColour_none()
+  {
+    SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new OverviewResColourFinder();
+
+    // gaps are grey, residues white
+    assertEquals(Color.white, rcf.getResidueColour(true,
+            av.getResidueShading(),
+            null, seq, 0, null));
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(true, av.getResidueShading(), null, seq, 2,
+                    null));
+
+    // unaffected by showBoxes setting
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 0, null));
+    assertEquals(Color.lightGray, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 2, null));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueBoxColour_zappo()
+  {
+    SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
+                                                            // gap
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new OverviewResColourFinder();
+    av.setGlobalColourScheme(new ZappoColourScheme());
+
+    // @see ResidueProperties.zappo
+    assertEquals(Color.pink,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 0, null)); // M
+    assertEquals(Color.green,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 2, null)); // T
+    assertEquals(Color.magenta,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 5, null)); // G
+    assertEquals(Color.orange,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 12, null)); // F
+
+    // gap colour not specified so gaps are lightGray
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(true, av.getResidueShading(), null, seq, 3,
+                    null));
+
+    // unaffected by showBoxes setting
+    assertEquals(Color.pink, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 0, null)); // M
+    assertEquals(Color.green, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 2, null)); // T
+    assertEquals(Color.magenta, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 5, null)); // G
+    assertEquals(Color.orange, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 12, null)); // F
+
+    // gap colour not specified so gaps are lightGray
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(false, av.getResidueShading(), null, seq, 3,
+                    null));
+
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueBoxColour_userdef()
+  {
+    SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
+                                                            // gap
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new OverviewResColourFinder();
+
+    Color[] newColours = new Color[24];
+    for (int i = 0; i < 24; i++)
+    {
+      newColours[i] = null;
+    }
+
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour not specified so gaps are lightGray
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(true, av.getResidueShading(), null, seq, 3,
+                    null));
+
+    newColours[23] = Color.pink;
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour specified as pink
+    assertEquals(Color.pink, rcf.getResidueColour(true,
+            av.getResidueShading(),
+            null, seq, 3, null));
+
+    // unaffected by showBoxes setting
+    // gap colour not specified so gaps are lightGray
+    newColours[23] = null;
+    assertEquals(Color.lightGray, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 3, null));
+
+    newColours[23] = Color.pink;
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour specified as pink
+    assertEquals(Color.pink, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 3, null));
+  }
+
+  @Test
+  public void testGetResidueBoxColour_group()
+  {
+    SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    
+    ColourSchemeI cs = new ZappoColourScheme();
+    ArrayList<SequenceI> seqlist = new ArrayList<>();
+    seqlist.add(seq);
+    SequenceGroup sg = new SequenceGroup(seqlist, "testgroup", cs, true,
+            true, true, 5, 9);
+    al.addGroup(sg);
+    SequenceGroup[] groups = new SequenceGroup[1];
+    groups[0] = sg;
+    
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new OverviewResColourFinder();
+    
+    // G in group specified as magenta in Zappo
+    assertEquals(Color.magenta, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 7, null));
+
+    // Residue outside group coloured white
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 0, null));
+
+    // Gap outside group coloured lightgray
+    assertEquals(Color.lightGray, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 2, null));
+
+    // use legacy colouring
+    rcf = new OverviewResColourFinder(true, Color.blue, Color.red);
+  
+    // G in group specified as magenta in Zappo
+    assertEquals(Color.magenta, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 7, null));
+
+    // Residue outside group coloured lightgray
+    assertEquals(Color.lightGray, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 0, null));
+
+    // Gap outside group coloured white
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 2, null));
+
+    // use new colouring
+    rcf = new OverviewResColourFinder(false, Color.blue, Color.red);
+
+    // G in group specified as magenta in Zappo
+    assertEquals(Color.magenta, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 7, null));
+
+    // Residue outside group coloured white
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 0, null));
+
+    // Gap outside group coloured blue
+    assertEquals(Color.blue, rcf.getResidueColour(false,
+            av.getResidueShading(), groups, seq, 2, null));
+  }
+
+  @Test
+  public void testGetBoxColour()
+  {
+    SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
+                                                            // gap
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+
+    // non-legacy colouring
+    ResidueColourFinder rcf = new OverviewResColourFinder();
+    ResidueShaderI shader = new ResidueShader();
+
+    // residues white
+    Color c = rcf.getBoxColour(shader, seq, 0);
+    assertEquals(Color.white, c);
+
+    // gaps gap colour
+    c = rcf.getBoxColour(shader, seq, 3);
+    assertEquals(
+            jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP,
+            c);
+
+    // legacy colouring set explicitly via constructor
+    rcf = new OverviewResColourFinder(true, Color.blue, Color.red);
+    shader = new ResidueShader();
+
+    // residues light gray
+    c = rcf.getBoxColour(shader, seq, 0);
+    assertEquals(Color.lightGray, c);
+
+    // gaps white
+    c = rcf.getBoxColour(shader, seq, 3);
+    assertEquals(Color.white, c);
+
+    // legacy colouring off
+    rcf = new OverviewResColourFinder();
+    shader = new ResidueShader();
+
+    // residues white
+    c = rcf.getBoxColour(shader, seq, 0);
+    assertEquals(Color.white, c);
+
+    // gaps gap colour
+    c = rcf.getBoxColour(shader, seq, 3);
+    assertEquals(
+            jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP,
+            c);
+
+    // non legacy colouring with colour scheme
+    rcf = new OverviewResColourFinder(false, Color.blue, Color.red);
+    shader = new ResidueShader(new ZappoColourScheme());
+
+    // M residue pink
+    c = rcf.getBoxColour(shader, seq, 0);
+    assertEquals(Color.pink, c);
+
+    // gaps blue
+    c = rcf.getBoxColour(shader, seq, 3);
+    assertEquals(Color.blue, c);
+
+    // legacy colouring with colour scheme
+    rcf = new OverviewResColourFinder(true, Color.blue, Color.red);
+
+    // M residue pink
+    c = rcf.getBoxColour(shader, seq, 0);
+    assertEquals(Color.pink, c);
+
+    // gaps white
+    c = rcf.getBoxColour(shader, seq, 3);
+    assertEquals(Color.white, c);
+  }
+}
diff --git a/test/jalview/renderer/ResidueColourFinderTest.java b/test/jalview/renderer/ResidueColourFinderTest.java
new file mode 100644 (file)
index 0000000..81fb2c0
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * 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.AssertJUnit.assertEquals;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignViewport;
+import jalview.gui.JvOptionPane;
+import jalview.schemes.UserColourScheme;
+import jalview.schemes.ZappoColourScheme;
+
+import java.awt.Color;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class ResidueColourFinderTest
+{
+
+  @BeforeClass(alwaysRun = true)
+  public void setUpJvOptionPane()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueColour_zappo()
+  {
+    SequenceI seq = new Sequence("name", "MATVLGSPRAPAFF"); // FER1_MAIZE...
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new ResidueColourFinder();
+    av.setGlobalColourScheme(new ZappoColourScheme());
+
+    // @see ResidueProperties.zappo
+    assertEquals(Color.pink,
+            rcf.getResidueColour(true, av.getResidueShading(), null, seq, 0,
+                    null)); // M
+    assertEquals(Color.green,
+            rcf.getResidueColour(true, av.getResidueShading(), null, seq, 2,
+                    null)); // T
+    assertEquals(Color.magenta,
+            rcf.getResidueColour(true, av.getResidueShading(), null, seq, 5,
+                    null)); // G
+    assertEquals(Color.orange,
+            rcf.getResidueColour(true, av.getResidueShading(), null, seq,
+                    12,
+                    null)); // F
+
+    // everything is white if showBoxes is false
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 0, null)); // M
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 2, null)); // T
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 5, null)); // G
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 12, null)); // F
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueColour_none()
+  {
+    SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new ResidueColourFinder();
+
+    assertEquals(Color.white,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 0, null));
+    assertEquals(Color.white,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 2, null));
+
+    // no change if showBoxes is false
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 0, null));
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 2, null));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueColour_userdef()
+  {
+    SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
+                                                            // gap
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    ResidueColourFinder rcf = new ResidueColourFinder();
+
+    Color[] newColours = new Color[24];
+    for (int i = 0; i < 24; i++)
+    {
+      newColours[i] = null;
+    }
+
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour not specified so gap colour is null
+    // this is consistent with previous behaviour, but may not be correct?
+    assertEquals(null, rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 3, null));
+
+    newColours[23] = Color.pink;
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour specified as pink
+    assertEquals(Color.pink, rcf.getResidueColour(true,
+            av.getResidueShading(),
+            null, seq, 3, null));
+
+    // everything is white if showBoxes is false
+    newColours[23] = null;
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(),
+            null, seq, 3, null));
+
+    newColours[23] = Color.pink;
+    av.setGlobalColourScheme(new UserColourScheme(newColours));
+
+    // gap colour specified as pink
+    assertEquals(Color.white, rcf.getResidueColour(false,
+            av.getResidueShading(), null, seq, 3, null));
+  }
+
+  // TODO more tests for getResidueColour covering groups, feature rendering...
+}
index 76fd9b4..eba5f59 100644 (file)
@@ -8,9 +8,15 @@ import jalview.analysis.Conservation;
 import jalview.datamodel.Profile;
 import jalview.datamodel.ProfileI;
 import jalview.datamodel.Profiles;
+import jalview.datamodel.ProfilesI;
+import jalview.datamodel.ResidueCount;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
+import jalview.schemes.ColourSchemeI;
 import jalview.schemes.PIDColourScheme;
+import jalview.schemes.ResidueProperties;
+import jalview.schemes.UserColourScheme;
+import jalview.schemes.ZappoColourScheme;
 
 import java.awt.Color;
 import java.util.Collections;
@@ -163,4 +169,165 @@ public class ResidueShaderTest
     assertEquals(Color.WHITE, ccs.applyConservation(colour, 12));
   }
 
+  @Test(groups = "Functional")
+  public void testFindColour_gapColour()
+  {
+    /*
+     * normally, a gap is coloured white
+     */
+    ResidueShader rs = new ResidueShader(new ZappoColourScheme());
+    assertEquals(Color.white, rs.findColour(' ', 7, null));
+
+    /*
+     * a User Colour Scheme may specify a bespoke gap colour
+     */
+    Color[] colours = new Color[ResidueProperties.maxProteinIndex + 1];
+    colours[5] = Color.blue; // Q colour
+    colours[23] = Color.red; // gap colour
+    ColourSchemeI cs = new UserColourScheme(colours);
+    rs = new ResidueShader(cs);
+  
+    assertEquals(Color.red, rs.findColour(' ', 7, null));
+    assertEquals(Color.blue, rs.findColour('Q', 7, null));
+  
+    /*
+     * stub Conservation to return a given consensus string
+     */
+    final String consSequence = "0123456789+*-";
+    Conservation cons = new Conservation(null,
+            Collections.<SequenceI> emptyList(), 0, 0)
+    {
+      @Override
+      public SequenceI getConsSequence()
+      {
+        return new Sequence("seq", consSequence);
+      }
+    };
+    rs.setConservation(cons);
+  
+    /*
+     * with 0% threshold, there should be no fading
+     */
+    rs.setConservationInc(0);
+    assertEquals(Color.red, rs.findColour(' ', 7, null));
+    assertEquals(Color.blue, rs.findColour('Q', 7, null));
+  
+    /*
+     * with 40% threshold, 'fade factor' is 
+     * (11-score)/10 * 40/20 = (11-score)/5
+     * so position 7, score 7 fades 80% of the way to white (255, 255, 255)
+     */
+    rs.setConservationInc(40);
+
+    /*
+     * gap colour is unchanged for Conservation
+     */
+    assertEquals(Color.red, rs.findColour(' ', 7, null));
+    assertEquals(Color.red, rs.findColour('-', 7, null));
+    assertEquals(Color.red, rs.findColour('.', 7, null));
+
+    /*
+     * residue colour is faded 80% of the way from
+     * blue(0, 0, 255) to white(255, 255, 255)
+     * making (204, 204, 255)
+     */
+    assertEquals(new Color(204, 204, 255), rs.findColour('Q', 7, null));
+
+    /*
+     * turn off By Conservation, apply Above Identity Threshold
+     * providing a stub Consensus that has modal residue "Q" with pid 60%
+     */
+    rs.setConservationApplied(false);
+    ProfilesI consensus = getStubConsensus("Q", 60f);
+    rs.setConsensus(consensus);
+
+    // with consensus pid (60) above threshold (50), colours are unchanged
+    rs.setThreshold(50, false);
+    assertEquals(Color.blue, rs.findColour('Q', 7, null));
+    assertEquals(Color.red, rs.findColour('-', 7, null));
+
+    // with consensus pid (60) below threshold (70),
+    // residue colour becomes white, gap colour is unchanged
+    rs.setThreshold(70, false);
+    assertEquals(Color.white, rs.findColour('Q', 7, null));
+    assertEquals(Color.red, rs.findColour('-', 7, null));
+  }
+
+  /**
+   * @param modalResidue
+   * @param pid
+   * @return
+   */
+  protected ProfilesI getStubConsensus(final String modalResidue,
+          final float pid)
+  {
+    ProfilesI consensus = new ProfilesI() {
+
+      @Override
+      public ProfileI get(int i)
+      {
+        return new ProfileI() {
+          @Override
+          public void setCounts(ResidueCount residueCounts)
+          {
+          }
+
+          @Override
+          public float getPercentageIdentity(boolean ignoreGaps)
+          {
+            return pid;
+          }
+
+          @Override
+          public ResidueCount getCounts()
+          {
+            return null;
+          }
+
+          @Override
+          public int getHeight()
+          {
+            return 0;
+          }
+
+          @Override
+          public int getGapped()
+          {
+            return 0;
+          }
+
+          @Override
+          public int getMaxCount()
+          {
+            return 0;
+          }
+
+          @Override
+          public String getModalResidue()
+          {
+            return modalResidue;
+          }
+
+          @Override
+          public int getNonGapped()
+          {
+            return 0;
+         }};
+      }
+
+      @Override
+      public int getStartColumn()
+      {
+        return 0;
+      }
+
+      @Override
+      public int getEndColumn()
+      {
+        return 0;
+      }
+      
+    };
+    return consensus;
+  }
 }
index c125ef6..aea3687 100644 (file)
@@ -20,8 +20,8 @@
  */
 package jalview.structures.models;
 
+import static org.testng.Assert.assertFalse;
 import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.api.AlignmentViewPanel;
@@ -36,6 +36,7 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.gui.JvOptionPane;
 import jalview.io.DataSourceType;
+import jalview.io.FileFormats;
 import jalview.schemes.ColourSchemeI;
 import jalview.structure.AtomSpec;
 import jalview.structure.StructureMappingcommandSet;
@@ -43,6 +44,7 @@ import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel.SuperposeData;
 
 import java.awt.Color;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.List;
@@ -98,6 +100,150 @@ public class AAStructureBindingModelTest
           + "ATOM     33  CA  ALA A  10      26.790   4.320   3.172  1.00 11.98           C  \n"
           + "ATOM     39  CA AVAL A  12      24.424   3.853   6.106  0.50 13.83           C  \n";
 
+  /**
+   * Multichain PDB with identical sequences imported - Binding should correctly
+   * recover chain mappings for each derived sequence
+   */
+  private static final String PDB_4_MC = "HEADER    HYDROLASE                               09-SEP-09   3A6S              \n"
+          + "ATOM      2  CA  MET A   1      15.366 -11.648  24.854  1.00 32.05           C  \n"
+          + "ATOM     10  CA  LYS A   2      16.846  -9.215  22.340  1.00 25.68           C  \n"
+          + "ATOM     19  CA  LYS A   3      15.412  -6.335  20.343  1.00 19.42           C  \n"
+          + "ATOM     28  CA  LEU A   4      15.629  -5.719  16.616  1.00 15.49           C  \n"
+          + "ATOM     36  CA  GLN A   5      14.412  -2.295  15.567  1.00 12.19           C  \n"
+          + "ATOM   1030  CA  MET B   1      18.869  -7.572   3.432  1.00 31.52           C  \n"
+          + "ATOM   1038  CA  LYS B   2      19.182 -10.025   6.313  1.00 26.41           C  \n"
+          + "ATOM   1047  CA  LYS B   3      17.107 -12.963   7.534  1.00 19.71           C  \n"
+          + "ATOM   1056  CA  LEU B   4      16.142 -13.579  11.164  1.00 14.81           C  \n"
+          + "ATOM   1064  CA  GLN B   5      14.648 -17.005  11.785  1.00 13.38           C  \n";
+
+  // TODO: JAL-2227 - import mmCIF PISA assembly & identify master/copy chains
+
+  @Test(groups= {"Functional"})
+  public void testImportPDBPreservesChainMappings() throws IOException
+  {
+    AlignmentI importedAl = new jalview.io.FormatAdapter().readFile(
+            PDB_4_MC, DataSourceType.PASTE, FileFormats.getInstance()
+                    .forName(jalview.io.FileFormat.PDB.toString()));
+    // ideally, we would match on the actual data for the 'File' handle for
+    // pasted files,
+    // see JAL-623 - pasting is still not correctly handled...
+    PDBEntry importedPDB = new PDBEntry("3A6S", "", Type.PDB,
+            "Paste");
+    AAStructureBindingModel binder = new AAStructureBindingModel(
+            new StructureSelectionManager(), new PDBEntry[]
+            { importedPDB },
+            new SequenceI[][]
+            { importedAl.getSequencesArray() }, null)
+    {
+      
+      @Override
+      public void updateColours(Object source)
+      {
+        // TODO Auto-generated method stub
+        
+      }
+      
+      @Override
+      public void releaseReferences(Object svl)
+      {
+        // TODO Auto-generated method stub
+        
+      }
+      
+      @Override
+      public String[] getStructureFiles()
+      {
+        // TODO Auto-generated method stub
+        return null;
+      }
+      
+      @Override
+      public String superposeStructures(AlignmentI[] alignments,
+              int[] structureIndices, HiddenColumns[] hiddenCols)
+      {
+        // TODO Auto-generated method stub
+        return null;
+      }
+      
+      @Override
+      public void setJalviewColourScheme(ColourSchemeI cs)
+      {
+        // TODO Auto-generated method stub
+        
+      }
+      
+      @Override
+      public void setBackgroundColour(Color col)
+      {
+        // TODO Auto-generated method stub
+        
+      }
+      
+      @Override
+      public void highlightAtoms(List<AtomSpec> atoms)
+      {
+        // TODO Auto-generated method stub
+        
+      }
+      
+      @Override
+      public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment)
+      {
+        // TODO Auto-generated method stub
+        return null;
+      }
+      
+      @Override
+      public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
+      {
+        // TODO Auto-generated method stub
+        return null;
+      }
+      
+      @Override
+      protected StructureMappingcommandSet[] getColourBySequenceCommands(
+              String[] files, SequenceRenderer sr, AlignmentViewPanel avp)
+      {
+        // TODO Auto-generated method stub
+        return null;
+      }
+      
+      @Override
+      public List<String> getChainNames()
+      {
+        // TODO Auto-generated method stub
+        return null;
+      }
+      
+      @Override
+      protected void colourBySequence(
+              StructureMappingcommandSet[] colourBySequenceCommands)
+      {
+        // TODO Auto-generated method stub
+        
+      }
+      
+      @Override
+      public void colourByCharge()
+      {
+        // TODO Auto-generated method stub
+        
+      }
+      
+      @Override
+      public void colourByChain()
+      {
+        // TODO Auto-generated method stub
+        
+      }
+    };
+    String[][] chains = binder.getChains();
+    assertFalse(chains == null || chains[0] == null,
+            "No chains discovered by binding");
+    assertEquals(2, chains[0].length);
+    assertEquals("A", chains[0][0]);
+    assertEquals("B", chains[0][1]);
+  }
   AAStructureBindingModel testee;
 
   AlignmentI al = null;
index 40737ca..c559966 100644 (file)
@@ -47,33 +47,26 @@ public class EBIFetchClientTest
     /*
      * EMBL
      */
-    assertEquals("http://www.ebi.ac.uk/ena/data/view/x53838&display=xml",
+    assertEquals("https://www.ebi.ac.uk/ena/data/view/x53838&display=xml",
             EBIFetchClient.buildUrl("X53838", "EMBL", "display=xml"));
 
     /*
      * EMBLCDS
      */
-    assertEquals("http://www.ebi.ac.uk/ena/data/view/caa37824&display=xml",
+    assertEquals("https://www.ebi.ac.uk/ena/data/view/caa37824&display=xml",
             EBIFetchClient.buildUrl("CAA37824", "EMBL", "display=xml"));
 
     /*
-     * Uniprot
-     */
-    assertEquals(
-            "http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/uniprot/p00340/uniprotxml",
-            EBIFetchClient.buildUrl("P00340", "UNIPROT", "uniprotxml"));
-
-    /*
      * PDB / pdb
      */
-    assertEquals("http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/pdb/3a6s/pdb",
+    assertEquals("https://www.ebi.ac.uk/Tools/dbfetch/dbfetch/pdb/3a6s/pdb",
             EBIFetchClient.buildUrl("3A6S", "PDB", "pdb"));
 
     /*
      * PDB / mmCIF
      */
     assertEquals(
-            "http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/pdb/3a6s/mmCIF",
+            "https://www.ebi.ac.uk/Tools/dbfetch/dbfetch/pdb/3a6s/mmCIF",
             EBIFetchClient.buildUrl("3A6S", "PDB", "mmCIF"));
   }
 
index 557700a..abe9d4b 100755 (executable)
@@ -1471,7 +1471,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>true</boolean>
                                                        </property>
                                                        <property name="destinationName">
-                                                               <string><![CDATA[min-jabaws-client-2.1.0.jar]]></string>
+                                                               <string><![CDATA[min-jabaws-client-2.2.0.jar]]></string>
                                                        </property>
                                                        <property name="fileSize">
                                                                <long>601804</long>
index b41aab3..946f71c 100644 (file)
                <allow pkg="javax.servlet"/>
                </subpackage>
 
+               <subpackage name="schemes">
+                       <allow pkg="org.exolab.castor"/>
+               </subpackage>
+
                <subpackage name="structure">
                <allow pkg="MCview"/>
                </subpackage>
+               
+               <subpackage name="urls">
+                       <allow pkg="javax.swing" class="jalview.urls.UrlLinkTableModel"/>
+                       <allow pkg="org.json"/>
+               </subpackage>
 
                <subpackage name="util">
                <allow pkg="javax.swing"/>