On 'save' menu entry check to see if fileformat is a valid output format and open...
[jalview.git] / src / jalview / gui / AlignFrame.java
index 70752be..72691cc 100755 (executable)
@@ -59,9 +59,13 @@ public class AlignFrame
   Vector alignPanels = new Vector();
 
 
-  /** DOCUMENT ME!! */
+  /** 
+   * Last format used to load or save alignments in this window 
+   */
   String currentFileFormat = null;
-
+  /**
+   * Current filename for this alignment
+   */
   String fileName = null;
 
 
@@ -156,7 +160,12 @@ public class AlignFrame
    addKeyListener();
 
   }
-
+  /**
+   * Change the filename and format for the alignment, and
+   * enable the 'reload' button functionality.
+   * @param file valid filename
+   * @param format format of file
+   */
   public void setFileName(String file, String format)
   {
      fileName = file;
@@ -498,14 +507,17 @@ public class AlignFrame
     showTranslation.setVisible( nucleotide );
     conservationMenuItem.setEnabled( !nucleotide );
     modifyConservation.setEnabled(   !nucleotide );
-
+    
     //Remember AlignFrame always starts as protein
     if(!nucleotide)
     {
       calculateMenu.remove(calculateMenu.getItemCount()-2);
     }
+    setShowProductsEnabled();
   }
 
+
+
   /**
    * Need to call this method when tabs are selected for multiple views,
    * or when loading from Jalview2XML.java
@@ -580,10 +592,18 @@ public class AlignFrame
 
     validate();
   }
-
-
-
-
+  /**
+   * 
+   * @return true if any progress bars are still active
+   */
+  public boolean operationInProgress()
+  {
+    if (progressBars!=null && progressBars.size()>0)
+    {
+      return true;
+    }
+    return false;
+  }
   /*
    Added so Castor Mapping file can obtain Jalview Version
   */
@@ -668,7 +688,7 @@ public class AlignFrame
   public void save_actionPerformed(ActionEvent e)
   {
     if(fileName==null
-       || currentFileFormat==null
+       || (currentFileFormat==null || jalview.io.AppletFormatAdapter.isValidFormat(currentFileFormat, true))
        || fileName.startsWith("http")
         )
     {
@@ -689,10 +709,8 @@ public class AlignFrame
   {
     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
         getProperty( "LAST_DIRECTORY"),
-        new String[]
-        { "fa, fasta, fastq", "aln", "pfam", "msf", "pir", "blc","jar" },
-        new String[]
-        { "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "Jalview" },
+        jalview.io.AppletFormatAdapter.WRITABLE_EXTENSIONS,
+        jalview.io.AppletFormatAdapter.WRITABLE_FNAMES,
         currentFileFormat,
         false);
 
@@ -722,7 +740,10 @@ public class AlignFrame
                                     currentFileFormat);
 
       jalview.bin.Cache.setProperty("LAST_DIRECTORY", fileName);
-
+      if (currentFileFormat.indexOf(" ")>-1)
+      {
+        currentFileFormat = currentFileFormat.substring(0, currentFileFormat.indexOf(" "));
+      }
       saveAlignment(fileName, currentFileFormat);
     }
   }
@@ -749,8 +770,18 @@ public class AlignFrame
 
     }
     else
-    {
-
+    {        
+      if (!jalview.io.AppletFormatAdapter.isValidFormat(format, true))
+      {
+        // JBPNote need to have a raise_gui flag here
+        JOptionPane.showInternalMessageDialog(
+                this, "Cannot save file " + fileName + " using format "+format,
+                "Alignment output format not supported",
+                JOptionPane.WARNING_MESSAGE);
+        saveAs_actionPerformed(null);
+        return false;
+      }
+        
       String[] omitHidden = null;
 
       if (viewport.hasHiddenColumns)
@@ -766,11 +797,11 @@ public class AlignFrame
           omitHidden = viewport.getViewAsString(false);
       }
       }
-
-      String output = new FormatAdapter().formatSequences(
+      FormatAdapter f = new FormatAdapter();
+      String output = f.formatSequences(
           format,
-          viewport.alignment.getSequencesArray(),
-          omitHidden);
+          (Alignment) viewport.alignment, // class cast exceptions will occur in the distant future
+          omitHidden, f.getCacheSuffixDefault(format), viewport.colSel);
 
       if (output == null)
       {
@@ -841,8 +872,8 @@ public class AlignFrame
 
     cap.setText(new FormatAdapter().formatSequences(
         e.getActionCommand(),
-        viewport.alignment.getSequencesArray(),
-        omitHidden));
+        viewport.alignment,
+        omitHidden, viewport.colSel));
   }
 
   /**
@@ -1425,9 +1456,9 @@ public class AlignFrame
   }
 
   /**
-   * DOCUMENT ME!
+   * Paste contents of Jalview clipboard
    *
-   * @param newAlignment DOCUMENT ME!
+   * @param newAlignment true to paste to a new alignment, otherwise add to this.
    */
   void paste(boolean newAlignment)
   {
@@ -1619,19 +1650,17 @@ public class AlignFrame
            AlignmentAnnotation sann[] = sequences[i].getAnnotation();
            if (sann == null)
              continue;
-           for (int avnum = 0; avnum < alview.length; avnum++)
+           for (int avnum=0;avnum<alview.length; avnum++)
            {
-
-             if (alview[avnum] != alignment)
+             if (alview[avnum]!=alignment)
              {
                // duplicate in a view other than the one with input focus
-               int avwidth = alview[avnum].getWidth() + 1;
-
-               // this relies on sann being preserved after we modify the sequence's annotation array
+               int avwidth = alview[avnum].getWidth()+1;
+               // this relies on sann being preserved after we 
+               // modify the sequence's annotation array for each duplication
                for (int a=0; a<sann.length; a++)
                {
                  AlignmentAnnotation newann = new AlignmentAnnotation(sann[a]);
-
                  sequences[i].addAlignmentAnnotation(newann);
                  newann.padAnnotation(avwidth);
                  alview[avnum].addAnnotation(newann); // annotation was duplicated earlier
@@ -2851,7 +2880,7 @@ public class AlignFrame
   {
     SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
     AlignmentSorter.sortByPID(viewport.getAlignment(),
-                              viewport.getAlignment().getSequenceAt(0));
+                              viewport.getAlignment().getSequenceAt(0), null);
     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
                                     viewport.alignment));
     alignPanel.paintAlignment(true);
@@ -3114,7 +3143,7 @@ public class AlignFrame
       public void actionPerformed(ActionEvent e)
       {
         SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
-        AlignmentSorter.sortByAnnotationScore(scoreLabel, viewport.getAlignment());
+        AlignmentSorter.sortByAnnotationScore(scoreLabel, viewport.getAlignment());//,viewport.getSelectionGroup());
         addHistoryItem(new OrderCommand("Sort by "+scoreLabel, oldOrder, viewport.alignment));
         alignPanel.paintAlignment(true);
       }
@@ -3318,21 +3347,28 @@ public class AlignFrame
     {
       String choice = chooser.getSelectedFile().getPath();
       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
-
+      jalview.io.NewickFile fin = null;
       try
       {
-        jalview.io.NewickFile fin = new jalview.io.NewickFile(choice,
+        fin = new jalview.io.NewickFile(choice,
             "File");
         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
       }
       catch (Exception ex)
       {
         JOptionPane.showMessageDialog(Desktop.desktop,
-                                      "Problem reading tree file",
                                       ex.getMessage(),
+                                      "Problem reading tree file",
                                       JOptionPane.WARNING_MESSAGE);
         ex.printStackTrace();
       }
+      if (fin!=null && fin.hasWarningMessage())
+      {
+        JOptionPane.showMessageDialog(Desktop.desktop,
+                fin.getWarningMessage(),
+                "Possible problem with tree file",
+                JOptionPane.WARNING_MESSAGE);
+      }
     }
   }
 
@@ -3397,7 +3433,7 @@ public class AlignFrame
     {
       ex.printStackTrace();
     }
-
+    
     return tp;
   }
 
@@ -3408,6 +3444,11 @@ public class AlignFrame
    */
   public void BuildWebServiceMenu()
   {
+    // TODO: add support for context dependent disabling of services based on alignment and current selection
+    // TODO: refactor to allow list of AbstractName/Handler bindings to be stored or retrieved from elsewhere
+    // TODO: add additional serviceHandle parameter to specify abstract handler class independently of AbstractName
+    // TODO: add in rediscovery GUI function to restart discoverer
+    // TODO: group services by location as well as function and/or introduce object broker mechanism.
     if ( (Discoverer.services != null)
         && (Discoverer.services.size() > 0))
     {
@@ -3507,9 +3548,6 @@ public class AlignFrame
       resetWebServiceMenu();
       this.webService.add(this.webServiceNoServices);
     }
-    // TODO: add in rediscovery function
-    // TODO: reduce code redundancy.
-    // TODO: group services by location as well as function.
   }
 
 
@@ -3529,7 +3567,7 @@ public class AlignFrame
 
       public void actionPerformed(ActionEvent e)
       {
-        new jalview.io.DBRefFetcher(
+        new jalview.ws.DBRefFetcher(
                 alignPanel.av.getSequenceSelection(),
                 alignPanel.alignFrame).fetchDBRefs(false);
       }
@@ -3555,8 +3593,164 @@ public class AlignFrame
       vs.storeJalview( chooser.getSelectedFile().getAbsolutePath(), this);
     }
   }*/
+  /**
+   * prototype of an automatically enabled/disabled analysis function
+   *
+   */
+  protected void setShowProductsEnabled()
+  {
+    SequenceI [] selection = viewport.getSequenceSelection();
+    if (canShowProducts(selection, viewport.getSelectionGroup()!=null, viewport.getAlignment().getDataset()))
+    {
+      showProducts.setEnabled(true);
+      
+    } else {
+      showProducts.setEnabled(false);
+    }
+  }
+  /**
+   * search selection for sequence xRef products and build the
+   * show products menu.
+   * @param selection
+   * @param dataset
+   * @return true if showProducts menu should be enabled.
+   */
+  public boolean canShowProducts(SequenceI[] selection, boolean isRegionSelection, Alignment dataset)
+  {
+    boolean showp=false;
+    try {
+      showProducts.removeAll();
+      final boolean dna = viewport.getAlignment().isNucleotide();
+      final Alignment ds = dataset;
+      String[] ptypes = CrossRef.findSequenceXrefTypes(dna, selection, dataset);
+      //Object[] prods = CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(), selection, dataset, true);
+      final SequenceI[] sel = selection;
+      for (int t=0; ptypes!=null && t<ptypes.length; t++)
+      {
+        showp=true;
+        final boolean isRegSel = isRegionSelection;
+        final AlignFrame af = this;
+        final String source = ptypes[t];
+        JMenuItem xtype = new JMenuItem(ptypes[t]);
+        xtype.addActionListener(new ActionListener() {
+
+          public void actionPerformed(ActionEvent e)
+          {
+            // TODO: new thread for this call with vis-delay
+            af.showProductsFor(sel, ds, isRegSel, dna, source);
+          }
+          
+        });
+        showProducts.add(xtype);
+      }
+      showProducts.setVisible(showp);
+      showProducts.setEnabled(showp);
+    } catch (Exception e)
+    {
+      jalview.bin.Cache.log.warn("canTranslate threw an exception - please report to help@jalview.org",e);
+     return false;
+    }
+    return showp;
+  }
+protected void showProductsFor(SequenceI[] sel, Alignment ds, boolean isRegSel, boolean dna, String source)
+  {
+  final boolean fisRegSel = isRegSel;
+  final boolean fdna = dna;
+  final String fsrc = source;
+  final AlignFrame ths = this;
+  final SequenceI[] fsel = sel;
+  Runnable foo = new Runnable() {
+
+    public void run()
+    {
+      final long sttime = System.currentTimeMillis();
+      ths.setProgressBar("Searching for sequences from "+fsrc, sttime);
+      try {
+        Alignment ds = ths.getViewport().alignment.getDataset(); // update our local dataset reference
+      Alignment prods = CrossRef.findXrefSequences(fsel, fdna, fsrc, ds);
+      if (prods!=null)
+      {
+        SequenceI[] sprods = new SequenceI[prods.getHeight()];
+        for (int s=0; s<sprods.length;s++)
+        {
+          sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
+          if (ds.getSequences()==null || !ds.getSequences().contains(sprods[s].getDatasetSequence()))
+            ds.addSequence(sprods[s].getDatasetSequence());
+          sprods[s].updatePDBIds();
+        }
+        Alignment al = new Alignment(sprods);
+        AlignedCodonFrame[] cf = prods.getCodonFrames();
+        for (int s=0; cf!=null && s<cf.length; s++)
+        {
+          al.addCodonFrame(cf[s]);
+          cf[s] = null;
+        }
+        al.setDataset(ds);
+        AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
+        String newtitle =""+((fdna) ? "Proteins " : "Nucleotides ") + " for "+((fisRegSel) ? "selected region of " : "")
+                + getTitle();
+        Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
+                DEFAULT_HEIGHT);
+      } else {
+        System.err.println("No Sequences generated for xRef type "+fsrc);
+      }
+      }
+      catch (Exception e)
+      {
+        jalview.bin.Cache.log.error("Exception when finding crossreferences",e);
+      }
+      catch (Error e)
+      {
+        jalview.bin.Cache.log.error("Error when finding crossreferences",e);
+      }
+      ths.setProgressBar("Finished searching for sequences from "+fsrc, sttime);
+    }
+    
+  };
+  Thread frunner = new Thread(foo);
+  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;
+  }
+}
+
+public void showProducts_actionPerformed(ActionEvent e)
+{
+  ///////////////////////////////
+  // Collect Data to be translated/transferred
+  
+  SequenceI [] selection = viewport.getSequenceSelection();
+  AlignmentI al  = null;
+  try {
+      al = jalview.analysis.Dna.CdnaTranslate(selection, viewport.getViewAsVisibleContigs(true),
+          viewport.getGapCharacter(), viewport.getAlignment().getDataset());
+    } catch (Exception ex) {
+      al = null;
+      jalview.bin.Cache.log.debug("Exception during translation.",ex);
+    }
+    if (al==null)
+    {
+      JOptionPane.showMessageDialog(Desktop.desktop,
+          "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
+          "Translation Failed",
+          JOptionPane.WARNING_MESSAGE);
+    } else {
+      AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
+      Desktop.addInternalFrame(af, "Translation of "+this.getTitle(),
+                               DEFAULT_WIDTH,
+                               DEFAULT_HEIGHT);
+    }
+  }
 
 public void showTranslation_actionPerformed(ActionEvent e)
 {
@@ -3569,7 +3763,7 @@ public void showTranslation_actionPerformed(ActionEvent e)
   try {
     al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring, viewport.getViewAsVisibleContigs(true),
         viewport.getGapCharacter(), viewport.alignment.getAlignmentAnnotation(),
-        viewport.alignment.getWidth());
+        viewport.alignment.getWidth(), viewport.getAlignment().getDataset());
   } catch (Exception ex) {
     al = null;
     jalview.bin.Cache.log.debug("Exception during translation.",ex);
@@ -3589,9 +3783,10 @@ public void showTranslation_actionPerformed(ActionEvent e)
 }
 
 /**
- * DOCUMENT ME!
- *
- * @param String DOCUMENT ME!
+ * Try to load a features file onto the alignment.
+ * @param file contents or path to retrieve file 
+ * @param type access mode of file (see jalview.io.AlignFile)
+ * @return true if features file was parsed corectly.
  */
 public boolean parseFeaturesFile(String file, String type)
 {
@@ -3692,12 +3887,15 @@ public void drop(DropTargetDropEvent evt)
     }
 }
 
-  // This method will attempt to load a "dropped" file first by testing
-  // whether its and Annotation file, then features file. If both are
-  // false then the user may have dropped an alignment file onto this
-  // AlignFrame
+  /**     
+   * Attempt to load a "dropped" file: First by testing
+   * whether it's and Annotation file, then a JNet file, and finally a features file. If all are
+   * false then the user may have dropped an alignment file onto this
+   * AlignFrame.
+   * @param file either a filename or a URL string.
+   */
    public void loadJalviewDataFile(String file)
-  {
+   {
     try
     {
       String protocol = "File";
@@ -3712,34 +3910,37 @@ public void drop(DropTargetDropEvent evt)
 
       if (!isAnnotation)
       {
-        boolean isGroupsFile = parseFeaturesFile(file,protocol);
-        if (!isGroupsFile)
+        // try to see if its a JNet 'concise' style annotation file *before* we try to parse it as a features file
+        String format = new IdentifyFile().Identify(file, protocol);
+        if(format.equalsIgnoreCase("JnetFile"))
         {
-          String format = new IdentifyFile().Identify(file, protocol);
-
-          if(format.equalsIgnoreCase("JnetFile"))
-          {
-            jalview.io.JPredFile predictions = new jalview.io.JPredFile(
-                file, protocol);
-            new JnetAnnotationMaker().add_annotation(predictions,
+          jalview.io.JPredFile predictions = new jalview.io.JPredFile(
+                  file, protocol);
+          new JnetAnnotationMaker().add_annotation(predictions,
                 viewport.getAlignment(),
                 0, false);
-            alignPanel.adjustAnnotationHeight();
-            alignPanel.paintAlignment(true);
-          }
-          else
+          isAnnotation=true;
+        }
+        else
+        {
+          // try to parse it as a features file
+          boolean isGroupsFile = parseFeaturesFile(file,protocol);
+          // if it wasn't a features file then we just treat it as a general alignment file to load into the current view.
+          if (!isGroupsFile)
           {
             new FileLoader().LoadFile(viewport, file, protocol, format);
+          } else {
+            alignPanel.paintAlignment(true);
+          }
         }
       }
-      }
-      else
+      if (isAnnotation)
       {
-        // (isAnnotation)
+        
         alignPanel.adjustAnnotationHeight();
         buildSortByAnnotationScoresMenu();
+        alignPanel.paintAlignment(true);
       }
-
     }
     catch (Exception ex)
     {
@@ -3787,7 +3988,7 @@ public void drop(DropTargetDropEvent evt)
   protected void extractScores_actionPerformed(ActionEvent e)
   {
     ParseProperties pp = new jalview.analysis.ParseProperties(viewport.alignment);
-    if (pp.getScoresFromDescription("col", "score column ", "\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)")>0)
+    if (pp.getScoresFromDescription("col", "score column ", "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)", true)>0)
     {
       buildSortByAnnotationScoresMenu();
     }