documentation
[jalview.git] / src / jalview / gui / AlignFrame.java
index e649089..c7887da 100755 (executable)
@@ -28,6 +28,7 @@ import java.awt.dnd.*;
 import java.awt.event.*;
 import java.awt.print.*;
 import javax.swing.*;
+import javax.swing.event.MenuEvent;
 
 import jalview.analysis.*;
 import jalview.commands.*;
@@ -141,7 +142,7 @@ public class AlignFrame
    }
 
    setMenusFromViewport(viewport);
-
+   buildSortByAnnotationScoresMenu();
    if (viewport.wrapAlignment)
    {
      wrapMenuItem_actionPerformed(null);
@@ -911,7 +912,8 @@ public class AlignFrame
     new AnnotationExporter().exportAnnotations(
       alignPanel,
         viewport.showAnnotation ? viewport.alignment.getAlignmentAnnotation() : null,
-      viewport.alignment.getGroups()
+      viewport.alignment.getGroups(),
+      ((Alignment)viewport.alignment).alignmentProperties
         );
   }
 
@@ -1047,8 +1049,28 @@ public class AlignFrame
     }
   }
 
-
-
+  /**
+   *
+   * @return alignment objects for all views
+   */
+  AlignmentI[] getViewAlignments()
+  {
+    if (alignPanels!=null)
+    {
+      Enumeration e = alignPanels.elements();
+      AlignmentI[] als = new AlignmentI[alignPanels.size()];
+      for (int i=0; e.hasMoreElements(); i++)
+      {
+        als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
+      }
+      return als;
+    }
+    if (viewport!=null)
+    {
+      return new AlignmentI[] { viewport.alignment };
+    }
+    return null;
+  }
   /**
    * DOCUMENT ME!
    *
@@ -1056,9 +1078,11 @@ public class AlignFrame
    */
   protected void undoMenuItem_actionPerformed(ActionEvent e)
   {
+    if (viewport.historyList.empty())
+      return;
     CommandI command = (CommandI)viewport.historyList.pop();
     viewport.redoList.push(command);
-    command.undoCommand();
+    command.undoCommand(getViewAlignments());
 
     AlignViewport originalSource = getOriginatingSource(command);
     updateEditMenuBar();
@@ -1086,7 +1110,7 @@ public class AlignFrame
 
     CommandI command = (CommandI) viewport.redoList.pop();
     viewport.historyList.push(command);
-    command.doCommand();
+    command.doCommand(getViewAlignments());
 
     AlignViewport originalSource = getOriginatingSource(command);
     updateEditMenuBar();
@@ -1310,7 +1334,7 @@ public class AlignFrame
       return;
     }
     //  TODO: preserve the ordering of displayed alignment annotation in any internal paste (particularly sequence associated annotation)
-    SequenceI [] seqs = viewport.getSelectionAsNewSequence();  
+    SequenceI [] seqs = viewport.getSelectionAsNewSequence();
     String[] omitHidden = null;
 
     if (viewport.hasHiddenColumns)
@@ -1477,7 +1501,7 @@ public class AlignFrame
 
      if (newAlignment)
      {
-       
+
        if (Desktop.jalviewClipboard != null)
        {
          // dataset is inherited
@@ -1503,10 +1527,10 @@ public class AlignFrame
         // an existing alignment
         Vector newDs = (importDs) ? new Vector() : null; // used to create
                                                           // minimum dataset set
-        
+
         for (int i = 0; i < sequences.length; i++)
         {
-          if (importDs) 
+          if (importDs)
           {
             newDs.addElement(null);
           }
@@ -1516,7 +1540,7 @@ public class AlignFrame
           {
             if (!newDs.contains(ds))
             {
-              newDs.setElementAt(ds, i); 
+              newDs.setElementAt(ds, i);
               ds = new Sequence(ds);
               // update with new dataset sequence
               sequences[i].setDatasetSequence(ds);
@@ -1580,18 +1604,44 @@ public class AlignFrame
            alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation was duplicated earlier
            alignment.setAnnotationIndex(sequences[i].getAnnotation()[a], a);
          }
-
-         
        }
      }
      if (!newAlignment) {
+
        // propagate alignment changed.
        viewport.setEndSeq(alignment.getHeight());
        if (annotationAdded)
        {
-         alignPanel.annotationPanel.adjustPanelHeight();
+         // Duplicate sequence annotation in all views.
+         AlignmentI[] alview = this.getViewAlignments();
+         for (int i = 0; i < sequences.length; i++)
+         {
+           AlignmentAnnotation sann[] = sequences[i].getAnnotation();
+           if (sann == null)
+             continue;
+           for (int avnum=0;avnum<alview.length; avnum++)
+           {
+             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 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
+                 alview[avnum].setAnnotationIndex(newann, a);
+               }
+             }
+           }
+         }
+         buildSortByAnnotationScoresMenu();
        }
        viewport.firePropertyChange("alignment", null, alignment.getSequences());
+
      } else {
        AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH, DEFAULT_HEIGHT);
        String newtitle = new String("Copied sequences");
@@ -1610,7 +1660,7 @@ public class AlignFrame
        //>>>This is a fix for the moment, until a better solution is found!!<<<
        af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().transferSettings(
            alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
-       
+
        // TODO: maintain provenance of an alignment, rather than just make the title a concatenation of operations.
        if (!externalPaste) {
          if (title.startsWith("Copied sequences"))
@@ -2266,6 +2316,65 @@ public class AlignFrame
     alignPanel.setAnnotationVisible(annotationPanelMenuItem.isSelected());
   }
 
+  public void alignmentProperties()
+  {
+    JEditorPane editPane = new JEditorPane("text/html","");
+    editPane.setEditable(false);
+    StringBuffer contents = new StringBuffer("<html>");
+
+    float avg  = 0;
+    int min=Integer.MAX_VALUE, max=0;
+    for(int i=0; i<viewport.alignment.getHeight(); i++)
+    {
+      int size = viewport.alignment.getSequenceAt(i).getEnd()
+          -viewport.alignment.getSequenceAt(i).getStart();
+      avg += size;
+      if(size>max)
+        max = size;
+      if(size<min)
+        min = size;
+    }
+    avg = avg/(float)viewport.alignment.getHeight();
+
+    contents.append("<br>Sequences: "+ viewport.alignment.getHeight());
+    contents.append("<br>Minimum Sequence Length: "+min);
+    contents.append("<br>Maximum Sequence Length: "+max);
+    contents.append("<br>Average Length: "+(int)avg);
+
+    if (((Alignment)viewport.alignment).getProperties() != null)
+    {
+      Hashtable props = ((Alignment)viewport.alignment).getProperties();
+      Enumeration en = props.keys();
+      contents.append("<br><br><table border=\"1\">");
+      while(en.hasMoreElements())
+      {
+        String key = en.nextElement().toString();
+        StringBuffer val = new StringBuffer();
+        String vals = props.get(key).toString();
+        int pos=0, npos;
+        do {
+          npos = vals.indexOf("\n",pos);
+          if (npos==-1)
+          {
+            val.append(vals.substring(pos));
+          } else {
+            val.append(vals.substring(pos, npos));
+            val.append("<br>");
+          }
+          pos = npos+1;
+        } while (npos!=-1);
+        contents.append("<tr><td>"+key+"</td><td>"+val+"</td></tr>");
+      }
+      contents.append("</table>");
+    }
+    editPane.setText(contents.toString()+"</html>");
+    JInternalFrame frame = new JInternalFrame();
+    frame.getContentPane().add(new JScrollPane(editPane));
+
+    Desktop.instance.addInternalFrame(frame,"Alignment Properties: "+getTitle(),500,400);
+  }
+
+
   /**
    * DOCUMENT ME!
    *
@@ -2772,7 +2881,6 @@ public class AlignFrame
 
     alignPanel.paintAlignment(true);
   }
-
   /**
    * DOCUMENT ME!
    *
@@ -2990,6 +3098,73 @@ public class AlignFrame
       }
     });
   }
+  /**
+   * Add a new sort by annotation score menu item
+   * @param sort the menu to add the option to
+   * @param scoreLabel the label used to retrieve scores for each sequence on the alignment
+   */
+  public void addSortByAnnotScoreMenuItem(JMenu sort, final String scoreLabel)
+  {
+    final JMenuItem item = new JMenuItem(scoreLabel);
+    sort.add(item);
+    item.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
+        AlignmentSorter.sortByAnnotationScore(scoreLabel, viewport.getAlignment());
+        addHistoryItem(new OrderCommand("Sort by "+scoreLabel, oldOrder, viewport.alignment));
+        alignPanel.paintAlignment(true);
+      }
+    });
+  }
+  /**
+   * last hash for alignment's annotation array - used to minimise cost of rebuild.
+   */
+  protected int _annotationScoreVectorHash;
+  /**
+   * search the alignment and rebuild the sort by annotation score submenu
+   * the last alignment annotation vector hash is stored to minimize
+   * cost of rebuilding in subsequence calls.
+   *
+   */
+  public void buildSortByAnnotationScoresMenu()
+  {
+    if(viewport.alignment.getAlignmentAnnotation()==null)
+    {
+      return;
+    }
+
+    if (viewport.alignment.getAlignmentAnnotation().hashCode()!=_annotationScoreVectorHash)
+    {
+      sortByAnnotScore.removeAll();
+      // almost certainly a quicker way to do this - but we keep it simple
+      Hashtable scoreSorts=new Hashtable();
+      AlignmentAnnotation aann[];
+      Enumeration sq = viewport.alignment.getSequences().elements();
+      while (sq.hasMoreElements())
+      {
+        aann = ((SequenceI) sq.nextElement()).getAnnotation();
+        for (int i=0;aann!=null && i<aann.length; i++)
+        {
+          if (aann[i].hasScore() && aann[i].sequenceRef!=null)
+          {
+            scoreSorts.put(aann[i].label, aann[i].label);
+          }
+        }
+      }
+      Enumeration labels = scoreSorts.keys();
+      while (labels.hasMoreElements())
+      {
+        addSortByAnnotScoreMenuItem(sortByAnnotScore, (String) labels.nextElement());
+      }
+      sortByAnnotScore.setVisible(scoreSorts.size()>0);
+      scoreSorts.clear();
+
+      _annotationScoreVectorHash =
+          viewport.alignment.getAlignmentAnnotation().hashCode();
+    }
+  }
 
   /**
    * Maintain the Order by->Displayed Tree menu.
@@ -3336,20 +3511,27 @@ public class AlignFrame
   }
 
 
+  /**
+   * empty the web service menu and add any ad-hoc functions
+   * not dynamically discovered.
+   *
+   */
   private void resetWebServiceMenu()
   {
     webService.removeAll();
     // Temporary hack - DBRef Fetcher always top level ws entry.
     JMenuItem rfetch = new JMenuItem("Fetch DB References");
+    rfetch.setToolTipText("Retrieve and parse uniprot records for the alignment or the currently selected sequences");
     webService.add(rfetch);
     rfetch.addActionListener(new ActionListener() {
 
       public void actionPerformed(ActionEvent e)
       {
         new jalview.io.DBRefFetcher(
-                alignPanel.av.getAlignment(), alignPanel.alignFrame).fetchDBRefs(false);        
+                alignPanel.av.getSequenceSelection(),
+                alignPanel.alignFrame).fetchDBRefs(false);
       }
-      
+
     });
   }
 
@@ -3379,7 +3561,7 @@ public void showTranslation_actionPerformed(ActionEvent e)
   ///////////////////////////////
   // Collect Data to be translated/transferred
 
-  SequenceI [] selection = viewport.getSelectionAsNewSequence();
+  SequenceI [] selection = viewport.getSequenceSelection();
   String [] seqstring = viewport.getViewAsString(true);
   AlignmentI al  = null;
   try {
@@ -3553,6 +3735,7 @@ public void drop(DropTargetDropEvent evt)
       {
         // (isAnnotation)
         alignPanel.adjustAnnotationHeight();
+        buildSortByAnnotationScoresMenu();
       }
 
     }
@@ -3594,6 +3777,19 @@ public void drop(DropTargetDropEvent evt)
   {
     return viewport;
   }
+
+
+  /**
+   * Open the dialog for regex description parsing.
+   */
+  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)
+    {
+      buildSortByAnnotationScoresMenu();
+    }
+  }
 }
 
 class PrintThread