file format enum wip changes
[jalview.git] / src / jalview / gui / AlignFrame.java
index 0d6efe4..cf5766f 100644 (file)
@@ -32,7 +32,7 @@ import jalview.api.AlignViewControllerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureSettingsControllerI;
-import jalview.api.FeatureSettingsI;
+import jalview.api.FeatureSettingsModelI;
 import jalview.api.SplitContainerI;
 import jalview.api.ViewStyleI;
 import jalview.api.analysis.ScoreModelI;
@@ -65,15 +65,20 @@ import jalview.gui.ViewSelectionMenu.ViewSetProvider;
 import jalview.io.AlignmentProperties;
 import jalview.io.AnnotationFile;
 import jalview.io.BioJsHTMLOutput;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatI;
 import jalview.io.FileLoader;
 import jalview.io.FormatAdapter;
 import jalview.io.HtmlSvgOutput;
 import jalview.io.IdentifyFile;
+import jalview.io.JPredFile;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
 import jalview.io.JnetAnnotationMaker;
 import jalview.io.NewickFile;
 import jalview.io.TCoffeeScoreFile;
+import jalview.io.gff.SequenceOntologyI;
 import jalview.jbgui.GAlignFrame;
 import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.BuriedColourScheme;
@@ -96,11 +101,12 @@ import jalview.schemes.ZappoColourScheme;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.ws.DBRefFetcher;
+import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
 import jalview.ws.SequenceFetcher;
 import jalview.ws.jws1.Discoverer;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.seqfetcher.ASequenceFetcher;
 import jalview.ws.seqfetcher.DbSourceProxy;
 
 import java.awt.BorderLayout;
@@ -111,13 +117,14 @@ import java.awt.datatransfer.Clipboard;
 import java.awt.datatransfer.DataFlavor;
 import java.awt.datatransfer.StringSelection;
 import java.awt.datatransfer.Transferable;
-import java.awt.dnd.DnDConstants;
 import java.awt.dnd.DropTargetDragEvent;
 import java.awt.dnd.DropTargetDropEvent;
 import java.awt.dnd.DropTargetEvent;
 import java.awt.dnd.DropTargetListener;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.KeyAdapter;
@@ -176,7 +183,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   /**
    * Last format used to load or save alignments in this window
    */
-  String currentFileFormat = null;
+  FileFormatI currentFileFormat = null;
 
   /**
    * Current filename for this alignment
@@ -465,6 +472,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       formatMenu.add(vsel);
     }
+    addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusGained(FocusEvent e)
+      {
+        Jalview.setCurrentAlignFrame(AlignFrame.this);
+      }
+    });
 
   }
 
@@ -477,7 +492,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param format
    *          format of file
    */
-  public void setFileName(String file, String format)
+  public void setFileName(String file, FileFormatI format)
   {
     fileName = file;
     setFileFormat(format);
@@ -910,10 +925,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             .setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
 
     showProducts.setEnabled(canShowProducts());
+    setGroovyEnabled(Desktop.getGroovyConsole() != null);
 
     updateEditMenuBar();
   }
 
+  /**
+   * Set the enabled state of the 'Run Groovy' option in the Calculate menu
+   * 
+   * @param b
+   */
+  public void setGroovyEnabled(boolean b)
+  {
+    runGroovy.setEnabled(b);
+  }
+
   private IProgressIndicator progressBar;
 
   /*
@@ -1116,10 +1142,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       fileName = chooser.getSelectedFile().getPath();
 
-      jalview.bin.Cache.setProperty("DEFAULT_FILE_FORMAT",
-              currentFileFormat);
+      Cache.setProperty("DEFAULT_FILE_FORMAT",
+              currentFileFormat.toString());
 
-      jalview.bin.Cache.setProperty("LAST_DIRECTORY", fileName);
+      Cache.setProperty("LAST_DIRECTORY", fileName);
       if (currentFileFormat.indexOf(" ") > -1)
       {
         currentFileFormat = currentFileFormat.substring(0,
@@ -1129,11 +1155,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
-  public boolean saveAlignment(String file, String format)
+  public boolean saveAlignment(String file, FileFormatI format)
   {
     boolean success = true;
 
-    if (format.equalsIgnoreCase("Jalview"))
+    if (format == FileFormat.Jalview)
     {
       String shortName = title;
 
@@ -1251,8 +1277,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     cap.setForInput(null);
     try
     {
+      FileFormatI format = FileFormat.valueOf(e.getActionCommand());
       cap.setText(new FormatAdapter(alignPanel, exportData.getSettings())
-              .formatSequences(e.getActionCommand(),
+              .formatSequences(format,
                       exportData.getAlignment(),
                       exportData.getOmitHidden(),
                       exportData.getStartEndPostions(),
@@ -1269,33 +1296,33 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   public static AlignmentExportData getAlignmentForExport(
-          String exportFormat, AlignViewportI viewport,
+          FileFormatI format, AlignViewportI viewport,
           AlignExportSettingI exportSettings)
   {
     AlignmentI alignmentToExport = null;
     AlignExportSettingI settings = exportSettings;
     String[] omitHidden = null;
-    int[] alignmentStartEnd = new int[2];
 
     HiddenSequences hiddenSeqs = viewport.getAlignment()
             .getHiddenSequences();
 
     alignmentToExport = viewport.getAlignment();
-    alignmentStartEnd = new int[] { 0, alignmentToExport.getWidth() - 1 };
 
     boolean hasHiddenSeqs = hiddenSeqs.getSize() > 0;
     if (settings == null)
     {
       settings = new AlignExportSettings(hasHiddenSeqs,
-              viewport.hasHiddenColumns(), exportFormat);
+              viewport.hasHiddenColumns(), format);
     }
     // settings.isExportAnnotations();
 
     if (viewport.hasHiddenColumns() && !settings.isExportHiddenColumns())
     {
-      omitHidden = viewport.getViewAsString(false);
+      omitHidden = viewport.getViewAsString(false,
+              settings.isExportHiddenSequences());
     }
 
+    int[] alignmentStartEnd = new int[2];
     if (hasHiddenSeqs && settings.isExportHiddenSequences())
     {
       alignmentToExport = hiddenSeqs.getFullAlignment();
@@ -1303,64 +1330,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     else
     {
       alignmentToExport = viewport.getAlignment();
-      alignmentStartEnd = getStartEnd(alignmentStartEnd, viewport
-              .getColumnSelection().getHiddenColumns());
     }
+    alignmentStartEnd = alignmentToExport
+            .getVisibleStartAndEndIndex(viewport.getColumnSelection()
+                    .getHiddenColumns());
     AlignmentExportData ed = new AlignmentExportData(alignmentToExport,
             omitHidden, alignmentStartEnd, settings);
     return ed;
   }
 
-  public static int[] getStartEnd(int[] aligmentStartEnd,
-          List<int[]> hiddenCols)
-  {
-    int startPos = aligmentStartEnd[0];
-    int endPos = aligmentStartEnd[1];
-
-    int[] lowestRange = new int[] { -1, -1 };
-    int[] higestRange = new int[] { -1, -1 };
-
-    for (int[] hiddenCol : hiddenCols)
-    {
-      lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
-      higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
-    }
-
-    if (lowestRange[0] == -1 && lowestRange[1] == -1)
-    {
-      startPos = aligmentStartEnd[0];
-    }
-    else
-    {
-      startPos = lowestRange[1] + 1;
-    }
-
-    if (higestRange[0] == -1 && higestRange[1] == -1)
-    {
-      endPos = aligmentStartEnd[1];
-    }
-    else
-    {
-      endPos = higestRange[0] - 1;
-    }
-
-    // System.out.println("Export range : " + startPos + " - " + endPos);
-    return new int[] { startPos, endPos };
-  }
-
-  public static void main(String[] args)
-  {
-    ArrayList<int[]> hiddenCols = new ArrayList<int[]>();
-    hiddenCols.add(new int[] { 0, 0 });
-    hiddenCols.add(new int[] { 6, 9 });
-    hiddenCols.add(new int[] { 11, 12 });
-    hiddenCols.add(new int[] { 33, 33 });
-    hiddenCols.add(new int[] { 50, 50 });
-
-    int[] x = getStartEnd(new int[] { 0, 50 }, hiddenCols);
-    // System.out.println("Export range : " + x[0] + " - " + x[1]);
-  }
-
   /**
    * DOCUMENT ME!
    * 
@@ -3666,8 +3644,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           @Override
           public void mousePressed(MouseEvent evt)
           {
-            if (evt.isControlDown()
-                    || SwingUtilities.isRightMouseButton(evt))
+            if (evt.isPopupTrigger())
             {
               radioItem.removeActionListener(radioItem.getActionListeners()[0]);
 
@@ -4171,7 +4148,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         JMenuItem tm = new JMenuItem();
         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
-        if (sm.isProtein() == !viewport.getAlignment().isNucleotide())
+        if (sm.isDNA() == viewport.getAlignment().isNucleotide()
+                || sm.isProtein() == !viewport.getAlignment()
+                        .isNucleotide())
         {
           String smn = MessageManager.getStringOrReturn(
                   "label.score_model_", sm.getName());
@@ -4346,7 +4325,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       jalview.io.NewickFile fin = null;
       try
       {
-        fin = new jalview.io.NewickFile(choice, "File");
+        fin = new NewickFile(choice, DataSourceType.FILE);
         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
       } catch (Exception ex)
       {
@@ -4706,6 +4685,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     return showp;
   }
 
+  /**
+   * Finds and displays cross-references for the selected sequences (protein
+   * products for nucleotide sequences, dna coding sequences for peptides).
+   * 
+   * @param sel
+   *          the sequences to show cross-references for
+   * @param dna
+   *          true if from a nucleotide alignment (so showing proteins)
+   * @param source
+   *          the database to show cross-references for
+   */
   protected void showProductsFor(final SequenceI[] sel, final boolean dna,
           final String source)
   {
@@ -4721,206 +4711,212 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 new Object[] { source }), sttime);
         try
         {
-          /*
-           * 'peer' sequences are any to add to this alignment, for example
-           * alternative protein products for my protein's gene
-           */
-          List<SequenceI> addedPeers = new ArrayList<SequenceI>();
           AlignmentI alignment = AlignFrame.this.getViewport()
                   .getAlignment();
-          Alignment xrefs = CrossRef.findXrefSequences(sel, dna, source,
-                  alignment, addedPeers);
+          AlignmentI xrefs = CrossRef.findXrefSequences(sel, dna, source,
+                  alignment);
           if (xrefs != null)
           {
             /*
-             * figure out colour scheme if any to apply to features
+             * get display scheme (if any) to apply to features
              */
-            ASequenceFetcher sftch = new SequenceFetcher();
-            List<DbSourceProxy> proxies = sftch.getSourceProxy(source);
-            FeatureSettingsI featureColourScheme = null;
-            for (DbSourceProxy proxy : proxies)
-            {
-              FeatureSettingsI preferredColours = proxy
-                      .getFeatureColourScheme();
-              if (preferredColours != null)
-              {
-                featureColourScheme = preferredColours;
-                break;
-              }
-            }
-            Alignment al = makeCrossReferencesAlignment(
+            FeatureSettingsModelI featureColourScheme = new SequenceFetcher()
+                    .getFeatureColourScheme(source);
+
+            AlignmentI al = makeCrossReferencesAlignment(
                     alignment.getDataset(), xrefs);
 
             AlignFrame newFrame = new AlignFrame(al, DEFAULT_WIDTH,
                     DEFAULT_HEIGHT);
+            if (Cache.getDefault("HIDE_INTRONS", true))
+            {
+              newFrame.hideFeatureColumns(SequenceOntologyI.EXON, false);
+            }
             String newtitle = String.format("%s %s %s",
                     MessageManager.getString(dna ? "label.proteins"
                             : "label.nucleotides"), MessageManager
                             .getString("label.for"), getTitle());
             newFrame.setTitle(newtitle);
 
-            boolean asSplitFrame = Cache.getDefault(
-                    Preferences.ENABLE_SPLIT_FRAME, true);
-            if (asSplitFrame)
+            if (!Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
             {
               /*
-               * Make a copy of this alignment (sharing the same dataset
-               * sequences). If we are DNA, drop introns and update mappings
+               * split frame display is turned off in preferences file
                */
-              AlignmentI copyAlignment = null;
-              final SequenceI[] sequenceSelection = AlignFrame.this.viewport
-                      .getSequenceSelection();
-              List<AlignedCodonFrame> cf = xrefs.getCodonFrames();
-              if (dna)
-              {
-                copyAlignment = AlignmentUtils.makeCdsAlignment(
-                        sequenceSelection, cf, alignment);
-                if (copyAlignment.getHeight() == 0)
-                {
-                  System.err.println("Failed to make CDS alignment");
-                }
-                al.getCodonFrames().clear();
-                al.getCodonFrames().addAll(cf);
-              }
-              else
+              Desktop.addInternalFrame(newFrame, newtitle, DEFAULT_WIDTH,
+                      DEFAULT_HEIGHT);
+              return; // via finally clause
+            }
+
+            /*
+             * Make a copy of this alignment (sharing the same dataset
+             * sequences). If we are DNA, drop introns and update mappings
+             */
+            AlignmentI copyAlignment = null;
+            final SequenceI[] sequenceSelection = AlignFrame.this.viewport
+                    .getSequenceSelection();
+            List<AlignedCodonFrame> cf = xrefs.getCodonFrames();
+            boolean copyAlignmentIsAligned = false;
+            if (dna)
+            {
+              copyAlignment = AlignmentUtils.makeCdsAlignment(
+                      sequenceSelection, cf, alignment);
+              if (copyAlignment.getHeight() == 0)
               {
-                copyAlignment = new Alignment(new Alignment(
-                        sequenceSelection));
-                copyAlignment.getCodonFrames().addAll(cf);
+                System.err.println("Failed to make CDS alignment");
               }
-              copyAlignment.setGapCharacter(AlignFrame.this.viewport
-                      .getGapCharacter());
-              StructureSelectionManager ssm = StructureSelectionManager
-                      .getStructureSelectionManager(Desktop.instance);
-              ssm.registerMappings(cf);
+              al.getCodonFrames().clear();
+              al.addCodonFrames(copyAlignment.getCodonFrames());
+              al.addCodonFrames(cf);
 
               /*
-               * add in any extra 'peer' sequences discovered
-               * (e.g. alternative protein products)
+               * pending getting Embl transcripts to 'align', 
+               * we are only doing this for Ensembl
                */
-              for (SequenceI peer : addedPeers)
+              // TODO proper criteria for 'can align as cdna'
+              if (DBRefSource.ENSEMBL.equalsIgnoreCase(source)
+                      || AlignmentUtils.looksLikeEnsembl(alignment))
               {
-                copyAlignment.addSequence(peer);
+                copyAlignment.alignAs(alignment);
+                copyAlignmentIsAligned = true;
               }
+            }
+            else
+            {
+              copyAlignment = AlignmentUtils.makeCopyAlignment(
+                      sequenceSelection, xrefs.getSequencesArray());
+              copyAlignment.addCodonFrames(cf);
+              al.addCodonFrames(copyAlignment.getCodonFrames());
+              al.addCodonFrames(cf);
+            }
+            copyAlignment.setGapCharacter(AlignFrame.this.viewport
+                    .getGapCharacter());
 
-              if (copyAlignment.getHeight() > 0)
-              {
-                /*
-                 * align protein to dna
-                 */
-                // FIXME what if the dna is not aligned :-O
-                if (dna)
-                {
-                  al.alignAs(copyAlignment);
-                }
-                else
-                {
-                  /*
-                   * align cdna to protein - currently only if 
-                   * fetching and aligning Ensembl transcripts!
-                   */
-                  if (DBRefSource.ENSEMBL.equalsIgnoreCase(source))
-                  {
-                    copyAlignment.alignAs(al);
-                  }
-                }
+            StructureSelectionManager ssm = StructureSelectionManager
+                    .getStructureSelectionManager(Desktop.instance);
+            ssm.registerMappings(cf);
 
-                AlignFrame copyThis = new AlignFrame(copyAlignment,
-                        AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
-                copyThis.setTitle(AlignFrame.this.getTitle());
-
-                boolean showSequenceFeatures = viewport
-                        .isShowSequenceFeatures();
-                newFrame.setShowSeqFeatures(showSequenceFeatures);
-                copyThis.setShowSeqFeatures(showSequenceFeatures);
-                FeatureRenderer myFeatureStyling = alignPanel.getSeqPanel().seqCanvas
-                        .getFeatureRenderer();
-
-                /*
-                 * copy feature rendering settings to split frame
-                 */
-                newFrame.alignPanel.getSeqPanel().seqCanvas
-                        .getFeatureRenderer().transferSettings(
-                                myFeatureStyling);
-                copyThis.alignPanel.getSeqPanel().seqCanvas
-                        .getFeatureRenderer().transferSettings(
-                                myFeatureStyling);
-
-                /*
-                 * apply 'database source' feature configuration
-                 * if any was found
-                 */
-                // TODO is this the feature colouring for the original
-                // alignment or the fetched xrefs? either could be Ensembl
-                newFrame.getViewport().applyFeaturesStyle(
-                        featureColourScheme);
-                copyThis.getViewport().applyFeaturesStyle(
-                        featureColourScheme);
-
-                SplitFrame sf = new SplitFrame(dna ? copyThis : newFrame,
-                        dna ? newFrame : copyThis);
-                newFrame.setVisible(true);
-                copyThis.setVisible(true);
-                String linkedTitle = MessageManager
-                        .getString("label.linked_view_title");
-                Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
-                sf.adjustDivider();
-              }
+            if (copyAlignment.getHeight() <= 0)
+            {
+              System.err.println("No Sequences generated for xRef type "
+                      + source);
+              return;
+            }
+            /*
+             * align protein to dna
+             */
+            if (dna && copyAlignmentIsAligned)
+            {
+              al.alignAs(copyAlignment);
             }
             else
             {
-              Desktop.addInternalFrame(newFrame, newtitle, DEFAULT_WIDTH,
-                      DEFAULT_HEIGHT);
+              /*
+               * align cdna to protein - currently only if 
+               * fetching and aligning Ensembl transcripts!
+               */
+              if (DBRefSource.ENSEMBL.equalsIgnoreCase(source))
+              {
+                copyAlignment.alignAs(al);
+              }
             }
-          }
-          else
-          {
-            System.err.println("No Sequences generated for xRef type "
-                    + source);
+
+            AlignFrame copyThis = new AlignFrame(copyAlignment,
+                    AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+            copyThis.setTitle(AlignFrame.this.getTitle());
+
+            boolean showSequenceFeatures = viewport
+                    .isShowSequenceFeatures();
+            newFrame.setShowSeqFeatures(showSequenceFeatures);
+            copyThis.setShowSeqFeatures(showSequenceFeatures);
+            FeatureRenderer myFeatureStyling = alignPanel.getSeqPanel().seqCanvas
+                    .getFeatureRenderer();
+
+            /*
+             * copy feature rendering settings to split frame
+             */
+            newFrame.alignPanel.getSeqPanel().seqCanvas
+                    .getFeatureRenderer()
+                    .transferSettings(myFeatureStyling);
+            copyThis.alignPanel.getSeqPanel().seqCanvas
+                    .getFeatureRenderer()
+                    .transferSettings(myFeatureStyling);
+
+            /*
+             * apply 'database source' feature configuration
+             * if any was found
+             */
+            // TODO is this the feature colouring for the original
+            // alignment or the fetched xrefs? either could be Ensembl
+            newFrame.getViewport().applyFeaturesStyle(featureColourScheme);
+            copyThis.getViewport().applyFeaturesStyle(featureColourScheme);
+
+            SplitFrame sf = new SplitFrame(dna ? copyThis : newFrame,
+                    dna ? newFrame : copyThis);
+            newFrame.setVisible(true);
+            copyThis.setVisible(true);
+            String linkedTitle = MessageManager
+                    .getString("label.linked_view_title");
+            Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
+            sf.adjustDivider();
           }
         } catch (Exception e)
         {
-          jalview.bin.Cache.log.error(
-                  "Exception when finding crossreferences", e);
+          Cache.log.error("Exception when finding crossreferences", e);
         } catch (OutOfMemoryError e)
         {
           new OOMWarning("whilst fetching crossreferences", e);
-        } catch (Error e)
+        } catch (Throwable e)
         {
-          jalview.bin.Cache.log.error("Error when finding crossreferences",
-                  e);
+          Cache.log.error("Error when finding crossreferences", e);
+        } finally
+        {
+          AlignFrame.this.setProgressBar(MessageManager.formatMessage(
+                  "status.finished_searching_for_sequences_from",
+                  new Object[] { source }), sttime);
         }
-        AlignFrame.this.setProgressBar(MessageManager.formatMessage(
-                "status.finished_searching_for_sequences_from",
-                new Object[] { source }), sttime);
       }
 
       /**
-       * Makes an alignment containing the given sequences; the sequences are
-       * added to the given alignment dataset, and the dataset is set on (shared
-       * by) the new alignment
+       * Makes an alignment containing the given sequences. If this is of the
+       * same type as the given dataset (nucleotide/protein), then the new
+       * alignment shares the same dataset, and its dataset sequences are added
+       * to it. Otherwise a new dataset sequence is created for the
+       * cross-references.
        * 
        * @param dataset
        * @param seqs
        * @return
        */
-      protected Alignment makeCrossReferencesAlignment(Alignment dataset,
-              Alignment seqs)
+      protected AlignmentI makeCrossReferencesAlignment(AlignmentI dataset,
+              AlignmentI seqs)
       {
+        boolean sameType = dataset.isNucleotide() == seqs.isNucleotide();
+
         SequenceI[] sprods = new SequenceI[seqs.getHeight()];
         for (int s = 0; s < sprods.length; s++)
         {
           sprods[s] = (seqs.getSequenceAt(s)).deriveSequence();
-          if (dataset.getSequences() == null
-                  || !dataset.getSequences().contains(
-                          sprods[s].getDatasetSequence()))
+          if (sameType)
           {
-            dataset.addSequence(sprods[s].getDatasetSequence());
+            if (dataset.getSequences() == null
+                    || !dataset.getSequences().contains(
+                            sprods[s].getDatasetSequence()))
+            {
+              dataset.addSequence(sprods[s].getDatasetSequence());
+            }
           }
           sprods[s].updatePDBIds();
         }
         Alignment al = new Alignment(sprods);
-        al.setDataset(dataset);
+        if (sameType)
+        {
+          al.setDataset((Alignment) dataset);
+        }
+        else
+        {
+          al.createDatasetAlignment();
+        }
         return al;
       }
 
@@ -4929,23 +4925,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     frunner.start();
   }
 
-  public boolean canShowTranslationProducts(SequenceI[] selection,
-          AlignmentI alignment)
-  {
-    // old way
-    try
-    {
-      return (jalview.analysis.Dna.canTranslate(selection,
-              viewport.getViewAsVisibleContigs(true)));
-    } catch (Exception e)
-    {
-      jalview.bin.Cache.log
-              .warn("canTranslate threw an exception - please report to help@jalview.org",
-                      e);
-      return false;
-    }
-  }
-
   /**
    * Construct and display a new frame containing the translation of this
    * frame's DNA sequences to their aligned protein (amino acid) equivalents.
@@ -4967,7 +4946,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               .getString("label.error_when_translating_sequences_submit_bug_report");
       final String errorTitle = MessageManager
               .getString("label.implementation_error")
-              + MessageManager.getString("translation_failed");
+              + MessageManager.getString("label.translation_failed");
       JOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
               JOptionPane.ERROR_MESSAGE);
       return;
@@ -5005,11 +4984,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   /**
    * Set the file format
    * 
-   * @param fileFormat
+   * @param format
    */
-  public void setFileFormat(String fileFormat)
+  public void setFileFormat(FileFormatI format)
   {
-    this.currentFileFormat = fileFormat;
+    this.currentFileFormat = format;
   }
 
   /**
@@ -5017,14 +4996,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param file
    *          contents or path to retrieve file
-   * @param type
+   * @param sourceType
    *          access mode of file (see jalview.io.AlignFile)
    * @return true if features file was parsed correctly.
    */
-  public boolean parseFeaturesFile(String file, String type)
+  public boolean parseFeaturesFile(String file, DataSourceType sourceType)
   {
-    return avc.parseFeaturesFile(file, type,
-            jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
+    return avc.parseFeaturesFile(file, sourceType,
+            Cache.getDefault("RELAXEDSEQIDMATCHING", false));
 
   }
 
@@ -5066,49 +5045,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void drop(DropTargetDropEvent evt)
   {
     Transferable t = evt.getTransferable();
-    java.util.List files = null;
+    List<String> files = new ArrayList<String>();
+    List<DataSourceType> protocols = new ArrayList<DataSourceType>();
 
     try
     {
-      DataFlavor uriListFlavor = new DataFlavor(
-              "text/uri-list;class=java.lang.String");
-      if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
-      {
-        // Works on Windows and MacOSX
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        files = (java.util.List) t
-                .getTransferData(DataFlavor.javaFileListFlavor);
-      }
-      else if (t.isDataFlavorSupported(uriListFlavor))
-      {
-        // This is used by Unix drag system
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        String data = (String) t.getTransferData(uriListFlavor);
-        files = new java.util.ArrayList(1);
-        for (java.util.StringTokenizer st = new java.util.StringTokenizer(
-                data, "\r\n"); st.hasMoreTokens();)
-        {
-          String s = st.nextToken();
-          if (s.startsWith("#"))
-          {
-            // the line is a comment (as per the RFC 2483)
-            continue;
-          }
-
-          java.net.URI uri = new java.net.URI(s);
-          // check to see if we can handle this kind of URI
-          if (uri.getScheme().toLowerCase().startsWith("http"))
-          {
-            files.add(uri.toString());
-          }
-          else
-          {
-            // otherwise preserve old behaviour: catch all for file objects
-            java.io.File file = new java.io.File(uri);
-            files.add(file.toString());
-          }
-        }
-      }
+      Desktop.transferFromDropTarget(files, protocols, evt, t);
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -5130,13 +5072,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         {
           String file = files.get(i).toString();
           String pdbfn = "";
-          String protocol = FormatAdapter.checkProtocol(file);
-          if (protocol == jalview.io.FormatAdapter.FILE)
+          DataSourceType protocol = FormatAdapter.checkProtocol(file);
+          if (protocol == DataSourceType.FILE)
           {
             File fl = new File(file);
             pdbfn = fl.getName();
           }
-          else if (protocol == jalview.io.FormatAdapter.URL)
+          else if (protocol == DataSourceType.URL)
           {
             URL url = new URL(file);
             pdbfn = url.getFile();
@@ -5269,21 +5211,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param file
    *          either a filename or a URL string.
    */
-  public void loadJalviewDataFile(String file, String protocol,
-          String format, SequenceI assocSeq)
+  public void loadJalviewDataFile(String file, DataSourceType sourceType,
+          FileFormatI format, SequenceI assocSeq)
   {
     try
     {
-      if (protocol == null)
+      if (sourceType == null)
       {
-        protocol = FormatAdapter.checkProtocol(file);
+        sourceType = FormatAdapter.checkProtocol(file);
       }
       // if the file isn't identified, or not positively identified as some
       // other filetype (PFAM is default unidentified alignment file type) then
       // try to parse as annotation.
-      boolean isAnnotation = (format == null || format
-              .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
-              .annotateAlignmentView(viewport, file, protocol) : false;
+      boolean isAnnotation = (format == null || format == FileFormat.Pfam) ? new AnnotationFile()
+              .annotateAlignmentView(viewport, file, sourceType) : false;
 
       if (!isAnnotation)
       {
@@ -5291,7 +5232,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         TCoffeeScoreFile tcf = null;
         try
         {
-          tcf = new TCoffeeScoreFile(file, protocol);
+          tcf = new TCoffeeScoreFile(file, sourceType);
           if (tcf.isValid())
           {
             if (tcf.annotateAlignment(viewport.getAlignment(), true))
@@ -5337,12 +5278,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           // try to parse it as a features file
           if (format == null)
           {
-            format = new IdentifyFile().identify(file, protocol);
+            format = new IdentifyFile().identify(file, sourceType);
           }
-          if (format.equalsIgnoreCase("JnetFile"))
+          if (format == FileFormat.Jnet)
           {
-            jalview.io.JPredFile predictions = new jalview.io.JPredFile(
-                    file, protocol);
+            JPredFile predictions = new JPredFile(
+                    file, sourceType);
             new JnetAnnotationMaker();
             JnetAnnotationMaker.add_annotation(predictions,
                     viewport.getAlignment(), 0, false);
@@ -5355,14 +5296,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else if (IdentifyFile.FeaturesFile.equals(format))
           {
-            if (parseFeaturesFile(file, protocol))
+            if (parseFeaturesFile(file, sourceType))
             {
               alignPanel.paintAlignment(true);
             }
           }
           else
           {
-            new FileLoader().LoadFile(viewport, file, protocol, format);
+            new FileLoader().LoadFile(viewport, file, sourceType, format);
           }
         }
       }
@@ -5387,8 +5328,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       }
       new OOMWarning(
               "loading data "
-                      + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
-                              : "using " + protocol + " from " + file)
+                      + (sourceType != null ? (sourceType == DataSourceType.PASTE ? "from clipboard."
+                              : "using " + sourceType + " from " + file)
                               : ".")
                       + (format != null ? "(parsing as '" + format
                               + "' file)" : ""), oom, Desktop.desktop);
@@ -5434,7 +5375,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void tabbedPane_mousePressed(MouseEvent e)
   {
-    if (SwingUtilities.isRightMouseButton(e))
+    if (e.isPopupTrigger())
     {
       String msg = MessageManager.getString("label.enter_view_name");
       String reply = JOptionPane.showInternalInputDialog(this, msg, msg,
@@ -5567,16 +5508,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         new Thread(new Runnable()
         {
-
           @Override
           public void run()
           {
-            boolean isNuclueotide = alignPanel.alignFrame.getViewport()
+            boolean isNucleotide = alignPanel.alignFrame.getViewport()
                     .getAlignment().isNucleotide();
-            new jalview.ws.DBRefFetcher(alignPanel.av
+            DBRefFetcher dbRefFetcher = new DBRefFetcher(alignPanel.av
                     .getSequenceSelection(), alignPanel.alignFrame, null,
-                    alignPanel.alignFrame.featureSettings, isNuclueotide)
-                    .fetchDBRefs(false);
+                    alignPanel.alignFrame.featureSettings, isNucleotide);
+            dbRefFetcher.addListener(new FetchFinishedListenerI()
+            {
+              @Override
+              public void finished()
+              {
+                AlignFrame.this.setMenusForViewport();
+              }
+            });
+            dbRefFetcher.fetchDBRefs(false);
           }
         }).start();
 
@@ -5644,14 +5592,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       @Override
                       public void run()
                       {
-                        boolean isNuclueotide = alignPanel.alignFrame
+                        boolean isNucleotide = alignPanel.alignFrame
                                 .getViewport().getAlignment()
                                 .isNucleotide();
-                        new jalview.ws.DBRefFetcher(alignPanel.av
-                                .getSequenceSelection(),
+                        DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                alignPanel.av.getSequenceSelection(),
                                 alignPanel.alignFrame, dassource,
                                 alignPanel.alignFrame.featureSettings,
-                                isNuclueotide).fetchDBRefs(false);
+                                isNucleotide);
+                        dbRefFetcher
+                                .addListener(new FetchFinishedListenerI()
+                                {
+                                  @Override
+                                  public void finished()
+                                  {
+                                    AlignFrame.this.setMenusForViewport();
+                                  }
+                                });
+                        dbRefFetcher.fetchDBRefs(false);
                       }
                     }).start();
                   }
@@ -5684,14 +5642,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       @Override
                       public void run()
                       {
-                        boolean isNuclueotide = alignPanel.alignFrame
+                        boolean isNucleotide = alignPanel.alignFrame
                                 .getViewport().getAlignment()
                                 .isNucleotide();
-                        new jalview.ws.DBRefFetcher(alignPanel.av
-                                .getSequenceSelection(),
+                        DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                alignPanel.av.getSequenceSelection(),
                                 alignPanel.alignFrame, dassource,
                                 alignPanel.alignFrame.featureSettings,
-                                isNuclueotide).fetchDBRefs(false);
+                                isNucleotide);
+                        dbRefFetcher
+                                .addListener(new FetchFinishedListenerI()
+                                {
+                                  @Override
+                                  public void finished()
+                                  {
+                                    AlignFrame.this.setMenusForViewport();
+                                  }
+                                });
+                        dbRefFetcher.fetchDBRefs(false);
                       }
                     }).start();
                   }
@@ -5739,14 +5707,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         @Override
                         public void run()
                         {
-                          boolean isNuclueotide = alignPanel.alignFrame
+                          boolean isNucleotide = alignPanel.alignFrame
                                   .getViewport().getAlignment()
                                   .isNucleotide();
-                          new jalview.ws.DBRefFetcher(alignPanel.av
-                                  .getSequenceSelection(),
+                          DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                  alignPanel.av.getSequenceSelection(),
                                   alignPanel.alignFrame, dassrc,
                                   alignPanel.alignFrame.featureSettings,
-                                  isNuclueotide).fetchDBRefs(false);
+                                  isNucleotide);
+                          dbRefFetcher
+                                  .addListener(new FetchFinishedListenerI()
+                                  {
+                                    @Override
+                                    public void finished()
+                                    {
+                                      AlignFrame.this.setMenusForViewport();
+                                    }
+                                  });
+                          dbRefFetcher.fetchDBRefs(false);
                         }
                       }).start();
                     }
@@ -5987,8 +5965,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void setAnnotationsVisibility(boolean visible,
           boolean forSequences, boolean forAlignment)
   {
-    for (AlignmentAnnotation aa : alignPanel.getAlignment()
-            .getAlignmentAnnotation())
+    AlignmentAnnotation[] anns = alignPanel.getAlignment()
+            .getAlignmentAnnotation();
+    if (anns == null)
+    {
+      return;
+    }
+    for (AlignmentAnnotation aa : anns)
     {
       /*
        * don't display non-positional annotations on an alignment
@@ -6104,15 +6087,75 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     try
     {
       Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
-
       al = dna.reverseCdna(complement);
       viewport.addAlignment(al, "");
+      addHistoryItem(new EditCommand(
+              MessageManager.getString("label.add_sequences"),
+              Action.PASTE, al.getSequencesArray(), 0, al.getWidth(),
+              viewport.getAlignment()));
     } catch (Exception ex)
     {
       System.err.println(ex.getMessage());
       return;
     }
   }
+
+  /**
+   * Try to run a script in the Groovy console, having first ensured that this
+   * AlignFrame is set as currentAlignFrame in Desktop, to allow the script to
+   * be targeted at this alignment.
+   */
+  @Override
+  protected void runGroovy_actionPerformed()
+  {
+    Jalview.setCurrentAlignFrame(this);
+    groovy.ui.Console console = Desktop.getGroovyConsole();
+    if (console != null)
+    {
+      try
+      {
+        console.runScript();
+      } catch (Exception ex)
+      {
+        System.err.println((ex.toString()));
+        JOptionPane
+                .showInternalMessageDialog(Desktop.desktop, MessageManager
+                        .getString("label.couldnt_run_groovy_script"),
+                        MessageManager
+                                .getString("label.groovy_support_failed"),
+                        JOptionPane.ERROR_MESSAGE);
+      }
+    }
+    else
+    {
+      System.err.println("Can't run Groovy script as console not found");
+    }
+  }
+
+  /**
+   * Hides columns containing (or not containing) a specified feature, provided
+   * that would not leave all columns hidden
+   * 
+   * @param featureType
+   * @param columnsContaining
+   * @return
+   */
+  public boolean hideFeatureColumns(String featureType,
+          boolean columnsContaining)
+  {
+    boolean notForHiding = avc.markColumnsContainingFeatures(
+            columnsContaining, false, false, featureType);
+    if (notForHiding)
+    {
+      if (avc.markColumnsContainingFeatures(!columnsContaining, false,
+              false, featureType))
+      {
+        getViewport().hideSelectedColumns();
+        return true;
+      }
+    }
+    return false;
+  }
 }
 
 class PrintThread extends Thread