Merge branch 'features/JAL-1553_select-columns-by-annotation' into develop
authorCharles Ofoegbu <tcnofoegbu@dundee.ac.uk>
Wed, 14 Jan 2015 12:53:54 +0000 (12:53 +0000)
committerCharles Ofoegbu <tcnofoegbu@dundee.ac.uk>
Wed, 14 Jan 2015 12:53:54 +0000 (12:53 +0000)
83 files changed:
.classpath
src/MCview/AppletPDBCanvas.java
src/MCview/PDBCanvas.java
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
src/ext/edu/ucsf/rbvi/strucviz2/port/ListenerThreads.java
src/jalview/analysis/Conservation.java
src/jalview/analysis/Finder.java
src/jalview/analysis/NJTree.java
src/jalview/analysis/SequenceIdMatcher.java
src/jalview/analysis/scoremodels/FeatureScoreModel.java [new file with mode: 0644]
src/jalview/api/AlignViewControllerGuiI.java
src/jalview/api/AlignViewControllerI.java
src/jalview/api/AlignViewportI.java
src/jalview/api/AlignmentViewPanel.java
src/jalview/api/FeatureRenderer.java
src/jalview/api/FeatureSettingsControllerI.java [new file with mode: 0644]
src/jalview/api/FeatureSettingsModelI.java [new file with mode: 0644]
src/jalview/api/FeaturesDisplayedI.java [new file with mode: 0644]
src/jalview/api/SequenceRenderer.java
src/jalview/api/analysis/ViewBasedAnalysisI.java [new file with mode: 0644]
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/appletgui/AppletJmol.java
src/jalview/appletgui/AppletJmolBinding.java
src/jalview/appletgui/ExtJmol.java
src/jalview/appletgui/FeatureColourChooser.java
src/jalview/appletgui/FeatureRenderer.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/Finder.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/appletgui/SeqCanvas.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/SequenceRenderer.java
src/jalview/appletgui/TreePanel.java
src/jalview/bin/JalviewLite.java
src/jalview/controller/AlignViewController.java
src/jalview/controller/FeatureSettingsController.java [new file with mode: 0644]
src/jalview/controller/FeatureSettingsControllerGuiI.java [new file with mode: 0644]
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/AnnotationExporter.java
src/jalview/gui/AppJmol.java
src/jalview/gui/AppJmolBinding.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/FeatureColourChooser.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/IdPanel.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/Jalview2XML_V1.java
src/jalview/gui/JalviewChimeraBindingModel.java
src/jalview/gui/OverviewPanel.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SequenceRenderer.java
src/jalview/gui/TreePanel.java
src/jalview/io/BioJsHTMLOutput.java
src/jalview/io/FeaturesFile.java
src/jalview/io/HTMLOutput.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/javascript/MouseOverStructureListener.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java [new file with mode: 0644]
src/jalview/schemes/AnnotationColourGradient.java
src/jalview/schemes/Blosum62ColourScheme.java
src/jalview/schemes/ResidueProperties.java
src/jalview/util/ColorUtils.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java [new file with mode: 0644]
src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java [new file with mode: 0644]
src/jalview/viewmodel/seqfeatures/FeatureSettingsModel.java [new file with mode: 0644]
src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java [new file with mode: 0644]
src/jalview/ws/AWSThread.java
src/jalview/ws/jws2/AADisorderClient.java
test/jalview/analysis/scoremodels/FeatureScoreModelTest.java [new file with mode: 0644]
test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java [new file with mode: 0644]
test/jalview/gui/SequenceRendererTest.java [new file with mode: 0644]
test/jalview/util/ColorUtilsTest.java

index b75a654..59772ae 100644 (file)
        <classpathentry kind="lib" path="lib/log4j-to-slf4j-2.0-rc2.jar"/>
        <classpathentry kind="lib" path="lib/slf4j-log4j12-1.7.7.jar"/>
        <classpathentry kind="lib" path="lib/VARNAv3-91.jar"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/plugin.jar"/>
        <classpathentry kind="lib" path="lib/xml-apis.jar"/>
        <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/Plugin.jar"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/plugin"/>
        <classpathentry kind="lib" path="lib/jfreesvg-2.1.jar"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/Java SE 6 [1.6.0_65-b14-462]"/>
        <classpathentry kind="output" path="classes"/>
 </classpath>
index a1bb272..9b0412a 100644 (file)
@@ -550,7 +550,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
 
     boolean showFeatures = false;
-    if (ap.av.getShowSequenceFeatures())
+    if (ap.av.isShowSequenceFeatures())
     {
       if (fr == null)
       {
index dc0f718..4fd7a35 100644 (file)
@@ -110,7 +110,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
   boolean seqColoursReady = false;
 
-  jalview.gui.FeatureRenderer fr;
+  jalview.renderer.seqfeatures.FeatureRenderer fr;
 
   Color backgroundColour = Color.black;
 
@@ -520,7 +520,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
 
     boolean showFeatures = false;
-    if (ap.av.getShowSequenceFeatures())
+    if (ap.av.isShowSequenceFeatures())
     {
       if (fr == null)
       {
index a7440e7..b45404e 100644 (file)
@@ -1,14 +1,21 @@
 package ext.edu.ucsf.rbvi.strucviz2;
 
+import jalview.ws.HttpClientUtils;
+
 import java.awt.Color;
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicNameValuePair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -20,10 +27,19 @@ import ext.edu.ucsf.rbvi.strucviz2.port.ListenerThreads;
  */
 public class ChimeraManager
 {
+  private static final boolean debug = false;
+
+  /*
+   * true: use REST API (recommended), false: use stdout/stdin (deprecated)
+   */
+  // TODO remove once definitely happy with using REST
+  private static final boolean USE_REST = true;
+
+  private int chimeraRestPort;
 
   private Process chimera;
 
-  private ListenerThreads chimeraListenerThreads;
+  private ListenerThreads chimeraListenerThread;
 
   private Map<Integer, ChimeraModel> currentModelsMap;
 
@@ -36,7 +52,7 @@ public class ChimeraManager
   {
     this.structureManager = structureManager;
     chimera = null;
-    chimeraListenerThreads = null;
+    chimeraListenerThread = null;
     currentModelsMap = new HashMap<Integer, ChimeraModel>();
 
   }
@@ -354,7 +370,15 @@ public class ChimeraManager
   {
     chimera = null;
     currentModelsMap.clear();
-    chimeraListenerThreads = null;
+    if (USE_REST)
+    {
+      this.chimeraRestPort = 0;
+    }
+    else
+    {
+      chimeraListenerThread.requestStop();
+      chimeraListenerThread = null;
+    }
     structureManager.clearOnChimeraExit();
   }
 
@@ -525,12 +549,13 @@ public class ChimeraManager
         List<String> args = new ArrayList<String>();
         args.add(chimeraPath);
         args.add("--start");
-        args.add("ReadStdin");
+        args.add(USE_REST ? "RESTServer" : "ReadStdin");
         ProcessBuilder pb = new ProcessBuilder(args);
         chimera = pb.start();
         error = "";
         workingPath = chimeraPath;
-        logger.info("Strarting " + chimeraPath);
+        logger.info("Starting " + chimeraPath + " with "
+                + (USE_REST ? "REST API" : "stdin/stdout"));
         break;
       } catch (Exception e)
       {
@@ -541,10 +566,18 @@ public class ChimeraManager
     // If no error, then Chimera was launched successfully
     if (error.length() == 0)
     {
-      // Initialize the listener threads
-      chimeraListenerThreads = new ListenerThreads(chimera,
-              structureManager);
-      chimeraListenerThreads.start();
+      if (USE_REST)
+      {
+        this.chimeraRestPort = getPortNumber();
+        System.out.println("Chimera REST API on port " + chimeraRestPort);
+      }
+      else
+      {
+        // Initialize the listener threads
+        chimeraListenerThread = new ListenerThreads(chimera,
+                structureManager);
+        chimeraListenerThread.start();
+      }
       // structureManager.initChimTable();
       structureManager.setChimeraPathProperty(workingPath);
       // TODO: [Optional] Check Chimera version and show a warning if below 1.8
@@ -559,6 +592,42 @@ public class ChimeraManager
   }
 
   /**
+   * Read and return the port number returned in the reply to --start RESTServer
+   */
+  private int getPortNumber()
+  {
+    int port = 0;
+    InputStream readChan = chimera.getInputStream();
+    BufferedReader lineReader = new BufferedReader(new InputStreamReader(
+            readChan));
+    String response = null;
+    try
+    {
+      // expect: REST server on host 127.0.0.1 port port_number
+      response = lineReader.readLine();
+      String [] tokens = response.split(" ");
+      if (tokens.length == 7 && "port".equals(tokens[5])) {
+        port = Integer.parseInt(tokens[6]);
+        logger.info("Chimera REST service listening on port "
+                + chimeraRestPort);
+      }
+    } catch (Exception e)
+    {
+      logger.error("Failed to get REST port number from " + response + ": "
+              + e.getMessage());
+    } finally
+    {
+      try
+      {
+        lineReader.close();
+      } catch (IOException e2)
+      {
+      }
+    }
+    return port;
+  }
+
+  /**
    * Determine the color that Chimera is using for this model.
    * 
    * @param model
@@ -683,7 +752,8 @@ public class ChimeraManager
    */
   public List<String> sendChimeraCommand(String command, boolean reply)
   {
-    if (!isChimeraLaunched())
+    if (!isChimeraLaunched() || command == null
+            || "".equals(command.trim()))
     {
       return null;
     }
@@ -699,38 +769,102 @@ public class ChimeraManager
       ;
     }
     busy = true;
+    long startTime = System.currentTimeMillis();
     try
     {
-      chimeraListenerThreads.clearResponse(command);
-      String text = command.concat("\n");
-      // System.out.println("send command to chimera: " + text);
-      try
+      if (USE_REST)
       {
-        // send the command
-        chimera.getOutputStream().write(text.getBytes());
-        chimera.getOutputStream().flush();
-      } catch (IOException e)
-      {
-        // logger.info("Unable to execute command: " + text);
-        // logger.info("Exiting...");
-        logger.warn("Unable to execute command: " + text);
-        logger.warn("Exiting...");
-        clearOnChimeraExit();
-        // busy = false;
-        return null;
+        return sendRestCommand(command);
       }
-      if (!reply)
+      else
       {
-        // busy = false;
-        return null;
+        return sendStdinCommand(command, reply);
       }
-      List<String> rsp = chimeraListenerThreads.getResponse(command);
-      // busy = false;
-      return rsp;
     } finally
     {
       busy = false;
+      if (debug)
+      {
+        System.out.println("Chimera command took "
+                + (System.currentTimeMillis() - startTime) + "ms: "
+                + command);
+      }
+
+    }
+  }
+
+  /**
+   * Sends the command to Chimera's REST API, and returns any response lines.
+   * 
+   * @param command
+   * @return
+   */
+  protected List<String> sendRestCommand(String command)
+  {
+    // TODO start a separate thread to do this so we don't block?
+    String restUrl = "http://127.0.0.1:" + this.chimeraRestPort + "/run";
+    List<NameValuePair> commands = new ArrayList<NameValuePair>(1);
+    commands.add(new BasicNameValuePair("command", command));
+
+    List<String> reply = new ArrayList<String>();
+    BufferedReader response = null;
+    try {
+      response = HttpClientUtils.doHttpUrlPost(restUrl,
+              commands);
+      String line = "";
+      while ((line = response.readLine()) != null) {
+        reply.add(line);
+      }
+    } catch (Exception e)
+    {
+      logger.error("REST call " + command + " failed: " + e.getMessage());
+    } finally
+    {
+      if (response != null)
+      {
+        try
+        {
+          response.close();
+        } catch (IOException e)
+        {
+        }
+      }
+    }
+    return reply;
+  }
+
+  /**
+   * Send a command to stdin of Chimera process, and optionally read any
+   * responses.
+   * 
+   * @param command
+   * @param readReply
+   * @return
+   */
+  protected List<String> sendStdinCommand(String command, boolean readReply)
+  {
+    chimeraListenerThread.clearResponse(command);
+    String text = command.concat("\n");
+    try
+    {
+      // send the command
+      chimera.getOutputStream().write(text.getBytes());
+      chimera.getOutputStream().flush();
+    } catch (IOException e)
+    {
+      // logger.info("Unable to execute command: " + text);
+      // logger.info("Exiting...");
+      logger.warn("Unable to execute command: " + text);
+      logger.warn("Exiting...");
+      clearOnChimeraExit();
+      return null;
+    }
+    if (!readReply)
+    {
+      return null;
     }
+    List<String> rsp = chimeraListenerThread.getResponse(command);
+    return rsp;
   }
 
   public StructureManager getStructureManager()
index 883d536..2b2ce48 100644 (file)
@@ -21,207 +21,282 @@ import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 /**
  * Reply listener thread
  */
-public class ListenerThreads extends Thread {
-       private InputStream readChan = null;
-       private BufferedReader lineReader = null;
-       private Process chimera = null;
-       private Map<String, List<String>> replyLog = null;
-       private Logger logger;
-       private StructureManager structureManager = null;
-
-       /**
-        * Create a new listener thread to read the responses from Chimera
-        * 
-        * @param chimera
-        *            a handle to the Chimera Process
-        * @param log
-        *            a handle to a List to post the responses to
-        * @param chimeraObject
-        *            a handle to the Chimera Object
-        */
-       public ListenerThreads(Process chimera, StructureManager structureManager) {
-               this.chimera = chimera;
-               this.structureManager = structureManager;
-               replyLog = new HashMap<String, List<String>>();
-               // Get a line-oriented reader
-               readChan = chimera.getInputStream();
-               lineReader = new BufferedReader(new InputStreamReader(readChan));
-               logger = LoggerFactory.getLogger(ext.edu.ucsf.rbvi.strucviz2.port.ListenerThreads.class);
-       }
-
-       /**
-        * Start the thread running
-        */
-       public void run() {
-               // System.out.println("ReplyLogListener running");
-               while (true) {
-                       try {
-                               chimeraRead();
-                       } catch (IOException e) {
-                               logger.warn("UCSF Chimera has exited: " + e.getMessage());
-                               return;
-                       }
-               }
-       }
-
-       public List<String> getResponse(String command) {
-               List<String> reply;
-               // System.out.println("getResponse: "+command);
+public class ListenerThreads extends Thread
+{
+  private BufferedReader lineReader = null;
+
+  private Process chimera = null;
+
+  private Map<String, List<String>> replyLog = null;
+
+  private Logger logger;
+
+  private StructureManager structureManager = null;
+
+  private boolean stopMe = false;
+
+  /**
+   * Create a new listener thread to read the responses from Chimera
+   * 
+   * @param chimera
+   *          a handle to the Chimera Process
+   * @param structureManager
+   *          a handle to the Chimera structure manager
+   */
+  public ListenerThreads(Process chimera, StructureManager structureManager)
+  {
+    this.chimera = chimera;
+    this.structureManager = structureManager;
+    replyLog = new HashMap<String, List<String>>();
+    // Get a line-oriented reader
+    InputStream readChan = chimera.getInputStream();
+    lineReader = new BufferedReader(new InputStreamReader(readChan));
+    logger = LoggerFactory
+            .getLogger(ext.edu.ucsf.rbvi.strucviz2.port.ListenerThreads.class);
+  }
+
+  /**
+   * Start the thread running
+   */
+  public void run()
+  {
+    // System.out.println("ReplyLogListener running");
+    while (!stopMe)
+    {
+      try
+      {
+        chimeraRead();
+      } catch (IOException e)
+      {
+        logger.warn("UCSF Chimera has exited: " + e.getMessage());
+        return;
+      } finally
+      {
+        if (lineReader != null)
+        {
+          try
+          {
+            lineReader.close();
+          } catch (IOException e)
+          {
+          }
+        }
+      }
+    }
+  }
+
+  public List<String> getResponse(String command)
+  {
+    List<String> reply;
+    // System.out.println("getResponse: "+command);
     // TODO do we need a maximum wait time before aborting?
-               while (!replyLog.containsKey(command)) {
-                       try {
-                               Thread.currentThread().sleep(100);
-                       } catch (InterruptedException e) {
-                       }
-               }
-
-               synchronized (replyLog) {
-                       reply = replyLog.get(command);
-                       // System.out.println("getResponse ("+command+") = "+reply);
-                       replyLog.remove(command);
-               }
-               return reply;
-       }
-
-       public void clearResponse(String command) {
-               try {
-                       Thread.currentThread().sleep(100);
-               } catch (InterruptedException e) {
-               }
-               if (replyLog.containsKey(command))
+    while (!replyLog.containsKey(command))
+    {
+      try
+      {
+        Thread.currentThread().sleep(100);
+      } catch (InterruptedException e)
+      {
+      }
+    }
+
+    synchronized (replyLog)
     {
+      reply = replyLog.get(command);
+      // System.out.println("getResponse ("+command+") = "+reply);
       replyLog.remove(command);
     }
-               return;
-       }
+    return reply;
+  }
 
-       /**
-        * Read input from Chimera
-        * 
-        * @return a List containing the replies from Chimera
-        */
-       private void chimeraRead() throws IOException {
-               if (chimera == null)
+  public void clearResponse(String command)
+  {
+    try
+    {
+      Thread.currentThread().sleep(100);
+    } catch (InterruptedException e)
+    {
+    }
+    if (replyLog.containsKey(command))
+    {
+      replyLog.remove(command);
+    }
+    return;
+  }
+
+  /**
+   * Read input from Chimera
+   * 
+   * @return a List containing the replies from Chimera
+   */
+  private void chimeraRead() throws IOException
+  {
+    if (chimera == null)
     {
       return;
     }
 
-               String line = null;
-               while ((line = lineReader.readLine()) != null) {
-                       // System.out.println("From Chimera-->" + line);
-                       if (line.startsWith("CMD")) {
-                               chimeraCommandRead(line.substring(4));
-                       } else if (line.startsWith("ModelChanged: ")) {
-                               (new ModelUpdater()).start();
-                       } else if (line.startsWith("SelectionChanged: ")) {
-                               (new SelectionUpdater()).start();
-                       } else if (line.startsWith("Trajectory residue network info:")) {
-                               (new NetworkUpdater(line)).start();
-                       }
-               }
-               return;
-       }
-
-       private void chimeraCommandRead(String command) throws IOException {
-               // Generally -- looking for:
-               // CMD command
-               // ........
-               // END
-               // We return the text in between
-               List<String> reply = new ArrayList<String>();
-               boolean updateModels = false;
-               boolean updateSelection = false;
-               boolean importNetwork = false;
-               String line = null;
-
-               synchronized (replyLog) {
-                       while ((line = lineReader.readLine()) != null) {
-                               // System.out.println("From Chimera (" + command + ") -->" + line);
-                               if (line.startsWith("CMD")) {
-                                       logger.warn("Got unexpected command from Chimera: " + line);
-
-                               } else if (line.startsWith("END")) {
-                                       break;
-                               }
-                               if (line.startsWith("ModelChanged: ")) {
-                                       updateModels = true;
-                               } else if (line.startsWith("SelectionChanged: ")) {
-                                       updateSelection = true;
-                               } else if (line.length() == 0) {
-                                       continue;
-                               } else if (!line.startsWith("CMD")) {
-                                       reply.add(line);
-                               } else if (line.startsWith("Trajectory residue network info:")) {
-                                       importNetwork = true;
-                               }
-                       }
-                       replyLog.put(command, reply);
-               }
-               if (updateModels)
+    String line = null;
+    while ((line = lineReader.readLine()) != null)
+    {
+      // System.out.println("From Chimera-->" + line);
+      if (line.startsWith("CMD"))
+      {
+        chimeraCommandRead(line.substring(4));
+      }
+      else if (line.startsWith("ModelChanged: "))
+      {
+        (new ModelUpdater()).start();
+      }
+      else if (line.startsWith("SelectionChanged: "))
+      {
+        (new SelectionUpdater()).start();
+      }
+      else if (line.startsWith("Trajectory residue network info:"))
+      {
+        (new NetworkUpdater(line)).start();
+      }
+    }
+    return;
+  }
+
+  private void chimeraCommandRead(String command) throws IOException
+  {
+    // Generally -- looking for:
+    // CMD command
+    // ........
+    // END
+    // We return the text in between
+    List<String> reply = new ArrayList<String>();
+    boolean updateModels = false;
+    boolean updateSelection = false;
+    boolean importNetwork = false;
+    String line = null;
+
+    synchronized (replyLog)
+    {
+      while ((line = lineReader.readLine()) != null)
+      {
+        // System.out.println("From Chimera (" + command + ") -->" + line);
+        if (line.startsWith("CMD"))
+        {
+          logger.warn("Got unexpected command from Chimera: " + line);
+
+        }
+        else if (line.startsWith("END"))
+        {
+          break;
+        }
+        if (line.startsWith("ModelChanged: "))
+        {
+          updateModels = true;
+        }
+        else if (line.startsWith("SelectionChanged: "))
+        {
+          updateSelection = true;
+        }
+        else if (line.length() == 0)
+        {
+          continue;
+        }
+        else if (!line.startsWith("CMD"))
+        {
+          reply.add(line);
+        }
+        else if (line.startsWith("Trajectory residue network info:"))
+        {
+          importNetwork = true;
+        }
+      }
+      replyLog.put(command, reply);
+    }
+    if (updateModels)
     {
       (new ModelUpdater()).start();
     }
-               if (updateSelection)
+    if (updateSelection)
     {
       (new SelectionUpdater()).start();
     }
-               if (importNetwork) {
-                       (new NetworkUpdater(line)).start();
-               }
-               return;
-       }
-
-       /**
-        * Model updater thread
-        */
-       class ModelUpdater extends Thread {
-
-               public ModelUpdater() {
-               }
-
-               public void run() {
-                       structureManager.updateModels();
-                       structureManager.modelChanged();
-               }
-       }
-
-       /**
-        * Selection updater thread
-        */
-       class SelectionUpdater extends Thread {
-
-               public SelectionUpdater() {
-               }
-
-               public void run() {
-                       try {
-                         logger.info("Responding to chimera selection");
-                         structureManager.chimeraSelectionChanged();
-                       } catch (Exception e) {
-                               logger.warn("Could not update selection", e);
-                       }
-               }
-       }
-
-       /**
-        * Selection updater thread
-        */
-       class NetworkUpdater extends Thread {
-
-               private String line;
-
-               public NetworkUpdater(String line) {
-                       this.line = line;
-               }
-
-               public void run() {
-                       try {
-//                             ((TaskManager<?, ?>) structureManager.getService(TaskManager.class))
-//                                             .execute(new ImportTrajectoryRINTaskFactory(structureManager, line)
-//                                                             .createTaskIterator());
-                       } catch (Exception e) {
-                               logger.warn("Could not import trajectory network", e);
-                       }
-               }
-       }
+    if (importNetwork)
+    {
+      (new NetworkUpdater(line)).start();
+    }
+    return;
+  }
+
+  /**
+   * Model updater thread
+   */
+  class ModelUpdater extends Thread
+  {
+
+    public ModelUpdater()
+    {
+    }
+
+    public void run()
+    {
+      structureManager.updateModels();
+      structureManager.modelChanged();
+    }
+  }
+
+  /**
+   * Selection updater thread
+   */
+  class SelectionUpdater extends Thread
+  {
+
+    public SelectionUpdater()
+    {
+    }
+
+    public void run()
+    {
+      try
+      {
+        logger.info("Responding to chimera selection");
+        structureManager.chimeraSelectionChanged();
+      } catch (Exception e)
+      {
+        logger.warn("Could not update selection", e);
+      }
+    }
+  }
+
+  /**
+   * Selection updater thread
+   */
+  class NetworkUpdater extends Thread
+  {
+
+    private String line;
+
+    public NetworkUpdater(String line)
+    {
+      this.line = line;
+    }
+
+    public void run()
+    {
+      try
+      {
+        // ((TaskManager<?, ?>) structureManager.getService(TaskManager.class))
+        // .execute(new ImportTrajectoryRINTaskFactory(structureManager, line)
+        // .createTaskIterator());
+      } catch (Exception e)
+      {
+        logger.warn("Could not import trajectory network", e);
+      }
+    }
+  }
+
+  /**
+   * Set a flag that this thread should clean up and exit.
+   */
+  public void requestStop()
+  {
+    this.stopMe = true;
+  }
 }
index 4d64685..66a6d78 100755 (executable)
@@ -71,6 +71,8 @@ public class Conservation
 
   int[][] cons2;
 
+  private String[] consSymbs;
+
   /**
    * Creates a new Conservation object.
    * 
@@ -366,17 +368,17 @@ public class Conservation
     {
       consString.append('-');
     }
-
+    consSymbs = new String[end-start+1];
     for (int i = start; i <= end; i++)
     {
       gapcons = countConsNGaps(i);
       totGaps = gapcons[1];
       pgaps = ((float) totGaps * 100) / (float) sequences.length;
-
+      consSymbs[i-start]=new String();
+      
       if (percentageGaps > pgaps)
       {
         resultHash = total[i - start];
-
         // Now find the verdict
         count = 0;
         enumeration = resultHash.keys();
@@ -385,12 +387,12 @@ public class Conservation
         {
           type = (String) enumeration.nextElement();
           result = (Integer) resultHash.get(type);
-
           // Do we want to count +ve conservation or +ve and -ve cons.?
           if (consflag)
           {
             if (result.intValue() == 1)
             {
+              consSymbs[i-start] = type+" "+consSymbs[i-start];
               count++;
             }
           }
@@ -398,6 +400,14 @@ public class Conservation
           {
             if (result.intValue() != -1)
             {
+              { 
+                 if (result.intValue()==0) {
+                   consSymbs[i-start] = consSymbs[i-start]+ " !"+type;
+                 } else {
+                   consSymbs[i-start] = type+" "+consSymbs[i-start];
+                 }
+              }
+              
               count++;
             }
           }
@@ -683,7 +693,7 @@ public class Conservation
       float vprop = value - min;
       vprop /= max;
       conservation.annotations[i] = new Annotation(String.valueOf(c),
-              String.valueOf(value), ' ', value, new Color(minR
+              consSymbs[i-start], ' ', value, new Color(minR
                       + (maxR * vprop), minG + (maxG * vprop), minB
                       + (maxB * vprop)));
 
index 96151d7..b87c170 100644 (file)
@@ -188,7 +188,7 @@ public class Finder
           {
             continue;
           }
-
+// if invalid string used, then regex has no matched to/from
           int sres = seq
                   .findPosition(resIndex
                           + Integer.parseInt(spaces.elementAt(resIndex)
index a8ca3f7..099400e 100644 (file)
  */
 package jalview.analysis;
 
-import java.util.*;
-
 import jalview.api.analysis.ScoreModelI;
-import jalview.datamodel.*;
-import jalview.io.*;
-import jalview.schemes.*;
-import jalview.util.*;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.BinaryNode;
+import jalview.datamodel.CigarArray;
+import jalview.datamodel.NodeTransformI;
+import jalview.datamodel.SeqCigar;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequenceNode;
+import jalview.io.NewickFile;
+import jalview.schemes.ResidueProperties;
+
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Vector;
 
 /**
  * DOCUMENT ME!
@@ -210,7 +218,7 @@ public class NJTree
    *          DOCUMENT ME!
    */
   public NJTree(SequenceI[] sequence, AlignmentView seqData, String type,
-          String pwtype, int start, int end)
+          String pwtype, ScoreModelI sm, int start, int end)
   {
     this.sequence = sequence;
     this.node = new Vector();
@@ -237,7 +245,7 @@ public class NJTree
       type = "AV";
     }
 
-    if (!(pwtype.equals("PID")))
+    if (sm == null && !(pwtype.equals("PID")))
     {
       if (ResidueProperties.getScoreMatrix(pwtype) == null)
       {
@@ -257,7 +265,7 @@ public class NJTree
 
     noseqs = i++;
 
-    distance = findDistances();
+    distance = findDistances(sm);
     // System.err.println("Made distances");// dbg
     makeLeaves();
     // System.err.println("Made leaves");// dbg
@@ -315,7 +323,7 @@ public class NJTree
 
           for (int j = 0; j < seqs.length; j++)
           {
-            seqs[j] = (SequenceI) list.get(j);
+            seqs[j] = list.get(j);
           }
 
           seqmatcher = new SequenceIdMatcher(seqs);
@@ -722,16 +730,18 @@ public class NJTree
    * 
    * @return similarity matrix used to compute tree
    */
-  public float[][] findDistances()
+  public float[][] findDistances(ScoreModelI _pwmatrix)
   {
 
     float[][] distance = new float[noseqs][noseqs];
-
-    // Pairwise substitution score (with no gap penalties)
-    ScoreModelI _pwmatrix = ResidueProperties.getScoreModel(pwtype);
     if (_pwmatrix == null)
     {
-      _pwmatrix = ResidueProperties.getScoreMatrix("BLOSUM62");
+      // Resolve substitution model
+      _pwmatrix = ResidueProperties.getScoreModel(pwtype);
+      if (_pwmatrix == null)
+      {
+        _pwmatrix = ResidueProperties.getScoreMatrix("BLOSUM62");
+      }
     }
     distance = _pwmatrix.findDistances(seqData);
     return distance;
@@ -857,12 +867,12 @@ public class NJTree
     {
       System.out
               .println("Leaf = " + ((SequenceI) node.element()).getName());
-      System.out.println("Dist " + ((SequenceNode) node).dist);
+      System.out.println("Dist " + node.dist);
       System.out.println("Boot " + node.getBootstrap());
     }
     else
     {
-      System.out.println("Dist " + ((SequenceNode) node).dist);
+      System.out.println("Dist " + node.dist);
       printNode((SequenceNode) node.left());
       printNode((SequenceNode) node.right());
     }
@@ -883,11 +893,11 @@ public class NJTree
 
     if ((node.left() == null) && (node.right() == null))
     {
-      float dist = ((SequenceNode) node).dist;
+      float dist = node.dist;
 
       if (dist > maxDistValue)
       {
-        maxdist = (SequenceNode) node;
+        maxdist = node;
         maxDistValue = dist;
       }
     }
@@ -1089,9 +1099,9 @@ public class NJTree
               + ((SequenceI) node.element()).getName());
     }
 
-    System.out.println(" dist = " + ((SequenceNode) node).dist + " "
-            + ((SequenceNode) node).count + " "
-            + ((SequenceNode) node).height);
+    System.out.println(" dist = " + node.dist + " "
+            + node.count + " "
+            + node.height);
   }
 
   /**
@@ -1137,13 +1147,13 @@ public class NJTree
       SequenceNode l = (SequenceNode) node.left();
       SequenceNode r = (SequenceNode) node.right();
 
-      ((SequenceNode) node).count = l.count + r.count;
-      ((SequenceNode) node).ycount = (l.ycount + r.ycount) / 2;
+      node.count = l.count + r.count;
+      node.ycount = (l.ycount + r.ycount) / 2;
     }
     else
     {
-      ((SequenceNode) node).count = 1;
-      ((SequenceNode) node).ycount = ycount++;
+      node.count = 1;
+      node.ycount = ycount++;
     }
     _lycount--;
   }
@@ -1282,7 +1292,9 @@ public class NJTree
   {
     for (Enumeration nodes = node.elements(); nodes.hasMoreElements(); nodeTransformI
             .transform((BinaryNode) nodes.nextElement()))
+    {
       ;
+    }
   }
 }
 
index aec7faf..bedce3f 100755 (executable)
@@ -42,10 +42,15 @@ public class SequenceIdMatcher
       // TODO: deal with ID collisions - SequenceI should be appended to list
       // associated with this key.
       names.put(new SeqIdName(seqs[i].getDisplayId(true)), seqs[i]);
+      SequenceI dbseq = seqs[i];
+      while (dbseq.getDatasetSequence()!=null)
+      {
+        dbseq = dbseq.getDatasetSequence();
+      }
       // add in any interesting identifiers
-      if (seqs[i].getDBRef() != null)
+      if (dbseq.getDBRef() != null)
       {
-        DBRefEntry dbr[] = seqs[i].getDBRef();
+        DBRefEntry dbr[] = dbseq.getDBRef();
         SeqIdName sid = null;
         for (int r = 0; r < dbr.length; r++)
         {
diff --git a/src/jalview/analysis/scoremodels/FeatureScoreModel.java b/src/jalview/analysis/scoremodels/FeatureScoreModel.java
new file mode 100644 (file)
index 0000000..e2a8b9a
--- /dev/null
@@ -0,0 +1,138 @@
+package jalview.analysis.scoremodels;
+
+import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.ViewBasedAnalysisI;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+
+public class FeatureScoreModel implements ScoreModelI, ViewBasedAnalysisI
+{
+  jalview.api.FeatureRenderer fr;
+
+  @Override
+  public boolean configureFromAlignmentView(
+          jalview.api.AlignmentViewPanel view)
+  {
+    fr = view.cloneFeatureRenderer();
+    return true;
+  }
+
+  @Override
+  public float[][] findDistances(AlignmentView seqData)
+  {
+    int nofeats = 0;
+    List<String> dft = Arrays.asList(fr.getDisplayedFeatureTypes());
+
+    if (dft != null)
+    {
+      nofeats = dft.size();
+    }
+
+    SequenceI[] sequenceString = seqData.getVisibleAlignment(
+            Comparison.GapChars.charAt(0)).getSequencesArray();
+    int noseqs = sequenceString.length;
+    int cpwidth = seqData.getWidth();
+    float[][] distance = new float[noseqs][noseqs];
+    if (nofeats == 0)
+    {
+      for (float[] d : distance)
+      {
+        for (int i = 0; i < d.length; d[i++] = 0f)
+        {
+          ;
+        }
+      }
+      return distance;
+    }
+    float max = 0;
+    for (int cpos = 0; cpos < cpwidth; cpos++)
+    {
+      // get visible features at cpos under view's display settings and compare
+      // them
+      List<Hashtable<String, SequenceFeature>> sfap = new ArrayList<Hashtable<String, SequenceFeature>>();
+      for (int i = 0; i < noseqs; i++)
+      {
+        Hashtable<String, SequenceFeature> types = new Hashtable<String, SequenceFeature>();
+        List<SequenceFeature> sfs = fr.findFeaturesAtRes(sequenceString[i],
+                sequenceString[i].findPosition(cpos));
+        for (SequenceFeature sf : sfs)
+        {
+          types.put(sf.getType(), sf);
+        }
+        sfap.add(types);
+      }
+      for (int i = 0; i < (noseqs - 1); i++)
+      {
+        if (cpos == 0)
+        {
+          distance[i][i] = 0f;
+        }
+        for (int j = i + 1; j < noseqs; j++)
+        {
+          int sfcommon = 0;
+          // compare the two lists of features...
+          Hashtable<String, SequenceFeature> fi = sfap.get(i), fk, fj = sfap
+                  .get(j);
+          if (fi.size() > fj.size())
+          {
+            fk = fj;
+          }
+          else
+          {
+            fk = fi;
+            fi = fj;
+          }
+          for (String k : fi.keySet())
+          {
+            SequenceFeature sfj = fk.get(k);
+            if (sfj != null)
+            {
+              sfcommon++;
+            }
+          }
+          distance[i][j] += (fi.size() + fk.size() - 2f * sfcommon);
+          distance[j][i] += distance[i][j];
+        }
+      }
+    }
+    for (int i = 0; i < noseqs; i++)
+    {
+      for (int j = i + 1; j < noseqs; j++)
+      {
+        distance[i][j] /= cpwidth;
+        distance[j][i] = distance[i][j];
+      }
+    }
+    return distance;
+  }
+
+  @Override
+  public String getName()
+  {
+    return "Sequence Feature Similarity";
+  }
+
+  @Override
+  public boolean isDNA()
+  {
+    return true;
+  }
+
+  @Override
+  public boolean isProtein()
+  {
+    return true;
+  }
+
+  public String toString()
+  {
+    return "Score between sequences based on hamming distance between binary vectors marking features displayed at each column";
+  }
+}
index b9638e5..4010e8b 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.api;
 
+import jalview.commands.CommandI;
+
 /**
  * Interface implemented by gui implementations managing a Jalview Alignment
  * View
@@ -37,4 +39,6 @@ public interface AlignViewControllerGuiI
    */
   void setStatus(String string);
 
+  void addHistoryItem(CommandI command);
+
 }
index 35f084f..9bd3f45 100644 (file)
@@ -64,4 +64,16 @@ public interface AlignViewControllerI
   boolean markColumnsContainingFeatures(boolean invert,
           boolean extendCurrent, boolean clearColumns, String featureType);
 
+  /**
+   * sort the alignment or current selection by average score over the given set of features
+   * @param typ list of feature names or null to use currently displayed features
+   */
+  void sortAlignmentByFeatureScore(String[] typ);
+
+  /**
+   * sort the alignment or current selection by distribution of the given set of features
+   * @param typ list of feature names or null to use currently displayed features
+   */
+  void sortAlignmentByFeatureDensity(String[] typ);
+
 }
index d8ba30d..24ff7a6 100644 (file)
@@ -200,4 +200,12 @@ public interface AlignViewportI
   List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
           boolean selectedOnly);
 
+  FeaturesDisplayedI getFeaturesDisplayed();
+
+  String getSequenceSetId();
+
+  boolean isShowSequenceFeatures();
+
+  void setShowSequenceFeatures(boolean b);
+
 }
index 0955cb4..a68c1f6 100644 (file)
@@ -51,6 +51,10 @@ public interface AlignmentViewPanel extends OOMHandlerI
    */
   void adjustAnnotationHeight();
 
+  FeatureRenderer getFeatureRenderer();
+
+  FeatureRenderer cloneFeatureRenderer();
+  
   /**
    * 
    * @return displayed name for the view
index 679cded..4fd89c1 100644 (file)
  */
 package jalview.api;
 
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 
 import java.awt.Color;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Abstract feature renderer interface
@@ -37,4 +41,38 @@ public interface FeatureRenderer
 
   void featuresAdded();
 
+  Object getFeatureStyle(String ft);
+
+  void setColour(String ft, Object ggc);
+
+  AlignViewportI getViewport();
+
+  FeaturesDisplayedI getFeaturesDisplayed();
+
+  Map<String,Object> getFeatureColours();
+
+  void findAllFeatures(boolean newMadeVisible);
+
+  Map<String,Object> getDisplayedFeatureCols();
+
+  List<String> getFeatureGroups();
+
+  List<String> getGroups(boolean visible);
+
+  void setGroupVisibility(List<String> toset, boolean visible);
+
+  void setGroupVisibility(String group, boolean visible);
+
+  List<SequenceFeature> findFeaturesAtRes(SequenceI sequence, int res);
+
+  boolean isTransparencyAvailable();
+
+  String[] getDisplayedFeatureTypes();
+
+  String[] getDisplayedFeatureGroups();
+
+  void setAllVisible(List<String> featureTypes);
+
+  void setVisible(String featureType);
+
 }
diff --git a/src/jalview/api/FeatureSettingsControllerI.java b/src/jalview/api/FeatureSettingsControllerI.java
new file mode 100644 (file)
index 0000000..c718e36
--- /dev/null
@@ -0,0 +1,6 @@
+package jalview.api;
+
+public interface FeatureSettingsControllerI
+{
+  
+}
diff --git a/src/jalview/api/FeatureSettingsModelI.java b/src/jalview/api/FeatureSettingsModelI.java
new file mode 100644 (file)
index 0000000..c148f3f
--- /dev/null
@@ -0,0 +1,6 @@
+package jalview.api;
+
+public interface FeatureSettingsModelI
+{
+
+}
diff --git a/src/jalview/api/FeaturesDisplayedI.java b/src/jalview/api/FeaturesDisplayedI.java
new file mode 100644 (file)
index 0000000..9ed5ae5
--- /dev/null
@@ -0,0 +1,29 @@
+package jalview.api;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+public interface FeaturesDisplayedI
+{
+
+  Iterator<String> getVisibleFeatures();
+
+  boolean isVisible(String featureType);
+
+  boolean areVisible(Collection<String> featureTypes);
+
+  void clear();
+
+  void setVisible(String featureType);
+
+  void setAllVisible(Collection<String> featureTypes);
+
+  boolean isRegistered(String type);
+
+  void setAllRegisteredVisible();
+
+  int getVisibleFeatureCount();
+
+  int getRegisterdFeaturesCount();
+
+}
index fbd5e4a..0ca1758 100644 (file)
@@ -29,4 +29,6 @@ public interface SequenceRenderer
 
   Color getResidueBoxColour(SequenceI sequenceI, int r);
 
+  Color getResidueColour(SequenceI seq, int position, FeatureRenderer fr);
+
 }
diff --git a/src/jalview/api/analysis/ViewBasedAnalysisI.java b/src/jalview/api/analysis/ViewBasedAnalysisI.java
new file mode 100644 (file)
index 0000000..3404afc
--- /dev/null
@@ -0,0 +1,17 @@
+package jalview.api.analysis;
+
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+
+public interface ViewBasedAnalysisI
+{
+
+  /**
+   * Parameterise the analysis model using the current view
+   * @param view
+   * @return true if model is applicable and calculation should proceed
+   */
+
+  boolean configureFromAlignmentView(AlignmentViewPanel view);
+
+}
index 98a4a1c..aa1ab21 100644 (file)
@@ -673,7 +673,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
               features, true, ap))
       {
         ap.alignFrame.sequenceFeatures.setState(true);
-        ap.av.showSequenceFeatures(true);
+        ap.av.setShowSequenceFeatures(true);;
         ap.highlightSearchResults(null);
       }
     }
@@ -732,7 +732,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
                       true,
                       true,
                       false,
-                      (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr.minmax
+                      (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr.getMinMax()
                               : null);
       contents.append("</p>");
     }
index ed64215..9fb91ed 100644 (file)
@@ -24,6 +24,7 @@ import jalview.analysis.AlignmentSorter;
 import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
 import jalview.api.SequenceStructureBinding;
+import jalview.api.FeatureRenderer;
 import jalview.bin.JalviewLite;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
@@ -89,9 +90,12 @@ import java.awt.event.WindowEvent;
 import java.io.IOException;
 import java.net.URL;
 import java.net.URLEncoder;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
@@ -285,7 +289,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     {
       featuresFile = new jalview.io.FeaturesFile(file, type)
               .parse(viewport.getAlignment(), alignPanel.seqPanel.seqCanvas
-                      .getFeatureRenderer().featureColours, featureLinks,
+                      .getFeatureRenderer().getFeatureColours(), featureLinks,
                       true, viewport.applet.getDefaultParameter(
                               "relaxedidmatch", false));
     } catch (Exception ex)
@@ -301,9 +305,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       }
       if (autoenabledisplay)
       {
-        viewport.showSequenceFeatures = true;
+        viewport.setShowSequenceFeatures(true);
         sequenceFeatures.setState(true);
       }
+      if (alignPanel.seqPanel.seqCanvas.fr != null)
+      {
+        // update the min/max ranges where necessary
+        alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
+      }
       if (viewport.featureSettings != null)
       {
         viewport.featureSettings.refreshTable();
@@ -729,7 +738,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
     else if (evt.getSource() == sequenceFeatures)
     {
-      viewport.showSequenceFeatures(sequenceFeatures.getState());
+      viewport.setShowSequenceFeatures(sequenceFeatures.getState());
       alignPanel.seqPanel.seqCanvas.repaint();
     }
     else if (evt.getSource() == conservationMenuItem)
@@ -1210,20 +1219,13 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     return annotation;
   }
 
-  private Hashtable getDisplayedFeatureCols()
+  private Map<String,Object> getDisplayedFeatureCols()
   {
     if (alignPanel.getFeatureRenderer() != null
-            && viewport.featuresDisplayed != null)
+            && viewport.getFeaturesDisplayed()!= null)
     {
-      FeatureRenderer fr = alignPanel.getFeatureRenderer();
-      Hashtable fcols = new Hashtable();
-      Enumeration en = viewport.featuresDisplayed.keys();
-      while (en.hasMoreElements())
-      {
-        Object col = en.nextElement();
-        fcols.put(col, fr.featureColours.get(col));
-      }
-      return fcols;
+      return alignPanel.getFeatureRenderer().getDisplayedFeatureCols();
+      
     }
     return null;
   }
@@ -1429,6 +1431,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   /**
    * TODO: JAL-1104
    */
+  @Override
   public void addHistoryItem(CommandI command)
   {
     if (command.getSize() > 0)
@@ -2271,7 +2274,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     if (alignPanel != null
             && (fr = alignPanel.getFeatureRenderer()) != null)
     {
-      return fr.getGroups();
+      List gps = fr.getFeatureGroups();
+      int p=0;
+      String[] _gps = new String[gps.size()];
+      for (Object gp:gps)
+      {
+        _gps[p++] = gp.toString();
+      }
+      return _gps;
     }
     return null;
   }
@@ -2289,7 +2299,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     if (alignPanel != null
             && (fr = alignPanel.getFeatureRenderer()) != null)
     {
-      return fr.getGroups(visible);
+      List gps = fr.getGroups(visible);
+      int p=0;
+      String[] _gps = new String[gps.size()];
+      for (Object gp:gps)
+      {
+        _gps[p++] = gp.toString();
+      }
+      return _gps;
     }
     return null;
   }
@@ -2306,11 +2323,12 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   {
     FeatureRenderer fr = null;
     this.sequenceFeatures.setState(true);
-    viewport.showSequenceFeatures(true);
+    viewport.setShowSequenceFeatures(true);
     if (alignPanel != null
             && (fr = alignPanel.getFeatureRenderer()) != null)
     {
-      fr.setGroupState(groups, state);
+      
+      fr.setGroupVisibility((List)Arrays.asList(groups), state);
       alignPanel.seqPanel.seqCanvas.repaint();
       if (alignPanel.overviewPanel != null)
       {
index 7400122..9a15e10 100644 (file)
@@ -58,8 +58,6 @@ public class AlignViewport extends AlignmentViewport implements
 
   boolean renderGaps = true;
 
-  boolean showSequenceFeatures = false;
-
   boolean showAnnotation = true;
 
   boolean upperCasebold = false;
@@ -86,10 +84,6 @@ public class AlignViewport extends AlignmentViewport implements
 
   boolean scaleRightWrapped = true;
 
-  // The following vector holds the features which are
-  // currently visible, in the correct order or rendering
-  public Hashtable featuresDisplayed;
-
   boolean showHiddenMarkers = true;
 
   public jalview.bin.JalviewLite applet;
@@ -256,16 +250,6 @@ public class AlignViewport extends AlignmentViewport implements
 
   }
 
-  public void showSequenceFeatures(boolean b)
-  {
-    showSequenceFeatures = b;
-  }
-
-  public boolean getShowSequenceFeatures()
-  {
-    return showSequenceFeatures;
-  }
-
   /**
    * get the consensus sequence as displayed under the PID consensus annotation
    * row.
index a90425f..5308a42 100644 (file)
@@ -157,12 +157,18 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
   {
     return seqPanel.seqCanvas.sr;
   }
-
-  public FeatureRenderer getFeatureRenderer()
+  @Override
+  public jalview.api.FeatureRenderer getFeatureRenderer()
   {
     return seqPanel.seqCanvas.fr;
   }
-
+  @Override
+  public jalview.api.FeatureRenderer cloneFeatureRenderer()
+  {
+    FeatureRenderer nfr = new FeatureRenderer(av);
+    nfr.transferSettings(seqPanel.seqCanvas.fr);
+    return nfr;
+  }
   public void alignmentChanged()
   {
     av.alignmentChanged(this);
index 42fbd70..322a60f 100644 (file)
@@ -519,7 +519,7 @@ public class AppletJmol extends EmbmenuFrame implements
     else if (evt.getSource() == seqColour)
     {
       setEnabled(seqColour);
-      jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
+      jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
     }
     else if (!allChainsSelected)
       centerViewer();
@@ -547,7 +547,7 @@ public class AppletJmol extends EmbmenuFrame implements
   public void updateColours(Object source)
   {
     AlignmentPanel ap = (AlignmentPanel) source;
-    jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
+    jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
   }
 
   public void updateTitleAndMenus()
@@ -558,7 +558,7 @@ public class AppletJmol extends EmbmenuFrame implements
       return;
     }
     setChainMenuItems(jmb.chainNames);
-    jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
+    jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
 
     setTitle(jmb.getViewerTitle());
   }
index 650693d..d2a6e6c 100644 (file)
@@ -52,7 +52,7 @@ class AppletJmolBinding extends jalview.ext.jmol.JalviewJmolBinding
           AlignmentViewPanel alignment)
   {
     AlignmentPanel ap = (AlignmentPanel) alignment;
-    if (appletJmolBinding.ap.av.showSequenceFeatures)
+    if (appletJmolBinding.ap.av.isShowSequenceFeatures())
     {
       if (appletJmolBinding.fr == null)
       {
@@ -105,7 +105,7 @@ class AppletJmolBinding extends jalview.ext.jmol.JalviewJmolBinding
   public void updateColours(Object source)
   {
     AlignmentPanel ap = (AlignmentPanel) source;
-    colourBySequence(ap.av.getShowSequenceFeatures(), ap);
+    colourBySequence(ap.av.isShowSequenceFeatures(), ap);
   }
 
   public void showUrl(String url)
index 6389250..1efb971 100644 (file)
@@ -78,7 +78,7 @@ public class ExtJmol extends JalviewJmolBinding
   public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
   {
     AlignmentPanel ap = (AlignmentPanel) alignment;
-    if (ap.av.showSequenceFeatures)
+    if (ap.av.isShowSequenceFeatures())
     {
       return ap.getFeatureRenderer();
     }
index 32ffdbf..c87803c 100644 (file)
@@ -73,10 +73,10 @@ public class FeatureColourChooser extends Panel implements ActionListener,
   {
     this.type = type;
     fr = frenderer;
-    float mm[] = ((float[][]) fr.minmax.get(type))[0];
+    float mm[] = ((float[][]) fr.getMinMax().get(type))[0];
     min = mm[0];
     max = mm[1];
-    oldcs = fr.featureColours.get(type);
+    oldcs = fr.getFeatureColours().get(type);
     if (oldcs instanceof GraduatedColor)
     {
       cs = new GraduatedColor((GraduatedColor) oldcs, min, max);
@@ -130,7 +130,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     {
       // cancel
       reset();
-      PaintRefresher.Refresh(this, fr.av.getSequenceSetId());
+      PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
       frame.setVisible(false);
     }
   }
@@ -289,7 +289,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     threshline.value = (float) slider.getValue() / 1000f;
     cs.setThresh(threshline.value);
     changeColour();
-    PaintRefresher.Refresh(this, fr.av.getSequenceSetId());
+    PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
     // ap.paintAlignment(false);
   }
 
@@ -402,16 +402,16 @@ public class FeatureColourChooser extends Panel implements ActionListener,
       }
     }
 
-    fr.featureColours.put(type, acg);
+    fr.setColour(type, acg);
     cs = acg;
-    PaintRefresher.Refresh(this, fr.av.getSequenceSetId());
+    PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
     // ap.paintAlignment(false);
   }
 
   void reset()
   {
-    fr.featureColours.put(type, oldcs);
-    PaintRefresher.Refresh(this, fr.av.getSequenceSetId());
+    fr.setColour(type, oldcs);
+    PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
     // ap.paintAlignment(true);
 
   }
@@ -433,7 +433,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     }
     else
     {
-      PaintRefresher.Refresh(this, fr.av.getSequenceSetId());
+      PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
     }
     // ap.paintAlignment(true);
   }
index 29c3ac5..dd2b873 100644 (file)
 package jalview.appletgui;
 
 import java.util.*;
-
 import java.awt.*;
-
 import java.awt.event.*;
 
 import jalview.datamodel.*;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.GraduatedColor;
 import jalview.util.MessageManager;
+import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
 
 /**
  * DOCUMENT ME!
@@ -37,34 +36,13 @@ import jalview.util.MessageManager;
  * @author $author$
  * @version $Revision$
  */
-public class FeatureRenderer implements jalview.api.FeatureRenderer
+public class FeatureRenderer extends jalview.renderer.seqfeatures.FeatureRenderer
 {
-  AlignViewport av;
-
-  Hashtable featureColours = new Hashtable();
-
-  // A higher level for grouping features of a
-  // particular type
-  Hashtable featureGroups = null;
 
   // Holds web links for feature groups and feature types
   // in the form label|link
   Hashtable featureLinks = null;
 
-  // This is actually an Integer held in the hashtable,
-  // Retrieved using the key feature type
-  Object currentColour;
-
-  String[] renderOrder;
-
-  FontMetrics fm;
-
-  int charOffset;
-
-  float transparency = 1f;
-
-  TransparencySetter transparencySetter = null;
-
   /**
    * Creates a new FeatureRenderer object.
    * 
@@ -73,39 +51,10 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
    */
   public FeatureRenderer(AlignViewport av)
   {
+    super();
     this.av = av;
 
-    if (!System.getProperty("java.version").startsWith("1.1"))
-    {
-      transparencySetter = new TransparencySetter();
-    }
-  }
-
-  public void transferSettings(FeatureRenderer fr)
-  {
-    renderOrder = fr.renderOrder;
-    featureGroups = fr.featureGroups;
-    featureColours = fr.featureColours;
-    transparency = fr.transparency;
-    if (av != null && fr.av != null && fr.av != av)
-    {
-      if (fr.av.featuresDisplayed != null)
-      {
-        if (av.featuresDisplayed == null)
-        {
-          av.featuresDisplayed = new Hashtable();
-        }
-        else
-        {
-          av.featuresDisplayed.clear();
-        }
-        Enumeration en = fr.av.featuresDisplayed.keys();
-        while (en.hasMoreElements())
-        {
-          av.featuresDisplayed.put(en.nextElement(), Boolean.TRUE);
-        }
-      }
-    }
+    setTransparencyAvailable(!System.getProperty("java.version").startsWith("1.1"));
   }
 
   static String lastFeatureAdded;
@@ -452,6 +401,7 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
         }
 
         ffile.parseDescriptionHTML(sf, false);
+        setVisible(lastFeatureAdded); // if user edited name then make sure new type is visible
       }
       if (deleteFeature)
       {
@@ -472,36 +422,17 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
           ffile.parseDescriptionHTML(features[i], false);
         }
 
-        if (av.featuresDisplayed == null)
-        {
-          av.featuresDisplayed = new Hashtable();
-        }
-
-        if (featureGroups == null)
-        {
-          featureGroups = new Hashtable();
-        }
-
         col = colourPanel.getBackground();
         // setColour(lastFeatureAdded, fcol);
 
         if (lastFeatureGroupAdded != null)
         {
-          featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
-        }
-        if (fcol instanceof Color)
-        {
-          setColour(lastFeatureAdded, fcol);
+          setGroupVisibility(lastFeatureGroupAdded, true);
         }
-        av.featuresDisplayed.put(lastFeatureAdded,
-                getFeatureStyle(lastFeatureAdded));
-
-        findAllFeatures();
-
-        String[] tro = new String[renderOrder.length];
-        tro[0] = renderOrder[renderOrder.length - 1];
-        System.arraycopy(renderOrder, 0, tro, 1, renderOrder.length - 1);
-        renderOrder = tro;
+        setColour(lastFeatureAdded, fcol);
+        setVisible(lastFeatureAdded);
+        findAllFeatures(false); // different to original applet behaviour ? 
+        // findAllFeatures();
       }
       else
       {
@@ -510,9 +441,9 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
       }
     }
     // refresh the alignment and the feature settings dialog
-    if (av.featureSettings != null)
+    if (((jalview.appletgui.AlignViewport) av).featureSettings != null)
     {
-      av.featureSettings.refreshTable();
+      ((jalview.appletgui.AlignViewport) av).featureSettings.refreshTable();
     }
     // findAllFeatures();
 
@@ -520,759 +451,4 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
 
     return true;
   }
-
-  public Color findFeatureColour(Color initialCol, SequenceI seq, int i)
-  {
-    overview = true;
-    if (!av.showSequenceFeatures)
-    {
-      return initialCol;
-    }
-
-    lastSeq = seq;
-    sequenceFeatures = lastSeq.getSequenceFeatures();
-    if (sequenceFeatures == null)
-    {
-      return initialCol;
-    }
-
-    sfSize = sequenceFeatures.length;
-
-    if (jalview.util.Comparison.isGap(lastSeq.getCharAt(i)))
-    {
-      return Color.white;
-    }
-
-    currentColour = null;
-
-    drawSequence(null, lastSeq, lastSeq.findPosition(i), -1, -1);
-
-    if (currentColour == null)
-    {
-      return initialCol;
-    }
-
-    return new Color(((Integer) currentColour).intValue());
-  }
-
-  /**
-   * This is used by the Molecule Viewer to get the accurate colour of the
-   * rendered sequence
-   */
-  boolean overview = false;
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param g
-   *          DOCUMENT ME!
-   * @param seq
-   *          DOCUMENT ME!
-   * @param sg
-   *          DOCUMENT ME!
-   * @param start
-   *          DOCUMENT ME!
-   * @param end
-   *          DOCUMENT ME!
-   * @param x1
-   *          DOCUMENT ME!
-   * @param y1
-   *          DOCUMENT ME!
-   * @param width
-   *          DOCUMENT ME!
-   * @param height
-   *          DOCUMENT ME!
-   */
-  // String type;
-  // SequenceFeature sf;
-  SequenceI lastSeq;
-
-  SequenceFeature[] sequenceFeatures;
-
-  int sfSize, sfindex, spos, epos;
-
-  synchronized public void drawSequence(Graphics g, SequenceI seq,
-          int start, int end, int y1)
-  {
-    if (seq.getSequenceFeatures() == null
-            || seq.getSequenceFeatures().length == 0)
-    {
-      return;
-    }
-
-    if (transparencySetter != null && g != null)
-    {
-      transparencySetter.setTransparency(g, transparency);
-    }
-
-    if (lastSeq == null || seq != lastSeq
-            || sequenceFeatures != seq.getSequenceFeatures())
-    {
-      lastSeq = seq;
-      sequenceFeatures = seq.getSequenceFeatures();
-      sfSize = sequenceFeatures.length;
-    }
-
-    if (av.featuresDisplayed == null || renderOrder == null)
-    {
-      findAllFeatures();
-      if (av.featuresDisplayed.size() < 1)
-      {
-        return;
-      }
-
-      sequenceFeatures = seq.getSequenceFeatures();
-      sfSize = sequenceFeatures.length;
-    }
-    if (!overview)
-    {
-      spos = lastSeq.findPosition(start);
-      epos = lastSeq.findPosition(end);
-      if (g != null)
-      {
-        fm = g.getFontMetrics();
-      }
-    }
-    String type;
-    for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
-    {
-      type = renderOrder[renderIndex];
-      if (!av.featuresDisplayed.containsKey(type))
-      {
-        continue;
-      }
-
-      // loop through all features in sequence to find
-      // current feature to render
-      for (sfindex = 0; sfindex < sfSize; sfindex++)
-      {
-        if (!sequenceFeatures[sfindex].type.equals(type))
-        {
-          continue;
-        }
-
-        if (featureGroups != null
-                && sequenceFeatures[sfindex].featureGroup != null
-                && featureGroups
-                        .containsKey(sequenceFeatures[sfindex].featureGroup)
-                && !((Boolean) featureGroups
-                        .get(sequenceFeatures[sfindex].featureGroup))
-                        .booleanValue())
-        {
-          continue;
-        }
-
-        if (!overview
-                && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
-                        .getEnd() < spos))
-        {
-          continue;
-        }
-
-        if (overview)
-        {
-          if (sequenceFeatures[sfindex].begin <= start
-                  && sequenceFeatures[sfindex].end >= start)
-          {
-            currentColour = new Integer(
-                    getColour(sequenceFeatures[sfindex]).getRGB());// av.featuresDisplayed
-            // .get(sequenceFeatures[sfindex].type);
-          }
-
-        }
-        else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
-        {
-
-          renderFeature(g, seq,
-                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                  getColour(sequenceFeatures[sfindex])
-                  // new Color(((Integer) av.featuresDisplayed
-                  // .get(sequenceFeatures[sfindex].type)).intValue())
-                  , start, end, y1);
-          renderFeature(g, seq,
-                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                  getColour(sequenceFeatures[sfindex])
-                  // new Color(((Integer) av.featuresDisplayed
-                  // .get(sequenceFeatures[sfindex].type)).intValue())
-                  , start, end, y1);
-
-        }
-        else
-        {
-          if (showFeature(sequenceFeatures[sfindex]))
-          {
-            renderFeature(g, seq,
-                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                    getColour(sequenceFeatures[sfindex]), start, end, y1);
-          }
-        }
-
-      }
-    }
-
-    if (transparencySetter != null && g != null)
-    {
-      transparencySetter.setTransparency(g, 1.0f);
-    }
-  }
-
-  char s;
-
-  int i;
-
-  void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
-          Color featureColour, int start, int end, int y1)
-  {
-
-    if (((fstart <= end) && (fend >= start)))
-    {
-      if (fstart < start)
-      { // fix for if the feature we have starts before the sequence start,
-        fstart = start; // but the feature end is still valid!!
-      }
-
-      if (fend >= end)
-      {
-        fend = end;
-      }
-
-      for (i = fstart; i <= fend; i++)
-      {
-        s = seq.getCharAt(i);
-
-        if (jalview.util.Comparison.isGap(s))
-        {
-          continue;
-        }
-
-        g.setColor(featureColour);
-
-        g.fillRect((i - start) * av.charWidth, y1, av.charWidth,
-                av.charHeight);
-
-        if (!av.validCharWidth)
-        {
-          continue;
-        }
-
-        g.setColor(Color.white);
-        charOffset = (av.charWidth - fm.charWidth(s)) / 2;
-        g.drawString(String.valueOf(s), charOffset
-                + (av.charWidth * (i - start)), (y1 + av.charHeight)
-                - av.charHeight / 5); // pady = height / 5;
-
-      }
-    }
-  }
-
-  Hashtable minmax = null;
-
-  /**
-   * Called when alignment in associated view has new/modified features to
-   * discover and display.
-   * 
-   */
-  public void featuresAdded()
-  {
-    lastSeq = null;
-    findAllFeatures();
-  }
-
-  /**
-   * find all features on the alignment
-   */
-  void findAllFeatures()
-  {
-    jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
-
-    av.featuresDisplayed = new Hashtable();
-    Vector allfeatures = new Vector();
-    minmax = new Hashtable();
-    AlignmentI alignment = av.getAlignment();
-    for (int i = 0; i < alignment.getHeight(); i++)
-    {
-      SequenceFeature[] features = alignment.getSequenceAt(i)
-              .getSequenceFeatures();
-
-      if (features == null)
-      {
-        continue;
-      }
-
-      int index = 0;
-      while (index < features.length)
-      {
-        if (features[index].begin == 0 && features[index].end == 0)
-        {
-          index++;
-          continue;
-        }
-        if (!av.featuresDisplayed.containsKey(features[index].getType()))
-        {
-          if (getColour(features[index].getType()) == null)
-          {
-            featureColours.put(features[index].getType(),
-                    ucs.createColourFromName(features[index].getType()));
-          }
-
-          av.featuresDisplayed.put(features[index].getType(), new Integer(
-                  getColour(features[index].getType()).getRGB()));
-          allfeatures.addElement(features[index].getType());
-        }
-        if (features[index].score != Float.NaN)
-        {
-          int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
-          float[][] mm = (float[][]) minmax.get(features[index].getType());
-          if (mm == null)
-          {
-            mm = new float[][]
-            { null, null };
-            minmax.put(features[index].getType(), mm);
-          }
-          if (mm[nonpos] == null)
-          {
-            mm[nonpos] = new float[]
-            { features[index].score, features[index].score };
-
-          }
-          else
-          {
-            if (mm[nonpos][0] > features[index].score)
-            {
-              mm[nonpos][0] = features[index].score;
-            }
-            if (mm[nonpos][1] < features[index].score)
-            {
-              mm[nonpos][1] = features[index].score;
-            }
-          }
-        }
-
-        index++;
-      }
-    }
-
-    renderOrder = new String[allfeatures.size()];
-    Enumeration en = allfeatures.elements();
-    int i = allfeatures.size() - 1;
-    while (en.hasMoreElements())
-    {
-      renderOrder[i] = en.nextElement().toString();
-      i--;
-    }
-  }
-
-  /**
-   * get a feature style object for the given type string. Creates a
-   * java.awt.Color for a featureType with no existing colourscheme. TODO:
-   * replace return type with object implementing standard abstract colour/style
-   * interface
-   * 
-   * @param featureType
-   * @return java.awt.Color or GraduatedColor
-   */
-  public Object getFeatureStyle(String featureType)
-  {
-    Object fc = featureColours.get(featureType);
-    if (fc == null)
-    {
-      jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
-      Color col = ucs.createColourFromName(featureType);
-      featureColours.put(featureType, fc = col);
-    }
-    return fc;
-  }
-
-  public Color getColour(String featureType)
-  {
-    Object fc = getFeatureStyle(featureType);
-
-    if (fc instanceof Color)
-    {
-      return (Color) fc;
-    }
-    else
-    {
-      if (fc instanceof GraduatedColor)
-      {
-        return ((GraduatedColor) fc).getMaxColor();
-      }
-    }
-    throw new Error(MessageManager.formatMessage("error.implementation_error_unrecognised_render_object_for_features_type", new String[]{fc.getClass().getCanonicalName(),featureType}));
-  }
-
-  /**
-   * 
-   * @param sequenceFeature
-   * @return true if feature is visible.
-   */
-  private boolean showFeature(SequenceFeature sequenceFeature)
-  {
-    Object fc = getFeatureStyle(sequenceFeature.type);
-    if (fc instanceof GraduatedColor)
-    {
-      return ((GraduatedColor) fc).isColored(sequenceFeature);
-    }
-    else
-    {
-      return true;
-    }
-  }
-
-  /**
-   * implement graduated colouring for features with scores
-   * 
-   * @param feature
-   * @return render colour for the given feature
-   */
-  public Color getColour(SequenceFeature feature)
-  {
-    Object fc = getFeatureStyle(feature.getType());
-    if (fc instanceof Color)
-    {
-      return (Color) fc;
-    }
-    else
-    {
-      if (fc instanceof GraduatedColor)
-      {
-        return ((GraduatedColor) fc).findColor(feature);
-      }
-    }
-    throw new Error("Implementation Error: Unrecognised render object "
-            + fc.getClass() + " for features of type " + feature.getType());
-  }
-
-  public void setColour(String featureType, Object col)
-  {
-    // overwrite
-    // Color _col = (col instanceof Color) ? ((Color) col) : (col instanceof
-    // GraduatedColor) ? ((GraduatedColor) col).getMaxColor() : null;
-    // Object c = featureColours.get(featureType);
-    // if (c == null || c instanceof Color || (c instanceof GraduatedColor &&
-    // !((GraduatedColor)c).getMaxColor().equals(_col)))
-    {
-      featureColours.put(featureType, col);
-    }
-  }
-
-  public void setFeaturePriority(Object[][] data)
-  {
-    // The feature table will display high priority
-    // features at the top, but theses are the ones
-    // we need to render last, so invert the data
-    if (av.featuresDisplayed != null)
-    {
-      av.featuresDisplayed.clear();
-    }
-
-    /*
-     * if (visibleNew) { if (av.featuresDisplayed != null) {
-     * av.featuresDisplayed.clear(); } else { av.featuresDisplayed = new
-     * Hashtable(); } } if (data == null) { return; }
-     */
-
-    renderOrder = new String[data.length];
-
-    if (data.length > 0)
-    {
-      for (int i = 0; i < data.length; i++)
-      {
-        String type = data[i][0].toString();
-        setColour(type, data[i][1]);
-        if (((Boolean) data[i][2]).booleanValue())
-        {
-          av.featuresDisplayed.put(type, new Integer(getColour(type)
-                  .getRGB()));
-        }
-
-        renderOrder[data.length - i - 1] = type;
-      }
-    }
-  }
-
-  /**
-   * @return a simple list of feature group names or null
-   */
-  public String[] getGroups()
-  {
-    buildGroupHash();
-    if (featureGroups != null)
-    {
-      String[] gps = new String[featureGroups.size()];
-      Enumeration gn = featureGroups.keys();
-      int i = 0;
-      while (gn.hasMoreElements())
-      {
-        gps[i++] = (String) gn.nextElement();
-      }
-      return gps;
-    }
-    return null;
-  }
-
-  /**
-   * get visible or invisible groups
-   * 
-   * @param visible
-   *          true to return visible groups, false to return hidden ones.
-   * @return list of groups
-   */
-  public String[] getGroups(boolean visible)
-  {
-    buildGroupHash();
-    if (featureGroups != null)
-    {
-      Vector gp = new Vector();
-
-      Enumeration gn = featureGroups.keys();
-      while (gn.hasMoreElements())
-      {
-        String nm = (String) gn.nextElement();
-        Boolean state = (Boolean) featureGroups.get(nm);
-        if (state.booleanValue() == visible)
-        {
-          gp.addElement(nm);
-        }
-      }
-      String[] gps = new String[gp.size()];
-      gp.copyInto(gps);
-
-      int i = 0;
-      while (gn.hasMoreElements())
-      {
-        gps[i++] = (String) gn.nextElement();
-      }
-      return gps;
-    }
-    return null;
-  }
-
-  /**
-   * set all feature groups in toset to be visible or invisible
-   * 
-   * @param toset
-   *          group names
-   * @param visible
-   *          the state of the named groups to set
-   */
-  public void setGroupState(String[] toset, boolean visible)
-  {
-    buildGroupHash();
-    if (toset != null && toset.length > 0 && featureGroups != null)
-    {
-      boolean rdrw = false;
-      for (int i = 0; i < toset.length; i++)
-      {
-        Object st = featureGroups.get(toset[i]);
-        featureGroups.put(toset[i], new Boolean(visible));
-        if (st != null)
-        {
-          rdrw = rdrw || (visible != ((Boolean) st).booleanValue());
-        }
-      }
-      if (rdrw)
-      {
-        if (this.av != null)
-          if (this.av.featureSettings != null)
-          {
-            av.featureSettings.rebuildGroups();
-            this.av.featureSettings.resetTable(true);
-          }
-          else
-          {
-            buildFeatureHash();
-          }
-        if (av != null)
-        {
-          av.alignmentChanged(null);
-        }
-      }
-    }
-  }
-
-  ArrayList<String> hiddenGroups = new ArrayList<String>();
-
-  /**
-   * analyse alignment for groups and hash tables (used to be embedded in
-   * FeatureSettings.setTableData)
-   * 
-   * @return true if features are on the alignment
-   */
-  public boolean buildGroupHash()
-  {
-    boolean alignmentHasFeatures = false;
-    if (featureGroups == null)
-    {
-      featureGroups = new Hashtable();
-    }
-    hiddenGroups = new ArrayList<String>();
-    hiddenGroups.addAll(featureGroups.keySet());
-    ArrayList allFeatures = new ArrayList();
-    ArrayList allGroups = new ArrayList();
-    SequenceFeature[] tmpfeatures;
-    String group;
-    AlignmentI alignment = av.getAlignment();
-    for (int i = 0; i < alignment.getHeight(); i++)
-    {
-      if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
-      {
-        continue;
-      }
-
-      alignmentHasFeatures = true;
-
-      tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
-      int index = 0;
-      while (index < tmpfeatures.length)
-      {
-        if (tmpfeatures[index].getFeatureGroup() != null)
-        {
-          group = tmpfeatures[index].featureGroup;
-          // Remove group from the hiddenGroup list
-          hiddenGroups.remove(group);
-          if (!allGroups.contains(group))
-          {
-            allGroups.add(group);
-
-            boolean visible = true;
-            if (featureGroups.containsKey(group))
-            {
-              visible = ((Boolean) featureGroups.get(group)).booleanValue();
-            }
-            else
-            {
-              featureGroups.put(group, new Boolean(visible));
-            }
-          }
-        }
-
-        if (!allFeatures.contains(tmpfeatures[index].getType()))
-        {
-          allFeatures.add(tmpfeatures[index].getType());
-        }
-        index++;
-      }
-    }
-
-    return alignmentHasFeatures;
-  }
-
-  /**
-   * rebuild the featuresDisplayed and renderorder list based on the
-   * featureGroups hash and any existing display state and force a repaint if
-   * necessary
-   * 
-   * @return true if alignment has visible features
-   */
-  public boolean buildFeatureHash()
-  {
-    boolean alignmentHasFeatures = false;
-    if (featureGroups == null)
-    {
-      alignmentHasFeatures = buildGroupHash();
-    }
-    if (!alignmentHasFeatures)
-      return false;
-    Hashtable fdisp = av.featuresDisplayed;
-    Vector allFeatures = new Vector();
-    SequenceFeature[] tmpfeatures;
-    String group;
-    AlignmentI alignment = av.getAlignment();
-    for (int i = 0; i < alignment.getHeight(); i++)
-    {
-      if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
-      {
-        continue;
-      }
-
-      alignmentHasFeatures = true;
-
-      tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
-      int index = 0;
-      while (index < tmpfeatures.length)
-      {
-        boolean visible = true;
-        if (tmpfeatures[index].getFeatureGroup() != null)
-        {
-          group = tmpfeatures[index].featureGroup;
-          if (featureGroups.containsKey(group))
-          {
-            visible = ((Boolean) featureGroups.get(group)).booleanValue();
-          }
-        }
-
-        if (visible && !allFeatures.contains(tmpfeatures[index].getType()))
-        {
-          allFeatures.addElement(tmpfeatures[index].getType());
-        }
-        index++;
-      }
-    }
-    if (allFeatures.size() > 0)
-    {
-      String[] neworder = new String[allFeatures.size()];
-      int p = neworder.length - 1;
-      for (int i = renderOrder.length - 1; i >= 0; i--)
-      {
-        if (allFeatures.contains(renderOrder[i]))
-        {
-          neworder[p--] = renderOrder[i];
-          allFeatures.removeElement(renderOrder[i]);
-        }
-        else
-        {
-          av.featuresDisplayed.remove(renderOrder[i]);
-        }
-      }
-      for (int i = allFeatures.size() - 1; i > 0; i++)
-      {
-        Object e = allFeatures.elementAt(i);
-        if (e != null)
-        {
-          neworder[p--] = (String) e;
-          av.featuresDisplayed.put(e, getColour((String) e));
-        }
-      }
-      renderOrder = neworder;
-      return true;
-    }
-
-    return alignmentHasFeatures;
-  }
-
-  /**
-   * 
-   * @return the displayed feature type as an array of strings
-   */
-  protected String[] getDisplayedFeatureTypes()
-  {
-    String[] typ = null;
-    synchronized (renderOrder)
-    {
-      typ = new String[renderOrder.length];
-      System.arraycopy(renderOrder, 0, typ, 0, typ.length);
-      for (int i = 0; i < typ.length; i++)
-      {
-        if (av.featuresDisplayed.get(typ[i]) == null)
-        {
-          typ[i] = null;
-        }
-      }
-    }
-    return typ;
-  }
-}
-
-class TransparencySetter
-{
-  void setTransparency(Graphics g, float value)
-  {
-    Graphics2D g2 = (Graphics2D) g;
-    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
-            value));
-  }
 }
index cd11a35..4b29f53 100755 (executable)
@@ -21,7 +21,7 @@
 package jalview.appletgui;
 
 import java.util.*;
-
+import java.util.List;
 import java.awt.*;
 import java.awt.event.*;
 
@@ -50,8 +50,6 @@ public class FeatureSettings extends Panel implements ItemListener,
 
   ScrollPane scrollPane;
 
-  boolean alignmentHasFeatures = false;
-
   Image linkImage;
 
   Scrollbar transparency;
@@ -64,9 +62,9 @@ public class FeatureSettings extends Panel implements ItemListener,
     fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
 
     transparency = new Scrollbar(Scrollbar.HORIZONTAL,
-            100 - (int) (fr.transparency * 100), 1, 1, 100);
+            100 - (int) (fr.getTransparency() * 100), 1, 1, 100);
 
-    if (fr.transparencySetter != null)
+    if (fr.isTransparencyAvailable())
     {
       transparency.addAdjustmentListener(this);
     }
@@ -81,9 +79,9 @@ public class FeatureSettings extends Panel implements ItemListener,
       linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
     }
 
-    if (av.featuresDisplayed == null)
+    if (av.isShowSequenceFeatures() || !fr.hasRenderOrder())
     {
-      fr.findAllFeatures();
+      fr.findAllFeatures(true); // was default - now true to make all visible
     }
 
     setTableData();
@@ -91,7 +89,7 @@ public class FeatureSettings extends Panel implements ItemListener,
     this.setLayout(new BorderLayout());
     scrollPane = new ScrollPane();
     scrollPane.add(featurePanel);
-    if (alignmentHasFeatures)
+    if (fr.getAllFeatureColours()!=null && fr.getAllFeatureColours().size()>0)
     {
       add(scrollPane, BorderLayout.CENTER);
     }
@@ -104,7 +102,7 @@ public class FeatureSettings extends Panel implements ItemListener,
 
     Panel tPanel = new Panel(new BorderLayout());
 
-    if (fr.transparencySetter != null)
+    if (fr.isTransparencyAvailable())
     {
       tPanel.add(transparency, BorderLayout.CENTER);
       tPanel.add(new Label("Transparency"), BorderLayout.EAST);
@@ -124,8 +122,8 @@ public class FeatureSettings extends Panel implements ItemListener,
     {
       groupPanel
               .setLayout(new GridLayout(
-                      (fr.featureGroups.size() - fr.hiddenGroups.size()) / 4 + 1,
-                      4));
+                      (fr.getFeatureGroupsSize()) / 4 + 1,
+                      4)); // JBPNote - this was scaled on number of visible groups. seems broken
       groupPanel.validate();
 
       add(groupPanel, BorderLayout.NORTH);
@@ -185,7 +183,7 @@ public class FeatureSettings extends Panel implements ItemListener,
 
       public void actionPerformed(ActionEvent e)
       {
-        me.sortByScore(new String[]
+        me.ap.alignFrame.avc.sortAlignmentByFeatureScore(new String[]
         { type });
       }
 
@@ -197,7 +195,7 @@ public class FeatureSettings extends Panel implements ItemListener,
 
       public void actionPerformed(ActionEvent e)
       {
-        me.sortByDens(new String[]
+        me.ap.alignFrame.avc.sortAlignmentByFeatureDensity(new String[]
         { type });
       }
 
@@ -253,8 +251,7 @@ public class FeatureSettings extends Panel implements ItemListener,
 
   public void setTableData()
   {
-    alignmentHasFeatures = fr.buildGroupHash();
-    if (alignmentHasFeatures)
+    if (fr.getAllFeatureColours()!=null && fr.getAllFeatureColours().size()>0)
     {
       rebuildGroups();
 
@@ -279,18 +276,17 @@ public class FeatureSettings extends Panel implements ItemListener,
     }
     // TODO: JAL-964 - smoothly incorporate new group entries if panel already
     // displayed and new groups present
-    Enumeration gps = fr.featureGroups.keys();
-    while (gps.hasMoreElements())
+    for (String group:(List<String>)fr.getFeatureGroups())
     {
-      String group = (String) gps.nextElement();
-      Boolean vis = (Boolean) fr.featureGroups.get(group);
-      Checkbox check = new MyCheckbox(group, vis.booleanValue(),
+      boolean vis = fr.checkGroupVisibility(group, false);
+      Checkbox check = new MyCheckbox(group, vis,
               (fr.featureLinks != null && fr.featureLinks
                       .containsKey(group)));
       check.addMouseListener(this);
       check.setFont(new Font("Serif", Font.BOLD, 12));
-      check.addItemListener(this);
-      check.setVisible(fr.hiddenGroups.contains(group));
+      check.addItemListener(groupItemListener);
+      // note - visibility seems to correlate with displayed. ???wtf ??
+      check.setVisible(vis);
       groupPanel.add(check);
     }
     if (rdrw)
@@ -298,7 +294,6 @@ public class FeatureSettings extends Panel implements ItemListener,
       groupPanel.validate();
     }
   }
-
   // This routine adds and removes checkboxes depending on
   // Group selection states
   void resetTable(boolean groupsChanged)
@@ -320,8 +315,7 @@ public class FeatureSettings extends Panel implements ItemListener,
       {
         group = tmpfeatures[index].featureGroup;
 
-        if (group == null || fr.featureGroups.get(group) == null
-                || ((Boolean) fr.featureGroups.get(group)).booleanValue())
+        if (group == null || fr.checkGroupVisibility(group, true))
         {
           type = tmpfeatures[index].getType();
           if (!visibleChecks.contains(type))
@@ -350,13 +344,14 @@ public class FeatureSettings extends Panel implements ItemListener,
       }
     }
 
-    if (fr.renderOrder != null)
+    if (fr.getRenderOrder() != null)
     {
       // First add the checks in the previous render order,
       // in case the window has been closed and reopened
-      for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
+      List<String> rol = fr.getRenderOrder();
+      for (int ro = rol.size() - 1; ro > -1; ro--)
       {
-        String item = fr.renderOrder[ro];
+        String item = rol.get(ro);
 
         if (!visibleChecks.contains(item))
         {
@@ -418,7 +413,7 @@ public class FeatureSettings extends Panel implements ItemListener,
     if (addCheck)
     {
       boolean selected = false;
-      if (groupsChanged || av.featuresDisplayed.containsKey(type))
+      if (groupsChanged || av.getFeaturesDisplayed().isVisible(type))
       {
         selected = true;
       }
@@ -455,26 +450,22 @@ public class FeatureSettings extends Panel implements ItemListener,
     selectionChanged();
   }
 
-  public void itemStateChanged(ItemEvent evt)
-  {
-    if (evt != null)
-    {
-      // Is the source a top level featureGroup?
+  private ItemListener groupItemListener = new ItemListener() {
+    public void itemStateChanged(ItemEvent evt) {
       Checkbox source = (Checkbox) evt.getSource();
-      if (fr.featureGroups.containsKey(source.getLabel()))
+      fr.setGroupVisibility(source.getLabel(),
+              source.getState());
+      ap.seqPanel.seqCanvas.repaint();
+      if (ap.overviewPanel != null)
       {
-        fr.featureGroups.put(source.getLabel(),
-                new Boolean(source.getState()));
-        ap.seqPanel.seqCanvas.repaint();
-        if (ap.overviewPanel != null)
-        {
-          ap.overviewPanel.updateOverviewImage();
-        }
-
-        resetTable(true);
-        return;
+        ap.overviewPanel.updateOverviewImage();
       }
-    }
+      resetTable(true);
+      return;
+    };
+  };
+  public void itemStateChanged(ItemEvent evt)
+  {
     selectionChanged();
   }
 
@@ -617,7 +608,7 @@ public class FeatureSettings extends Panel implements ItemListener,
     MyCheckbox check = (MyCheckbox) evt.getSource();
     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
     {
-      this.popupSort(check, fr.minmax, evt.getX(), evt.getY());
+      this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
     }
     if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
     {
@@ -657,7 +648,7 @@ public class FeatureSettings extends Panel implements ItemListener,
 
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
-    fr.transparency = ((float) (100 - transparency.getValue()) / 100f);
+    fr.setTransparency((float) (100 - transparency.getValue()) / 100f);
     ap.seqPanel.seqCanvas.repaint();
 
   }
@@ -771,81 +762,4 @@ public class FeatureSettings extends Panel implements ItemListener,
     }
   }
 
-  protected void sortByDens(String[] typ)
-  {
-    sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
-  }
-
-  private String[] getDisplayedFeatureTypes()
-  {
-    String[] typ = null;
-    if (fr != null)
-    {
-      synchronized (fr.renderOrder)
-      {
-        typ = new String[fr.renderOrder.length];
-        System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
-        for (int i = 0; i < typ.length; i++)
-        {
-          if (av.featuresDisplayed.get(typ[i]) == null)
-          {
-            typ[i] = null;
-          }
-        }
-      }
-    }
-    return typ;
-  }
-
-  protected void sortBy(String[] typ, String methodText, final String method)
-  {
-    if (typ == null)
-    {
-      typ = getDisplayedFeatureTypes();
-    }
-    String gps[] = null;
-    gps = fr.getGroups(true);
-    if (typ != null)
-    {
-      for (int i = 0; i < typ.length; i++)
-      {
-        System.err.println("Sorting on Types:" + typ[i]);
-      }
-    }
-    if (gps != null)
-    {
-
-      for (int i = 0; i < gps.length; i++)
-      {
-        System.err.println("Sorting on groups:" + gps[i]);
-      }
-    }
-    AlignmentPanel alignPanel = ap;
-    AlignmentI al = alignPanel.av.getAlignment();
-
-    int start, stop;
-    SequenceGroup sg = alignPanel.av.getSelectionGroup();
-    if (sg != null)
-    {
-      start = sg.getStartRes();
-      stop = sg.getEndRes();
-    }
-    else
-    {
-      start = 0;
-      stop = al.getWidth();
-    }
-    SequenceI[] oldOrder = al.getSequencesArray();
-    AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
-    this.ap.alignFrame.addHistoryItem(new OrderCommand(methodText,
-            oldOrder, alignPanel.av.getAlignment()));
-    alignPanel.paintAlignment(true);
-
-  }
-
-  protected void sortByScore(String[] typ)
-  {
-    sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
-  }
-
 }
index 6ca6ddf..4796bca 100644 (file)
@@ -113,7 +113,7 @@ public class Finder extends Panel implements ActionListener
             features, true, ap))
     {
       ap.alignFrame.sequenceFeatures.setState(true);
-      av.showSequenceFeatures(true);
+      av.setShowSequenceFeatures(true);
       ap.highlightSearchResults(null);
     }
   }
index 2c2c41a..8486fe0 100755 (executable)
@@ -69,7 +69,6 @@ public class OverviewPanel extends Panel implements Runnable,
     sr.renderGaps = false;
     sr.forOverview = true;
     fr = new FeatureRenderer(av);
-    fr.overview = true;
 
     // scale the initial size of overviewpanel to shape of alignment
     float initialScale = (float) av.getAlignment().getWidth()
@@ -229,10 +228,9 @@ public class OverviewPanel extends Panel implements Runnable,
       return;
     }
 
-    if (av.showSequenceFeatures)
+    if (av.isShowSequenceFeatures())
     {
-      fr.featureGroups = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups;
-      fr.featureColours = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours;
+      fr.transferSettings(ap.seqPanel.seqCanvas.fr);
     }
 
     resizing = true;
@@ -260,7 +258,7 @@ public class OverviewPanel extends Panel implements Runnable,
     int alwidth = av.getAlignment().getWidth();
     int alheight = av.getAlignment().getHeight();
 
-    if (av.showSequenceFeatures)
+    if (av.isShowSequenceFeatures())
     {
       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
     }
@@ -337,7 +335,7 @@ public class OverviewPanel extends Panel implements Runnable,
         {
           color = sr.getResidueBoxColour(seq, lastcol);
 
-          if (av.showSequenceFeatures)
+          if (av.isShowSequenceFeatures())
           {
             color = fr.findFeatureColour(color, seq, lastcol);
           }
index 33caf53..53cca3c 100755 (executable)
@@ -589,7 +589,7 @@ public class SeqCanvas extends Panel
       sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
               startRes, endRes, offset + ((i - startSeq) * av.charHeight));
 
-      if (av.showSequenceFeatures)
+      if (av.isShowSequenceFeatures())
       {
         fr.drawSequence(g, nextSeq, startRes, endRes, offset
                 + ((i - startSeq) * av.charHeight));
index 592fd4f..a95dd27 100644 (file)
@@ -817,18 +817,14 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     {
       for (int i = 0; i < features.length; i++)
       {
-        if (av.featuresDisplayed == null
-                || !av.featuresDisplayed.containsKey(features[i].getType()))
+        if (av.getFeaturesDisplayed() == null
+                || !av.getFeaturesDisplayed().isVisible(features[i].getType()))
         {
           continue;
         }
 
         if (features[i].featureGroup != null
-                && seqCanvas.fr.featureGroups != null
-                && seqCanvas.fr.featureGroups
-                        .containsKey(features[i].featureGroup)
-                && !((Boolean) seqCanvas.fr.featureGroups
-                        .get(features[i].featureGroup)).booleanValue())
+                && !seqCanvas.fr.checkGroupVisibility(features[i].featureGroup,false))
         {
           continue;
         }
index 697c0d1..92d5beb 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.appletgui;
 
+import jalview.api.FeatureRenderer;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -86,6 +87,31 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     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
+   * overridden by a feature colour.
+   * 
+   * @param seq
+   * @param position
+   * @param fr
+   * @return
+   */
+  @Override
+  public Color getResidueColour(final SequenceI seq, int position,
+          FeatureRenderer fr)
+  {
+    // TODO replace 8 or so code duplications with calls to this method
+    // (refactored as needed)
+    Color col = getResidueBoxColour(seq, position);
+
+    if (fr != null)
+    {
+      col = fr.findFeatureColour(col, seq, position);
+    }
+    return col;
+  }
+
   void getBoxColour(ColourSchemeI cs, SequenceI seq, int i)
   {
     if (cs != null)
index b7c766a..655e182 100644 (file)
  */
 package jalview.appletgui;
 
-import java.awt.*;
-import java.awt.event.*;
-
-import jalview.analysis.*;
-import jalview.datamodel.*;
-import jalview.io.*;
+import jalview.analysis.NJTree;
+import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.ViewBasedAnalysisI;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SequenceI;
+import jalview.io.NewickFile;
+import jalview.schemes.ResidueProperties;
 import jalview.util.MessageManager;
 
+import java.awt.BorderLayout;
+import java.awt.CheckboxMenuItem;
+import java.awt.Color;
+import java.awt.Menu;
+import java.awt.MenuBar;
+import java.awt.MenuItem;
+import java.awt.ScrollPane;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
 public class TreePanel extends EmbmenuFrame implements ActionListener,
         ItemListener
 {
@@ -204,8 +219,8 @@ public class TreePanel extends EmbmenuFrame implements ActionListener,
       {
         int start, end;
         SequenceI[] seqs;
-        boolean selview = (av.getSelectionGroup() != null)
-                && (av.getSelectionGroup().getSize() > 1);
+        boolean selview = av.getSelectionGroup() != null
+                && av.getSelectionGroup().getSize() > 1;
         AlignmentView seqStrings = av.getAlignmentView(selview);
         if (!selview)
         {
@@ -220,8 +235,27 @@ public class TreePanel extends EmbmenuFrame implements ActionListener,
           seqs = av.getSelectionGroup().getSequencesInOrder(
                   av.getAlignment());
         }
-
-        tree = new NJTree(seqs, seqStrings, type, pwtype, start, end);
+        ScoreModelI sm = ResidueProperties.getScoreModel(pwtype);
+        if (sm instanceof ViewBasedAnalysisI)
+        {
+          try
+          {
+            sm = sm.getClass().newInstance();
+            ((ViewBasedAnalysisI) sm)
+                    .configureFromAlignmentView(treeCanvas.ap);
+          } catch (Exception q)
+          {
+            System.err.println("Couldn't create a scoremodel instance for "
+                    + sm.getName());
+            q.printStackTrace();
+          }
+          tree = new NJTree(seqs, seqStrings, type, pwtype, sm, start, end);
+        }
+        else
+        {
+          tree = new NJTree(seqs, seqStrings, type, pwtype, null, start,
+                  end);
+        }
       }
 
       tree.reCount(tree.getTopNode());
index ae8bc98..833dd7e 100644 (file)
@@ -1990,7 +1990,7 @@ public class JalviewLite extends Applet implements
         param = applet.getParameter("showFeatureSettings");
         if (param != null && param.equalsIgnoreCase("true"))
         {
-          newAlignFrame.viewport.showSequenceFeatures(true);
+          newAlignFrame.viewport.setShowSequenceFeatures(true);
           new FeatureSettings(newAlignFrame.alignPanel);
         }
 
index dbc3524..21cf630 100644 (file)
 package jalview.controller;
 
 import java.awt.Color;
+import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.List;
 
+import jalview.analysis.AlignmentSorter;
 import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureRenderer;
+import jalview.commands.OrderCommand;
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceFeature;
@@ -296,4 +299,74 @@ public class AlignViewController implements AlignViewControllerI
       return false;
     }
   }
+
+  @Override
+  public void sortAlignmentByFeatureDensity(String[] typ)
+  {
+    sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
+  }
+
+  protected void sortBy(String[] typ, String methodText, final String method)
+  {
+    FeatureRenderer fr = alignPanel.getFeatureRenderer();
+    if (typ == null)
+    {
+      typ = fr==null ? null : fr.getDisplayedFeatureTypes();
+    }
+    String gps[] = null;
+    gps = fr==null ? null : fr.getDisplayedFeatureGroups();
+    if (typ != null)
+    {
+      ArrayList types = new ArrayList();
+      for (int i = 0; i < typ.length; i++)
+      {
+        if (typ[i] != null)
+        {
+          types.add(typ[i]);
+        }
+        typ = new String[types.size()];
+        types.toArray(typ);
+      }
+    }
+    if (gps != null)
+    {
+      ArrayList grps = new ArrayList();
+
+      for (int i = 0; i < gps.length; i++)
+      {
+        if (gps[i] != null)
+        {
+          grps.add(gps[i]);
+        }
+      }
+      gps = new String[grps.size()];
+      grps.toArray(gps);
+    }
+    AlignmentI al = viewport.getAlignment();
+
+    int start, stop;
+    SequenceGroup sg = viewport.getSelectionGroup();
+    if (sg != null)
+    {
+      start = sg.getStartRes();
+      stop = sg.getEndRes();
+    }
+    else
+    {
+      start = 0;
+      stop = al.getWidth();
+    }
+    SequenceI[] oldOrder = al.getSequencesArray();
+    AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
+    avcg.addHistoryItem(new OrderCommand(methodText, oldOrder, viewport
+            .getAlignment()));
+    alignPanel.paintAlignment(true);
+
+  }
+
+  @Override
+  public void sortAlignmentByFeatureScore(String[] typ)
+  {
+    sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
+  }
 }
diff --git a/src/jalview/controller/FeatureSettingsController.java b/src/jalview/controller/FeatureSettingsController.java
new file mode 100644 (file)
index 0000000..ebf4958
--- /dev/null
@@ -0,0 +1,12 @@
+package jalview.controller;
+
+import jalview.api.FeatureRenderer;
+import jalview.api.FeatureSettingsModelI;
+
+public class FeatureSettingsController implements jalview.api.FeatureSettingsControllerI
+{
+  FeatureSettingsControllerGuiI settingUI;
+  FeatureRenderer fr;
+  FeatureSettingsModelI fsettings;
+  
+}
diff --git a/src/jalview/controller/FeatureSettingsControllerGuiI.java b/src/jalview/controller/FeatureSettingsControllerGuiI.java
new file mode 100644 (file)
index 0000000..781759e
--- /dev/null
@@ -0,0 +1,6 @@
+package jalview.controller;
+
+public interface FeatureSettingsControllerGuiI
+{
+
+}
index d3c8c09..4afc526 100644 (file)
@@ -22,17 +22,20 @@ package jalview.ext.rbvi.chimera;
 
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
-import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureMappingcommandSet;
 import jalview.structure.StructureSelectionManager;
-import jalview.util.Format;
+import jalview.util.ColorUtils;
+import jalview.util.Comparison;
 
 import java.awt.Color;
 import java.util.ArrayList;
-import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 
 /**
  * Routines for generating Chimera commands for Jalview/Chimera binding
@@ -55,104 +58,233 @@ public class ChimeraCommands
           SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
           AlignmentI alignment)
   {
+    Map<Color, Map<Integer, Map<String, List<int[]>>>> colourMap = buildColoursMap(
+            ssm, files, sequence, sr, fr, alignment);
 
-    ArrayList<StructureMappingcommandSet> cset = new ArrayList<StructureMappingcommandSet>();
-    Hashtable<String,StringBuffer> colranges=new Hashtable<String,StringBuffer>();
+    List<String> colourCommands = buildColourCommands(colourMap);
+
+    StructureMappingcommandSet cs = new StructureMappingcommandSet(
+            ChimeraCommands.class, null,
+            colourCommands.toArray(new String[0]));
+
+    return new StructureMappingcommandSet[]
+    { cs };
+  }
+
+  /**
+   * Traverse the map of colours/models/chains/positions to construct a list of
+   * 'color' commands (one per distinct colour used). The format of each command
+   * is
+   * 
+   * <blockquote> color colorname #modelnumber:range.chain e.g. color #00ff00
+   * #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,...
+   * 
+   * @see http
+   *      ://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec
+   *      .html </pre>
+   * 
+   * @param colourMap
+   * @return
+   */
+  protected static List<String> buildColourCommands(
+          Map<Color, Map<Integer, Map<String, List<int[]>>>> colourMap)
+  {
+    /*
+     * This version concatenates all commands into a single String (semi-colon
+     * delimited). If length limit issues arise, refactor to return one color
+     * command per colour.
+     */
+    List<String> commands = new ArrayList<String>();
+    StringBuilder sb = new StringBuilder(256);
+    boolean firstColour = true;
+    for (Color colour : colourMap.keySet())
+    {
+      String colourCode = ColorUtils.toTkCode(colour);
+      if (!firstColour)
+      {
+        sb.append("; ");
+      }
+      sb.append("color ").append(colourCode).append(" ");
+      firstColour = false;
+      boolean firstModelForColour = true;
+      final Map<Integer, Map<String, List<int[]>>> colourData = colourMap.get(colour);
+      for (Integer model : colourData.keySet())
+      {
+        boolean firstPositionForModel = true;
+        if (!firstModelForColour)
+        {
+          sb.append("|");
+        }
+        firstModelForColour = false;
+        sb.append("#").append(model).append(":");
+
+        final Map<String, List<int[]>> modelData = colourData.get(model);
+        for (String chain : modelData.keySet())
+        {
+          for (int[] range : modelData.get(chain))
+          {
+            if (!firstPositionForModel)
+            {
+              sb.append(",");
+            }
+            if (range[0] == range[1])
+            {
+              sb.append(range[0]);
+            }
+            else
+            {
+              sb.append(range[0]).append("-").append(range[1]);
+            }
+            sb.append(".").append(chain);
+            firstPositionForModel = false;
+          }
+        }
+      }
+    }
+    commands.add(sb.toString());
+    return commands;
+  }
+
+  /**
+   * <pre>
+   * Build a data structure which maps contiguous subsequences for each colour. 
+   * This generates a data structure from which we can easily generate the 
+   * Chimera command for colour by sequence.
+   * Color
+   *     Model number
+   *         Chain
+   *             list of start/end ranges
+   * Ordering is by order of addition (for colours and positions), natural ordering (for models and chains)
+   * </pre>
+   */
+  protected static Map<Color, Map<Integer, Map<String, List<int[]>>>> buildColoursMap(
+          StructureSelectionManager ssm, String[] files,
+          SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
+          AlignmentI alignment)
+  {
+    Map<Color, Map<Integer, Map<String, List<int[]>>>> colourMap = new LinkedHashMap<Color, Map<Integer, Map<String, List<int[]>>>>();
+    Color lastColour = null;
     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
     {
-      float cols[] = new float[4];
       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
-      StringBuffer command = new StringBuffer();
-      StructureMappingcommandSet smc;
-      ArrayList<String> str = new ArrayList<String>();
 
       if (mapping == null || mapping.length < 1)
+      {
         continue;
+      }
 
-      int startPos = -1, lastPos = -1, startModel = -1, lastModel = -1;
-      String startChain = "", lastChain = "";
-      Color lastCol = null;
+      int startPos = -1, lastPos = -1;
+      String lastChain = "";
       for (int s = 0; s < sequence[pdbfnum].length; s++)
       {
         for (int sp, m = 0; m < mapping.length; m++)
         {
-          if (mapping[m].getSequence() == sequence[pdbfnum][s]
-                  && (sp = alignment.findIndex(sequence[pdbfnum][s])) > -1)
+          final SequenceI seq = sequence[pdbfnum][s];
+          if (mapping[m].getSequence() == seq
+                  && (sp = alignment.findIndex(seq)) > -1)
           {
             SequenceI asp = alignment.getSequenceAt(sp);
             for (int r = 0; r < asp.getLength(); r++)
             {
               // no mapping to gaps in sequence
-              if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
+              if (Comparison.isGap(asp.getCharAt(r)))
               {
                 continue;
               }
               int pos = mapping[m].getPDBResNum(asp.findPosition(r));
 
               if (pos < 1 || pos == lastPos)
+              {
                 continue;
+              }
 
-              Color col = sr.getResidueBoxColour(sequence[pdbfnum][s], r);
+              Color colour = sr.getResidueColour(seq, r, fr);
+              final String chain = mapping[m].getChain();
 
-              if (fr != null)
-                col = fr.findFeatureColour(col, sequence[pdbfnum][s], r);
-              if (lastCol != col || lastPos + 1 != pos
-                      || pdbfnum != lastModel
-                      || !mapping[m].getChain().equals(lastChain))
+              /*
+               * Just keep incrementing the end position for this colour range
+               * _unless_ colour, PDB model or chain has changed, or there is a
+               * gap in the mapped residue sequence
+               */
+              final boolean newColour = !colour.equals(lastColour);
+              final boolean nonContig = lastPos + 1 != pos;
+              final boolean newChain = !chain.equals(lastChain);
+              if (newColour || nonContig || newChain)
               {
-                if (lastCol != null)
+                if (startPos != -1)
                 {
-                  addColourRange(colranges, lastCol,startModel,startPos,lastPos,lastChain); 
+                  addColourRange(colourMap, lastColour, pdbfnum, startPos,
+                          lastPos, lastChain);
                 }
-                lastCol = null;
                 startPos = pos;
-                startModel = pdbfnum;
-                startChain = mapping[m].getChain();
               }
-              lastCol = col;
+              lastColour = colour;
               lastPos = pos;
-              lastModel = pdbfnum;
-              lastChain = mapping[m].getChain();
+              lastChain = chain;
             }
             // final colour range
-            if (lastCol != null)
+            if (lastColour != null)
             {
-              addColourRange(colranges, lastCol,startModel,startPos,lastPos,lastChain); 
+              addColourRange(colourMap, lastColour, pdbfnum, startPos,
+                      lastPos, lastChain);
             }
             break;
           }
         }
       }
-      // Finally, add the command set ready to be returned.
-      StringBuffer coms=new StringBuffer();
-      for (String cr:colranges.keySet())
-      {
-        coms.append("color #"+cr+" "+colranges.get(cr)+";");
-      }
-      cset.add(new StructureMappingcommandSet(ChimeraCommands.class,
-              files[pdbfnum], new String[] { coms.toString() }));
     }
-    return cset.toArray(new StructureMappingcommandSet[cset.size()]);
+    return colourMap;
   }
 
-  private static void addColourRange(Hashtable<String, StringBuffer> colranges, Color lastCol, int startModel,
-          int startPos, int lastPos, String lastChain)
+  /**
+   * Helper method to add one contiguous colour range to the colour map.
+   * 
+   * @param colourMap
+   * @param colour
+   * @param model
+   * @param startPos
+   * @param endPos
+   * @param chain
+   */
+  protected static void addColourRange(
+          Map<Color, Map<Integer, Map<String, List<int[]>>>> colourMap,
+          Color colour, int model, int startPos, int endPos, String chain)
   {
-    
-    String colstring = ((lastCol.getRed()< 16) ? "0":"")+Integer.toHexString(lastCol.getRed())
-            + ((lastCol.getGreen()< 16) ? "0":"")+Integer.toHexString(lastCol.getGreen())
-            + ((lastCol.getBlue()< 16) ? "0":"")+Integer.toHexString(lastCol.getBlue());
-    StringBuffer currange = colranges.get(colstring);
-    if (currange==null)
+    /*
+     * Get/initialize map of data for the colour
+     */
+    Map<Integer, Map<String, List<int[]>>> colourData = colourMap
+            .get(colour);
+    if (colourData == null)
+    {
+      colourMap
+              .put(colour,
+                      colourData = new TreeMap<Integer, Map<String, List<int[]>>>());
+    }
+
+    /*
+     * Get/initialize map of data for the colour and model
+     */
+    Map<String, List<int[]>> modelData = colourData.get(model);
+    if (modelData == null)
     {
-      colranges.put(colstring,currange = new StringBuffer());
+      colourData.put(model, modelData = new TreeMap<String, List<int[]>>());
     }
-    if (currange.length()>0)
+
+    /*
+     * Get/initialize map of data for colour, model and chain
+     */
+    List<int[]> chainData = modelData.get(chain);
+    if (chainData == null)
     {
-      currange.append("|");
+      modelData.put(chain, chainData = new ArrayList<int[]>());
     }
-    currange.append("#" + startModel + ":" + ((startPos==lastPos) ? startPos : startPos + "-"
-            + lastPos) + "." + lastChain);
+
+    /*
+     * Add the start/end positions
+     */
+    chainData.add(new int[]
+    { startPos, endPos });
   }
 
 }
index 82f5e5c..b5bfbaa 100644 (file)
@@ -58,6 +58,9 @@ public abstract class JalviewChimeraBinding extends
         SequenceStructureBinding, StructureSelectionManagerProvider
 
 {
+
+  private static final boolean debug = false;
+
   private static final String PHOSPHORUS = "P";
 
   private static final String ALPHACARBON = "CA";
@@ -654,10 +657,12 @@ public abstract class JalviewChimeraBinding extends
       }
       if (selectioncom.length() > 0)
       {
-        // TODO remove debug output
-        System.out.println("Select regions:\n" + selectioncom.toString());
-        System.out
-                .println("Superimpose command(s):\n" + command.toString());
+        if (debug)
+        {
+          System.out.println("Select regions:\n" + selectioncom.toString());
+          System.out.println("Superimpose command(s):\n"
+                  + command.toString());
+        }
         allComs.append("~display all; chain @CA|P; ribbon "
                 + selectioncom.toString() + ";"+command.toString());
         // selcom.append("; ribbons; ");
@@ -669,7 +674,10 @@ public abstract class JalviewChimeraBinding extends
       {
         selectioncom.setLength(selectioncom.length() - 1);
       }
-      System.out.println("Select regions:\n" + selectioncom.toString());
+      if (debug)
+      {
+        System.out.println("Select regions:\n" + selectioncom.toString());
+      }
       allComs.append("; ~display all; chain @CA|P; ribbon "
               + selectioncom.toString() + "; focus");
       // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
@@ -954,8 +962,6 @@ public abstract class JalviewChimeraBinding extends
     }
   }
 
-  boolean debug = false;
-
   private void log(String message)
   {
     System.err.println("## Chimera log: " + message);
index 34ccf9c..4c45aad 100644 (file)
@@ -768,7 +768,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     setColourSelected(ColourSchemeProperty.getColourName(av
             .getGlobalColourScheme()));
 
-    showSeqFeatures.setSelected(av.showSequenceFeatures);
+    showSeqFeatures.setSelected(av.isShowSequenceFeatures());
     hiddenMarkers.setState(av.showHiddenMarkers);
     applyToAllGroups.setState(av.getColourAppliesToAllGroups());
     showNpFeatsMenuitem.setSelected(av.isShowNpFeats());
@@ -3092,7 +3092,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
             .isSelected());
-    if (viewport.getShowSequenceFeaturesHeight())
+    if (viewport.isShowSequenceFeaturesHeight())
     {
       // ensure we're actually displaying features
       viewport.setShowSequenceFeatures(true);
@@ -4888,7 +4888,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       featuresFile = new FeaturesFile(file, type).parse(viewport
               .getAlignment().getDataset(), alignPanel.getSeqPanel().seqCanvas
-              .getFeatureRenderer().featureColours, false,
+              .getFeatureRenderer().getFeatureColours(), false,
               jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
     } catch (Exception ex)
     {
@@ -4897,7 +4897,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     if (featuresFile)
     {
-      viewport.showSequenceFeatures = true;
+      viewport.setShowSequenceFeatures(true);
       showSeqFeatures.setSelected(true);
       if (alignPanel.getSeqPanel().seqCanvas.fr != null)
       {
index cff9e94..29d936f 100644 (file)
@@ -95,10 +95,6 @@ public class AlignViewport extends AlignmentViewport implements
 
   boolean renderGaps = true;
 
-  boolean showSequenceFeatures = false;
-
-  private boolean showAnnotation = true;
-
   SequenceAnnotationOrder sortAnnotationsBy = null;
 
   int charHeight;
@@ -125,12 +121,6 @@ public class AlignViewport extends AlignmentViewport implements
 
   boolean cursorMode = false;
 
-  /**
-   * Keys are the feature types which are currently visible. Note: Values are
-   * not used!
-   */
-  private Hashtable featuresDisplayed = null;
-
   boolean antiAlias = false;
 
   Rectangle explodedPosition;
@@ -148,7 +138,6 @@ public class AlignViewport extends AlignmentViewport implements
   Color textColour = Color.black;
 
   Color textColour2 = Color.white;
-
   private boolean rightAlignIds = false;
 
   private AnnotationColumnChooser currentAnnotationColumnSelectionState;
@@ -369,22 +358,6 @@ public class AlignViewport extends AlignmentViewport implements
   }
 
   /**
-   * set the flag
-   * 
-   * @param b
-   *          features are displayed if true
-   */
-  public void setShowSequenceFeatures(boolean b)
-  {
-    showSequenceFeatures = b;
-  }
-
-  public boolean getShowSequenceFeatures()
-  {
-    return showSequenceFeatures;
-  }
-
-  /**
    * centre columnar annotation labels in displayed alignment annotation TODO:
    * add to jalviewXML and annotation display settings
    */
@@ -838,27 +811,6 @@ public class AlignViewport extends AlignmentViewport implements
    * 
    * @return DOCUMENT ME!
    */
-  public boolean getShowAnnotation()
-  {
-    return isShowAnnotation();
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param b
-   *          DOCUMENT ME!
-   */
-  public void setShowAnnotation(boolean b)
-  {
-    showAnnotation = b;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
   public boolean getScaleAboveWrapped()
   {
     return scaleAboveWrapped;
@@ -1102,8 +1054,6 @@ public class AlignViewport extends AlignmentViewport implements
     return followSelection;
   }
 
-  boolean showSeqFeaturesHeight;
-
   public void sendSelection()
   {
     jalview.structure.StructureSelectionManager
@@ -1112,16 +1062,6 @@ public class AlignViewport extends AlignmentViewport implements
                     new ColumnSelection(getColumnSelection()), this);
   }
 
-  public void setShowSequenceFeaturesHeight(boolean selected)
-  {
-    showSeqFeaturesHeight = selected;
-  }
-
-  public boolean getShowSequenceFeaturesHeight()
-  {
-    return showSeqFeaturesHeight;
-  }
-
   /**
    * return the alignPanel containing the given viewport. Use this to get the
    * components currently handling the given viewport.
@@ -1275,16 +1215,6 @@ public class AlignViewport extends AlignmentViewport implements
     }
   }
 
-
-  public Hashtable getFeaturesDisplayed()
-  {
-    return featuresDisplayed;
-  }
-
-  public void setFeaturesDisplayed(Hashtable featuresDisplayed)
-  {
-    this.featuresDisplayed = featuresDisplayed;
-  }
   protected SequenceAnnotationOrder getSortAnnotationsBy()
   {
     return sortAnnotationsBy;
@@ -1305,9 +1235,10 @@ public class AlignViewport extends AlignmentViewport implements
     this.showAutocalculatedAbove = showAutocalculatedAbove;
   }
 
+
   public boolean isShowAnnotation()
   {
-    return showAnnotation;
+    return super.isShowAnnotation();
   }
 
   public boolean isRightAlignIds()
index 6c80464..e859fb1 100644 (file)
@@ -1553,18 +1553,23 @@ public class AlignmentPanel extends GAlignmentPanel implements
     new OOMWarning(string, error, this);
   }
 
-  public FeatureRenderer cloneFeatureRenderer()
+  @Override
+  public jalview.api.FeatureRenderer cloneFeatureRenderer()
   {
 
     return new FeatureRenderer(this);
   }
-
-  public void updateFeatureRenderer(FeatureRenderer fr)
+  @Override 
+  public jalview.api.FeatureRenderer getFeatureRenderer()
+  {
+    return seqPanel.seqCanvas.getFeatureRenderer();
+  }
+  public void updateFeatureRenderer(jalview.renderer.seqfeatures.FeatureRenderer fr)
   {
     fr.transferSettings(getSeqPanel().seqCanvas.getFeatureRenderer());
   }
 
-  public void updateFeatureRendererFrom(FeatureRenderer fr)
+  public void updateFeatureRendererFrom(jalview.api.FeatureRenderer fr)
   {
     if (getSeqPanel().seqCanvas.getFeatureRenderer() != null)
     {
index e966c92..e43c06b 100644 (file)
@@ -33,7 +33,6 @@ import java.awt.Color;
 import java.awt.FlowLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
 
@@ -128,14 +127,16 @@ public class AnnotationExporter extends JPanel
         if (GFFFormat.isSelected())
         {
           text = new FeaturesFile().printGFFFormat(ap.av.getAlignment()
-                  .getDataset().getSequencesArray(),
-                  getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());// ap.av.featuresDisplayed//);
+                  .getDataset().getSequencesArray(), ap
+                  .getFeatureRenderer().getDisplayedFeatureCols(), true,
+                  ap.av.isShowNpFeats());// ap.av.featuresDisplayed//);
         }
         else
         {
           text = new FeaturesFile().printJalviewFormat(ap.av.getAlignment()
-                  .getDataset().getSequencesArray(),
-                  getDisplayedFeatureCols(), true, ap.av.isShowNpFeats()); // ap.av.featuresDisplayed);
+                  .getDataset().getSequencesArray(), ap
+                  .getFeatureRenderer().getDisplayedFeatureCols(), true,
+                  ap.av.isShowNpFeats()); // ap.av.featuresDisplayed);
         }
       }
       else
@@ -175,14 +176,14 @@ public class AnnotationExporter extends JPanel
       if (GFFFormat.isSelected())
       {
         text = new FeaturesFile().printGFFFormat(ap.av.getAlignment()
-                .getDataset().getSequencesArray(),
-                getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());
+                .getDataset().getSequencesArray(), ap.getFeatureRenderer()
+                .getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());
       }
       else
       {
         text = new FeaturesFile().printJalviewFormat(ap.av.getAlignment()
-                .getDataset().getSequencesArray(),
-                getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());
+                .getDataset().getSequencesArray(), ap.getFeatureRenderer()
+                .getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());
       }
     }
     else if (!features)
@@ -222,28 +223,6 @@ public class AnnotationExporter extends JPanel
 
     close_actionPerformed(null);
   }
-
-  private Hashtable getDisplayedFeatureCols()
-  {
-    Hashtable fcols = new Hashtable();
-    if (ap.av.getFeaturesDisplayed() == null)
-    {
-      return fcols;
-    }
-
-    Enumeration en = ap.av.getFeaturesDisplayed().keys();
-    FeatureRenderer fr = ap.getSeqPanel().seqCanvas.getFeatureRenderer(); // consider
-                                                                     // higher
-                                                                     // level
-                                                                     // method ?
-    while (en.hasMoreElements())
-    {
-      Object col = en.nextElement();
-      fcols.put(col, fr.featureColours.get(col));
-    }
-    return fcols;
-  }
-
   public void close_actionPerformed(ActionEvent e)
   {
     try
index 4dfd18a..09b0fdd 100644 (file)
@@ -1074,7 +1074,7 @@ public class AppJmol extends GStructureViewer implements Runnable,
       // Set the colour using the current view for the associated alignframe
       for (AlignmentPanel ap : _colourwith)
       {
-        jmb.colourBySequence(ap.av.showSequenceFeatures, ap);
+        jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
       }
     }
   }
index 7fa300e..42a2f74 100644 (file)
@@ -58,11 +58,11 @@ public class AppJmolBinding extends jalview.ext.jmol.JalviewJmolBinding
   {
     AlignmentPanel ap = (alignment == null) ? appJmolWindow.ap
             : (AlignmentPanel) alignment;
-    if (ap.av.showSequenceFeatures)
+    if (ap.av.isShowSequenceFeatures())
     {
       if (fr == null)
       {
-        fr = ap.cloneFeatureRenderer();
+        fr = (jalview.gui.FeatureRenderer) ap.cloneFeatureRenderer();
       }
       else
       {
@@ -133,7 +133,7 @@ public class AppJmolBinding extends jalview.ext.jmol.JalviewJmolBinding
       return;
     if (!isLoadingFromArchive())
     {
-      colourBySequence(ap.av.getShowSequenceFeatures(), ap);
+      colourBySequence(ap.av.isShowSequenceFeatures(), ap);
     }
   }
 
index 74d4aac..48123fa 100644 (file)
@@ -984,7 +984,7 @@ public class ChimeraViewFrame extends GStructureViewer implements Runnable,
       // Set the colour using the current view for the associated alignframe
       for (AlignmentPanel ap : _colourwith)
       {
-        jmb.colourBySequence(ap.av.showSequenceFeatures, ap);
+        jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
       }
     }
   }
index 8ab7c85..d1d1b6d 100644 (file)
@@ -109,10 +109,10 @@ public class FeatureColourChooser extends JalviewDialog
       }
     });
 
-    float mm[] = ((float[][]) fr.minmax.get(type))[0];
+    float mm[] = ((float[][]) fr.getMinMax().get(type))[0];
     min = mm[0];
     max = mm[1];
-    oldcs = fr.featureColours.get(type);
+    oldcs = fr.getFeatureColours().get(type);
     if (oldcs instanceof GraduatedColor)
     {
       if (((GraduatedColor) oldcs).isAutoScale())
@@ -470,7 +470,7 @@ public class FeatureColourChooser extends JalviewDialog
       maxColour.setForeground(oldmaxColour);
       minColour.setForeground(oldminColour);
     }
-    fr.featureColours.put(type, acg);
+    fr.setColour(type, acg);
     cs = acg;
     ap.paintAlignment(false);
   }
@@ -495,7 +495,7 @@ public class FeatureColourChooser extends JalviewDialog
 
   void reset()
   {
-    fr.featureColours.put(type, oldcs);
+    fr.setColour(type, oldcs);
     ap.paintAlignment(false);
     cs = null;
   }
index 80f1255..3ce831e 100644 (file)
  */
 package jalview.gui;
 
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.*;
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
-
-import javax.swing.*;
-
-import jalview.datamodel.*;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
 import jalview.schemes.GraduatedColor;
 import jalview.util.MessageManager;
 
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.JColorChooser;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSpinner;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+
 /**
  * DOCUMENT ME!
  * 
  * @author $author$
  * @version $Revision$
  */
-public class FeatureRenderer implements jalview.api.FeatureRenderer
+public class FeatureRenderer extends jalview.renderer.seqfeatures.FeatureRenderer implements jalview.api.FeatureRenderer
 {
-  AlignmentPanel ap;
-
-  AlignViewport av;
-
   Color resBoxColour;
 
-  /**
-   * global transparency for feature
-   */
-  float transparency = 1.0f;
-
-  FontMetrics fm;
-
-  int charOffset;
-
-  Map featureColours = new ConcurrentHashMap();
-
-  // A higher level for grouping features of a
-  // particular type
-  Map featureGroups = new ConcurrentHashMap();
-
-  // This is actually an Integer held in the hashtable,
-  // Retrieved using the key feature type
-  Object currentColour;
-
-  String[] renderOrder;
-
-  PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
-
-  Vector allfeatures;
+  AlignmentPanel ap;
 
   /**
    * Creates a new FeatureRenderer object.
@@ -82,6 +69,7 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
    */
   public FeatureRenderer(AlignmentPanel ap)
   {
+    super();
     this.ap = ap;
     this.av = ap.av;
     if (ap != null && ap.getSeqPanel() != null && ap.getSeqPanel().seqCanvas != null
@@ -91,895 +79,6 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
     }
   }
 
-  public class FeatureRendererSettings implements Cloneable
-  {
-    String[] renderOrder;
-
-    Map featureGroups;
-
-    Map featureColours;
-
-    float transparency;
-
-    Map featureOrder;
-
-    public FeatureRendererSettings(String[] renderOrder,
-            Hashtable featureGroups, Hashtable featureColours,
-            float transparency, Hashtable featureOrder)
-    {
-      super();
-      this.renderOrder = renderOrder;
-      this.featureGroups = featureGroups;
-      this.featureColours = featureColours;
-      this.transparency = transparency;
-      this.featureOrder = featureOrder;
-    }
-
-    /**
-     * create an independent instance of the feature renderer settings
-     * 
-     * @param fr
-     */
-    public FeatureRendererSettings(FeatureRenderer fr)
-    {
-      renderOrder = null;
-      featureGroups = new ConcurrentHashMap();
-      featureColours = new ConcurrentHashMap();
-      featureOrder = new ConcurrentHashMap();
-      if (fr.renderOrder != null)
-      {
-        this.renderOrder = new String[fr.renderOrder.length];
-        System.arraycopy(fr.renderOrder, 0, renderOrder, 0,
-                fr.renderOrder.length);
-      }
-      if (fr.featureGroups != null)
-      {
-        this.featureGroups = new ConcurrentHashMap(fr.featureGroups);
-      }
-      if (fr.featureColours != null)
-      {
-        this.featureColours = new ConcurrentHashMap(fr.featureColours);
-      }
-      Iterator en = fr.featureColours.keySet().iterator();
-      while (en.hasNext())
-      {
-        Object next = en.next();
-        Object val = featureColours.get(next);
-        if (val instanceof GraduatedColor)
-        {
-          featureColours
-                  .put(next, new GraduatedColor((GraduatedColor) val));
-        }
-      }
-      this.transparency = fr.transparency;
-      if (fr.featureOrder != null)
-      {
-        this.featureOrder = new ConcurrentHashMap(fr.featureOrder);
-      }
-    }
-  }
-
-  public FeatureRendererSettings getSettings()
-  {
-    return new FeatureRendererSettings(this);
-  }
-
-  public void transferSettings(FeatureRendererSettings fr)
-  {
-    this.renderOrder = fr.renderOrder;
-    this.featureGroups = fr.featureGroups;
-    this.featureColours = fr.featureColours;
-    this.transparency = fr.transparency;
-    this.featureOrder = fr.featureOrder;
-  }
-
-  /**
-   * update from another feature renderer
-   * 
-   * @param fr
-   *          settings to copy
-   */
-  public void transferSettings(FeatureRenderer fr)
-  {
-    FeatureRendererSettings frs = new FeatureRendererSettings(fr);
-    this.renderOrder = frs.renderOrder;
-    this.featureGroups = frs.featureGroups;
-    this.featureColours = frs.featureColours;
-    this.transparency = frs.transparency;
-    this.featureOrder = frs.featureOrder;
-    if (av != null && av != fr.av)
-    {
-      // copy over the displayed feature settings
-      if (fr.av != null)
-      {
-        if (fr.av.getFeaturesDisplayed() != null)
-        {
-          // update display settings
-          if (av.getFeaturesDisplayed() == null)
-          {
-            av.setFeaturesDisplayed(new Hashtable(fr.av.getFeaturesDisplayed()));
-          }
-          else
-          {
-            av.getFeaturesDisplayed().clear();
-            Enumeration en = fr.av.getFeaturesDisplayed().keys();
-            while (en.hasMoreElements())
-            {
-              av.getFeaturesDisplayed().put(en.nextElement(), Boolean.TRUE);
-            }
-
-          }
-        }
-      }
-    }
-  }
-
-  BufferedImage offscreenImage;
-
-  boolean offscreenRender = false;
-
-  public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
-  {
-    return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
-  }
-
-  /**
-   * This is used by the Molecule Viewer and Overview to get the accurate
-   * colourof the rendered sequence
-   */
-  public synchronized int findFeatureColour(int initialCol, SequenceI seq,
-          int column)
-  {
-    if (!av.showSequenceFeatures)
-    {
-      return initialCol;
-    }
-
-    if (seq != lastSeq)
-    {
-      lastSeq = seq;
-      sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
-      if (sequenceFeatures != null)
-      {
-        sfSize = sequenceFeatures.length;
-      }
-    }
-
-    if (sequenceFeatures != lastSeq.getDatasetSequence()
-            .getSequenceFeatures())
-    {
-      sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
-      if (sequenceFeatures != null)
-      {
-        sfSize = sequenceFeatures.length;
-      }
-    }
-
-    if (sequenceFeatures == null || sfSize == 0)
-    {
-      return initialCol;
-    }
-
-    if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
-    {
-      return Color.white.getRGB();
-    }
-
-    // Only bother making an offscreen image if transparency is applied
-    if (transparency != 1.0f && offscreenImage == null)
-    {
-      offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
-    }
-
-    currentColour = null;
-    // TODO: non-threadsafe - each rendering thread needs its own instance of
-    // the feature renderer - or this should be synchronized.
-    offscreenRender = true;
-
-    if (offscreenImage != null)
-    {
-      offscreenImage.setRGB(0, 0, initialCol);
-      drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
-
-      return offscreenImage.getRGB(0, 0);
-    }
-    else
-    {
-      drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
-
-      if (currentColour == null)
-      {
-        return initialCol;
-      }
-      else
-      {
-        return ((Integer) currentColour).intValue();
-      }
-    }
-
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param g
-   *          DOCUMENT ME!
-   * @param seq
-   *          DOCUMENT ME!
-   * @param sg
-   *          DOCUMENT ME!
-   * @param start
-   *          DOCUMENT ME!
-   * @param end
-   *          DOCUMENT ME!
-   * @param x1
-   *          DOCUMENT ME!
-   * @param y1
-   *          DOCUMENT ME!
-   * @param width
-   *          DOCUMENT ME!
-   * @param height
-   *          DOCUMENT ME!
-   */
-  // String type;
-  // SequenceFeature sf;
-  SequenceI lastSeq;
-
-  SequenceFeature[] sequenceFeatures;
-
-  int sfSize, sfindex, spos, epos;
-
-  /**
-   * show scores as heights
-   */
-  protected boolean varyHeight = false;
-
-  synchronized public void drawSequence(Graphics g, SequenceI seq,
-          int start, int end, int y1)
-  {
-
-    if (seq.getDatasetSequence().getSequenceFeatures() == null
-            || seq.getDatasetSequence().getSequenceFeatures().length == 0)
-    {
-      return;
-    }
-
-    if (g != null)
-    {
-      fm = g.getFontMetrics();
-    }
-
-    if (av.getFeaturesDisplayed() == null || renderOrder == null
-            || newFeatureAdded)
-    {
-      findAllFeatures();
-      if (av.getFeaturesDisplayed().size() < 1)
-      {
-        return;
-      }
-
-      sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
-    }
-
-    if (lastSeq == null
-            || seq != lastSeq
-            || seq.getDatasetSequence().getSequenceFeatures() != sequenceFeatures)
-    {
-      lastSeq = seq;
-      sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
-    }
-
-    if (transparency != 1 && g != null)
-    {
-      Graphics2D g2 = (Graphics2D) g;
-      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
-              transparency));
-    }
-
-    if (!offscreenRender)
-    {
-      spos = lastSeq.findPosition(start);
-      epos = lastSeq.findPosition(end);
-    }
-
-    sfSize = sequenceFeatures.length;
-    String type;
-    for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
-    {
-      type = renderOrder[renderIndex];
-
-      if (type == null || !av.getFeaturesDisplayed().containsKey(type))
-      {
-        continue;
-      }
-
-      // loop through all features in sequence to find
-      // current feature to render
-      for (sfindex = 0; sfindex < sfSize; sfindex++)
-      {
-        if (!sequenceFeatures[sfindex].type.equals(type))
-        {
-          continue;
-        }
-
-        if (featureGroups != null
-                && sequenceFeatures[sfindex].featureGroup != null
-                && sequenceFeatures[sfindex].featureGroup.length() != 0
-                && featureGroups
-                        .containsKey(sequenceFeatures[sfindex].featureGroup)
-                && !((Boolean) featureGroups
-                        .get(sequenceFeatures[sfindex].featureGroup))
-                        .booleanValue())
-        {
-          continue;
-        }
-
-        if (!offscreenRender
-                && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
-                        .getEnd() < spos))
-        {
-          continue;
-        }
-
-        if (offscreenRender && offscreenImage == null)
-        {
-          if (sequenceFeatures[sfindex].begin <= start
-                  && sequenceFeatures[sfindex].end >= start)
-          {
-            // this is passed out to the overview and other sequence renderers
-            // (e.g. molecule viewer) to get displayed colour for rendered
-            // sequence
-            currentColour = new Integer(
-                    getColour(sequenceFeatures[sfindex]).getRGB());
-            // used to be retreived from av.featuresDisplayed
-            // currentColour = av.featuresDisplayed
-            // .get(sequenceFeatures[sfindex].type);
-
-          }
-        }
-        else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
-        {
-
-          renderFeature(g, seq,
-                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                  getColour(sequenceFeatures[sfindex])
-                  // new Color(((Integer) av.featuresDisplayed
-                  // .get(sequenceFeatures[sfindex].type)).intValue())
-                  , start, end, y1);
-          renderFeature(g, seq,
-                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                  getColour(sequenceFeatures[sfindex])
-                  // new Color(((Integer) av.featuresDisplayed
-                  // .get(sequenceFeatures[sfindex].type)).intValue())
-                  , start, end, y1);
-
-        }
-        else if (showFeature(sequenceFeatures[sfindex]))
-        {
-          if (av.showSeqFeaturesHeight
-                  && sequenceFeatures[sfindex].score != Float.NaN)
-          {
-            renderScoreFeature(g, seq,
-                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                    getColour(sequenceFeatures[sfindex]), start, end, y1,
-                    normaliseScore(sequenceFeatures[sfindex]));
-          }
-          else
-          {
-            renderFeature(g, seq,
-                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                    getColour(sequenceFeatures[sfindex]), start, end, y1);
-          }
-        }
-
-      }
-
-    }
-
-    if (transparency != 1.0f && g != null)
-    {
-      Graphics2D g2 = (Graphics2D) g;
-      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
-              1.0f));
-    }
-  }
-
-  Hashtable minmax = new Hashtable();
-
-  /**
-   * normalise a score against the max/min bounds for the feature type.
-   * 
-   * @param sequenceFeature
-   * @return byte[] { signed, normalised signed (-127 to 127) or unsigned
-   *         (0-255) value.
-   */
-  private final byte[] normaliseScore(SequenceFeature sequenceFeature)
-  {
-    float[] mm = ((float[][]) minmax.get(sequenceFeature.type))[0];
-    final byte[] r = new byte[]
-    { 0, (byte) 255 };
-    if (mm != null)
-    {
-      if (r[0] != 0 || mm[0] < 0.0)
-      {
-        r[0] = 1;
-        r[1] = (byte) ((int) 128.0 + 127.0 * (sequenceFeature.score / mm[1]));
-      }
-      else
-      {
-        r[1] = (byte) ((int) 255.0 * (sequenceFeature.score / mm[1]));
-      }
-    }
-    return r;
-  }
-
-  char s;
-
-  int i;
-
-  void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
-          Color featureColour, int start, int end, int y1)
-  {
-
-    if (((fstart <= end) && (fend >= start)))
-    {
-      if (fstart < start)
-      { // fix for if the feature we have starts before the sequence start,
-        fstart = start; // but the feature end is still valid!!
-      }
-
-      if (fend >= end)
-      {
-        fend = end;
-      }
-      int pady = (y1 + av.charHeight) - av.charHeight / 5;
-      for (i = fstart; i <= fend; i++)
-      {
-        s = seq.getCharAt(i);
-
-        if (jalview.util.Comparison.isGap(s))
-        {
-          continue;
-        }
-
-        g.setColor(featureColour);
-
-        g.fillRect((i - start) * av.charWidth, y1, av.charWidth,
-                av.charHeight);
-
-        if (offscreenRender || !av.validCharWidth)
-        {
-          continue;
-        }
-
-        g.setColor(Color.white);
-        charOffset = (av.charWidth - fm.charWidth(s)) / 2;
-        g.drawString(String.valueOf(s), charOffset
-                + (av.charWidth * (i - start)), pady);
-
-      }
-    }
-  }
-
-  void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
-          Color featureColour, int start, int end, int y1, byte[] bs)
-  {
-
-    if (((fstart <= end) && (fend >= start)))
-    {
-      if (fstart < start)
-      { // fix for if the feature we have starts before the sequence start,
-        fstart = start; // but the feature end is still valid!!
-      }
-
-      if (fend >= end)
-      {
-        fend = end;
-      }
-      int pady = (y1 + av.charHeight) - av.charHeight / 5;
-      int ystrt = 0, yend = av.charHeight;
-      if (bs[0] != 0)
-      {
-        // signed - zero is always middle of residue line.
-        if (bs[1] < 128)
-        {
-          yend = av.charHeight * (128 - bs[1]) / 512;
-          ystrt = av.charHeight - yend / 2;
-        }
-        else
-        {
-          ystrt = av.charHeight / 2;
-          yend = av.charHeight * (bs[1] - 128) / 512;
-        }
-      }
-      else
-      {
-        yend = av.charHeight * bs[1] / 255;
-        ystrt = av.charHeight - yend;
-
-      }
-      for (i = fstart; i <= fend; i++)
-      {
-        s = seq.getCharAt(i);
-
-        if (jalview.util.Comparison.isGap(s))
-        {
-          continue;
-        }
-
-        g.setColor(featureColour);
-        int x = (i - start) * av.charWidth;
-        g.drawRect(x, y1, av.charWidth, av.charHeight);
-        g.fillRect(x, y1 + ystrt, av.charWidth, yend);
-
-        if (offscreenRender || !av.validCharWidth)
-        {
-          continue;
-        }
-
-        g.setColor(Color.black);
-        charOffset = (av.charWidth - fm.charWidth(s)) / 2;
-        g.drawString(String.valueOf(s), charOffset
-                + (av.charWidth * (i - start)), pady);
-
-      }
-    }
-  }
-
-  boolean newFeatureAdded = false;
-
-  /**
-   * Called when alignment in associated view has new/modified features to
-   * discover and display.
-   * 
-   */
-  public void featuresAdded()
-  {
-    lastSeq = null;
-    findAllFeatures();
-  }
-
-  boolean findingFeatures = false;
-
-  /**
-   * search the alignment for all new features, give them a colour and display
-   * them. Then fires a PropertyChangeEvent on the changeSupport object.
-   * 
-   */
-  void findAllFeatures()
-  {
-    synchronized (firing)
-    {
-      if (firing.equals(Boolean.FALSE))
-      {
-        firing = Boolean.TRUE;
-        findAllFeatures(true); // add all new features as visible
-        changeSupport.firePropertyChange("changeSupport", null, null);
-        firing = Boolean.FALSE;
-      }
-    }
-  }
-
-  /**
-   * Searches alignment for all features and updates colours
-   * 
-   * @param newMadeVisible
-   *          if true newly added feature types will be rendered immediatly
-   */
-  synchronized void findAllFeatures(boolean newMadeVisible)
-  {
-    newFeatureAdded = false;
-
-    if (findingFeatures)
-    {
-      newFeatureAdded = true;
-      return;
-    }
-
-    findingFeatures = true;
-
-    if (av.getFeaturesDisplayed() == null)
-    {
-      av.setFeaturesDisplayed(new Hashtable());
-    }
-
-    allfeatures = new Vector();
-    Vector oldfeatures = new Vector();
-    if (renderOrder != null)
-    {
-      for (int i = 0; i < renderOrder.length; i++)
-      {
-        if (renderOrder[i] != null)
-        {
-          oldfeatures.addElement(renderOrder[i]);
-        }
-      }
-    }
-    if (minmax == null)
-    {
-      minmax = new Hashtable();
-    }
-    AlignmentI alignment = av.getAlignment();
-    for (int i = 0; i < alignment.getHeight(); i++)
-    {
-      SequenceFeature[] features = alignment.getSequenceAt(i)
-              .getDatasetSequence().getSequenceFeatures();
-
-      if (features == null)
-      {
-        continue;
-      }
-
-      int index = 0;
-      while (index < features.length)
-      {
-        if (!av.getFeaturesDisplayed().containsKey(features[index].getType()))
-        {
-
-          if (featureGroups.containsKey(features[index].getType()))
-          {
-            boolean visible = ((Boolean) featureGroups
-                    .get(features[index].featureGroup)).booleanValue();
-
-            if (!visible)
-            {
-              index++;
-              continue;
-            }
-          }
-
-          if (!(features[index].begin == 0 && features[index].end == 0))
-          {
-            // If beginning and end are 0, the feature is for the whole sequence
-            // and we don't want to render the feature in the normal way
-
-            if (newMadeVisible
-                    && !oldfeatures.contains(features[index].getType()))
-            {
-              // this is a new feature type on the alignment. Mark it for
-              // display.
-              av.getFeaturesDisplayed().put(features[index].getType(),
-                      new Integer(getColour(features[index].getType())
-                              .getRGB()));
-              setOrder(features[index].getType(), 0);
-            }
-          }
-        }
-        if (!allfeatures.contains(features[index].getType()))
-        {
-          allfeatures.addElement(features[index].getType());
-        }
-        if (features[index].score != Float.NaN)
-        {
-          int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
-          float[][] mm = (float[][]) minmax.get(features[index].getType());
-          if (mm == null)
-          {
-            mm = new float[][]
-            { null, null };
-            minmax.put(features[index].getType(), mm);
-          }
-          if (mm[nonpos] == null)
-          {
-            mm[nonpos] = new float[]
-            { features[index].score, features[index].score };
-
-          }
-          else
-          {
-            if (mm[nonpos][0] > features[index].score)
-            {
-              mm[nonpos][0] = features[index].score;
-            }
-            if (mm[nonpos][1] < features[index].score)
-            {
-              mm[nonpos][1] = features[index].score;
-            }
-          }
-        }
-        index++;
-      }
-    }
-    updateRenderOrder(allfeatures);
-    findingFeatures = false;
-  }
-
-  protected Boolean firing = Boolean.FALSE;
-
-  /**
-   * replaces the current renderOrder with the unordered features in
-   * allfeatures. The ordering of any types in both renderOrder and allfeatures
-   * is preserved, and all new feature types are rendered on top of the existing
-   * types, in the order given by getOrder or the order given in allFeatures.
-   * Note. this operates directly on the featureOrder hash for efficiency. TODO:
-   * eliminate the float storage for computing/recalling the persistent ordering
-   * New Cability: updates min/max for colourscheme range if its dynamic
-   * 
-   * @param allFeatures
-   */
-  private void updateRenderOrder(Vector allFeatures)
-  {
-    Vector allfeatures = new Vector(allFeatures);
-    String[] oldRender = renderOrder;
-    renderOrder = new String[allfeatures.size()];
-    Object mmrange, fc = null;
-    boolean initOrders = (featureOrder == null);
-    int opos = 0;
-    if (oldRender != null && oldRender.length > 0)
-    {
-      for (int j = 0; j < oldRender.length; j++)
-      {
-        if (oldRender[j] != null)
-        {
-          if (initOrders)
-          {
-            setOrder(oldRender[j], (1 - (1 + (float) j)
-                    / (float) oldRender.length));
-          }
-          if (allfeatures.contains(oldRender[j]))
-          {
-            renderOrder[opos++] = oldRender[j]; // existing features always
-            // appear below new features
-            allfeatures.removeElement(oldRender[j]);
-            if (minmax != null)
-            {
-              mmrange = minmax.get(oldRender[j]);
-              if (mmrange != null)
-              {
-                fc = featureColours.get(oldRender[j]);
-                if (fc != null && fc instanceof GraduatedColor
-                        && ((GraduatedColor) fc).isAutoScale())
-                {
-                  ((GraduatedColor) fc).updateBounds(
-                          ((float[][]) mmrange)[0][0],
-                          ((float[][]) mmrange)[0][1]);
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-    if (allfeatures.size() == 0)
-    {
-      // no new features - leave order unchanged.
-      return;
-    }
-    int i = allfeatures.size() - 1;
-    int iSize = i;
-    boolean sort = false;
-    String[] newf = new String[allfeatures.size()];
-    float[] sortOrder = new float[allfeatures.size()];
-    Enumeration en = allfeatures.elements();
-    // sort remaining elements
-    while (en.hasMoreElements())
-    {
-      newf[i] = en.nextElement().toString();
-      if (minmax != null)
-      {
-        // update from new features minmax if necessary
-        mmrange = minmax.get(newf[i]);
-        if (mmrange != null)
-        {
-          fc = featureColours.get(newf[i]);
-          if (fc != null && fc instanceof GraduatedColor
-                  && ((GraduatedColor) fc).isAutoScale())
-          {
-            ((GraduatedColor) fc).updateBounds(((float[][]) mmrange)[0][0],
-                    ((float[][]) mmrange)[0][1]);
-          }
-        }
-      }
-      if (initOrders || !featureOrder.containsKey(newf[i]))
-      {
-        int denom = initOrders ? allfeatures.size() : featureOrder.size();
-        // new unordered feature - compute persistent ordering at head of
-        // existing features.
-        setOrder(newf[i], i / (float) denom);
-      }
-      // set order from newly found feature from persisted ordering.
-      sortOrder[i] = 2 - ((Float) featureOrder.get(newf[i])).floatValue();
-      if (i < iSize)
-      {
-        // only sort if we need to
-        sort = sort || sortOrder[i] > sortOrder[i + 1];
-      }
-      i--;
-    }
-    if (iSize > 1 && sort)
-    {
-      jalview.util.QuickSort.sort(sortOrder, newf);
-    }
-    sortOrder = null;
-    System.arraycopy(newf, 0, renderOrder, opos, newf.length);
-  }
-
-  /**
-   * get a feature style object for the given type string. Creates a
-   * java.awt.Color for a featureType with no existing colourscheme. TODO:
-   * replace return type with object implementing standard abstract colour/style
-   * interface
-   * 
-   * @param featureType
-   * @return java.awt.Color or GraduatedColor
-   */
-  public Object getFeatureStyle(String featureType)
-  {
-    Object fc = featureColours.get(featureType);
-    if (fc == null)
-    {
-      jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
-      Color col = ucs.createColourFromName(featureType);
-      featureColours.put(featureType, fc = col);
-    }
-    return fc;
-  }
-
-  /**
-   * return a nominal colour for this feature
-   * 
-   * @param featureType
-   * @return standard color, or maximum colour for graduated colourscheme
-   */
-  public Color getColour(String featureType)
-  {
-    Object fc = getFeatureStyle(featureType);
-
-    if (fc instanceof Color)
-    {
-      return (Color) fc;
-    }
-    else
-    {
-      if (fc instanceof GraduatedColor)
-      {
-        return ((GraduatedColor) fc).getMaxColor();
-      }
-    }
-    throw new Error(MessageManager.formatMessage("error.implementation_error_unrecognised_render_object_for_features_type", new String[]{fc.getClass().toString(),featureType}));
-  }
-
-  /**
-   * calculate the render colour for a specific feature using current feature
-   * settings.
-   * 
-   * @param feature
-   * @return render colour for the given feature
-   */
-  public Color getColour(SequenceFeature feature)
-  {
-    Object fc = getFeatureStyle(feature.getType());
-    if (fc instanceof Color)
-    {
-      return (Color) fc;
-    }
-    else
-    {
-      if (fc instanceof GraduatedColor)
-      {
-        return ((GraduatedColor) fc).findColor(feature);
-      }
-    }
-    throw new Error(MessageManager.formatMessage("error.implementation_error_unrecognised_render_object_for_features_type", new String[]{fc.getClass().toString(),feature.getType()}));
-  }
-
-  private boolean showFeature(SequenceFeature sequenceFeature)
-  {
-    Object fc = getFeatureStyle(sequenceFeature.type);
-    if (fc instanceof GraduatedColor)
-    {
-      return ((GraduatedColor) fc).isColored(sequenceFeature);
-    }
-    else
-    {
-      return true;
-    }
-  }
-
   // // /////////////
   // // Feature Editing Dialog
   // // Will be refactored in next release.
@@ -1235,7 +334,9 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
       lastDescriptionAdded = description.getText().replaceAll("\n", " ");
       // TODO: determine if the null feature group is valid
       if (lastFeatureGroupAdded.length() < 1)
+      {
         lastFeatureGroupAdded = null;
+      }
     }
 
     if (!newFeatures)
@@ -1253,7 +354,7 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
         sf.description = lastDescriptionAdded;
 
         setColour(sf.type, fcol);
-        av.getFeaturesDisplayed().put(sf.type, getColour(sf.type));
+        getFeaturesDisplayed().setVisible(sf.type);
 
         try
         {
@@ -1274,27 +375,19 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
         for (int i = 0; i < sequences.length; i++)
         {
           features[i].type = lastFeatureAdded;
-          if (lastFeatureGroupAdded != null)
-            features[i].featureGroup = lastFeatureGroupAdded;
+          // fix for JAL-1538 - always set feature group here
+          features[i].featureGroup = lastFeatureGroupAdded;
           features[i].description = lastDescriptionAdded;
           sequences[i].addSequenceFeature(features[i]);
           ffile.parseDescriptionHTML(features[i], false);
         }
 
-        if (av.getFeaturesDisplayed() == null)
-        {
-          av.setFeaturesDisplayed(new Hashtable());
-        }
-
         if (lastFeatureGroupAdded != null)
         {
-          if (featureGroups == null)
-            featureGroups = new Hashtable();
-          featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
+          setGroupVisibility(lastFeatureGroupAdded, true);
         }
         setColour(lastFeatureAdded, fcol);
-        av.getFeaturesDisplayed().put(lastFeatureAdded,
-                getColour(lastFeatureAdded));
+        setVisible(lastFeatureAdded);
 
         findAllFeatures(false);
 
@@ -1313,6 +406,7 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
     return true;
   }
 
+
   /**
    * update the amend feature button dependent on the given style
    * 
@@ -1340,145 +434,4 @@ public class FeatureRenderer implements jalview.api.FeatureRenderer
       // colour.setForeground(colour.getBackground());
     }
   }
-
-  public void setColour(String featureType, Object col)
-  {
-    // overwrite
-    // Color _col = (col instanceof Color) ? ((Color) col) : (col instanceof
-    // GraduatedColor) ? ((GraduatedColor) col).getMaxColor() : null;
-    // Object c = featureColours.get(featureType);
-    // if (c == null || c instanceof Color || (c instanceof GraduatedColor &&
-    // !((GraduatedColor)c).getMaxColor().equals(_col)))
-    {
-      featureColours.put(featureType, col);
-    }
-  }
-
-  public void setTransparency(float value)
-  {
-    transparency = value;
-  }
-
-  public float getTransparency()
-  {
-    return transparency;
-  }
-
-  /**
-   * Replace current ordering with new ordering
-   * 
-   * @param data
-   *          { String(Type), Colour(Type), Boolean(Displayed) }
-   */
-  public void setFeaturePriority(Object[][] data)
-  {
-    setFeaturePriority(data, true);
-  }
-
-  /**
-   * 
-   * @param data
-   *          { String(Type), Colour(Type), Boolean(Displayed) }
-   * @param visibleNew
-   *          when true current featureDisplay list will be cleared
-   */
-  public void setFeaturePriority(Object[][] data, boolean visibleNew)
-  {
-    if (visibleNew)
-    {
-      if (av.getFeaturesDisplayed() != null)
-      {
-        av.getFeaturesDisplayed().clear();
-      }
-      else
-      {
-        av.setFeaturesDisplayed(new Hashtable());
-      }
-    }
-    if (data == null)
-    {
-      return;
-    }
-
-    // The feature table will display high priority
-    // features at the top, but theses are the ones
-    // we need to render last, so invert the data
-    renderOrder = new String[data.length];
-
-    if (data.length > 0)
-    {
-      for (int i = 0; i < data.length; i++)
-      {
-        String type = data[i][0].toString();
-        setColour(type, data[i][1]); // todo : typesafety - feature color
-        // interface object
-        if (((Boolean) data[i][2]).booleanValue())
-        {
-          av.getFeaturesDisplayed().put(type, new Integer(getColour(type)
-                  .getRGB()));
-        }
-
-        renderOrder[data.length - i - 1] = type;
-      }
-    }
-
-  }
-
-  Map featureOrder = null;
-
-  /**
-   * analogous to colour - store a normalized ordering for all feature types in
-   * this rendering context.
-   * 
-   * @param type
-   *          Feature type string
-   * @param position
-   *          normalized priority - 0 means always appears on top, 1 means
-   *          always last.
-   */
-  public float setOrder(String type, float position)
-  {
-    if (featureOrder == null)
-    {
-      featureOrder = new Hashtable();
-    }
-    featureOrder.put(type, new Float(position));
-    return position;
-  }
-
-  /**
-   * get the global priority (0 (top) to 1 (bottom))
-   * 
-   * @param type
-   * @return [0,1] or -1 for a type without a priority
-   */
-  public float getOrder(String type)
-  {
-    if (featureOrder != null)
-    {
-      if (featureOrder.containsKey(type))
-      {
-        return ((Float) featureOrder.get(type)).floatValue();
-      }
-    }
-    return -1;
-  }
-
-  /**
-   * @param listener
-   * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
-   */
-  public void addPropertyChangeListener(PropertyChangeListener listener)
-  {
-    changeSupport.addPropertyChangeListener(listener);
-  }
-
-  /**
-   * @param listener
-   * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
-   */
-  public void removePropertyChangeListener(PropertyChangeListener listener)
-  {
-    changeSupport.removePropertyChangeListener(listener);
-  }
 }
index 0f14bed..3475fe3 100644 (file)
  */
 package jalview.gui;
 
-import jalview.analysis.AlignmentSorter;
 import jalview.bin.Cache;
-import jalview.commands.OrderCommand;
-import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.gui.Help.HelpId;
 import jalview.io.JalviewFileChooser;
@@ -56,10 +52,10 @@ import java.io.FileOutputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.Vector;
 
 import javax.help.HelpSetException;
@@ -107,6 +103,8 @@ public class FeatureSettings extends JPanel
 
   Object[][] originalData;
 
+  private float originalTransparency;
+
   final JInternalFrame frame;
 
   JScrollPane scrollPane = new JScrollPane();
@@ -123,8 +121,8 @@ public class FeatureSettings extends JPanel
   {
     this.af = af;
     fr = af.getFeatureRenderer();
-
-    transparency.setMaximum(100 - (int) (fr.transparency * 100));
+    // allow transparency to be recovered
+    transparency.setMaximum(100 - (int) ((originalTransparency=fr.getTransparency()) * 100));
 
     try
     {
@@ -165,8 +163,8 @@ public class FeatureSettings extends JPanel
         if (SwingUtilities.isRightMouseButton(evt))
         {
           popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
-                  table.getValueAt(selectedRow, 1), fr.minmax, evt.getX(),
-                  evt.getY());
+                  table.getValueAt(selectedRow, 1), fr.getMinMax(),
+                  evt.getX(), evt.getY());
         }
         else if (evt.getClickCount() == 2)
         {
@@ -185,7 +183,8 @@ public class FeatureSettings extends JPanel
         if (evt.isPopupTrigger())
         {
           popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
-                  table.getValueAt(selectedRow, 1), fr.minmax, evt.getX(),
+                  table.getValueAt(selectedRow, 1), fr.getMinMax(),
+                  evt.getX(),
                   evt.getY());
         }
       }
@@ -222,8 +221,7 @@ public class FeatureSettings extends JPanel
     dassourceBrowser = new DasSourceBrowser(this);
     dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
 
-    if (af.getViewport().getFeaturesDisplayed() == null
-            || fr.renderOrder == null)
+    if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
     {
       fr.findAllFeatures(true); // display everything!
     }
@@ -288,7 +286,7 @@ public class FeatureSettings extends JPanel
 
       public void actionPerformed(ActionEvent e)
       {
-        me.sortByScore(new String[]
+        me.af.avc.sortAlignmentByFeatureScore(new String[]
         { type });
       }
 
@@ -300,7 +298,7 @@ public class FeatureSettings extends JPanel
 
       public void actionPerformed(ActionEvent e)
       {
-        me.sortByDens(new String[]
+        me.af.avc.sortAlignmentByFeatureDensity(new String[]
         { type });
       }
 
@@ -424,10 +422,6 @@ public class FeatureSettings extends JPanel
 
   synchronized public void setTableData()
   {
-    if (fr.featureGroups == null)
-    {
-      fr.featureGroups = new Hashtable();
-    }
     Vector allFeatures = new Vector();
     Vector allGroups = new Vector();
     SequenceFeature[] tmpfeatures;
@@ -458,10 +452,7 @@ public class FeatureSettings extends JPanel
           if (!allGroups.contains(group))
           {
             allGroups.addElement(group);
-            if (group != null)
-            {
-              checkGroupState(group);
-            }
+            checkGroupState(group);
           }
         }
 
@@ -479,21 +470,14 @@ public class FeatureSettings extends JPanel
   }
 
   /**
+   * Synchronise gui group list and check visibility of group
    * 
    * @param group
-   * @return true if group has been seen before and is already added to set.
+   * @return true if group is visible
    */
   private boolean checkGroupState(String group)
   {
-    boolean visible;
-    if (fr.featureGroups.containsKey(group))
-    {
-      visible = ((Boolean) fr.featureGroups.get(group)).booleanValue();
-    }
-    else
-    {
-      visible = true; // new group is always made visible
-    }
+    boolean visible = fr.checkGroupVisibility(group, true);
 
     if (groupPanel == null)
     {
@@ -514,10 +498,8 @@ public class FeatureSettings extends JPanel
     if (alreadyAdded)
     {
 
-      return true;
+      return visible;
     }
-
-    fr.featureGroups.put(group, new Boolean(visible));
     final String grp = group;
     final JCheckBox check = new JCheckBox(group, visible);
     check.setFont(new Font("Serif", Font.BOLD, 12));
@@ -525,8 +507,7 @@ public class FeatureSettings extends JPanel
     {
       public void itemStateChanged(ItemEvent evt)
       {
-        fr.featureGroups.put(check.getText(),
-                new Boolean(check.isSelected()));
+        fr.setGroupVisibility(check.getText(), check.isSelected());
         af.alignPanel.getSeqPanel().seqCanvas.repaint();
         if (af.alignPanel.overviewPanel != null)
         {
@@ -538,7 +519,7 @@ public class FeatureSettings extends JPanel
       }
     });
     groupPanel.add(check);
-    return false;
+    return visible;
   }
 
   boolean resettingTable = false;
@@ -582,13 +563,8 @@ public class FeatureSettings extends JPanel
           continue;
         }
 
-        if (group == null || fr.featureGroups.get(group) == null
-                || ((Boolean) fr.featureGroups.get(group)).booleanValue())
+        if (group == null || checkGroupState(group))
         {
-          if (group != null)
-          {
-            checkGroupState(group);
-          }
           type = tmpfeatures[index].getType();
           if (!visibleChecks.contains(type))
           {
@@ -623,19 +599,20 @@ public class FeatureSettings extends JPanel
     Object[][] data = new Object[fSize][3];
     int dataIndex = 0;
 
-    if (fr.renderOrder != null)
+    if (fr.hasRenderOrder())
     {
       if (!handlingUpdate)
-       {
+      {
         fr.findAllFeatures(groupChanged != null); // prod to update
+        // colourschemes. but don't
+        // affect display
+        // First add the checks in the previous render order,
+        // in case the window has been closed and reopened
       }
-      // colourschemes. but don't
-      // affect display
-      // First add the checks in the previous render order,
-      // in case the window has been closed and reopened
-      for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
+      List<String> frl = fr.getRenderOrder();
+      for (int ro = frl.size() - 1; ro > -1; ro--)
       {
-        type = fr.renderOrder[ro];
+        type = frl.get(ro);
 
         if (!visibleChecks.contains(type))
         {
@@ -644,8 +621,8 @@ public class FeatureSettings extends JPanel
 
         data[dataIndex][0] = type;
         data[dataIndex][1] = fr.getFeatureStyle(type);
-        data[dataIndex][2] = new Boolean(
-                af.getViewport().getFeaturesDisplayed().containsKey(type));
+        data[dataIndex][2] = new Boolean(af.getViewport()
+                .getFeaturesDisplayed().isVisible(type));
         dataIndex++;
         visibleChecks.removeElement(type);
       }
@@ -663,7 +640,7 @@ public class FeatureSettings extends JPanel
       if (data[dataIndex][1] == null)
       {
         // "Colour has been updated in another view!!"
-        fr.renderOrder = null;
+        fr.clearRenderOrder();
         return;
       }
 
@@ -685,8 +662,8 @@ public class FeatureSettings extends JPanel
 
     if (groupPanel != null)
     {
-      groupPanel.setLayout(new GridLayout(fr.featureGroups.size() / 4 + 1,
-              4));
+      groupPanel.setLayout(new GridLayout(
+              fr.getFeatureGroupsSize() / 4 + 1, 4));
 
       groupPanel.validate();
       bigPanel.add(groupPanel, BorderLayout.NORTH);
@@ -843,9 +820,10 @@ public class FeatureSettings extends JPanel
         PrintWriter out = new PrintWriter(new OutputStreamWriter(
                 new FileOutputStream(choice), "UTF-8"));
 
-        Iterator e = fr.featureColours.keySet().iterator();
-        float[] sortOrder = new float[fr.featureColours.size()];
-        String[] sortTypes = new String[fr.featureColours.size()];
+        Set fr_colours = fr.getAllFeatureColours();
+        Iterator e = fr_colours.iterator();
+        float[] sortOrder = new float[fr_colours.size()];
+        String[] sortTypes = new String[fr_colours.size()];
         int i = 0;
         while (e.hasNext())
         {
@@ -1068,7 +1046,7 @@ public class FeatureSettings extends JPanel
     {
       public void actionPerformed(ActionEvent e)
       {
-        sortByScore(null);
+        af.avc.sortAlignmentByFeatureScore(null);
       }
     });
     sortByDens.setFont(JvSwingUtils.getLabelFont());
@@ -1078,7 +1056,22 @@ public class FeatureSettings extends JPanel
     {
       public void actionPerformed(ActionEvent e)
       {
-        sortByDens(null);
+        af.avc.sortAlignmentByFeatureDensity(null);
+      }
+    });
+    help.setFont(JvSwingUtils.getLabelFont());
+    help.setText(MessageManager.getString("action.help"));
+    help.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        try
+        {
+          Help.showHelpWindow(HelpId.SequenceFeatureSettings);
+        } catch (HelpSetException e1)
+        {
+          e1.printStackTrace();
+        }
       }
     });
     help.setFont(JvSwingUtils.getLabelFont());
@@ -1102,6 +1095,7 @@ public class FeatureSettings extends JPanel
     {
       public void actionPerformed(ActionEvent e)
       {
+        fr.setTransparency(originalTransparency);
         updateFeatureRenderer(originalData);
         close();
       }
@@ -1198,131 +1192,6 @@ public class FeatureSettings extends JPanel
     settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
   }
 
-  protected void sortByDens(String[] typ)
-  {
-    sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
-  }
-
-  protected void sortBy(String[] typ, String methodText, final String method)
-  {
-    if (typ == null)
-    {
-      typ = getDisplayedFeatureTypes();
-    }
-    String gps[] = null;
-    gps = getDisplayedFeatureGroups();
-    if (typ != null)
-    {
-      ArrayList types = new ArrayList();
-      for (int i = 0; i < typ.length; i++)
-      {
-        if (typ[i] != null)
-        {
-          types.add(typ[i]);
-        }
-        typ = new String[types.size()];
-        types.toArray(typ);
-      }
-    }
-    if (gps != null)
-    {
-      ArrayList grps = new ArrayList();
-
-      for (int i = 0; i < gps.length; i++)
-      {
-        if (gps[i] != null)
-        {
-          grps.add(gps[i]);
-        }
-      }
-      gps = new String[grps.size()];
-      grps.toArray(gps);
-    }
-    AlignmentPanel alignPanel = af.alignPanel;
-    AlignmentI al = alignPanel.av.getAlignment();
-
-    int start, stop;
-    SequenceGroup sg = alignPanel.av.getSelectionGroup();
-    if (sg != null)
-    {
-      start = sg.getStartRes();
-      stop = sg.getEndRes();
-    }
-    else
-    {
-      start = 0;
-      stop = al.getWidth();
-    }
-    SequenceI[] oldOrder = al.getSequencesArray();
-    AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
-    af.addHistoryItem(new OrderCommand(methodText, oldOrder, alignPanel.av
-            .getAlignment()));
-    alignPanel.paintAlignment(true);
-
-  }
-
-  protected void sortByScore(String[] typ)
-  {
-    sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
-  }
-
-  private String[] getDisplayedFeatureTypes()
-  {
-    String[] typ = null;
-    if (fr != null)
-    {
-      synchronized (fr.renderOrder)
-      {
-        typ = new String[fr.renderOrder.length];
-        System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
-        for (int i = 0; i < typ.length; i++)
-        {
-          if (af.viewport.getFeaturesDisplayed().get(typ[i]) == null)
-          {
-            typ[i] = null;
-          }
-        }
-      }
-    }
-    return typ;
-  }
-
-  private String[] getDisplayedFeatureGroups()
-  {
-    String[] gps = null;
-    ArrayList<String> _gps = new ArrayList<String>();
-    if (fr != null)
-    {
-
-      if (fr.featureGroups != null)
-      {
-        Iterator en = fr.featureGroups.keySet().iterator();
-        int g = 0;
-        boolean valid = false;
-        while (en.hasNext())
-        {
-          String gp = (String) en.next();
-          Boolean on = (Boolean) fr.featureGroups.get(gp);
-          if (on != null && on.booleanValue())
-          {
-            valid = true;
-            _gps.add(gp);
-          }
-        }
-        if (!valid)
-        {
-          return null;
-        }
-        else
-        {
-          gps = new String[_gps.size()];
-          _gps.toArray(gps);
-        }
-      }
-    }
-    return gps;
-  }
-
   public void fetchDAS_actionPerformed(ActionEvent e)
   {
     fetchDAS.setEnabled(false);
index 3486f72..a22e918 100755 (executable)
@@ -111,7 +111,7 @@ public class IdPanel extends JPanel implements MouseListener,
       seqAnnotReport
               .createSequenceAnnotationReport(tip, sequence,
                       av.isShowDbRefs(), av.isShowNpFeats(),
-                      sp.seqCanvas.fr.minmax);
+                      sp.seqCanvas.fr.getMinMax());
       setToolTipText("<html>" + sequence.getDisplayId(true) + " "
               + tip.toString() + "</html>");
     }
index 4e72236..5d11901 100644 (file)
@@ -69,6 +69,8 @@ import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.util.jarInputStreamProvider;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
+import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.dm.AAConSettings;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
@@ -1128,7 +1130,7 @@ public class Jalview2XML
       view.setShowColourText(av.getColourText());
       view.setShowFullId(av.getShowJVSuffix());
       view.setRightAlignIds(av.isRightAlignIds());
-      view.setShowSequenceFeatures(av.showSequenceFeatures);
+      view.setShowSequenceFeatures(av.isShowSequenceFeatures());
       view.setShowText(av.getShowText());
       view.setShowUnconserved(av.getShowUnconserved());
       view.setWrapAlignment(av.getWrapAlignment());
@@ -1149,7 +1151,8 @@ public class Jalview2XML
       {
         jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
 
-        String[] renderOrder = ap.getSeqPanel().seqCanvas.getFeatureRenderer().renderOrder;
+        String[] renderOrder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                .getRenderOrder().toArray(new String[0]);
 
         Vector settingsAdded = new Vector();
         Object gstyle = null;
@@ -1180,8 +1183,8 @@ public class Jalview2XML
                       .getColour(renderOrder[ro]).getRGB());
             }
 
-            setting.setDisplay(av.getFeaturesDisplayed()
-                    .containsKey(renderOrder[ro]));
+            setting.setDisplay(av.getFeaturesDisplayed().isVisible(
+                    renderOrder[ro]));
             float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
                     .getOrder(renderOrder[ro]);
             if (rorder > -1)
@@ -1194,8 +1197,8 @@ public class Jalview2XML
         }
 
         // Make sure we save none displayed feature settings
-        Iterator en = ap.getSeqPanel().seqCanvas.getFeatureRenderer().featureColours
-                .keySet().iterator();
+        Iterator en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                .getFeatureColours().keySet().iterator();
         while (en.hasNext())
         {
           String key = en.next().toString();
@@ -1219,8 +1222,9 @@ public class Jalview2XML
           fs.addSetting(setting);
           settingsAdded.addElement(key);
         }
-        en = ap.getSeqPanel().seqCanvas.getFeatureRenderer().featureGroups
-                .keySet().iterator();
+        // is groups actually supposed to be a map here ?
+        en = ap.getSeqPanel().seqCanvas.getFeatureRenderer().getFeatureGroups()
+                .iterator();
         Vector groupsAdded = new Vector();
         while (en.hasNext())
         {
@@ -1232,7 +1236,7 @@ public class Jalview2XML
           Group g = new Group();
           g.setName(grp);
           g.setDisplay(((Boolean) ap.getSeqPanel().seqCanvas
-                  .getFeatureRenderer().featureGroups.get(grp))
+                  .getFeatureRenderer().checkGroupVisibility(grp, false))
                   .booleanValue());
           fs.addGroup(g);
           groupsAdded.addElement(grp);
@@ -1429,6 +1433,7 @@ public class Jalview2XML
           an.addProperty(prop);
         }
       }
+
       AnnotationElement ae;
       if (aa[i].annotations != null)
       {
@@ -3536,10 +3541,8 @@ public class Jalview2XML
 
     af.viewport.setColourAppliesToAllGroups(true);
 
-    if (view.getShowSequenceFeatures())
-    {
-      af.viewport.showSequenceFeatures = true;
-    }
+    af.viewport.setShowSequenceFeatures(view.getShowSequenceFeatures());
+
     if (view.hasCentreColumnLabels())
     {
       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
@@ -3606,9 +3609,14 @@ public class Jalview2XML
     // recover featre settings
     if (jms.getFeatureSettings() != null)
     {
-      af.viewport.setFeaturesDisplayed(new Hashtable());
+      FeaturesDisplayed fdi;
+      af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
       String[] renderOrder = new String[jms.getFeatureSettings()
               .getSettingCount()];
+      Hashtable featureGroups = new Hashtable();
+      Hashtable featureColours = new Hashtable();
+      Hashtable featureOrder = new Hashtable();
+
       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
       {
         Setting setting = jms.getFeatureSettings().getSetting(fs);
@@ -3635,41 +3643,42 @@ public class Jalview2XML
             gc.setColourByLabel(setting.getColourByLabel());
           }
           // and put in the feature colour table.
-          af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setColour(
-                  setting.getType(), gc);
+          featureColours.put(setting.getType(), gc);
         }
         else
         {
-          af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setColour(
-                  setting.getType(),
+          featureColours.put(setting.getType(),
                   new java.awt.Color(setting.getColour()));
         }
         renderOrder[fs] = setting.getType();
         if (setting.hasOrder())
         {
-          af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setOrder(
-                  setting.getType(), setting.getOrder());
+          featureOrder.put(setting.getType(), setting.getOrder());
         }
         else
         {
-          af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setOrder(
-                  setting.getType(),
-                  fs / jms.getFeatureSettings().getSettingCount());
+          featureOrder.put(setting.getType(), new Float(fs
+                  / jms.getFeatureSettings().getSettingCount()));
         }
         if (setting.getDisplay())
         {
-          af.viewport.getFeaturesDisplayed().put(setting.getType(), new Integer(
-                  setting.getColour()));
+          fdi.setVisible(setting.getType());
         }
       }
-      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
-      Hashtable fgtable;
-      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
+      Hashtable fgtable = new Hashtable();
       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
       {
         Group grp = jms.getFeatureSettings().getGroup(gs);
         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
       }
+      // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
+      // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ?
+      // jms.getFeatureSettings().getTransparency() : 0.0, featureOrder);
+      FeatureRendererSettings frs = new FeatureRendererSettings(
+              renderOrder, fgtable, featureColours, 1.0f, featureOrder);
+      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+              .transferSettings(frs);
+
     }
 
     if (view.getHiddenColumnsCount() > 0)
@@ -4531,5 +4540,4 @@ public class Jalview2XML
   {
     skipList = skipList2;
   }
-
 }
index 033b6dc..586e2fa 100755 (executable)
@@ -40,6 +40,7 @@ import jalview.schemes.ResidueProperties;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.util.jarInputStreamProvider;
+import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 
 import java.io.InputStreamReader;
 import java.util.Hashtable;
@@ -411,29 +412,31 @@ public class Jalview2XML_V1
     }
 
     af.viewport.setColourAppliesToAllGroups(true);
-    af.viewport.showSequenceFeatures = view.getShowSequenceFeatures();
+    af.viewport.setShowSequenceFeatures(view.getShowSequenceFeatures());
 
     if (jms.getFeatureSettings() != null)
     {
-      af.viewport.setFeaturesDisplayed(new Hashtable());
+      Hashtable featuresDisplayed = new Hashtable();
+      Hashtable featureColours = new Hashtable();
       String[] renderOrder = new String[jms.getFeatureSettings()
               .getSettingCount()];
       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
       {
         Setting setting = jms.getFeatureSettings().getSetting(fs);
 
-        af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setColour(
+        featureColours.put(
                 setting.getType(), new java.awt.Color(setting.getColour()));
 
         renderOrder[fs] = setting.getType();
 
         if (setting.getDisplay())
         {
-          af.viewport.getFeaturesDisplayed().put(setting.getType(), new Integer(
+          featuresDisplayed.put(setting.getType(), new Integer(
                   setting.getColour()));
         }
       }
-      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
+      FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder, new Hashtable(), featureColours, 1.0f, null);
+      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().transferSettings(frs);
     }
 
     af.setMenusFromViewport(af.viewport);
index f68b585..ece8856 100644 (file)
@@ -26,11 +26,11 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
   {
     AlignmentPanel ap = (alignment == null) ? cvf.ap
             : (AlignmentPanel) alignment;
-    if (ap.av.showSequenceFeatures)
+    if (ap.av.isShowSequenceFeatures())
     {
       if (fr == null)
       {
-        fr = ap.cloneFeatureRenderer();
+        fr = (jalview.gui.FeatureRenderer) ap.cloneFeatureRenderer();
       }
       else
       {
@@ -71,7 +71,7 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
     }
     if (!isLoadingFromArchive())
     {
-      colourBySequence(ap.av.getShowSequenceFeatures(), ap);
+      colourBySequence(ap.av.isShowSequenceFeatures(), ap);
     }
   }
   @Override
index 95e14b4..5df60d2 100755 (executable)
@@ -67,7 +67,7 @@ public class OverviewPanel extends JPanel implements Runnable
   // main visible SeqCanvas
   SequenceRenderer sr;
 
-  FeatureRenderer fr;
+  jalview.renderer.seqfeatures.FeatureRenderer fr;
 
   /**
    * Creates a new OverviewPanel object.
@@ -85,7 +85,7 @@ public class OverviewPanel extends JPanel implements Runnable
     sr.renderGaps = false;
     sr.forOverview = true;
     fr = new FeatureRenderer(ap);
-
+    
     // scale the initial size of overviewpanel to shape of alignment
     float initialScale = (float) av.getAlignment().getWidth()
             / (float) av.getAlignment().getHeight();
@@ -254,7 +254,7 @@ public class OverviewPanel extends JPanel implements Runnable
   {
     miniMe = null;
 
-    if (av.showSequenceFeatures)
+    if (av.isShowSequenceFeatures())
     {
       fr.transferSettings(ap.getSeqPanel().seqCanvas.getFeatureRenderer());
     }
@@ -344,7 +344,7 @@ public class OverviewPanel extends JPanel implements Runnable
         {
           color = sr.getResidueBoxColour(seq, lastcol).getRGB();
 
-          if (av.showSequenceFeatures)
+          if (av.isShowSequenceFeatures())
           {
             color = fr.findFeatureColour(color, seq, lastcol);
           }
index 4cc3d6a..3ea689f 100644 (file)
@@ -1943,7 +1943,9 @@ public class PopupMenu extends JPopupMenu
                       true,
                       true,
                       false,
-                      (ap.getSeqPanel().seqCanvas.fr != null) ? ap.getSeqPanel().seqCanvas.fr.minmax
+                      (ap.getSeqPanel().seqCanvas.fr != null) ? ap
+                              .getSeqPanel().seqCanvas.fr
+                              .getMinMax()
                               : null);
       contents.append("</p>");
     }
index 54abcc2..4d1546b 100755 (executable)
@@ -711,7 +711,7 @@ public class SeqCanvas extends JComponent
       sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
               startRes, endRes, offset + ((i - startSeq) * av.charHeight));
 
-      if (av.showSequenceFeatures)
+      if (av.isShowSequenceFeatures())
       {
         fr.drawSequence(g, nextSeq, startRes, endRes, offset
                 + ((i - startSeq) * av.charHeight));
index fbe9ff8..7c6a202 100644 (file)
@@ -243,45 +243,6 @@ public class SeqPanel extends JPanel implements MouseListener,
     return seq;
   }
 
-  SequenceFeature[] findFeaturesAtRes(SequenceI sequence, int res)
-  {
-    Vector tmp = new Vector();
-    SequenceFeature[] features = sequence.getSequenceFeatures();
-    if (features != null)
-    {
-      for (int i = 0; i < features.length; i++)
-      {
-        if (av.getFeaturesDisplayed() == null
-                || !av.getFeaturesDisplayed().containsKey(
-                        features[i].getType()))
-        {
-          continue;
-        }
-
-        if (features[i].featureGroup != null
-                && seqCanvas.fr.featureGroups != null
-                && seqCanvas.fr.featureGroups
-                        .containsKey(features[i].featureGroup)
-                && !((Boolean) seqCanvas.fr.featureGroups
-                        .get(features[i].featureGroup)).booleanValue())
-        {
-          continue;
-        }
-
-        if ((features[i].getBegin() <= res)
-                && (features[i].getEnd() >= res))
-        {
-          tmp.addElement(features[i]);
-        }
-      }
-    }
-
-    features = new SequenceFeature[tmp.size()];
-    tmp.copyInto(features);
-
-    return features;
-  }
-
   void endEditing()
   {
     if (editCommand != null && editCommand.getSize() > 0)
@@ -744,14 +705,14 @@ public class SeqPanel extends JPanel implements MouseListener,
     }
 
     // use aa to see if the mouse pointer is on a
-    if (av.showSequenceFeatures)
+    if (av.isShowSequenceFeatures())
     {
       int rpos;
-      SequenceFeature[] features = findFeaturesAtRes(
+      List<SequenceFeature> features = ap.getFeatureRenderer().findFeaturesAtRes(
               sequence.getDatasetSequence(),
               rpos = sequence.findPosition(res));
       seqARep.appendFeatures(tooltipText, rpos, features,
-              this.ap.getSeqPanel().seqCanvas.fr.minmax);
+              this.ap.getSeqPanel().seqCanvas.fr.getMinMax());
     }
     if (tooltipText.length() == 6) // <html></html>
     {
@@ -1390,21 +1351,21 @@ public class SeqPanel extends JPanel implements MouseListener,
         av.setSelectionGroup(null);
       }
 
-      SequenceFeature[] features = findFeaturesAtRes(
+      List<SequenceFeature> features = seqCanvas.getFeatureRenderer().findFeaturesAtRes(
               sequence.getDatasetSequence(),
               sequence.findPosition(findRes(evt)));
 
-      if (features != null && features.length > 0)
+      if (features != null && features.size()> 0)
       {
         SearchResults highlight = new SearchResults();
-        highlight.addResult(sequence, features[0].getBegin(),
-                features[0].getEnd());
+        highlight.addResult(sequence, features.get(0).getBegin(),
+                features.get(0).getEnd());
         seqCanvas.highlightSearchResults(highlight);
       }
-      if (features != null && features.length > 0)
+      if (features != null && features.size()> 0)
       {
         seqCanvas.getFeatureRenderer().amendFeatures(new SequenceI[]
-        { sequence }, features, false, ap);
+        { sequence }, features.toArray(new SequenceFeature[features.size()]), false, ap);
 
         seqCanvas.highlightSearchResults(null);
       }
@@ -1519,16 +1480,16 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     if (javax.swing.SwingUtilities.isRightMouseButton(evt))
     {
-      SequenceFeature[] allFeatures = findFeaturesAtRes(
+      List<SequenceFeature> allFeatures = ap.getFeatureRenderer().findFeaturesAtRes(
               sequence.getDatasetSequence(), sequence.findPosition(res));
       Vector links = new Vector();
-      for (int i = 0; i < allFeatures.length; i++)
+      for (SequenceFeature sf:allFeatures)
       {
-        if (allFeatures[i].links != null)
+        if (sf.links != null)
         {
-          for (int j = 0; j < allFeatures[i].links.size(); j++)
+          for (int j = 0; j < sf.links.size(); j++)
           {
-            links.addElement(allFeatures[i].links.elementAt(j));
+            links.addElement(sf.links.elementAt(j));
           }
         }
       }
index bcbebbd..177fc83 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.gui;
 
+import jalview.api.FeatureRenderer;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -80,11 +81,12 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     // If EPS graphics, stringWidth will be a double, not an int
     double dwidth = fm.getStringBounds("M", g).getWidth();
 
-    monospacedFont = (dwidth == fm.getStringBounds("|", g).getWidth() && (float) av.charWidth == dwidth);
+    monospacedFont = (dwidth == fm.getStringBounds("|", g).getWidth() && av.charWidth == dwidth);
 
     this.renderGaps = renderGaps;
   }
 
+  @Override
   public Color getResidueBoxColour(SequenceI seq, int i)
   {
     allGroups = av.getAlignment().findAllGroups(seq);
@@ -105,6 +107,31 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   }
 
   /**
+   * Get the residue colour at the given sequence position - as determined by
+   * the sequence group colour (if any), else the colour scheme, possibly
+   * overridden by a feature colour.
+   * 
+   * @param seq
+   * @param position
+   * @param fr
+   * @return
+   */
+  @Override
+  public Color getResidueColour(final SequenceI seq, int position,
+          FeatureRenderer fr)
+  {
+    // TODO replace 8 or so code duplications with calls to this method
+    // (refactored as needed)
+    Color col = getResidueBoxColour(seq, position);
+
+    if (fr != null)
+    {
+      col = fr.findFeatureColour(col, seq, position);
+    }
+    return col;
+  }
+
+  /**
    * DOCUMENT ME!
    * 
    * @param cs
@@ -188,7 +215,9 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           int y1)
   {
     if (seq == null)
+     {
       return; // fix for racecondition
+    }
     int i = start;
     int length = seq.getLength();
 
index ee0bfaf..6ce68b3 100755 (executable)
  */
 package jalview.gui;
 
-import java.beans.*;
-import java.io.*;
-import java.util.*;
-import java.util.List;
-
-import javax.imageio.*;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.*;
-import javax.swing.*;
-
-import org.jibble.epsgraphics.*;
-import jalview.analysis.*;
+import jalview.analysis.AlignmentSorter;
+import jalview.analysis.NJTree;
+import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.ViewBasedAnalysisI;
+import jalview.bin.Cache;
 import jalview.commands.CommandI;
 import jalview.commands.OrderCommand;
-import jalview.datamodel.*;
-import jalview.io.*;
-import jalview.jbgui.*;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.BinaryNode;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.NodeTransformI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequenceNode;
+import jalview.io.JalviewFileChooser;
+import jalview.io.JalviewFileView;
+import jalview.io.NewickFile;
+import jalview.jbgui.GTreePanel;
+import jalview.schemes.ResidueProperties;
 import jalview.util.MessageManager;
 
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeEvent;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+import javax.swing.ButtonGroup;
+import javax.swing.JMenuItem;
+import javax.swing.JRadioButtonMenuItem;
+
+import org.jibble.epsgraphics.EpsGraphics2D;
+
 /**
  * DOCUMENT ME!
  * 
@@ -293,8 +313,26 @@ public class TreePanel extends GTreePanel
           seqs = av.getSelectionGroup().getSequencesInOrder(
                   av.getAlignment());
         }
-
-        tree = new NJTree(seqs, seqStrings, type, pwtype, start, end);
+        ScoreModelI sm = ResidueProperties.getScoreModel(pwtype);
+        if (sm instanceof ViewBasedAnalysisI)
+        {
+          try
+          {
+            sm = sm.getClass().newInstance();
+            ((ViewBasedAnalysisI) sm)
+                    .configureFromAlignmentView(treeCanvas.ap);
+          } catch (Exception q)
+          {
+            Cache.log.error("Couldn't create a scoremodel instance for "
+                    + sm.getName());
+          }
+          tree = new NJTree(seqs, seqStrings, type, pwtype, sm, start, end);
+        }
+        else
+        {
+          tree = new NJTree(seqs, seqStrings, type, pwtype, null, start,
+                  end);
+        }
         showDistances(true);
       }
 
index 772337a..db43a3f 100644 (file)
@@ -1,5 +1,6 @@
 package jalview.io;
 
+import jalview.api.FeaturesDisplayedI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
@@ -13,13 +14,13 @@ import jalview.json.binding.v1.BioJsSeqPojo;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.util.MessageManager;
 
+import java.awt.Color;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.net.URL;
 import java.util.ArrayList;
-import java.util.Hashtable;
 
 import com.json.JSONException;
 
@@ -27,11 +28,11 @@ public class BioJsHTMLOutput
 {
   private AlignViewport av;
 
-  private FeatureRenderer fr;
+  private jalview.api.FeatureRenderer fr;
 
   private String globalColorScheme;
 
-  private Hashtable displayedFeatures;
+  private FeaturesDisplayedI displayedFeatures;
 
   private String jalviewVersion;
 
@@ -50,12 +51,9 @@ public class BioJsHTMLOutput
       this.av = ap.av;
       this.globalColorScheme = ColourSchemeProperty.getColourName(av
               .getGlobalColourScheme());
-      this.fr = new FeatureRenderer(ap);
-    fr.transferSettings(fr1);
+      this.fr = ap.cloneFeatureRenderer();
       displayedFeatures = av.getFeaturesDisplayed();
-
-    exportJalviewAlignmentAsBioJsHtmlFile();
-  }
+    }
   }
 
   private void exportJalviewAlignmentAsBioJsHtmlFile()
@@ -147,10 +145,13 @@ public class BioJsHTMLOutput
         for (SequenceFeature sf : seqFeatures)
         {
           if (displayedFeatures != null
-                  && displayedFeatures.get(sf.getType()) != null)
+                  && displayedFeatures.isVisible(sf.getType()))
           {
+
+            // TODO: translate graduated/complex colourschemes to biojs model
             String featureColour = jalview.util.Format.getHexString(fr
-                    .getColour(sf));
+                    .findFeatureColour(Color.white, seq,
+                            seq.findIndex(sf.getBegin())));
             BioJsFeaturePojo bjsFeature = new BioJsFeaturePojo();
             bjsFeature.setFillColor(featureColour);
             bjsFeature.setXstart(seq.findIndex(sf.getBegin()) - 1);
index 87b829f..5b621e7 100755 (executable)
@@ -678,7 +678,7 @@ public class FeaturesFile extends AlignFile
    *          hash of feature types and colours
    * @return features file contents
    */
-  public String printJalviewFormat(SequenceI[] seqs, Hashtable visible)
+  public String printJalviewFormat(SequenceI[] seqs, Map<String,Object> visible)
   {
     return printJalviewFormat(seqs, visible, true, true);
   }
@@ -697,7 +697,7 @@ public class FeaturesFile extends AlignFile
    *          of group or type)
    * @return features file contents
    */
-  public String printJalviewFormat(SequenceI[] seqs, Hashtable visible,
+  public String printJalviewFormat(SequenceI[] seqs, Map visible,
           boolean visOnly, boolean nonpos)
   {
     StringBuffer out = new StringBuffer();
@@ -714,11 +714,11 @@ public class FeaturesFile extends AlignFile
       // write feature colours only if we're given them and we are generating
       // viewed features
       // TODO: decide if feature links should also be written here ?
-      Enumeration en = visible.keys();
+      Iterator en = visible.keySet().iterator();
       String type, color;
-      while (en.hasMoreElements())
+      while (en.hasNext())
       {
-        type = en.nextElement().toString();
+        type = en.next().toString();
 
         if (visible.get(type) instanceof GraduatedColor)
         {
@@ -926,12 +926,12 @@ public class FeaturesFile extends AlignFile
    * @param visible
    * @return
    */
-  public String printGFFFormat(SequenceI[] seqs, Hashtable visible)
+  public String printGFFFormat(SequenceI[] seqs, Map<String,Object> visible)
   {
     return printGFFFormat(seqs, visible, true, true);
   }
 
-  public String printGFFFormat(SequenceI[] seqs, Hashtable visible,
+  public String printGFFFormat(SequenceI[] seqs, Map<String,Object> visible,
           boolean visOnly, boolean nonpos)
   {
     StringBuffer out = new StringBuffer();
index 50b07e4..2d2626f 100755 (executable)
@@ -34,7 +34,7 @@ public class HTMLOutput
 
   SequenceRenderer sr;
 
-  FeatureRenderer fr;
+  jalview.renderer.seqfeatures.FeatureRenderer fr;
 
   Color color;
 
index 6fba9c6..1757239 100644 (file)
@@ -22,6 +22,7 @@ package jalview.io;
 
 import java.util.ArrayList;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.Vector;
 
 import jalview.datamodel.DBRefEntry;
@@ -54,30 +55,30 @@ public class SequenceAnnotationReport
    *          TODO refactor to Jalview 'utilities' somehow.
    */
   public void appendFeatures(final StringBuffer tooltipText2, int rpos,
-          SequenceFeature[] features)
+          List<SequenceFeature> features)
   {
     appendFeatures(tooltipText2, rpos, features, null);
   }
 
   public void appendFeatures(final StringBuffer tooltipText2, int rpos,
-          SequenceFeature[] features, Hashtable minmax)
+          List<SequenceFeature> features, Hashtable minmax)
   {
     String tmpString;
     if (features != null)
     {
-      for (int i = 0; i < features.length; i++)
+      for (SequenceFeature feature:features)
       {
-        if (features[i].getType().equals("disulfide bond"))
+        if (feature.getType().equals("disulfide bond"))
         {
-          if (features[i].getBegin() == rpos
-                  || features[i].getEnd() == rpos)
+          if (feature.getBegin() == rpos
+                  || feature.getEnd() == rpos)
           {
             if (tooltipText2.length() > 6)
             {
               tooltipText2.append("<br>");
             }
-            tooltipText2.append("disulfide bond " + features[i].getBegin()
-                    + ":" + features[i].getEnd());
+            tooltipText2.append("disulfide bond " + feature.getBegin()
+                    + ":" + feature.getEnd());
           }
         }
         else
@@ -87,25 +88,25 @@ public class SequenceAnnotationReport
             tooltipText2.append("<br>");
           }
           // TODO: remove this hack to display link only features
-          boolean linkOnly = features[i].getValue("linkonly") != null;
+          boolean linkOnly = feature.getValue("linkonly") != null;
           if (!linkOnly)
           {
-            tooltipText2.append(features[i].getType() + " ");
+            tooltipText2.append(feature.getType() + " ");
             if (rpos != 0)
             {
               // we are marking a positional feature
-              tooltipText2.append(features[i].begin);
+              tooltipText2.append(feature.begin);
             }
-            if (features[i].begin != features[i].end)
+            if (feature.begin != feature.end)
             {
-              tooltipText2.append(" " + features[i].end);
+              tooltipText2.append(" " + feature.end);
             }
 
-            if (features[i].getDescription() != null
-                    && !features[i].description.equals(features[i]
+            if (feature.getDescription() != null
+                    && !feature.description.equals(feature
                             .getType()))
             {
-              tmpString = features[i].getDescription();
+              tmpString = feature.getDescription();
               String tmp2up = tmpString.toUpperCase();
               int startTag = tmp2up.indexOf("<HTML>");
               if (startTag > -1)
@@ -150,27 +151,27 @@ public class SequenceAnnotationReport
               }
             }
             // check score should be shown
-            if (features[i].getScore() != Float.NaN)
+            if (feature.getScore() != Float.NaN)
             {
               float[][] rng = (minmax == null) ? null : ((float[][]) minmax
-                      .get(features[i].getType()));
+                      .get(feature.getType()));
               if (rng != null && rng[0] != null && rng[0][0] != rng[0][1])
               {
-                tooltipText2.append(" Score=" + features[i].getScore());
+                tooltipText2.append(" Score=" + feature.getScore());
               }
             }
-            if (features[i].getValue("status") != null)
+            if (feature.getValue("status") != null)
             {
-              String status = features[i].getValue("status").toString();
+              String status = feature.getValue("status").toString();
               if (status.length() > 0)
               {
-                tooltipText2.append("; (" + features[i].getValue("status")
+                tooltipText2.append("; (" + feature.getValue("status")
                         + ")");
               }
             }
           }
         }
-        if (features[i].links != null)
+        if (feature.links != null)
         {
           if (linkImageURL != null)
           {
@@ -178,7 +179,7 @@ public class SequenceAnnotationReport
           }
           else
           {
-            for (String urlstring : (Vector<String>) features[i].links)
+            for (String urlstring : (Vector<String>) feature.links)
             {
               try
               {
@@ -364,7 +365,6 @@ public class SequenceAnnotationReport
 
     // ADD NON POSITIONAL SEQUENCE INFO
     SequenceFeature[] features = ds.getSequenceFeatures();
-    SequenceFeature[] tfeat = new SequenceFeature[1];
     if (showNpFeats && features != null)
     {
       for (int i = 0; i < features.length; i++)
@@ -372,7 +372,8 @@ public class SequenceAnnotationReport
         if (features[i].begin == 0 && features[i].end == 0)
         {
           int sz = -tip.length();
-          tfeat[0] = features[i];
+          List<SequenceFeature> tfeat = new ArrayList<SequenceFeature>();
+          tfeat.add(features[i]);
           appendFeatures(tip, 0, tfeat, minmax);
           sz += tip.length();
           maxWidth = Math.max(maxWidth, sz);
index 465a672..2ecaf6c 100644 (file)
@@ -202,7 +202,7 @@ public class MouseOverStructureListener extends JSFunctionExec implements
       SequenceRenderer sr = ((jalview.appletgui.AlignmentPanel) source)
               .getSequenceRenderer();
       FeatureRenderer fr = ((jalview.appletgui.AlignmentPanel) source).av
-              .getShowSequenceFeatures() ? new jalview.appletgui.FeatureRenderer(
+              .isShowSequenceFeatures() ? new jalview.appletgui.FeatureRenderer(
               ((jalview.appletgui.AlignmentPanel) source).av) : null;
       if (fr != null)
       {
diff --git a/src/jalview/renderer/seqfeatures/FeatureRenderer.java b/src/jalview/renderer/seqfeatures/FeatureRenderer.java
new file mode 100644 (file)
index 0000000..5e6ac29
--- /dev/null
@@ -0,0 +1,435 @@
+package jalview.renderer.seqfeatures;
+
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+public class FeatureRenderer extends
+        jalview.viewmodel.seqfeatures.FeatureRendererModel
+{
+
+  FontMetrics fm;
+
+  int charOffset;
+
+  boolean offscreenRender = false;
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param g
+   *          DOCUMENT ME!
+   * @param seq
+   *          DOCUMENT ME!
+   * @param sg
+   *          DOCUMENT ME!
+   * @param start
+   *          DOCUMENT ME!
+   * @param end
+   *          DOCUMENT ME!
+   * @param x1
+   *          DOCUMENT ME!
+   * @param y1
+   *          DOCUMENT ME!
+   * @param width
+   *          DOCUMENT ME!
+   * @param height
+   *          DOCUMENT ME!
+   */
+  protected SequenceI lastSeq;
+
+  char s;
+
+  int i;
+
+  int av_charHeight, av_charWidth;
+
+  boolean av_validCharWidth, av_isShowSeqFeatureHeight;
+
+  protected void updateAvConfig()
+  {
+    av_charHeight = av.getCharHeight();
+    av_charWidth = av.getCharWidth();
+    av_validCharWidth = av.isValidCharWidth();
+    av_isShowSeqFeatureHeight = av.isShowSequenceFeaturesHeight();
+  }
+
+  void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
+          Color featureColour, int start, int end, int y1)
+  {
+    updateAvConfig();
+    if (((fstart <= end) && (fend >= start)))
+    {
+      if (fstart < start)
+      { // fix for if the feature we have starts before the sequence start,
+        fstart = start; // but the feature end is still valid!!
+      }
+
+      if (fend >= end)
+      {
+        fend = end;
+      }
+      int pady = (y1 + av_charHeight) - av_charHeight / 5;
+      for (i = fstart; i <= fend; i++)
+      {
+        s = seq.getCharAt(i);
+
+        if (jalview.util.Comparison.isGap(s))
+        {
+          continue;
+        }
+
+        g.setColor(featureColour);
+
+        g.fillRect((i - start) * av_charWidth, y1, av_charWidth,
+                av_charHeight);
+
+        if (offscreenRender || !av_validCharWidth)
+        {
+          continue;
+        }
+
+        g.setColor(Color.white);
+        charOffset = (av_charWidth - fm.charWidth(s)) / 2;
+        g.drawString(String.valueOf(s), charOffset
+                + (av_charWidth * (i - start)), pady);
+
+      }
+    }
+  }
+
+  void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
+          Color featureColour, int start, int end, int y1, byte[] bs)
+  {
+    updateAvConfig();
+    if (((fstart <= end) && (fend >= start)))
+    {
+      if (fstart < start)
+      { // fix for if the feature we have starts before the sequence start,
+        fstart = start; // but the feature end is still valid!!
+      }
+
+      if (fend >= end)
+      {
+        fend = end;
+      }
+      int pady = (y1 + av_charHeight) - av_charHeight / 5;
+      int ystrt = 0, yend = av_charHeight;
+      if (bs[0] != 0)
+      {
+        // signed - zero is always middle of residue line.
+        if (bs[1] < 128)
+        {
+          yend = av_charHeight * (128 - bs[1]) / 512;
+          ystrt = av_charHeight - yend / 2;
+        }
+        else
+        {
+          ystrt = av_charHeight / 2;
+          yend = av_charHeight * (bs[1] - 128) / 512;
+        }
+      }
+      else
+      {
+        yend = av_charHeight * bs[1] / 255;
+        ystrt = av_charHeight - yend;
+
+      }
+      for (i = fstart; i <= fend; i++)
+      {
+        s = seq.getCharAt(i);
+
+        if (jalview.util.Comparison.isGap(s))
+        {
+          continue;
+        }
+
+        g.setColor(featureColour);
+        int x = (i - start) * av_charWidth;
+        g.drawRect(x, y1, av_charWidth, av_charHeight);
+        g.fillRect(x, y1 + ystrt, av_charWidth, yend);
+
+        if (offscreenRender || !av_validCharWidth)
+        {
+          continue;
+        }
+
+        g.setColor(Color.black);
+        charOffset = (av_charWidth - fm.charWidth(s)) / 2;
+        g.drawString(String.valueOf(s), charOffset
+                + (av_charWidth * (i - start)), pady);
+
+      }
+    }
+  }
+
+  BufferedImage offscreenImage;
+
+  public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
+  {
+    return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
+  }
+
+  /**
+   * This is used by the Molecule Viewer and Overview to get the accurate
+   * colourof the rendered sequence
+   */
+  public synchronized int findFeatureColour(int initialCol, final SequenceI seq,
+          int column)
+  {
+    if (!av.isShowSequenceFeatures())
+    {
+      return initialCol;
+    }
+
+    final SequenceI aseq = (seq.getDatasetSequence() != null) ? seq
+            .getDatasetSequence() : seq;
+    if (seq != lastSeq)
+    {
+      lastSeq = seq;
+      sequenceFeatures = aseq.getSequenceFeatures();
+      if (sequenceFeatures != null)
+      {
+        sfSize = sequenceFeatures.length;
+      }
+    }
+    else
+    {
+      if (sequenceFeatures != aseq.getSequenceFeatures())
+      {
+        sequenceFeatures = aseq.getSequenceFeatures();
+        if (sequenceFeatures != null)
+        {
+          sfSize = sequenceFeatures.length;
+        }
+      }
+    }
+
+    if (sequenceFeatures == null || sfSize == 0)
+    {
+      return initialCol;
+    }
+
+    if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
+    {
+      return Color.white.getRGB();
+    }
+
+    // Only bother making an offscreen image if transparency is applied
+    if (transparency != 1.0f && offscreenImage == null)
+    {
+      offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
+    }
+
+    currentColour = null;
+    // TODO: non-threadsafe - each rendering thread needs its own instance of
+    // the feature renderer - or this should be synchronized.
+    offscreenRender = true;
+
+    if (offscreenImage != null)
+    {
+      offscreenImage.setRGB(0, 0, initialCol);
+      drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
+
+      return offscreenImage.getRGB(0, 0);
+    }
+    else
+    {
+      drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
+
+      if (currentColour == null)
+      {
+        return initialCol;
+      }
+      else
+      {
+        return ((Integer) currentColour).intValue();
+      }
+    }
+
+  }
+
+  private volatile SequenceFeature[] sequenceFeatures;
+
+  int sfSize;
+
+  int sfindex;
+
+  int spos;
+
+  int epos;
+
+  public synchronized void drawSequence(Graphics g, final SequenceI seq,
+          int start, int end, int y1)
+  {
+    final SequenceI aseq = (seq.getDatasetSequence() != null) ? seq
+            .getDatasetSequence() : seq;
+    if (aseq.getSequenceFeatures() == null
+            || aseq.getSequenceFeatures().length == 0)
+    {
+      return;
+    }
+
+    if (g != null)
+    {
+      fm = g.getFontMetrics();
+    }
+
+    updateFeatures();
+
+    if (lastSeq == null || seq != lastSeq
+            || aseq.getSequenceFeatures() != sequenceFeatures)
+    {
+      lastSeq = seq;
+      sequenceFeatures = aseq.getSequenceFeatures();
+    }
+
+    if (transparency != 1 && g != null)
+    {
+      Graphics2D g2 = (Graphics2D) g;
+      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+              transparency));
+    }
+
+    if (!offscreenRender)
+    {
+      spos = lastSeq.findPosition(start);
+      epos = lastSeq.findPosition(end);
+    }
+
+    sfSize = sequenceFeatures.length;
+    String type;
+    for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
+    {
+      type = renderOrder[renderIndex];
+
+      if (type == null || !showFeatureOfType(type))
+      {
+        continue;
+      }
+
+      // loop through all features in sequence to find
+      // current feature to render
+      for (sfindex = 0; sfindex < sfSize; sfindex++)
+      {
+        if (!sequenceFeatures[sfindex].type.equals(type))
+        {
+          continue;
+        }
+
+        if (featureGroups != null
+                && sequenceFeatures[sfindex].featureGroup != null
+                && sequenceFeatures[sfindex].featureGroup.length() != 0
+                && featureGroups
+                        .containsKey(sequenceFeatures[sfindex].featureGroup)
+                && !featureGroups
+                        .get(sequenceFeatures[sfindex].featureGroup)
+                        .booleanValue())
+        {
+          continue;
+        }
+
+        if (!offscreenRender
+                && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
+                        .getEnd() < spos))
+        {
+          continue;
+        }
+
+        if (offscreenRender && offscreenImage == null)
+        {
+          if (sequenceFeatures[sfindex].begin <= start
+                  && sequenceFeatures[sfindex].end >= start)
+          {
+            // this is passed out to the overview and other sequence renderers
+            // (e.g. molecule viewer) to get displayed colour for rendered
+            // sequence
+            currentColour = new Integer(
+                    getColour(sequenceFeatures[sfindex]).getRGB());
+            // used to be retreived from av.featuresDisplayed
+            // currentColour = av.featuresDisplayed
+            // .get(sequenceFeatures[sfindex].type);
+
+          }
+        }
+        else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
+        {
+
+          renderFeature(g, seq,
+                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
+                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
+                  getColour(sequenceFeatures[sfindex])
+                  // new Color(((Integer) av.featuresDisplayed
+                  // .get(sequenceFeatures[sfindex].type)).intValue())
+                  , start, end, y1);
+          renderFeature(g, seq,
+                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
+                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
+                  getColour(sequenceFeatures[sfindex])
+                  // new Color(((Integer) av.featuresDisplayed
+                  // .get(sequenceFeatures[sfindex].type)).intValue())
+                  , start, end, y1);
+
+        }
+        else if (showFeature(sequenceFeatures[sfindex]))
+        {
+          if (av_isShowSeqFeatureHeight
+                  && sequenceFeatures[sfindex].score != Float.NaN)
+          {
+            renderScoreFeature(g, seq,
+                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
+                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
+                    getColour(sequenceFeatures[sfindex]), start, end, y1,
+                    normaliseScore(sequenceFeatures[sfindex]));
+          }
+          else
+          {
+            renderFeature(g, seq,
+                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
+                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
+                    getColour(sequenceFeatures[sfindex]), start, end, y1);
+          }
+        }
+
+      }
+
+    }
+
+    if (transparency != 1.0f && g != null && transparencyAvailable)
+    {
+      Graphics2D g2 = (Graphics2D) g;
+      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+              1.0f));
+    }
+  }
+
+  boolean transparencyAvailable = true;
+
+  protected void setTransparencyAvailable(boolean isTransparencyAvailable)
+  {
+    transparencyAvailable = isTransparencyAvailable;
+  }
+
+  @Override
+  public boolean isTransparencyAvailable()
+  {
+    return transparencyAvailable;
+  }
+
+  /**
+   * Called when alignment in associated view has new/modified features to
+   * discover and display.
+   * 
+   */
+  public void featuresAdded()
+  {
+    lastSeq = null;
+    findAllFeatures();
+  }
+}
index efad430..85ec3ae 100755 (executable)
@@ -258,7 +258,7 @@ public class AnnotationColourGradient extends FollowerColourScheme
   public Color findColour(char c, int j, SequenceI seq)
   {
     Color currentColour = Color.white;
-    AlignmentAnnotation annotation = (seqAssociated ? seqannot.get(seq)
+    AlignmentAnnotation annotation = (seqAssociated && seqannot!=null ? seqannot.get(seq)
             : this.annotation);
     if (annotation == null)
     {
index 41f7781..70c7685 100755 (executable)
 package jalview.schemes;
 
 import jalview.analysis.AAFrequency;
-
-import java.awt.Color;
-import java.util.Map;
-
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
 
+import java.awt.Color;
+import java.util.Map;
+
 public class Blosum62ColourScheme extends ResidueColourScheme
 {
   public Blosum62ColourScheme()
@@ -59,6 +58,7 @@ public class Blosum62ColourScheme extends ResidueColourScheme
 
       if (max.indexOf(res) > -1)
       {
+        // TODO use a constant here?
         currentColour = new Color(154, 154, 255);
       }
       else
@@ -74,6 +74,7 @@ public class Blosum62ColourScheme extends ResidueColourScheme
 
         if (c > 0)
         {
+          // TODO use a constant here?
           currentColour = new Color(204, 204, 255);
         }
         else
index 8acf1f2..d13f0a9 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.schemes;
 
+import jalview.analysis.scoremodels.FeatureScoreModel;
 import jalview.analysis.scoremodels.PIDScoreModel;
 import jalview.api.analysis.ScoreModelI;
 
@@ -1498,6 +1499,7 @@ public class ResidueProperties
     // scoreMatrices.put("Conservation EnhPos", new
     // ScoreMatrix("Conservation EnhPos",propMatrixEpos,0));
     scoreMatrices.put("PID", new PIDScoreModel());
+    scoreMatrices.put("Displayed Features", new FeatureScoreModel());
   }
 
   private ResidueProperties()
index fd76086..44ce010 100644 (file)
@@ -60,6 +60,26 @@ public class ColorUtils
   }
 
   /**
+   * Convert to Tk colour code format
+   * 
+   * @param colour
+   * @return
+   * @see http
+   *      ://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/colortool.html#
+   *      tkcode
+   */
+  public static final String toTkCode(Color colour)
+  {
+    String colstring = "#" + ((colour.getRed() < 16) ? "0" : "")
+            + Integer.toHexString(colour.getRed())
+            + ((colour.getGreen() < 16) ? "0" : "")
+            + Integer.toHexString(colour.getGreen())
+            + ((colour.getBlue() < 16) ? "0" : "")
+            + Integer.toHexString(colour.getBlue());
+    return colstring;
+  }
+
+  /**
    * Returns a colour three shades darker. Note you can't guarantee that
    * brighterThan reverses this, as darkerThan may result in black.
    * 
index 1b42faf..a2da591 100644 (file)
@@ -24,6 +24,7 @@ import jalview.analysis.Conservation;
 import jalview.api.AlignCalcManagerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.FeaturesDisplayedI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
@@ -696,6 +697,7 @@ public abstract class AlignmentViewport implements AlignViewportI
     sequenceSetID = new String(newid);
   }
 
+  @Override
   public String getSequenceSetId()
   {
     if (sequenceSetID == null)
@@ -1659,4 +1661,97 @@ public abstract class AlignmentViewport implements AlignViewportI
   {
     sequenceColours = null;
   };
+
+  FeaturesDisplayedI featuresDisplayed = null;
+
+  @Override
+  public FeaturesDisplayedI getFeaturesDisplayed()
+  {
+    return featuresDisplayed;
+  }
+
+  public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
+  {
+    featuresDisplayed = featuresDisplayedI;
+  }
+
+  public boolean areFeaturesDisplayed()
+  {
+    return featuresDisplayed != null && featuresDisplayed.getRegisterdFeaturesCount()>0;
+  }
+
+  /**
+   * display setting for showing/hiding sequence features on alignment view
+   */
+  boolean showSequenceFeatures = false;
+
+  /**
+   * set the flag
+   * 
+   * @param b
+   *          features are displayed if true
+   */
+  @Override
+  public void setShowSequenceFeatures(boolean b)
+  {
+    showSequenceFeatures = b;
+  }
+  @Override
+  public boolean isShowSequenceFeatures()
+  {
+    return showSequenceFeatures;
+  }
+
+  boolean showSeqFeaturesHeight;
+
+  public void setShowSequenceFeaturesHeight(boolean selected)
+  {
+    showSeqFeaturesHeight = selected;
+  }
+
+  public boolean isShowSequenceFeaturesHeight()
+  {
+    return showSeqFeaturesHeight;
+  }
+
+  private boolean showAnnotation = true;
+
+  private boolean rightAlignIds = false;
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  public boolean getShowAnnotation()
+  {
+    return isShowAnnotation();
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param b
+   *          DOCUMENT ME!
+   */
+  public void setShowAnnotation(boolean b)
+  {
+    showAnnotation = b;
+  }
+
+  public boolean isShowAnnotation()
+  {
+    return showAnnotation;
+  }
+
+  public boolean isRightAlignIds()
+  {
+    return rightAlignIds;
+  }
+
+  public void setRightAlignIds(boolean rightAlignIds)
+  {
+    this.rightAlignIds = rightAlignIds;
+  }
+
 }
diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
new file mode 100644 (file)
index 0000000..c7cee04
--- /dev/null
@@ -0,0 +1,949 @@
+package jalview.viewmodel.seqfeatures;
+
+import jalview.api.AlignViewportI;
+import jalview.api.FeaturesDisplayedI;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureRenderer;
+import jalview.schemes.GraduatedColor;
+import jalview.viewmodel.AlignmentViewport;
+
+import java.awt.Color;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public abstract class FeatureRendererModel implements
+        jalview.api.FeatureRenderer
+{
+
+  /**
+   * global transparency for feature
+   */
+  protected float transparency = 1.0f;
+
+  protected Map<String, Object> featureColours = new ConcurrentHashMap<String, Object>();
+
+  protected Map<String, Boolean> featureGroups = new ConcurrentHashMap<String, Boolean>();
+
+  protected Object currentColour;
+
+  protected String[] renderOrder;
+
+  protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+          this);
+
+  protected AlignmentViewport av;
+
+  public AlignViewportI getViewport()
+  {
+    return av;
+  }
+
+  public FeatureRendererSettings getSettings()
+  {
+    return new FeatureRendererSettings(this);
+  }
+
+  public void transferSettings(FeatureRendererSettings fr)
+  {
+    this.renderOrder = fr.renderOrder;
+    this.featureGroups = fr.featureGroups;
+    this.featureColours = fr.featureColours;
+    this.transparency = fr.transparency;
+    this.featureOrder = fr.featureOrder;
+  }
+
+  /**
+   * update from another feature renderer
+   * 
+   * @param fr
+   *          settings to copy
+   */
+  public void transferSettings(jalview.api.FeatureRenderer _fr)
+  {
+    FeatureRenderer fr = (FeatureRenderer) _fr;
+    FeatureRendererSettings frs = new FeatureRendererSettings(fr);
+    this.renderOrder = frs.renderOrder;
+    this.featureGroups = frs.featureGroups;
+    this.featureColours = frs.featureColours;
+    this.transparency = frs.transparency;
+    this.featureOrder = frs.featureOrder;
+    if (av != null && av != fr.getViewport())
+    {
+      // copy over the displayed feature settings
+      if (_fr.getFeaturesDisplayed() != null)
+      {
+        FeaturesDisplayedI fd = getFeaturesDisplayed();
+        if (fd == null)
+        {
+          setFeaturesDisplayedFrom(_fr.getFeaturesDisplayed());
+        }
+        else
+        {
+          synchronized (fd)
+          {
+            fd.clear();
+            java.util.Iterator<String> fdisp = _fr.getFeaturesDisplayed()
+                    .getVisibleFeatures();
+            while (fdisp.hasNext())
+            {
+              fd.setVisible(fdisp.next());
+            }
+          }
+        }
+      }
+    }
+  }
+
+  public void setFeaturesDisplayedFrom(FeaturesDisplayedI featuresDisplayed)
+  {
+    av.setFeaturesDisplayed(new FeaturesDisplayed(featuresDisplayed));
+  }
+
+  @Override
+  public void setVisible(String featureType)
+  {
+    FeaturesDisplayedI fdi = av.getFeaturesDisplayed();
+    if (fdi == null)
+    {
+      av.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
+    }
+    if (!fdi.isRegistered(featureType))
+    {
+      pushFeatureType(Arrays.asList(new String[]
+      { featureType }));
+    }
+    fdi.setVisible(featureType);
+  }
+
+  @Override
+  public void setAllVisible(List<String> featureTypes)
+  {
+    FeaturesDisplayedI fdi = av.getFeaturesDisplayed();
+    if (fdi == null)
+    {
+      av.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
+    }
+    List<String> nft = new ArrayList<String>();
+    for (String featureType : featureTypes)
+    {
+      if (!fdi.isRegistered(featureType))
+      {
+        nft.add(featureType);
+      }
+    }
+    if (nft.size() > 0)
+    {
+      pushFeatureType(nft);
+    }
+    fdi.setAllVisible(featureTypes);
+  }
+
+  /**
+   * push a set of new types onto the render order stack. Note - this is a
+   * direct mechanism rather than the one employed in updateRenderOrder
+   * 
+   * @param types
+   */
+  private void pushFeatureType(List<String> types)
+  {
+
+    int ts = types.size();
+    String neworder[] = new String[(renderOrder == null ? 0
+            : renderOrder.length) + ts];
+    types.toArray(neworder);
+    if (renderOrder != null)
+    {
+      System.arraycopy(neworder,0,neworder,renderOrder.length,ts);
+      System.arraycopy(renderOrder, 0, neworder, 0, renderOrder.length);
+    }
+    renderOrder = neworder;
+  }
+
+  protected Hashtable minmax = new Hashtable();
+
+  public Hashtable getMinMax()
+  {
+    return minmax;
+  }
+
+  /**
+   * normalise a score against the max/min bounds for the feature type.
+   * 
+   * @param sequenceFeature
+   * @return byte[] { signed, normalised signed (-127 to 127) or unsigned
+   *         (0-255) value.
+   */
+  protected final byte[] normaliseScore(SequenceFeature sequenceFeature)
+  {
+    float[] mm = ((float[][]) minmax.get(sequenceFeature.type))[0];
+    final byte[] r = new byte[]
+    { 0, (byte) 255 };
+    if (mm != null)
+    {
+      if (r[0] != 0 || mm[0] < 0.0)
+      {
+        r[0] = 1;
+        r[1] = (byte) ((int) 128.0 + 127.0 * (sequenceFeature.score / mm[1]));
+      }
+      else
+      {
+        r[1] = (byte) ((int) 255.0 * (sequenceFeature.score / mm[1]));
+      }
+    }
+    return r;
+  }
+
+  boolean newFeatureAdded = false;
+
+  boolean findingFeatures = false;
+
+  protected boolean updateFeatures()
+  {
+    if (av.getFeaturesDisplayed() == null || renderOrder == null
+            || newFeatureAdded)
+    {
+      findAllFeatures();
+      if (av.getFeaturesDisplayed().getVisibleFeatureCount() < 1)
+      {
+        return false;
+      }
+    }
+    // TODO: decide if we should check for the visible feature count first
+    return true;
+  }
+
+  /**
+   * search the alignment for all new features, give them a colour and display
+   * them. Then fires a PropertyChangeEvent on the changeSupport object.
+   * 
+   */
+  protected void findAllFeatures()
+  {
+    synchronized (firing)
+    {
+      if (firing.equals(Boolean.FALSE))
+      {
+        firing = Boolean.TRUE;
+        findAllFeatures(true); // add all new features as visible
+        changeSupport.firePropertyChange("changeSupport", null, null);
+        firing = Boolean.FALSE;
+      }
+    }
+  }
+
+  @Override
+  public List<SequenceFeature> findFeaturesAtRes(SequenceI sequence, int res)
+  {
+    ArrayList<SequenceFeature> tmp = new ArrayList<SequenceFeature>();
+    SequenceFeature[] features = sequence.getSequenceFeatures();
+
+    while (features == null && sequence.getDatasetSequence() != null)
+    {
+      sequence = sequence.getDatasetSequence();
+      features = sequence.getSequenceFeatures();
+    }
+
+    if (features != null)
+    {
+      for (int i = 0; i < features.length; i++)
+      {
+        if (!av.areFeaturesDisplayed()
+                || !av.getFeaturesDisplayed().isVisible(
+                        features[i].getType()))
+        {
+          continue;
+        }
+
+        if (features[i].featureGroup != null
+                && featureGroups != null
+                && featureGroups.containsKey(features[i].featureGroup)
+                && !featureGroups.get(features[i].featureGroup)
+                        .booleanValue())
+        {
+          continue;
+        }
+
+        if ((features[i].getBegin() <= res)
+                && (features[i].getEnd() >= res))
+        {
+          tmp.add(features[i]);
+        }
+      }
+    }
+    return tmp;
+  }
+
+  /**
+   * Searches alignment for all features and updates colours
+   * 
+   * @param newMadeVisible
+   *          if true newly added feature types will be rendered immediatly
+   *          TODO: check to see if this method should actually be proxied so
+   *          repaint events can be propagated by the renderer code
+   */
+  @Override
+  public synchronized void findAllFeatures(boolean newMadeVisible)
+  {
+    newFeatureAdded = false;
+
+    if (findingFeatures)
+    {
+      newFeatureAdded = true;
+      return;
+    }
+
+    findingFeatures = true;
+    if (av.getFeaturesDisplayed() == null)
+    {
+      av.setFeaturesDisplayed(new FeaturesDisplayed());
+    }
+    FeaturesDisplayedI featuresDisplayed = av.getFeaturesDisplayed();
+
+    ArrayList<String> allfeatures = new ArrayList<String>();
+    ArrayList<String> oldfeatures = new ArrayList<String>();
+    if (renderOrder != null)
+    {
+      for (int i = 0; i < renderOrder.length; i++)
+      {
+        if (renderOrder[i] != null)
+        {
+          oldfeatures.add(renderOrder[i]);
+        }
+      }
+    }
+    if (minmax == null)
+    {
+      minmax = new Hashtable();
+    }
+    AlignmentI alignment = av.getAlignment();
+    for (int i = 0; i < alignment.getHeight(); i++)
+    {
+      SequenceI asq = alignment.getSequenceAt(i);
+      SequenceI dasq = asq.getDatasetSequence();
+      SequenceFeature[] features = dasq != null ? dasq
+              .getSequenceFeatures() : asq.getSequenceFeatures();
+
+      if (features == null)
+      {
+        continue;
+      }
+
+      int index = 0;
+      while (index < features.length)
+      {
+        if (!featuresDisplayed.isRegistered(features[index].getType()))
+        {
+          String fgrp = features[index].getFeatureGroup();
+          if (fgrp != null)
+          {
+            Boolean groupDisplayed = featureGroups.get(fgrp);
+            if (groupDisplayed == null)
+            {
+              groupDisplayed = Boolean.valueOf(newMadeVisible);
+              featureGroups.put(fgrp, groupDisplayed);
+            }
+            if (!groupDisplayed.booleanValue())
+            {
+              index++;
+              continue;
+            }
+          }
+          if (!(features[index].begin == 0 && features[index].end == 0))
+          {
+            // If beginning and end are 0, the feature is for the whole sequence
+            // and we don't want to render the feature in the normal way
+
+            if (newMadeVisible
+                    && !oldfeatures.contains(features[index].getType()))
+            {
+              // this is a new feature type on the alignment. Mark it for
+              // display.
+              featuresDisplayed.setVisible(features[index].getType());
+              setOrder(features[index].getType(), 0);
+            }
+          }
+        }
+        if (!allfeatures.contains(features[index].getType()))
+        {
+          allfeatures.add(features[index].getType());
+        }
+        if (features[index].score != Float.NaN)
+        {
+          int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
+          float[][] mm = (float[][]) minmax.get(features[index].getType());
+          if (mm == null)
+          {
+            mm = new float[][]
+            { null, null };
+            minmax.put(features[index].getType(), mm);
+          }
+          if (mm[nonpos] == null)
+          {
+            mm[nonpos] = new float[]
+            { features[index].score, features[index].score };
+
+          }
+          else
+          {
+            if (mm[nonpos][0] > features[index].score)
+            {
+              mm[nonpos][0] = features[index].score;
+            }
+            if (mm[nonpos][1] < features[index].score)
+            {
+              mm[nonpos][1] = features[index].score;
+            }
+          }
+        }
+        index++;
+      }
+    }
+    updateRenderOrder(allfeatures);
+    findingFeatures = false;
+  }
+
+  protected Boolean firing = Boolean.FALSE;
+
+  /**
+   * replaces the current renderOrder with the unordered features in
+   * allfeatures. The ordering of any types in both renderOrder and allfeatures
+   * is preserved, and all new feature types are rendered on top of the existing
+   * types, in the order given by getOrder or the order given in allFeatures.
+   * Note. this operates directly on the featureOrder hash for efficiency. TODO:
+   * eliminate the float storage for computing/recalling the persistent ordering
+   * New Cability: updates min/max for colourscheme range if its dynamic
+   * 
+   * @param allFeatures
+   */
+  private void updateRenderOrder(List<String> allFeatures)
+  {
+    List<String> allfeatures = new ArrayList<String>(allFeatures);
+    String[] oldRender = renderOrder;
+    renderOrder = new String[allfeatures.size()];
+    Object mmrange, fc = null;
+    boolean initOrders = (featureOrder == null);
+    int opos = 0;
+    if (oldRender != null && oldRender.length > 0)
+    {
+      for (int j = 0; j < oldRender.length; j++)
+      {
+        if (oldRender[j] != null)
+        {
+          if (initOrders)
+          {
+            setOrder(oldRender[j], (1 - (1 + (float) j)
+                    / oldRender.length));
+          }
+          if (allfeatures.contains(oldRender[j]))
+          {
+            renderOrder[opos++] = oldRender[j]; // existing features always
+            // appear below new features
+            allfeatures.remove(oldRender[j]);
+            if (minmax != null)
+            {
+              mmrange = minmax.get(oldRender[j]);
+              if (mmrange != null)
+              {
+                fc = featureColours.get(oldRender[j]);
+                if (fc != null && fc instanceof GraduatedColor
+                        && ((GraduatedColor) fc).isAutoScale())
+                {
+                  ((GraduatedColor) fc).updateBounds(
+                          ((float[][]) mmrange)[0][0],
+                          ((float[][]) mmrange)[0][1]);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    if (allfeatures.size() == 0)
+    {
+      // no new features - leave order unchanged.
+      return;
+    }
+    int i = allfeatures.size() - 1;
+    int iSize = i;
+    boolean sort = false;
+    String[] newf = new String[allfeatures.size()];
+    float[] sortOrder = new float[allfeatures.size()];
+    for (String newfeat : allfeatures)
+    {
+      newf[i] = newfeat;
+      if (minmax != null)
+      {
+        // update from new features minmax if necessary
+        mmrange = minmax.get(newf[i]);
+        if (mmrange != null)
+        {
+          fc = featureColours.get(newf[i]);
+          if (fc != null && fc instanceof GraduatedColor
+                  && ((GraduatedColor) fc).isAutoScale())
+          {
+            ((GraduatedColor) fc).updateBounds(((float[][]) mmrange)[0][0],
+                    ((float[][]) mmrange)[0][1]);
+          }
+        }
+      }
+      if (initOrders || !featureOrder.containsKey(newf[i]))
+      {
+        int denom = initOrders ? allfeatures.size() : featureOrder.size();
+        // new unordered feature - compute persistent ordering at head of
+        // existing features.
+        setOrder(newf[i], i / (float) denom);
+      }
+      // set order from newly found feature from persisted ordering.
+      sortOrder[i] = 2 - ((Float) featureOrder.get(newf[i])).floatValue();
+      if (i < iSize)
+      {
+        // only sort if we need to
+        sort = sort || sortOrder[i] > sortOrder[i + 1];
+      }
+      i--;
+    }
+    if (iSize > 1 && sort)
+    {
+      jalview.util.QuickSort.sort(sortOrder, newf);
+    }
+    sortOrder = null;
+    System.arraycopy(newf, 0, renderOrder, opos, newf.length);
+  }
+
+  /**
+   * get a feature style object for the given type string. Creates a
+   * java.awt.Color for a featureType with no existing colourscheme. TODO:
+   * replace return type with object implementing standard abstract colour/style
+   * interface
+   * 
+   * @param featureType
+   * @return java.awt.Color or GraduatedColor
+   */
+  public Object getFeatureStyle(String featureType)
+  {
+    Object fc = featureColours.get(featureType);
+    if (fc == null)
+    {
+      jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
+      Color col = ucs.createColourFromName(featureType);
+      featureColours.put(featureType, fc = col);
+    }
+    return fc;
+  }
+
+  /**
+   * return a nominal colour for this feature
+   * 
+   * @param featureType
+   * @return standard color, or maximum colour for graduated colourscheme
+   */
+  public Color getColour(String featureType)
+  {
+    Object fc = getFeatureStyle(featureType);
+
+    if (fc instanceof Color)
+    {
+      return (Color) fc;
+    }
+    else
+    {
+      if (fc instanceof GraduatedColor)
+      {
+        return ((GraduatedColor) fc).getMaxColor();
+      }
+    }
+    throw new Error("Implementation Error: Unrecognised render object "
+            + fc.getClass() + " for features of type " + featureType);
+  }
+
+  /**
+   * calculate the render colour for a specific feature using current feature
+   * settings.
+   * 
+   * @param feature
+   * @return render colour for the given feature
+   */
+  public Color getColour(SequenceFeature feature)
+  {
+    Object fc = getFeatureStyle(feature.getType());
+    if (fc instanceof Color)
+    {
+      return (Color) fc;
+    }
+    else
+    {
+      if (fc instanceof GraduatedColor)
+      {
+        return ((GraduatedColor) fc).findColor(feature);
+      }
+    }
+    throw new Error("Implementation Error: Unrecognised render object "
+            + fc.getClass() + " for features of type " + feature.getType());
+  }
+
+  protected boolean showFeature(SequenceFeature sequenceFeature)
+  {
+    Object fc = getFeatureStyle(sequenceFeature.type);
+    if (fc instanceof GraduatedColor)
+    {
+      return ((GraduatedColor) fc).isColored(sequenceFeature);
+    }
+    else
+    {
+      return true;
+    }
+  }
+
+  protected boolean showFeatureOfType(String type)
+  {
+    return av.getFeaturesDisplayed().isVisible(type);
+  }
+
+  public void setColour(String featureType, Object col)
+  {
+    // overwrite
+    // Color _col = (col instanceof Color) ? ((Color) col) : (col instanceof
+    // GraduatedColor) ? ((GraduatedColor) col).getMaxColor() : null;
+    // Object c = featureColours.get(featureType);
+    // if (c == null || c instanceof Color || (c instanceof GraduatedColor &&
+    // !((GraduatedColor)c).getMaxColor().equals(_col)))
+    {
+      featureColours.put(featureType, col);
+    }
+  }
+
+  public void setTransparency(float value)
+  {
+    transparency = value;
+  }
+
+  public float getTransparency()
+  {
+    return transparency;
+  }
+
+  Map featureOrder = null;
+
+  /**
+   * analogous to colour - store a normalized ordering for all feature types in
+   * this rendering context.
+   * 
+   * @param type
+   *          Feature type string
+   * @param position
+   *          normalized priority - 0 means always appears on top, 1 means
+   *          always last.
+   */
+  public float setOrder(String type, float position)
+  {
+    if (featureOrder == null)
+    {
+      featureOrder = new Hashtable();
+    }
+    featureOrder.put(type, new Float(position));
+    return position;
+  }
+
+  /**
+   * get the global priority (0 (top) to 1 (bottom))
+   * 
+   * @param type
+   * @return [0,1] or -1 for a type without a priority
+   */
+  public float getOrder(String type)
+  {
+    if (featureOrder != null)
+    {
+      if (featureOrder.containsKey(type))
+      {
+        return ((Float) featureOrder.get(type)).floatValue();
+      }
+    }
+    return -1;
+  }
+
+  @Override
+  public Map<String, Object> getFeatureColours()
+  {
+    return new ConcurrentHashMap<String, Object>(featureColours);
+  }
+
+  /**
+   * Replace current ordering with new ordering
+   * 
+   * @param data
+   *          { String(Type), Colour(Type), Boolean(Displayed) }
+   */
+  public void setFeaturePriority(Object[][] data)
+  {
+    setFeaturePriority(data, true);
+  }
+
+  /**
+   * 
+   * @param data
+   *          { String(Type), Colour(Type), Boolean(Displayed) }
+   * @param visibleNew
+   *          when true current featureDisplay list will be cleared
+   */
+  public void setFeaturePriority(Object[][] data, boolean visibleNew)
+  {
+    FeaturesDisplayedI av_featuresdisplayed = null;
+    if (visibleNew)
+    {
+      if ((av_featuresdisplayed = av.getFeaturesDisplayed()) != null)
+      {
+        av.getFeaturesDisplayed().clear();
+      }
+      else
+      {
+        av.setFeaturesDisplayed(av_featuresdisplayed = new FeaturesDisplayed());
+      }
+    }
+    else
+    {
+      av_featuresdisplayed = av.getFeaturesDisplayed();
+    }
+    if (data == null)
+    {
+      return;
+    }
+    // The feature table will display high priority
+    // features at the top, but theses are the ones
+    // we need to render last, so invert the data
+    renderOrder = new String[data.length];
+
+    if (data.length > 0)
+    {
+      for (int i = 0; i < data.length; i++)
+      {
+        String type = data[i][0].toString();
+        setColour(type, data[i][1]); // todo : typesafety - feature color
+        // interface object
+        if (((Boolean) data[i][2]).booleanValue())
+        {
+          av_featuresdisplayed.setVisible(type);
+        }
+
+        renderOrder[data.length - i - 1] = type;
+      }
+    }
+
+  }
+
+  /**
+   * @param listener
+   * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
+   */
+  public void addPropertyChangeListener(PropertyChangeListener listener)
+  {
+    changeSupport.addPropertyChangeListener(listener);
+  }
+
+  /**
+   * @param listener
+   * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
+   */
+  public void removePropertyChangeListener(PropertyChangeListener listener)
+  {
+    changeSupport.removePropertyChangeListener(listener);
+  }
+
+  public Set getAllFeatureColours()
+  {
+    return featureColours.keySet();
+  }
+
+  public void clearRenderOrder()
+  {
+    renderOrder = null;
+  }
+
+  public boolean hasRenderOrder()
+  {
+    return renderOrder != null;
+  }
+
+  public List<String> getRenderOrder()
+  {
+    if (renderOrder == null)
+    {
+      return Arrays.asList(new String[]
+      {});
+    }
+    return Arrays.asList(renderOrder);
+  }
+
+  public int getFeatureGroupsSize()
+  {
+    return featureGroups != null ? 0 : featureGroups.size();
+  }
+
+  @Override
+  public List<String> getFeatureGroups()
+  {
+    // conflict between applet and desktop - featureGroups returns the map in
+    // the desktop featureRenderer
+    return (featureGroups == null) ? Arrays.asList(new String[0]) : Arrays
+            .asList(featureGroups.keySet().toArray(new String[0]));
+  }
+
+  public boolean checkGroupVisibility(String group, boolean newGroupsVisible)
+  {
+    if (featureGroups == null)
+    {
+      // then an exception happens next..
+    }
+    if (featureGroups.containsKey(group))
+    {
+      return featureGroups.get(group).booleanValue();
+    }
+    if (newGroupsVisible)
+    {
+      featureGroups.put(group, new Boolean(true));
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * get visible or invisible groups
+   * 
+   * @param visible
+   *          true to return visible groups, false to return hidden ones.
+   * @return list of groups
+   */
+  @Override
+  public List getGroups(boolean visible)
+  {
+    if (featureGroups != null)
+    {
+      ArrayList gp = new ArrayList();
+
+      for (Object grp : featureGroups.keySet())
+      {
+        Boolean state = featureGroups.get(grp);
+        if (state.booleanValue() == visible)
+        {
+          gp.add(grp);
+        }
+      }
+      return gp;
+    }
+    return null;
+  }
+
+  @Override
+  public void setGroupVisibility(String group, boolean visible)
+  {
+    featureGroups.put(group, new Boolean(visible));
+  }
+
+  @Override
+  public void setGroupVisibility(List<String> toset, boolean visible)
+  {
+    if (toset != null && toset.size() > 0 && featureGroups != null)
+    {
+      boolean rdrw = false;
+      for (String gst : toset)
+      {
+        Boolean st = featureGroups.get(gst);
+        featureGroups.put(gst, new Boolean(visible));
+        if (st != null)
+        {
+          rdrw = rdrw || (visible != st.booleanValue());
+        }
+      }
+      if (rdrw)
+      {
+        // set local flag indicating redraw needed ?
+      }
+    }
+  }
+
+  @Override
+  public Hashtable getDisplayedFeatureCols()
+  {
+    Hashtable fcols = new Hashtable();
+    if (getViewport().getFeaturesDisplayed() == null)
+    {
+      return fcols;
+    }
+    Iterator<String> en = getViewport().getFeaturesDisplayed()
+            .getVisibleFeatures();
+    while (en.hasNext())
+    {
+      String col = en.next();
+      fcols.put(col, getColour(col));
+    }
+    return fcols;
+  }
+
+  @Override
+  public FeaturesDisplayedI getFeaturesDisplayed()
+  {
+    return av.getFeaturesDisplayed();
+  }
+
+  @Override
+  public String[] getDisplayedFeatureTypes()
+  {
+    String[] typ = null;
+    typ = getRenderOrder().toArray(new String[0]);
+    FeaturesDisplayedI feature_disp = av.getFeaturesDisplayed();
+    if (feature_disp != null)
+    {
+      synchronized (feature_disp)
+      {
+        for (int i = 0; i < typ.length; i++)
+        {
+          if (!feature_disp.isVisible(typ[i]))
+          {
+            typ[i] = null;
+          }
+        }
+      }
+    }
+    return typ;
+  }
+
+  @Override
+  public String[] getDisplayedFeatureGroups()
+  {
+    String[] gps = null;
+    ArrayList<String> _gps = new ArrayList<String>();
+    Iterator en = getFeatureGroups().iterator();
+    int g = 0;
+    boolean valid = false;
+    while (en.hasNext())
+    {
+      String gp = (String) en.next();
+      if (checkGroupVisibility(gp, false))
+      {
+        valid = true;
+        _gps.add(gp);
+      }
+      if (!valid)
+      {
+        return null;
+      }
+      else
+      {
+        gps = new String[_gps.size()];
+        _gps.toArray(gps);
+      }
+    }
+    return gps;
+  }
+
+}
diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java
new file mode 100644 (file)
index 0000000..6e85b83
--- /dev/null
@@ -0,0 +1,77 @@
+package jalview.viewmodel.seqfeatures;
+
+import jalview.schemes.GraduatedColor;
+
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class FeatureRendererSettings implements Cloneable
+{
+  String[] renderOrder;
+
+  Map featureGroups;
+
+  Map featureColours;
+
+  float transparency;
+
+  Map featureOrder;
+
+  public FeatureRendererSettings(String[] renderOrder,
+          Hashtable featureGroups, Hashtable featureColours,
+          float transparency, Hashtable featureOrder)
+  {
+    super();
+    this.renderOrder = Arrays.copyOf(renderOrder,renderOrder.length);
+    this.featureGroups = new ConcurrentHashMap(featureGroups);
+    this.featureColours = new ConcurrentHashMap(featureColours);
+    this.transparency = transparency;
+    this.featureOrder = new ConcurrentHashMap(featureOrder);
+  }
+
+  /**
+   * create an independent instance of the feature renderer settings
+   * 
+   * @param fr
+   */
+  public FeatureRendererSettings(
+          jalview.viewmodel.seqfeatures.FeatureRendererModel fr)
+  {
+    renderOrder = null;
+    featureGroups = new ConcurrentHashMap();
+    featureColours = new ConcurrentHashMap();
+    featureOrder = new ConcurrentHashMap();
+    if (fr.renderOrder != null)
+    {
+      this.renderOrder = new String[fr.renderOrder.length];
+      System.arraycopy(fr.renderOrder, 0, renderOrder, 0,
+              fr.renderOrder.length);
+    }
+    if (fr.featureGroups != null)
+    {
+      this.featureGroups = new ConcurrentHashMap(fr.featureGroups);
+    }
+    if (fr.featureColours != null)
+    {
+      this.featureColours = new ConcurrentHashMap(fr.featureColours);
+    }
+    Iterator en = fr.featureColours.keySet().iterator();
+    while (en.hasNext())
+    {
+      Object next = en.next();
+      Object val = featureColours.get(next);
+      if (val instanceof GraduatedColor)
+      {
+        featureColours.put(next, new GraduatedColor((GraduatedColor) val));
+      }
+    }
+    this.transparency = fr.transparency;
+    if (fr.featureOrder != null)
+    {
+      this.featureOrder = new ConcurrentHashMap(fr.featureOrder);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/jalview/viewmodel/seqfeatures/FeatureSettingsModel.java b/src/jalview/viewmodel/seqfeatures/FeatureSettingsModel.java
new file mode 100644 (file)
index 0000000..57d57da
--- /dev/null
@@ -0,0 +1,8 @@
+package jalview.viewmodel.seqfeatures;
+
+import jalview.api.FeatureSettingsModelI;
+
+public class FeatureSettingsModel implements FeatureSettingsModelI
+{
+
+}
diff --git a/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java b/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java
new file mode 100644 (file)
index 0000000..b04764c
--- /dev/null
@@ -0,0 +1,94 @@
+package jalview.viewmodel.seqfeatures;
+
+import jalview.api.FeaturesDisplayedI;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+
+public class FeaturesDisplayed implements FeaturesDisplayedI
+{
+  private HashSet<String> featuresDisplayed = new HashSet<String>();
+
+  private HashSet<String> featuresRegistered = new HashSet<String>();
+
+  public FeaturesDisplayed(FeaturesDisplayedI featuresDisplayed2)
+  {
+    Iterator<String> fdisp = featuresDisplayed2.getVisibleFeatures();
+    String ftype;
+    while (fdisp.hasNext())
+    {
+      ftype = fdisp.next();
+      featuresDisplayed.add(ftype);
+      featuresRegistered.add(ftype);
+    }
+  }
+
+  public FeaturesDisplayed()
+  {
+    // TODO Auto-generated constructor stub
+  }
+
+  @Override
+  public Iterator<String> getVisibleFeatures()
+  {
+    return featuresDisplayed.iterator();
+  }
+
+  @Override
+  public boolean isVisible(String featureType)
+  {
+    return featuresDisplayed.contains(featureType);
+  }
+
+  @Override
+  public boolean areVisible(Collection featureTypes)
+  {
+    return featuresDisplayed.containsAll(featureTypes);
+  }
+
+  @Override
+  public void clear()
+  {
+    featuresDisplayed.clear();
+    featuresRegistered.clear();
+  }
+
+  @Override
+  public void setAllVisible(Collection makeVisible)
+  {
+    featuresDisplayed.addAll(makeVisible);
+    featuresRegistered.addAll(makeVisible);
+  }
+
+  @Override
+  public void setAllRegisteredVisible()
+  {
+    featuresDisplayed.addAll(featuresRegistered);
+  }
+
+  @Override
+  public void setVisible(String featureType)
+  {
+    featuresDisplayed.add(featureType);
+    featuresRegistered.add(featureType);
+  }
+
+  @Override
+  public boolean isRegistered(String type)
+  {
+    return featuresRegistered.contains(type);
+  }
+
+  @Override
+  public int getVisibleFeatureCount()
+  {
+    return featuresDisplayed.size();
+  }
+
+  @Override
+  public int getRegisterdFeaturesCount()
+  {
+    return featuresRegistered.size();
+  }
+}
index edb56a9..d3682d4 100644 (file)
@@ -28,7 +28,7 @@ import jalview.datamodel.AlignmentView;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.WebserviceInfo;
-import jalview.gui.FeatureRenderer.FeatureRendererSettings;
+import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 import jalview.util.MessageManager;
 
 public abstract class AWSThread extends Thread
index 6c438be..929581d 100644 (file)
@@ -351,7 +351,7 @@ public class AADisorderClient extends JabawsCalcWorker implements
       {
         if (dispFeatures)
         {
-          jalview.gui.FeatureRenderer fr = ((jalview.gui.AlignmentPanel) ap)
+          jalview.api.FeatureRenderer fr = ((jalview.gui.AlignmentPanel) ap)
                   .cloneFeatureRenderer();
           for (String ft : fc.keySet())
           {
diff --git a/test/jalview/analysis/scoremodels/FeatureScoreModelTest.java b/test/jalview/analysis/scoremodels/FeatureScoreModelTest.java
new file mode 100644 (file)
index 0000000..1dbaa4a
--- /dev/null
@@ -0,0 +1,74 @@
+package jalview.analysis.scoremodels;
+
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.io.FileLoader;
+import jalview.io.FormatAdapter;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class FeatureScoreModelTest
+{
+  public static String alntestFile = "FER1_MESCR/72-76 DVYIL\nFER1_SPIOL/71-75 DVYIL\nFER3_RAPSA/21-25 DVYVL\nFER1_MAIZE/73-77 DVYIL\n";
+
+  int[] sf1 = new int[]
+  { 74, 74, 73, 73, 23, 23, -1, -1 };
+
+  int[] sf2 = new int[]
+  { -1, -1, 74, 75, -1, -1, 76, 77 };
+
+  int[] sf3 = new int[]
+  { -1, -1, -1, -1, -1, -1, 76, 77 };
+
+  @Test
+  public void testFeatureScoreModel() throws Exception
+  {
+    AlignFrame alf = new FileLoader(false).LoadFileWaitTillLoaded(alntestFile,
+            FormatAdapter.PASTE);
+    AlignmentI al = alf.getViewport().getAlignment();
+    Assert.assertEquals(4, al.getHeight());
+    Assert.assertEquals(5, al.getWidth());
+    for (int i = 0; i < 4; i++)
+    {
+      SequenceI ds = al.getSequenceAt(i).getDatasetSequence();
+      if (sf1[i * 2] > 0)
+      {
+        ds.addSequenceFeature(new SequenceFeature("sf1", "sf1", "sf1",
+                sf1[i * 2], sf1[i * 2 + 1], "sf1"));
+      }
+      if (sf2[i * 2] > 0)
+      {
+        ds.addSequenceFeature(new SequenceFeature("sf2", "sf2", "sf2",
+                sf2[i * 2], sf2[i * 2 + 1], "sf2"));
+      }
+      if (sf3[i * 2] > 0)
+      {
+        ds.addSequenceFeature(new SequenceFeature("sf3", "sf3", "sf3",
+                sf3[i * 2], sf3[i * 2 + 1], "sf3"));
+      }
+    }
+    alf.setShowSeqFeatures(true);
+    alf.getFeatureRenderer().setVisible("sf1");
+    alf.getFeatureRenderer().setVisible("sf2");
+    alf.getFeatureRenderer().setVisible("sf3");
+    alf.getFeatureRenderer().findAllFeatures(true);
+    Assert.assertEquals("Number of feature types", 3, alf
+            .getFeatureRenderer().getDisplayedFeatureTypes().length);
+    Assert.assertTrue(alf.getCurrentView().areFeaturesDisplayed());
+    FeatureScoreModel fsm = new FeatureScoreModel();
+    Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
+            .getAlignPanel()));
+    alf.selectAllSequenceMenuItem_actionPerformed(null);
+    float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView(
+            true));
+    Assert.assertTrue("FER1_MESCR should be identical with RAPSA (2)",
+            dm[0][2] == 0f);
+    Assert.assertTrue(
+            "FER1_MESCR should be further from SPIOL (1) than it is from RAPSA (2)",
+            dm[0][1] > dm[0][2]);
+
+  }
+}
diff --git a/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java b/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java
new file mode 100644 (file)
index 0000000..7dfbba1
--- /dev/null
@@ -0,0 +1,92 @@
+package jalview.ext.rbvi.chimera;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.awt.Color;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+public class ChimeraCommandsTest
+{
+  @Test
+  public void testAddColourRange()
+  {
+    Map<Color, Map<Integer, Map<String, List<int[]>>>> map = new LinkedHashMap<Color, Map<Integer, Map<String, List<int[]>>>>();
+    ChimeraCommands.addColourRange(map, Color.pink, 1, 2, 4, "A");
+    ChimeraCommands.addColourRange(map, Color.pink, 1, 8, 8, "A");
+    ChimeraCommands.addColourRange(map, Color.pink, 1, 5, 7, "B");
+    ChimeraCommands.addColourRange(map, Color.red, 1, 3, 5, "A");
+    ChimeraCommands.addColourRange(map, Color.red, 0, 1, 4, "B");
+    ChimeraCommands.addColourRange(map, Color.orange, 0, 5, 9, "C");
+
+    // three colours mapped
+    assertEquals(3, map.keySet().size());
+
+    // Red has two models, Pink and Orange one each
+    assertEquals(2, map.get(Color.red).keySet().size());
+    assertEquals(1, map.get(Color.orange).keySet().size());
+    assertEquals(1, map.get(Color.pink).keySet().size());
+
+    // pink model 1 has two chains, red.0 / red.1 / orange.0 one each
+    assertEquals(2, map.get(Color.pink).get(1).keySet().size());
+    assertEquals(1, map.get(Color.red).get(0).keySet().size());
+    assertEquals(1, map.get(Color.red).get(1).keySet().size());
+    assertEquals(1, map.get(Color.orange).get(0).keySet().size());
+
+    // inspect positions
+    List<int[]> posList = map.get(Color.pink).get(1).get("A");
+    assertEquals(2, posList.size());
+    assertTrue(Arrays.equals(new int[]
+      { 2, 4 }, posList.get(0)));
+    assertTrue(Arrays.equals(new int[]
+      { 8, 8 }, posList.get(1)));
+
+    posList = map.get(Color.pink).get(1).get("B");
+    assertEquals(1, posList.size());
+    assertTrue(Arrays.equals(new int[]
+      { 5, 7 }, posList.get(0)));
+
+    posList = map.get(Color.red).get(0).get("B");
+    assertEquals(1, posList.size());
+    assertTrue(Arrays.equals(new int[]
+      { 1, 4 }, posList.get(0)));
+
+    posList = map.get(Color.red).get(1).get("A");
+    assertEquals(1, posList.size());
+    assertTrue(Arrays.equals(new int[]
+      { 3, 5 }, posList.get(0)));
+
+    posList = map.get(Color.orange).get(0).get("C");
+    assertEquals(1, posList.size());
+    assertTrue(Arrays.equals(new int[]
+      { 5, 9 }, posList.get(0)));
+  }
+
+  @Test
+  public void testBuildColourCommands()
+  {
+
+    Map<Color, Map<Integer, Map<String, List<int[]>>>> map = new LinkedHashMap<Color, Map<Integer, Map<String, List<int[]>>>>();
+    ChimeraCommands.addColourRange(map, Color.blue, 0, 2, 5, "A");
+    ChimeraCommands.addColourRange(map, Color.blue, 0, 7, 7, "B");
+    ChimeraCommands.addColourRange(map, Color.blue, 0, 9, 23, "A");
+    ChimeraCommands.addColourRange(map, Color.blue, 1, 1, 1, "A");
+    ChimeraCommands.addColourRange(map, Color.blue, 1, 4, 7, "B");
+    ChimeraCommands.addColourRange(map, Color.yellow, 1, 8, 8, "A");
+    ChimeraCommands.addColourRange(map, Color.yellow, 1, 3, 5, "A");
+    ChimeraCommands.addColourRange(map, Color.red, 0, 3, 5, "A");
+
+    // Colours should appear in the Chimera command in the order in which
+    // they were added; within colour, by model, by chain, and positions as
+    // added
+    String command = ChimeraCommands.buildColourCommands(map).get(0);
+    assertEquals(
+            "color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B; color #ffff00 #1:8.A,3-5.A; color #ff0000 #0:3-5.A",
+            command);
+  }
+}
diff --git a/test/jalview/gui/SequenceRendererTest.java b/test/jalview/gui/SequenceRendererTest.java
new file mode 100644 (file)
index 0000000..3f8b96a
--- /dev/null
@@ -0,0 +1,36 @@
+package jalview.gui;
+
+import static org.junit.Assert.assertEquals;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.schemes.ZappoColourScheme;
+
+import java.awt.Color;
+
+import org.junit.Test;
+
+public class SequenceRendererTest
+{
+
+  @Test
+  public void testGetResidueBoxColour_zappo()
+  {
+    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.getResidueColour(seq, 0, null)); // M
+    assertEquals(Color.green, sr.getResidueColour(seq, 2, null)); // T
+    assertEquals(Color.magenta, sr.getResidueColour(seq, 5, null)); // G
+    assertEquals(Color.orange, sr.getResidueColour(seq, 12, null)); // F
+  }
+  // TODO more tests for getResidueBoxColour covering groups, feature rendering,
+  // gaps, overview...
+
+}
index da2e6ca..3bbcf27 100644 (file)
@@ -39,4 +39,17 @@ public class ColorUtilsTest
             ColorUtils.brighterThan(darkColour));
     assertNull(ColorUtils.brighterThan(null));
   }
+
+  /**
+   * @see http://www.rtapo.com/notes/named_colors.html
+   */
+  @Test
+  public void testToTkCode()
+  {
+    assertEquals("#fffafa", ColorUtils.toTkCode(new Color(255, 250, 250))); // snow
+    assertEquals("#e6e6fa", ColorUtils.toTkCode(new Color(230, 230, 250))); // lavender
+    assertEquals("#dda0dd", ColorUtils.toTkCode(new Color(221, 160, 221))); // plum
+    assertEquals("#800080", ColorUtils.toTkCode(new Color(128, 0, 128))); // purple
+    assertEquals("#00ff00", ColorUtils.toTkCode(new Color(0, 255, 0))); // lime
+  }
 }