JAL-3851 allow multiple instances of Endpoints. Fixes to naming of IGV colour scheme
authorBen Soares <b.soares@dundee.ac.uk>
Fri, 25 Mar 2022 15:17:46 +0000 (15:17 +0000)
committerBen Soares <b.soares@dundee.ac.uk>
Fri, 25 Mar 2022 15:17:46 +0000 (15:17 +0000)
12 files changed:
j11lib/jersey-json-1.19.4.jar [deleted file]
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/gui/AlignFrame.java
src/jalview/gui/CutAndPasteTransfer.java
src/jalview/rest/AbstractEndpoint.java
src/jalview/rest/AbstractEndpointAsync.java
src/jalview/rest/InputAlignmentEndpoint.java
src/jalview/rest/RestHandler.java
src/jalview/schemes/IGVColourScheme.java [new file with mode: 0755]
src/jalview/schemes/JalviewColourScheme.java
src/jalview/schemes/ResidueProperties.java

diff --git a/j11lib/jersey-json-1.19.4.jar b/j11lib/jersey-json-1.19.4.jar
deleted file mode 100644 (file)
index c79271b..0000000
Binary files a/j11lib/jersey-json-1.19.4.jar and /dev/null differ
index 9ce5a63..5921107 100644 (file)
@@ -204,6 +204,11 @@ label.colourScheme_nucleotide = Nucleotide
 label.colourScheme_t-coffeescores = T-Coffee Scores
 label.colourScheme_rnahelices = By RNA Helices
 label.colourScheme_sequenceid = Sequence ID Colour
+label.colourScheme_gecos-flower = Gecos: flower
+label.colourScheme_gecos-blossom = Gecos: blossom
+label.colourScheme_gecos-sunset = Gecos: sunset
+label.colourScheme_gecos-ocean = Gecos: ocean
+label.colourScheme_igv = IGV Nucleotide
 label.blc = BLC
 label.fasta = Fasta
 label.msf = MSF
index fb87f7d..7fb0b58 100644 (file)
@@ -197,6 +197,11 @@ label.colourScheme_nucleotide = Nucle
 label.colourScheme_t-coffeescores = Puntuación del T-Coffee
 label.colourScheme_rnahelices = Por hélices de RNA
 label.colourScheme_sequenceid = Color de ID de secuencia
+label.colourScheme_gecos-flower = Gecos: flower
+label.colourScheme_gecos-blossom = Gecos: blossom
+label.colourScheme_gecos-sunset = Gecos: sunset
+label.colourScheme_gecos-ocean = Gecos: ocean
+label.colourScheme_igv = IGV Nucleótido
 label.blc = BLC
 label.fasta = Fasta
 label.msf = MSF
index 168f6c5..237e0c6 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.gui;
 
-import java.util.Locale;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -60,6 +58,7 @@ import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Vector;
 import java.util.concurrent.CompletableFuture;
@@ -3076,11 +3075,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void wrapMenuItem_actionPerformed(ActionEvent e)
   {
-    scaleAbove.setVisible(wrapMenuItem.isSelected());
-    scaleLeft.setVisible(wrapMenuItem.isSelected());
-    scaleRight.setVisible(wrapMenuItem.isSelected());
-    viewport.setWrapAlignment(wrapMenuItem.isSelected());
+    wrapMenuItem_actionPerformed(e, wrapMenuItem.isSelected());
+  }
+
+  public void wrapMenuItem_actionPerformed(ActionEvent e, boolean select)
+  {
+    scaleAbove.setVisible(select);
+    scaleLeft.setVisible(select);
+    scaleRight.setVisible(select);
+    viewport.setWrapAlignment(select);
     alignPanel.updateLayout();
+    wrapMenuItem.setSelected(select);
   }
 
   @Override
@@ -3372,12 +3377,19 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
   {
-    final boolean setVisible = annotationPanelMenuItem.isSelected();
+    annotationPanelMenuItem_actionPerformed(e,
+            annotationPanelMenuItem.isSelected());
+  }
+
+  public void annotationPanelMenuItem_actionPerformed(ActionEvent e,
+          boolean setVisible)
+  {
     viewport.setShowAnnotation(setVisible);
     this.showAllSeqAnnotations.setEnabled(setVisible);
     this.hideAllSeqAnnotations.setEnabled(setVisible);
     this.showAllAlAnnotations.setEnabled(setVisible);
     this.hideAllAlAnnotations.setEnabled(setVisible);
+    annotationPanelMenuItem.setSelected(setVisible);
     alignPanel.updateLayout();
   }
 
index 112d502..091b7dd 100644 (file)
  */
 package jalview.gui;
 
-import jalview.bin.Cache;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.SwingUtilities;
+
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.ComplexAlignFile;
 import jalview.api.FeatureSettingsModelI;
 import jalview.api.FeaturesDisplayedI;
 import jalview.api.FeaturesSourceI;
+import jalview.bin.Cache;
 import jalview.bin.Jalview;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.HiddenColumns;
@@ -45,22 +63,6 @@ import jalview.json.binding.biojson.v1.ColourSchemeMapper;
 import jalview.schemes.ColourSchemeI;
 import jalview.util.MessageManager;
 
-import java.awt.Toolkit;
-import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.DataFlavor;
-import java.awt.datatransfer.StringSelection;
-import java.awt.datatransfer.Transferable;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.MouseEvent;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import javax.swing.JMenuItem;
-import javax.swing.JPopupMenu;
-import javax.swing.SwingUtilities;
-
 /**
  * Cut'n'paste files into the desktop See JAL-1105
  * 
@@ -215,6 +217,11 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
   @Override
   public void ok_actionPerformed(ActionEvent e)
   {
+    ok_actionPerformed(e, new HashMap<String, String>());
+  }
+
+  public void ok_actionPerformed(ActionEvent e, Map<String, String> options)
+  {
     String text = getText();
     if (text.trim().length() < 1)
     {
@@ -251,7 +258,6 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
       FormatAdapter fa = new FormatAdapter(alignpanel);
       al = fa.readFile(getText(), DataSourceType.PASTE, format);
       source = fa.getAlignFile();
-
     } catch (IOException ex)
     {
       JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
@@ -263,9 +269,10 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
 
     if (al != null && al.hasValidSequence())
     {
-      String title = MessageManager
-              .formatMessage("label.input_cut_paste_params", new String[]
-              { format.getName() });
+      String title = options.containsKey("title") ? options.get("title")
+              : MessageManager.formatMessage("label.input_cut_paste_params",
+                      new String[]
+                      { format.getName() });
       FeatureSettingsModelI proxyColourScheme = source
               .getFeatureColourScheme();
 
@@ -320,6 +327,20 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
           af.getViewport().applyFeaturesStyle(proxyColourScheme);
         }
         af.currentFileFormat = format;
+        if (options.containsKey("wrap"))
+        {
+          af.wrapMenuItem_actionPerformed(e,
+                  Boolean.parseBoolean(options.get("wrap")));
+        }
+        if (options.containsKey("showannotations"))
+        {
+          af.annotationPanelMenuItem_actionPerformed(e,
+                  Boolean.parseBoolean(options.get("showannotations")));
+        }
+        if (options.containsKey("colourscheme"))
+        {
+          af.changeColour_actionPerformed(options.get("colourscheme"));
+        }
         Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
                 AlignFrame.DEFAULT_HEIGHT);
         af.setStatus(MessageManager
index c84b7b7..ae9a938 100644 (file)
@@ -2,6 +2,9 @@ package jalview.rest;
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -23,6 +26,16 @@ public abstract class AbstractEndpoint implements EndpointI
 
   private final String description;
 
+  private String[] pathParameters = null;
+
+  private Map<String, String[]> queryParameters = null;
+
+  private Map<String, String> options = null;
+
+  private String id = null;
+
+  private String fromId = null;
+
   public AbstractEndpoint(API api, String path, String name,
           String parameters, String description)
   {
@@ -65,12 +78,89 @@ public abstract class AbstractEndpoint implements EndpointI
    * Shared methods below here
    */
 
+  protected String getId(HttpServletRequest request)
+  {
+    if (id != null)
+      return id;
+    id = getQueryParameter(request, "id");
+    return id;
+  }
+
+  protected String getFromId(HttpServletRequest request)
+  {
+    if (fromId != null)
+      return fromId;
+    fromId = getQueryParameter(request, "fromId");
+    return fromId;
+  }
+
+  protected Map<String, String[]> getQueryParameters(
+          HttpServletRequest request)
+  {
+    if (queryParameters != null)
+      return queryParameters;
+    queryParameters = request.getParameterMap();
+    return queryParameters;
+  }
+
+  protected String getQueryParameter(HttpServletRequest request,
+          String param)
+  {
+    String[] vals = getQueryParameters(request).get(param);
+    return (vals == null || vals.length < 1) ? null : vals[0];
+  }
+
   protected String[] getEndpointPathParameters(HttpServletRequest request)
   {
+    if (pathParameters != null)
+      return pathParameters;
     String pathInfo = request.getPathInfo();
     int slashpos = pathInfo.indexOf('/', 1);
-    return slashpos < 1 ? null
-            : pathInfo.substring(slashpos + 1).split("/");
+    if (slashpos < 1)
+      return null;
+
+    pathParameters = pathInfo.substring(slashpos + 1).split("/");
+    return pathParameters;
+  }
+
+  protected Map<String, String> getOptions(HttpServletRequest request)
+  {
+    if (this.options != null)
+      return this.options;
+    Map<String, String> opts = new HashMap<>();
+    for (Entry<String, String[]> e : getQueryParameters(null).entrySet())
+    {
+      if (e.getKey().startsWith("option:"))
+      {
+        for (int i = 0; i < e.getValue().length; i++)
+        {
+          opts.put(e.getKey().substring(7), e.getValue()[i]);
+        }
+      }
+    }
+
+    String[] params = getEndpointPathParameters(request);
+    if (params != null)
+    {
+      for (int i = 0; i < params.length; i++)
+      {
+        String param = params[i];
+        if (param.startsWith("option:"))
+        {
+          int ePos = param.indexOf("=");
+          if (ePos == -1)
+          {
+            opts.put(param.substring(7), null);
+          }
+          else
+          {
+            opts.put(param.substring(7, ePos), param.substring(ePos + 1));
+          }
+        }
+      }
+    }
+    this.options = opts;
+    return opts;
   }
 
   protected void returnError(HttpServletRequest request,
@@ -161,7 +251,7 @@ public abstract class AbstractEndpoint implements EndpointI
   protected AlignFrame[] getAlignFrames(HttpServletRequest request,
           String idParam, boolean all)
   {
-    String fromIdString = request.getParameter(idParam);
+    String fromIdString = this.getQueryParameter(request, idParam);
 
     if (fromIdString != null)
     {
@@ -187,8 +277,7 @@ public abstract class AbstractEndpoint implements EndpointI
           String idParam)
   {
     AlignFrame[] afs = getAlignFrames(request, idParam, false);
-    return (afs == null || afs.length < 1 || afs[0] == null) ? null
-            : afs[0];
+    return (afs == null || afs.length < 1) ? null : afs[0];
   }
 
   protected boolean deleteFromCache()
index cb1ff34..989bd27 100644 (file)
@@ -71,8 +71,19 @@ public abstract class AbstractEndpointAsync extends AbstractEndpoint
           HttpServletResponse response)
   {
     // should be overridden
-    // must setId(request, extension)
-    setId(request, null);
+    // MUST setId(request, extension)
+    this.setId(request, null);
+    // and do this
+    this.saveParameters(request);
+  }
+
+  protected void saveParameters(HttpServletRequest request)
+  {
+    this.getId(request);
+    this.getFromId(request);
+    this.getEndpointPathParameters(request);
+    this.getQueryParameters(request);
+    this.getOptions(request);
   }
 
   protected abstract void processAsync(HttpServletRequest request,
@@ -143,7 +154,8 @@ public abstract class AbstractEndpointAsync extends AbstractEndpoint
 
   protected String setId(HttpServletRequest request, String extension)
   {
-    String idString = request.getParameter("id");
+    String idString = getId(request);
+    Console.debug("GOT ID '" + idString + "'");
     if (idString == null)
     {
       setIdExtension(extension);
index caad39f..246f511 100644 (file)
@@ -62,13 +62,14 @@ public class InputAlignmentEndpoint extends AbstractEndpointAsync
     isPost = method.equalsIgnoreCase("post");
     hasData = data != null;
 
+    this.saveParameters(request);
+
     if (isPost)
     {
       access = "post";
       try
       {
         body = RestHandler.getInstance().getRequestBody(request, response);
-        Console.debug("BODY='" + body + "'");
       } catch (IOException e)
       {
         returnError(request, response, "could not read POST body");
@@ -143,7 +144,10 @@ public class InputAlignmentEndpoint extends AbstractEndpointAsync
       // use File -> Input Alignment -> from Textbox
       CutAndPasteTransfer cap = new CutAndPasteTransfer();
       cap.setText(content);
-      cap.ok_actionPerformed(null);
+
+      Map<String, String> options = getOptions(request);
+
+      cap.ok_actionPerformed(null, options);
       cap.cancel_actionPerformed(null);
     }
     else if (fileString != null)
index c4ada0e..1575cff 100644 (file)
@@ -24,6 +24,7 @@ import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
 import java.net.BindException;
 import java.util.HashMap;
 import java.util.Map;
@@ -73,13 +74,32 @@ public class RestHandler extends AbstractRequestHandler
   private boolean init = false;
 
   // map of method names and method handlers
-  private Map<String, EndpointI> endpoints = null;
+  private Map<String, AbstractEndpoint> endpoints = null;
 
-  protected Map<String, EndpointI> getEndpoints()
+  protected Map<String, AbstractEndpoint> getEndpoints()
   {
     return endpoints;
   }
 
+  protected AbstractEndpoint getNewEndpoint(String name)
+  {
+    if (getEndpoints() == null)
+    {
+      return null;
+    }
+    try
+    {
+      return getEndpoints().get(name).getClass()
+              .getDeclaredConstructor(API.class).newInstance(this);
+    } catch (InstantiationException | IllegalAccessException
+            | IllegalArgumentException | InvocationTargetException
+            | NoSuchMethodException | SecurityException e)
+    {
+      Console.debug("Could not instantiate new endpoint '" + name + "'", e);
+    }
+    return null;
+  }
+
   /**
    * Singleton instance of this class
    */
@@ -135,13 +155,21 @@ public class RestHandler extends AbstractRequestHandler
     // This "pointless" call to request.getInputStream() seems to preserve the
     // InputStream for use later in getRequestBody.
     request.getInputStream();
-    if (endpoints == null)
+
+    String remoteAddr = request.getRemoteAddr();
+    if (!("127.0.0.1".equals(remoteAddr) || "localhost".equals(remoteAddr)))
+    {
+      returnError(request, response, "Not authorised: " + remoteAddr,
+              HttpServletResponse.SC_UNAUTHORIZED);
+      return;
+    }
+    if (getEndpoints() == null)
     {
       final String queryString = request.getQueryString();
       final String reply = "REST not yet implemented; received "
               + request.getMethod() + ": " + request.getRequestURL()
               + (queryString == null ? "" : "?" + queryString);
-      System.out.println(reply);
+      Console.error(reply);
 
       response.setHeader("Cache-Control", "no-cache/no-store");
       response.setHeader("Content-type", "text/plain");
@@ -152,8 +180,8 @@ public class RestHandler extends AbstractRequestHandler
 
     String endpointName = getRequestedEndpointName(request);
 
-    if (!endpoints.containsKey(endpointName)
-            || endpoints.get(endpointName) == null)
+    if (!getEndpoints().containsKey(endpointName)
+            || getEndpoints().get(endpointName) == null)
     {
 
       response.setHeader("Cache-Control", "no-cache/no-store");
@@ -167,7 +195,7 @@ public class RestHandler extends AbstractRequestHandler
       ContextHandler ch = HttpServer.getInstance().getContextHandler(this);
       String base = HttpServer.getInstance().getUri().toString();
       String contextPath = ch == null ? "" : ch.getContextPath();
-      for (String key : endpoints.keySet())
+      for (String key : getEndpoints().keySet())
       {
         writer.write(base + contextPath + "/" + key + "\n");
       }
@@ -177,7 +205,7 @@ public class RestHandler extends AbstractRequestHandler
 
     response.setHeader("Cache-Control", "no-cache/no-store");
     response.setHeader("Content-type", "text/plain");
-    EndpointI ep = endpoints.get(endpointName);
+    EndpointI ep = getNewEndpoint(endpointName);
     ep.processEndpoint(request, response);
 
     return;
@@ -209,15 +237,14 @@ public class RestHandler extends AbstractRequestHandler
     // e.g. registerHandler and addEndpoints
   }
 
-  protected boolean addEndpoint(EndpointI ep)
+  protected void addEndpoint(AbstractEndpoint ep)
   {
-    if (endpoints == null)
+    if (getEndpoints() == null)
     {
       endpoints = new HashMap<>();
     }
-    AbstractEndpoint e = (AbstractEndpoint) ep;
     endpoints.put(ep.getPath(), ep);
-    return true;
+    Console.debug("REST API, added endpoint '" + ep.getPath() + "'");
   }
 
   protected String getRequestedEndpointName(HttpServletRequest request)
@@ -228,18 +255,17 @@ public class RestHandler extends AbstractRequestHandler
             : pathInfo.substring(1);
   }
 
-  protected String[] getEndpointPathParameters(HttpServletRequest request)
+  protected void returnError(HttpServletRequest request,
+          HttpServletResponse response, String message)
   {
-    String pathInfo = request.getPathInfo();
-    int slashpos = pathInfo.indexOf('/', 1);
-    return slashpos < 1 ? null
-            : pathInfo.substring(slashpos + 1).split("/");
+    returnError(request, response, message,
+            HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
   }
 
   protected void returnError(HttpServletRequest request,
-          HttpServletResponse response, String message)
+          HttpServletResponse response, String message, int statusCode)
   {
-    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+    response.setStatus(statusCode);
     String endpointName = getRequestedEndpointName(request);
     Console.error(getName() + " error: endpoint " + endpointName
             + " failed: '" + message + "'");
@@ -277,7 +303,7 @@ public class RestHandler extends AbstractRequestHandler
     StringBuilder sb = new StringBuilder();
     BufferedReader reader = null;
     Console.debug("REQUEST=" + request.toString());
-    Console.debug("REQUEST.Content-Lenggth=" + request.getContentLength());
+    Console.debug("REQUEST.Content-Length=" + request.getContentLength());
     try
     {
       reader = request.getReader();
@@ -285,8 +311,6 @@ public class RestHandler extends AbstractRequestHandler
     } catch (IllegalStateException e)
     {
       ServletInputStream is = request.getInputStream();
-      Console.debug("INPUTSTREAM "
-              + (is.isFinished() ? "FINISHED" : "NOT FINISHED"));
       reader = new BufferedReader(new InputStreamReader(is));
       Console.debug("Using getInputStream()");
     }
diff --git a/src/jalview/schemes/IGVColourScheme.java b/src/jalview/schemes/IGVColourScheme.java
new file mode 100755 (executable)
index 0000000..58ba3ba
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.schemes;
+
+import jalview.api.AlignViewportI;
+import jalview.datamodel.AnnotatedCollectionI;
+
+/**
+ * DOCUMENT ME!
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+public class IGVColourScheme extends ResidueColourScheme
+{
+  /**
+   * Creates a new NucleotideColourScheme object.
+   */
+  public IGVColourScheme()
+  {
+    super(ResidueProperties.nucleotideIndex, ResidueProperties.igv);
+  }
+
+  @Override
+  public boolean isNucleotideSpecific()
+  {
+    return true;
+  }
+
+  @Override
+  public String getSchemeName()
+  {
+    return JalviewColourScheme.IGV.toString();
+  }
+
+  /**
+   * Returns a new instance of this colour scheme with which the given data may
+   * be coloured
+   */
+  @Override
+  public ColourSchemeI getInstance(AlignViewportI view,
+          AnnotatedCollectionI coll)
+  {
+    return new IGVColourScheme();
+  }
+}
index 965a26b..d1817fe 100644 (file)
@@ -34,16 +34,17 @@ public enum JalviewColourScheme
   PID("% Identity", PIDColourScheme.class),
   Zappo("Zappo", ZappoColourScheme.class),
   Taylor("Taylor", TaylorColourScheme.class),
-  Flower("gecos:flower", FlowerColourScheme.class),
-  Blossom("gecos:blossom", BlossomColourScheme.class),
-  Sunset("gecos:sunset", SunsetColourScheme.class),
-  Ocean("gecos:ocean", OceanColourScheme.class),
+  Flower("gecos-flower", FlowerColourScheme.class),
+  Blossom("gecos-blossom", BlossomColourScheme.class),
+  Sunset("gecos-sunset", SunsetColourScheme.class),
+  Ocean("gecos-ocean", OceanColourScheme.class),
   Hydrophobic("Hydrophobic", HydrophobicColourScheme.class),
   Helix("Helix Propensity", HelixColourScheme.class),
   Strand("Strand Propensity", StrandColourScheme.class),
   Turn("Turn Propensity", TurnColourScheme.class),
   Buried("Buried Index", BuriedColourScheme.class),
   Nucleotide("Nucleotide", NucleotideColourScheme.class),
+  IGV("IGV", IGVColourScheme.class),
   PurinePyrimidine("Purine/Pyrimidine", PurinePyrimidineColourScheme.class),
   RNAHelices("RNA Helices", RNAHelicesColour.class),
   TCoffee("T-Coffee Scores", TCoffeeColourScheme.class),
index 221dcdb..75fa083 100755 (executable)
@@ -365,6 +365,20 @@ public class ResidueProperties
       Color.white, // Gap
   };
 
+  public static final Color[] igv = {
+      new Color(0, 150, 0), // A
+      Color.blue, // C
+      new Color(209, 113, 5), // G
+      Color.red, // T
+      Color.red, // U
+      Color.white, // I (inosine)
+      Color.white, // X (xanthine)
+      Color.white, // R
+      Color.white, // Y
+      Color.white, // N
+      Color.white, // Gap
+  };
+
   // Added for PurinePyrimidineColourScheme
   public static final Color[] purinepyrimidine = { new Color(255, 131, 250), // A,
                                                                              // G,