JAL-845 applet colour by tree; translate as cDNA; pull up history list
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 19 Feb 2015 10:31:38 +0000 (10:31 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 19 Feb 2015 10:31:38 +0000 (10:31 +0000)
28 files changed:
resources/lang/Messages.properties
src/jalview/analysis/AlignmentUtils.java
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/EmbmenuFrame.java
src/jalview/appletgui/FontChooser.java
src/jalview/appletgui/RedundancyPanel.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/SplitFrame.java
src/jalview/appletgui/TreeCanvas.java
src/jalview/bin/JalviewLite.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/Finder.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/SplitFrame.java
src/jalview/gui/TreeCanvas.java
src/jalview/gui/TreePanel.java
src/jalview/io/BioJsHTMLOutput.java
src/jalview/util/MappingUtils.java
src/jalview/viewmodel/AlignmentViewport.java
test/jalview/datamodel/SequenceTest.java
test/jalview/ext/rbvi/chimera/ChimeraConnect.java
test/jalview/gui/PaintRefresherTest.java
test/jalview/util/MappingUtilsTest.java [new file with mode: 0644]

index 801d7a0..3499fdd 100644 (file)
@@ -485,8 +485,6 @@ label.load_associated_tree = Load Associated Tree ...
 label.load_features_annotations = Load Features/Annotations ...
 label.export_features = Export Features ...
 label.export_annotations = Export Annotations ...
-label.jalview_copy = Copy (Jalview Only)
-label.jalview_cut = Cut (Jalview Only)
 label.to_upper_case = To Upper Case
 label.to_lower_case = To Lower Case
 label.toggle_case = Toggle Case
@@ -694,7 +692,7 @@ label.save_png_image = Save As PNG Image
 label.load_tree_for_sequence_set = Load a tree for this sequence set
 label.export_image = Export Image
 label.vamsas_store = VAMSAS store
-label.translate_cDNA = Translate cDNA
+label.translate_cDNA = Translate as cDNA
 label.cdna = cDNA
 label.link_cdna = Link cDNA
 label.link_cdna_tip = Link to any compatible cDNA alignments.<br>Sequences are linked that have the same name and compatible lengths.
@@ -706,7 +704,7 @@ label.align_cdna_tip = Any linked cDNA sequences will be realigned to match this
 label.cdna_aligned = {0} sequences in {1} alignments were realigned
 label.view_as_cdna = Show aligned cDNA
 label.view_as_cdna_tip = Open a new alignment of the related cDNA sequences
-label.linked_view_title = {0} and {1}
+label.linked_view_title = Linked cDNA and protein view
 label.align = Align
 label.extract_scores = Extract Scores
 label.get_cross_refs = Get Cross References
index 7116af9..0441b1d 100644 (file)
@@ -218,6 +218,11 @@ public class AlignmentUtils
           final AlignmentI proteinAlignment,
           final AlignmentI cdnaAlignment)
   {
+    if (proteinAlignment == null || cdnaAlignment == null)
+    {
+      return MappingResult.NotMapped;
+    }
+
     boolean mappingPossible = false;
     boolean mappingPerformed = false;
 
@@ -305,10 +310,15 @@ public class AlignmentUtils
   public static MapList mapProteinToCdna(SequenceI proteinSeq,
           SequenceI cdnaSeq)
   {
-    String aaSeqString = proteinSeq.getDatasetSequence()
-            .getSequenceAsString();
-    String cdnaSeqString = cdnaSeq.getDatasetSequence()
-            .getSequenceAsString();
+    /*
+     * Here we handle either dataset sequence set (desktop) or absent (applet)
+     */
+    final SequenceI proteinDataset = proteinSeq.getDatasetSequence();
+    String aaSeqString = proteinDataset != null ? proteinDataset
+            .getSequenceAsString() : proteinSeq.getSequenceAsString();
+    final SequenceI cdnaDataset = cdnaSeq.getDatasetSequence();
+    String cdnaSeqString = cdnaDataset != null ? cdnaDataset
+            .getSequenceAsString() : cdnaSeq.getSequenceAsString();
     if (aaSeqString == null || cdnaSeqString == null)
     {
       return null;
index bb290d4..38a0f58 100644 (file)
@@ -114,10 +114,9 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
   Menu editMenu = new Menu(MessageManager.getString("action.edit"));
 
-  MenuItem copy = new MenuItem(
-          MessageManager.getString("label.jalview_copy"));
+  MenuItem copy = new MenuItem(MessageManager.getString("action.copy"));
 
-  MenuItem cut = new MenuItem(MessageManager.getString("label.jalview_cut"));
+  MenuItem cut = new MenuItem(MessageManager.getString("action.cut"));
 
   MenuItem toUpper = new MenuItem(
           MessageManager.getString("label.to_upper_case"));
index aa7aed3..cd6c9e5 100644 (file)
@@ -92,6 +92,7 @@ import java.io.IOException;
 import java.net.URL;
 import java.net.URLEncoder;
 import java.util.Arrays;
+import java.util.Deque;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -1433,12 +1434,12 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   void updateEditMenuBar()
   {
 
-    if (viewport.historyList.size() > 0)
+    if (viewport.getHistoryList().size() > 0)
     {
       undoMenuItem.setEnabled(true);
-      CommandI command = (CommandI) viewport.historyList.peek();
+      CommandI command = viewport.getHistoryList().peek();
       undoMenuItem.setLabel(MessageManager.formatMessage(
-              "label.undo_command", new String[]
+              "label.undo_command", new Object[]
               { command.getDescription() }));
     }
     else
@@ -1447,13 +1448,13 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       undoMenuItem.setLabel(MessageManager.getString("action.undo"));
     }
 
-    if (viewport.redoList.size() > 0)
+    if (viewport.getRedoList().size() > 0)
     {
       redoMenuItem.setEnabled(true);
 
-      CommandI command = (CommandI) viewport.redoList.peek();
+      CommandI command = viewport.getRedoList().peek();
       redoMenuItem.setLabel(MessageManager.formatMessage(
-              "label.redo_command", new String[]
+              "label.redo_command", new Object[]
               { command.getDescription() }));
     }
     else
@@ -1471,8 +1472,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   {
     if (command.getSize() > 0)
     {
-      viewport.historyList.push(command);
-      viewport.redoList.removeAllElements();
+      viewport.addToHistoryList(command);
+      viewport.clearRedoList();
       updateEditMenuBar();
       viewport.updateHiddenColumns();
     }
@@ -1486,13 +1487,13 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
    */
   protected void undoMenuItem_actionPerformed()
   {
-    if (viewport.historyList.size() < 1)
+    if (viewport.getHistoryList().isEmpty())
     {
       return;
     }
 
-    CommandI command = (CommandI) viewport.historyList.pop();
-    viewport.redoList.push(command);
+    CommandI command = viewport.getHistoryList().pop();
+    viewport.addToRedoList(command);
     command.undoCommand(null);
 
     AlignViewport originalSource = getOriginatingSource(command);
@@ -1518,13 +1519,13 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
    */
   protected void redoMenuItem_actionPerformed()
   {
-    if (viewport.redoList.size() < 1)
+    if (viewport.getRedoList().isEmpty())
     {
       return;
     }
 
-    CommandI command = (CommandI) viewport.redoList.pop();
-    viewport.historyList.push(command);
+    CommandI command = viewport.getRedoList().pop();
+    viewport.addToHistoryList(command);
     command.doCommand(null);
 
     AlignViewport originalSource = getOriginatingSource(command);
@@ -1681,11 +1682,12 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
 
     boolean appendHistoryItem = false;
-    if (viewport.historyList != null && viewport.historyList.size() > 0
-            && viewport.historyList.peek() instanceof SlideSequencesCommand)
+    Deque<CommandI> historyList = viewport.getHistoryList();
+    if (historyList != null && historyList.size() > 0
+            && historyList.peek() instanceof SlideSequencesCommand)
     {
       appendHistoryItem = ssc
-              .appendSlideCommand((SlideSequencesCommand) viewport.historyList
+              .appendSlideCommand((SlideSequencesCommand) historyList
                       .peek());
     }
 
@@ -2290,8 +2292,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
     newaf.setTitle(title.toString());
 
-    newaf.viewport.historyList = viewport.historyList;
-    newaf.viewport.redoList = viewport.redoList;
+    newaf.viewport.setHistoryList(viewport.getHistoryList());
+    newaf.viewport.setRedoList(viewport.getRedoList());
     return newaf;
   }
 
@@ -3592,8 +3594,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     // view cannot be closed if its actually on the page
     fileMenu.remove(closeMenuItem);
     fileMenu.remove(3); // Remove Separator
-    embeddedMenu = makeEmbeddedPopupMenu(alignFrameMenuBar,
-            FONT_ARIAL_PLAIN_11, false, false); // use our own fonts.
+    // construct embedded menu, using default font
+    embeddedMenu = makeEmbeddedPopupMenu(alignFrameMenuBar, false, false);
     // and actually add the components to the applet area
     theApplet.setLayout(new BorderLayout());
     theApplet.add(embeddedMenu, BorderLayout.NORTH);
index 156c517..58b2644 100644 (file)
@@ -23,6 +23,7 @@ package jalview.appletgui;
 import jalview.analysis.NJTree;
 import jalview.api.AlignViewportI;
 import jalview.bin.JalviewLite;
+import jalview.commands.CommandI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.Sequence;
@@ -30,15 +31,16 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.schemes.UserColourScheme;
+import jalview.structure.CommandListener;
 import jalview.structure.SelectionSource;
+import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
 import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Font;
-import java.util.Stack;
 
 public class AlignViewport extends AlignmentViewport implements
-        AlignViewportI, SelectionSource, VamsasSource
+        AlignViewportI, SelectionSource, VamsasSource, CommandListener
 {
   int startRes;
 
@@ -60,10 +62,6 @@ public class AlignViewport extends AlignmentViewport implements
 
   boolean MAC = false;
 
-  Stack historyList = new Stack();
-
-  Stack redoList = new Stack();
-
   private AnnotationColumnChooser annotationColumnSelectionState;
 
   public void finalize()
@@ -395,13 +393,25 @@ public class AlignViewport extends AlignmentViewport implements
 
   public void sendSelection()
   {
-    jalview.structure.StructureSelectionManager
-            .getStructureSelectionManager(applet).sendSelection(
+    getStructureSelectionManager().sendSelection(
                     new SequenceGroup(getSelectionGroup()),
                     new ColumnSelection(getColumnSelection()), this);
   }
 
   /**
+   * Returns an instance of the StructureSelectionManager scoped to this applet
+   * instance.
+   * 
+   * @return
+   */
+  @Override
+  public StructureSelectionManager getStructureSelectionManager()
+  {
+    return jalview.structure.StructureSelectionManager
+            .getStructureSelectionManager(applet);
+  }
+
+  /**
    * synthesize a column selection if none exists so it covers the given
    * selection group. if wholewidth is false, no column selection is made if the
    * selection group covers the whole alignment width.
@@ -464,4 +474,41 @@ public class AlignViewport extends AlignmentViewport implements
     this.annotationColumnSelectionState = annotationColumnSelectionState;
   }
 
+  @Override
+  public void mirrorCommand(CommandI command, boolean undo,
+          StructureSelectionManager ssm, VamsasSource source)
+  {
+    // TODO refactor so this can be pulled up to superclass or controller
+    /*
+     * Do nothing unless we are a 'complement' of the source. May replace this
+     * with direct calls not via SSM.
+     */
+    if (source instanceof AlignViewportI
+            && ((AlignViewportI) source).getCodingComplement() == this)
+    {
+      // ok to continue;
+    }
+    else
+    {
+      return;
+    }
+
+    CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(),
+            getGapCharacter());
+    if (mappedCommand != null)
+    {
+      mappedCommand.doCommand(null);
+      firePropertyChange("alignment", null, getAlignment().getSequences());
+
+      // ap.scalePanelHolder.repaint();
+      // ap.repaint();
+    }
+  }
+
+  @Override
+  public VamsasSource getVamsasSource()
+  {
+    return this;
+  }
+
 }
index 4eb3086..a4c1006 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.appletgui;
 
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.FlowLayout;
@@ -35,8 +33,8 @@ import java.awt.Panel;
 import java.awt.PopupMenu;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
-import java.util.Enumeration;
-import java.util.Hashtable;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * This class implements a pattern for embedding toolbars as a panel with popups
@@ -56,10 +54,12 @@ public class EmbmenuFrame extends Frame implements MouseListener
   protected static final Font FONT_ARIAL_PLAIN_11 = new Font(
             "Arial", Font.PLAIN, 11);
 
+  public static final Font DEFAULT_MENU_FONT = FONT_ARIAL_PLAIN_11;
+
   /**
    * map from labels to popup menus for the embedded menubar
    */
-  protected Hashtable embeddedPopup;
+  protected Map<Label, PopupMenu> embeddedPopup = new HashMap<Label, PopupMenu>();
 
   /**
    * the embedded menu is built on this and should be added to the frame at the
@@ -97,9 +97,8 @@ public class EmbmenuFrame extends Frame implements MouseListener
     // DEBUG Hint: can test embedded menus by inserting true here.
     if (new jalview.util.Platform().isAMac())
     {
-      // Build the embedded menu panel
-      embeddedMenu = makeEmbeddedPopupMenu(topMenuBar, FONT_ARIAL_PLAIN_11,
-              true, false); // try to pickup system font.
+      // Build the embedded menu panel, allowing override with system font
+      embeddedMenu = makeEmbeddedPopupMenu(topMenuBar, true, false);
       setMenuBar(null);
       // add the components to the Panel area.
       add(embeddedMenu, BorderLayout.NORTH);
@@ -116,34 +115,25 @@ public class EmbmenuFrame extends Frame implements MouseListener
    * menuBar from the Frame if it is already attached.
    * 
    * @param menuBar
-   * @param font
    * @param overrideFonts
    * @param append
    *          true means existing menu will be emptied before adding new
    *          elements
    * @return
    */
-  protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, Font font,
+  protected Panel makeEmbeddedPopupMenu(MenuBar menuBar,
           boolean overrideFonts, boolean append)
   {
     if (!append)
     {
-      if (embeddedPopup != null)
-      {
-        embeddedPopup.clear(); // TODO: check if j1.1
-      }
+      embeddedPopup.clear(); // TODO: check if j1.1
       if (embeddedMenu != null)
       {
         embeddedMenu.removeAll();
       }
     }
-    if (embeddedPopup == null)
-    {
-      embeddedPopup = new Hashtable();
-    }
-
-    embeddedMenu = makeEmbeddedPopupMenu(menuBar, font,
-            overrideFonts, embeddedPopup, new Panel(), this);
+    embeddedMenu = makeEmbeddedPopupMenu(menuBar, DEFAULT_MENU_FONT,
+            overrideFonts, new Panel(), this);
     return embeddedMenu;
   }
 
@@ -158,8 +148,6 @@ public class EmbmenuFrame extends Frame implements MouseListener
    *          must be non-null
    * @param font
    * @param overrideFonts
-   * @param embeddedPopup
-   *          must be non-null
    * @param embeddedMenu
    *          if null, a new panel will be created and returned
    * @param clickHandler
@@ -169,13 +157,9 @@ public class EmbmenuFrame extends Frame implements MouseListener
    */
   protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, Font font,
           boolean overrideFonts,
-          Hashtable embeddedPopup, Panel embeddedMenu,
+          Panel embeddedMenu,
           MouseListener clickHandler)
   {
-    if (embeddedPopup == null)
-    {
-      throw new Error(MessageManager.getString("error.implementation_error_embeddedpopup_not_null"));
-    }
     if (overrideFonts)
     {
       Font mbf = menuBar.getFont();
@@ -237,7 +221,7 @@ public class EmbmenuFrame extends Frame implements MouseListener
    */
   PopupMenu getPopupMenu(Label source)
   {
-    return (PopupMenu) embeddedPopup.get(source);
+    return embeddedPopup.get(source);
   }
 
   public void mouseClicked(MouseEvent evt)
@@ -264,10 +248,8 @@ public class EmbmenuFrame extends Frame implements MouseListener
   {
     if (embeddedPopup != null)
     {
-      Enumeration e = embeddedPopup.keys();
-      while (e.hasMoreElements())
+      for (Label lb : embeddedPopup.keySet())
       {
-        Label lb = (Label) e.nextElement();
         lb.removeMouseListener(this);
       }
       embeddedPopup.clear();
index 6bc67b5..b422cac 100644 (file)
  */
 package jalview.appletgui;
 
+import jalview.api.ViewStyleI;
 import jalview.util.MessageManager;
 
-import java.awt.*;
-import java.awt.event.*;
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.Choice;
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Frame;
+import java.awt.Label;
+import java.awt.Panel;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 
 public class FontChooser extends Panel implements ActionListener,
         ItemListener
@@ -34,6 +48,8 @@ public class FontChooser extends Panel implements ActionListener,
 
   Font oldFont;
 
+  int oldCharWidth = 0;
+
   boolean init = true;
 
   Frame frame;
@@ -65,6 +81,7 @@ public class FontChooser extends Panel implements ActionListener,
 
     this.ap = ap;
     oldFont = ap.av.getFont();
+    oldCharWidth = ap.av.getViewStyle().getCharWidth();
     init();
   }
 
@@ -144,6 +161,12 @@ public class FontChooser extends Panel implements ActionListener,
     if (ap != null)
     {
       ap.av.setFont(oldFont);
+      ViewStyleI style = ap.av.getViewStyle();
+      if (style.getCharWidth() != oldCharWidth)
+      {
+        style.setCharWidth(oldCharWidth);
+        ap.av.setViewStyle(style);
+      }
       ap.paintAlignment(true);
     }
     else if (tp != null)
index 8e364f0..a81ffd4 100644 (file)
@@ -233,9 +233,9 @@ public class RedundancyPanel extends SliderPanel implements Runnable,
     CommandI command = (CommandI) historyList.pop();
     command.undoCommand(null);
 
-    if (ap.av.historyList.contains(command))
+    if (ap.av.getHistoryList().contains(command))
     {
-      ap.av.historyList.removeElement(command);
+      ap.av.getHistoryList().remove(command);
       ap.alignFrame.updateEditMenuBar();
       ap.av.firePropertyChange("alignment", null, ap.av.getAlignment().getSequences());
     }
index cf0a047..bea86b2 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.appletgui;
 
+import jalview.api.AlignViewportI;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.datamodel.ColumnSelection;
@@ -29,10 +30,12 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ResidueProperties;
+import jalview.structure.SelectionListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.SequenceListener;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -47,7 +50,7 @@ import java.awt.event.MouseMotionListener;
 import java.util.Vector;
 
 public class SeqPanel extends Panel implements MouseMotionListener,
-        MouseListener, SequenceListener
+        MouseListener, SequenceListener, SelectionListener
 {
 
   public SeqCanvas seqCanvas;
@@ -110,6 +113,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     seqCanvas.addMouseListener(this);
     ssm = StructureSelectionManager.getStructureSelectionManager(av.applet);
     ssm.addStructureViewerListener(this);
+    ssm.addSelectionListener(this);
 
     seqCanvas.repaint();
   }
@@ -1729,6 +1733,16 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     {
       return;
     }
+
+    /*
+     * Check for selection in a view of which this one is a dna/protein
+     * complement.
+     */
+    if (selectionFromTranslation(seqsel, colsel, source))
+    {
+      return;
+    }
+
     // do we want to thread this ? (contention with seqsel and colsel locks, I
     // suspect)
     // rules are: colsel is copied if there is a real intersection between
@@ -1846,4 +1860,46 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     ap.scrollTo(column, column, ap.av.startSeq, true, true);
   }
 
+  /**
+   * If this panel is a cdna/protein translation view of the selection source,
+   * tries to map the source selection to a local one, and returns true. Else
+   * returns false.
+   * 
+   * @param seqsel
+   * @param colsel
+   * @param source
+   */
+  protected boolean selectionFromTranslation(SequenceGroup seqsel,
+          ColumnSelection colsel, SelectionSource source)
+  {
+    if (!(source instanceof AlignViewportI)) {
+      return false;
+    }
+    final AlignViewportI sourceAv = (AlignViewportI) source;
+    if (sourceAv.getCodingComplement() != av && av.getCodingComplement() != sourceAv)
+    {
+      return false;
+    }
+  
+    /*
+     * Map sequence selection
+     */
+    SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av);
+    av.setSelectionGroup(sg);
+    av.isSelectionGroupChanged(true);
+  
+    /*
+     * Map column selection
+     */
+    ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
+            av);
+    av.setColumnSelection(cs);
+    av.isColSelChanged(true);
+  
+    firePropertyChange("alignment", null, av.getAlignment().getSequences());
+    // PaintRefresher.Refresh(this, av.getSequenceSetId());
+  
+    return true;
+  }
+
 }
index d77f331..a012092 100644 (file)
@@ -1,6 +1,11 @@
 package jalview.appletgui;
 
+import jalview.analysis.AlignmentUtils;
+import jalview.analysis.AlignmentUtils.MappingResult;
+import jalview.api.ViewStyleI;
 import jalview.bin.JalviewLite;
+import jalview.datamodel.AlignmentI;
+import jalview.structure.StructureSelectionManager;
 
 import java.awt.BorderLayout;
 import java.awt.GridLayout;
@@ -45,6 +50,42 @@ public class SplitFrame extends EmbmenuFrame
 
     addAlignFrameComponents(topFrame, topPanel);
     addAlignFrameComponents(bottomFrame, bottomPanel);
+
+    /*
+     * Try to make and add dna/protein sequence mappings
+     */
+    final AlignViewport topViewport = topFrame.viewport;
+    final AlignViewport bottomViewport = bottomFrame.viewport;
+    final AlignmentI topAlignment = topViewport.getAlignment();
+    final AlignmentI bottomAlignment = bottomViewport.getAlignment();
+    // topAlignment.setDataset(null);
+    // bottomAlignment.setDataset(null);
+    AlignViewport cdna = topAlignment.isNucleotide() ? topViewport
+            : (bottomAlignment.isNucleotide() ? bottomViewport : null);
+    AlignViewport protein = !topAlignment.isNucleotide() ? topViewport
+            : (!bottomAlignment.isNucleotide() ? bottomViewport : null);
+    MappingResult mapped = AlignmentUtils.mapProteinToCdna(
+            protein.getAlignment(), cdna.getAlignment());
+    if (mapped != MappingResult.NotMapped)
+    {
+      final StructureSelectionManager ssm = StructureSelectionManager
+              .getStructureSelectionManager(topViewport.applet);
+      ssm.addMappings(protein.getAlignment().getCodonFrames());
+      topViewport.setCodingComplement(bottomViewport);
+      ssm.addCommandListener(cdna);
+      ssm.addCommandListener(protein);
+    }
+
+    /*
+     * Expand protein to 3 times character width of dna
+     */
+    if (protein != null && cdna != null)
+    {
+      ViewStyleI vs = protein.getViewStyle();
+      vs.setCharWidth(3 * vs.getCharWidth());
+      protein.setViewStyle(vs);
+    }
+
   }
 
   /**
@@ -58,7 +99,8 @@ public class SplitFrame extends EmbmenuFrame
   private void addAlignFrameComponents(AlignFrame af, Panel panel)
   {
     panel.setLayout(new BorderLayout());
-    Panel menuPanel = makeEmbeddedPopupMenu(af.getMenuBar(), FONT_ARIAL_PLAIN_11, true, false);
+    Panel menuPanel = af
+            .makeEmbeddedPopupMenu(af.getMenuBar(), true, false);
     panel.add(menuPanel, BorderLayout.NORTH);
     panel.add(af.statusBar, BorderLayout.SOUTH);
     panel.add(af.alignPanel, BorderLayout.CENTER);
index e8d8363..6630d8e 100755 (executable)
@@ -22,6 +22,7 @@ package jalview.appletgui;
 
 import jalview.analysis.Conservation;
 import jalview.analysis.NJTree;
+import jalview.api.AlignViewportI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -31,6 +32,7 @@ import jalview.schemes.ColourSchemeProperty;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
 import jalview.util.Format;
+import jalview.util.MappingUtils;
 
 import java.awt.Color;
 import java.awt.Dimension;
@@ -600,6 +602,13 @@ public class TreeCanvas extends Panel implements MouseListener,
         av.setSelectionGroup(null);
         av.getAlignment().deleteAllGroups();
         av.clearSequenceColours();
+        final AlignViewportI codingComplement = av.getCodingComplement();
+        if (codingComplement != null)
+        {
+          codingComplement.setSelectionGroup(null);
+          codingComplement.getAlignment().deleteAllGroups();
+          codingComplement.clearSequenceColours();
+        }
 
         colourGroups();
 
@@ -684,9 +693,31 @@ public class TreeCanvas extends Panel implements MouseListener,
 
       av.getAlignment().addGroup(sg);
 
+      // TODO this is duplicated with gui TreeCanvas - refactor
+      av.getAlignment().addGroup(sg);
+      final AlignViewportI codingComplement = av.getCodingComplement();
+      if (codingComplement != null)
+      {
+        SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av,
+                codingComplement);
+        if (mappedGroup.getSequences().size() > 0)
+        {
+          codingComplement.getAlignment().addGroup(mappedGroup);
+          for (SequenceI seq : mappedGroup.getSequences())
+          {
+            // TODO why does gui require col.brighter() here??
+            codingComplement.setSequenceColour(seq, col);
+          }
+        }
+      }
+
     }
     ap.updateAnnotation();
-
+    if (av.getCodingComplement() != null)
+    {
+      ((AlignViewport) av.getCodingComplement()).firePropertyChange(
+              "alignment", null, ap.av.getAlignment().getSequences());
+    }
   }
 
   public void setShowDistances(boolean state)
index ca3de8b..a004f24 100644 (file)
@@ -1916,6 +1916,7 @@ public class JalviewLite extends Applet implements
         if ((al != null) && (al.getHeight() > 0))
         {
           dbgMsg("Successfully loaded file.");
+          al.setDataset(null);
           AlignFrame newAlignFrame = new AlignFrame(al, applet,
                   resolvedFile, embedded, false);
           newAlignFrame.setTitle(resolvedFile);
index 9c046c6..beb6b51 100755 (executable)
@@ -1307,6 +1307,9 @@ public class SequenceGroup implements AnnotatedCollectionI
     return false;
   }
 
+  /**
+   * Remove all sequences from the group (leaving other properties unchanged).
+   */
   public void clear()
   {
     sequences.clear();
index 30749a5..38940d2 100644 (file)
@@ -4927,7 +4927,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   /**
    * Construct and display a new frame containing the translation of this
-   * frame's cDNA sequences to their aligned protein (amino acid) equivalents.
+   * frame's DNA sequences to their aligned protein (amino acid) equivalents.
    */
   @Override
   public void showTranslation_actionPerformed(ActionEvent e)
@@ -4964,10 +4964,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
       af.setFileFormat(this.currentFileFormat);
-      Desktop.addInternalFrame(af, MessageManager.formatMessage(
+      final String newTitle = MessageManager.formatMessage(
               "label.translation_of_params", new Object[]
-              { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
-      // enable next line for linked editing
+              { this.getTitle() });
+      af.setTitle(newTitle);
+      viewport.openSplitFrame(af, viewport.getAlignment());
+      // Desktop.addInternalFrame(af, newTitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
+      // // enable next line for linked editing
       // viewport.getStructureSelectionManager().addCommandListener(viewport);
     }
   }
index 6208597..8fc10d7 100644 (file)
@@ -67,10 +67,7 @@ import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Rectangle;
-import java.io.File;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Deque;
 import java.util.Hashtable;
 import java.util.Set;
 import java.util.Vector;
@@ -85,7 +82,7 @@ import javax.swing.JOptionPane;
  * @version $Revision: 1.141 $
  */
 public class AlignViewport extends AlignmentViewport implements
-        SelectionSource, VamsasSource, AlignViewportI, CommandListener
+        SelectionSource, AlignViewportI, CommandListener
 {
   int startRes;
 
@@ -112,10 +109,6 @@ public class AlignViewport extends AlignmentViewport implements
 
   private boolean gatherViewsHere = false;
 
-  private Deque<CommandI> historyList = new ArrayDeque<CommandI>();
-
-  private Deque<CommandI> redoList = new ArrayDeque<CommandI>();
-
   private AnnotationColumnChooser annotationColumnSelectionState;
   /**
    * Creates a new AlignViewport object.
@@ -762,6 +755,10 @@ public class AlignViewport extends AlignmentViewport implements
     }
   }
 
+  /**
+   * Returns the (Desktop) instance of the StructureSelectionManager
+   */
+  @Override
   public StructureSelectionManager getStructureSelectionManager()
   {
     return StructureSelectionManager
@@ -891,8 +888,8 @@ public class AlignViewport extends AlignmentViewport implements
           StructureSelectionManager ssm, VamsasSource source)
   {
     /*
-     * ...work in progress... do nothing unless we are a 'complement' of the
-     * source May replace this with direct calls not via SSM.
+     * Do nothing unless we are a 'complement' of the source. May replace this
+     * with direct calls not via SSM.
      */
     if (source instanceof AlignViewportI
             && ((AlignViewportI) source).getCodingComplement() == this)
@@ -914,76 +911,6 @@ public class AlignViewport extends AlignmentViewport implements
     }
   }
 
-  @Override
-  public VamsasSource getVamsasSource()
-  {
-    return this;
-  }
-
-  /**
-   * Add one command to the command history list.
-   * 
-   * @param command
-   */
-  public void addToHistoryList(CommandI command)
-  {
-    if (this.historyList != null)
-    {
-      this.historyList.push(command);
-      broadcastCommand(command, false);
-    }
-  }
-
-  protected void broadcastCommand(CommandI command, boolean undo)
-  {
-    getStructureSelectionManager().commandPerformed(command, undo, getVamsasSource());
-  }
-
-  /**
-   * Add one command to the command redo list.
-   * 
-   * @param command
-   */
-  public void addToRedoList(CommandI command)
-  {
-    if (this.redoList != null)
-    {
-      this.redoList.push(command);
-    }
-    broadcastCommand(command, true);
-  }
-
-  /**
-   * Clear the command redo list.
-   */
-  public void clearRedoList()
-  {
-    if (this.redoList != null)
-    {
-      this.redoList.clear();
-    }
-  }
-
-  public void setHistoryList(Deque<CommandI> list)
-  {
-    this.historyList = list;
-  }
-
-  public Deque<CommandI> getHistoryList()
-  {
-    return this.historyList;
-  }
-
-  public void setRedoList(Deque<CommandI> list)
-  {
-    this.redoList = list;
-  }
-
-  public Deque<CommandI> getRedoList()
-  {
-    return this.redoList;
-  }
-
   /**
    * Add the sequences from the given alignment to this viewport. Optionally,
    * may give the user the option to open a new frame, or split panel, with cDNA
@@ -1103,8 +1030,6 @@ public class AlignViewport extends AlignmentViewport implements
      * added to the protein alignment.
      */
     MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, cdna);
-    final StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
     if (mapped != MappingResult.Mapped)
     {
       /*
@@ -1128,55 +1053,72 @@ public class AlignViewport extends AlignmentViewport implements
 
     if (openSplitPane)
     {
-      // TODO: move this kind of constructor stuff to a factory/controller
-      // method ?
-      /*
-       * Open in split pane. DNA sequence above, protein below.
-       */
-      AlignFrame copyMe = new AlignFrame(thisAlignment,
-              AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
-      copyMe.setTitle(getAlignPanel().alignFrame.getTitle());
-      final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
-              : newAlignFrame;
-      final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame
-              : copyMe;
-      protein = proteinFrame.viewport.getAlignment();
-
-      cdnaFrame.setVisible(true);
-      proteinFrame.setVisible(true);
-      String sep = String.valueOf(File.separatorChar);
-      String proteinShortName = proteinFrame.getTitle().substring(
-              proteinFrame.getTitle().lastIndexOf(sep) + 1);
-      String dnaShortName = cdnaFrame.getTitle().substring(
-              cdnaFrame.getTitle().lastIndexOf(sep) + 1);
-      String linkedTitle = MessageManager.formatMessage(
-              "label.linked_view_title", dnaShortName, proteinShortName);
-      JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame);
-      Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
-
-      /*
-       * Set the frames to listen for each other's edit and sort commands.
-       */
-      ssm.addCommandListener(cdnaFrame.getViewport());
-      ssm.addCommandListener(proteinFrame.getViewport());
-
-      /*
-       * 'Coding complement' (dna/protein) views will mirror each others' edits,
-       * selections, sorting etc as decided from time to time by the relevant
-       * authorities.
-       */
-      proteinFrame.getViewport().setCodingComplement(cdnaFrame.getViewport());
+      protein = openSplitFrame(newAlignFrame, thisAlignment);
     }
 
     /*
      * Register the mappings (held on the protein alignment) with the
      * StructureSelectionManager (for mouseover linking).
      */
+    final StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
     ssm.addMappings(protein.getCodonFrames());
 
     return true;
   }
 
+  /**
+   * Helper method to open a new SplitFrame holding linked dna and protein
+   * alignments.
+   * 
+   * @param newAlignFrame
+   *          containing a new alignment to be shown
+   * @param existingAlignment
+   *          an existing alignment to be copied for display in the split frame
+   * @return the protein alignment in the split frame
+   */
+  protected AlignmentI openSplitFrame(AlignFrame newAlignFrame,
+          AlignmentI existingAlignment)
+  {
+    // TODO: move this to a factory/controller method ?
+    /*
+     * Open in split pane. DNA sequence above, protein below.
+     */
+    AlignFrame copyMe = new AlignFrame(existingAlignment,
+            AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+    copyMe.setTitle(getAlignPanel().alignFrame.getTitle());
+
+    AlignmentI al = newAlignFrame.viewport.getAlignment();
+    final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
+            : newAlignFrame;
+    final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame
+            : copyMe;
+    AlignmentI protein = proteinFrame.viewport.getAlignment();
+
+    cdnaFrame.setVisible(true);
+    proteinFrame.setVisible(true);
+    String linkedTitle = MessageManager
+            .getString("label.linked_view_title");
+    JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame);
+    Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
+
+    /*
+     * Set the frames to listen for each other's edit and sort commands.
+     */
+    final StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
+    ssm.addCommandListener(cdnaFrame.getViewport());
+    ssm.addCommandListener(proteinFrame.getViewport());
+
+    /*
+     * 'Coding complement' (dna/protein) views will mirror each others' edits,
+     * selections, sorting etc as decided from time to time by the relevant
+     * authorities.
+     */
+    proteinFrame.getViewport().setCodingComplement(cdnaFrame.getViewport());
+    return protein;
+  }
+
   public AnnotationColumnChooser getAnnotationColumnSelectionState()
   {
     return annotationColumnSelectionState;
index d0da010..7132ff0 100644 (file)
@@ -1328,24 +1328,24 @@ public class AlignmentPanel extends GAlignmentPanel implements
           for (res = 0; res < alwidth; res++)
           {
             text = new StringBuffer();
-            Object obj = null;
+            String triplet = null;
             if (av.getAlignment().isNucleotide())
             {
-              obj = ResidueProperties.nucleotideName.get(seq.getCharAt(res)
+              triplet = ResidueProperties.nucleotideName.get(seq
+                      .getCharAt(res)
                       + "");
             }
             else
             {
-              obj = ResidueProperties.aa2Triplet.get(seq.getCharAt(res)
+              triplet = ResidueProperties.aa2Triplet.get(seq.getCharAt(res)
                       + "");
             }
 
-            if (obj == null)
+            if (triplet == null)
             {
               continue;
             }
 
-            String triplet = obj.toString();
             int alIndex = seq.findPosition(res);
             gSize = groups.length;
             for (g = 0; g < gSize; g++)
index 6f3e7b8..3cc82c5 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.jbgui.GFinder;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.event.ActionEvent;
 import java.util.Vector;
@@ -52,7 +53,7 @@ public class Finder extends GFinder
 
   private static final int WIDTH = 340;
 
-  AlignViewport av;
+  AlignmentViewport av;
 
   AlignmentPanel ap;
 
@@ -80,7 +81,7 @@ public class Finder extends GFinder
    * @param viewport
    * @param alignPanel
    */
-  public Finder(AlignViewport viewport, AlignmentPanel alignPanel)
+  public Finder(AlignmentViewport viewport, AlignmentPanel alignPanel)
   {
     av = viewport;
     ap = alignPanel;
index e1d1925..86fbde8 100644 (file)
@@ -3604,7 +3604,7 @@ public class Jalview2XML
 
     if (view.getSequenceSetId() != null)
     {
-      jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
+      AlignmentViewport av = (AlignmentViewport) viewportsAdded
               .get(uniqueSeqSetId);
 
       af.viewport.setSequenceSetId(uniqueSeqSetId);
index 791e04f..c46bcd4 100644 (file)
@@ -1,8 +1,11 @@
 package jalview.gui;
 
+import jalview.api.ViewStyleI;
+import jalview.datamodel.AlignmentI;
 import jalview.jbgui.GAlignFrame;
 import jalview.jbgui.GSplitFrame;
 import jalview.structure.StructureSelectionManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Component;
 import java.awt.Toolkit;
@@ -51,6 +54,8 @@ public class SplitFrame extends GSplitFrame
   {
     setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 20);
 
+    setCharacterWidth();
+
     addCloseFrameListener();
     
     addKeyListener();
@@ -59,6 +64,27 @@ public class SplitFrame extends GSplitFrame
   }
 
   /**
+   * Set the character width for protein to 3 times that for dna.
+   */
+  private void setCharacterWidth()
+  {
+    final AlignViewport topViewport = ((AlignFrame) getTopFrame()).viewport;
+    final AlignViewport bottomViewport = ((AlignFrame) getBottomFrame()).viewport;
+    final AlignmentI topAlignment = topViewport.getAlignment();
+    final AlignmentI bottomAlignment = bottomViewport.getAlignment();
+    AlignmentViewport cdna = topAlignment.isNucleotide() ? topViewport
+            : (bottomAlignment.isNucleotide() ? bottomViewport : null);
+    AlignmentViewport protein = !topAlignment.isNucleotide() ? topViewport
+            : (!bottomAlignment.isNucleotide() ? bottomViewport : null);
+    if (protein != null && cdna != null)
+    {
+      ViewStyleI vs = cdna.getViewStyle();
+      vs.setCharWidth(3 * vs.getCharWidth());
+      protein.setViewStyle(vs);
+    }
+  }
+
+  /**
    * Add a listener to tidy up when the frame is closed.
    */
   protected void addCloseFrameListener()
index d02a3eb..fc0a758 100755 (executable)
@@ -992,8 +992,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
           codingComplement.getAlignment().addGroup(mappedGroup);
           for (SequenceI seq : mappedGroup.getSequences())
           {
-            codingComplement.setSequenceColour(seq,
-                    mappedGroup.getIdColour().brighter());
+            codingComplement.setSequenceColour(seq, col.brighter());
           }
         }
       }
index d7809cb..cde6d5f 100755 (executable)
@@ -43,6 +43,7 @@ import jalview.io.NewickFile;
 import jalview.jbgui.GTreePanel;
 import jalview.schemes.ResidueProperties;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Font;
 import java.awt.Graphics;
@@ -138,7 +139,7 @@ public class TreePanel extends GTreePanel
     return treeCanvas.av.getAlignment();
   }
 
-  public AlignViewport getViewPort()
+  public AlignmentViewport getViewPort()
   {
     return treeCanvas.av;
   }
@@ -619,7 +620,7 @@ public class TreePanel extends GTreePanel
 
   public CommandI sortAlignmentIn(AlignmentPanel ap)
   {
-    AlignViewport av = ap.av;
+    AlignmentViewport av = ap.av;
     SequenceI[] oldOrder = av.getAlignment().getSequencesArray();
     AlignmentSorter.sortByTree(av.getAlignment(), tree);
     CommandI undo;
index db43a3f..ad3b79f 100644 (file)
@@ -5,7 +5,6 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.exceptions.NoFileSelectedException;
-import jalview.gui.AlignViewport;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.FeatureRenderer;
 import jalview.json.binding.v1.BioJsAlignmentPojo;
@@ -13,6 +12,7 @@ import jalview.json.binding.v1.BioJsFeaturePojo;
 import jalview.json.binding.v1.BioJsSeqPojo;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Color;
 import java.io.BufferedReader;
@@ -26,7 +26,7 @@ import com.json.JSONException;
 
 public class BioJsHTMLOutput
 {
-  private AlignViewport av;
+  private AlignmentViewport av;
 
   private jalview.api.FeatureRenderer fr;
 
index 6dfebfe..1f2e8db 100644 (file)
@@ -16,7 +16,6 @@ import jalview.datamodel.SearchResults.Match;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
-import jalview.gui.AlignViewport;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -384,8 +383,8 @@ public final class MappingUtils
     }
 
     /*
-     * Have to align the sequences before constructing the OrderCommand - which
-     * then realigns them?!?
+     * Have to sort the sequences before constructing the OrderCommand - which
+     * then resorts them?!?
      */
     final SequenceI[] mappedOrderArray = mappedOrder
             .toArray(new SequenceI[mappedOrder.size()]);
@@ -407,7 +406,7 @@ public final class MappingUtils
    * @return
    */
   public static ColumnSelection mapColumnSelection(ColumnSelection colsel,
-          AlignViewportI mapFrom, AlignViewport mapTo)
+          AlignViewportI mapFrom, AlignViewportI mapTo)
   {
     boolean targetIsNucleotide = mapTo.isNucleotide();
     AlignViewportI protein = targetIsNucleotide ? mapFrom : mapTo;
@@ -416,6 +415,12 @@ public final class MappingUtils
     ColumnSelection mappedColumns = new ColumnSelection();
     char fromGapChar = mapFrom.getAlignment().getGapCharacter();
 
+    // FIXME allow for hidden columns
+
+    /*
+     * For each mapped column, find the range of columns that residues in that
+     * column map to.
+     */
     for (Object obj : colsel.getSelected())
     {
       int col = ((Integer) obj).intValue();
@@ -462,13 +467,15 @@ public final class MappingUtils
               // System.out.println(fromSeq.getName() + " mapped to cols "
               // + mappedStartCol + ":" + mappedEndCol);
               break;
-              // TODO remove break if we ever want to map one to many sequences
+              // note: remove break if we ever want to map one to many sequences
             }
           }
         }
       }
       /*
-       * Add mapped columns to mapped selection (converting base 1 to base 0)
+       * Add the range of mapped columns to the mapped selection (converting
+       * base 1 to base 0). Note that this may include intron-only regions which
+       * lie between the start and end ranges of the selection.
        */
       for (int i = mappedToMin; i <= mappedToMax; i++)
       {
index 67db6fc..4fc587e 100644 (file)
@@ -26,6 +26,7 @@ import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeaturesDisplayedI;
 import jalview.api.ViewStyleI;
+import jalview.commands.CommandI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
@@ -40,14 +41,19 @@ import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.PIDColourScheme;
 import jalview.schemes.ResidueProperties;
+import jalview.structure.CommandListener;
+import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasSource;
 import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
 import jalview.workers.ConsensusThread;
 import jalview.workers.StrucConsensusThread;
 
 import java.awt.Color;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.BitSet;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
@@ -61,7 +67,7 @@ import java.util.Map;
  * 
  */
 public abstract class AlignmentViewport implements AlignViewportI,
-        ViewStyleI
+        ViewStyleI, CommandListener, VamsasSource
 {
   protected ViewStyleI viewStyle = new ViewStyle();
 
@@ -71,6 +77,12 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   AlignViewportI codingComplement = null;
 
+  FeaturesDisplayedI featuresDisplayed = null;
+
+  protected Deque<CommandI> historyList = new ArrayDeque<CommandI>();
+
+  protected Deque<CommandI> redoList = new ArrayDeque<CommandI>();
+
   /**
    * @param name
    * @see jalview.api.ViewStyleI#setFontName(java.lang.String)
@@ -1958,8 +1970,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return getAlignment() == null ? false : getAlignment().isNucleotide();
   }
 
-  FeaturesDisplayedI featuresDisplayed = null;
-
   @Override
   public FeaturesDisplayedI getFeaturesDisplayed()
   {
@@ -2242,4 +2252,76 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     viewStyle.setShowNPFeats(shownpfeats);
   }
+
+  public abstract StructureSelectionManager getStructureSelectionManager();
+
+  /**
+   * Add one command to the command history list.
+   * 
+   * @param command
+   */
+  public void addToHistoryList(CommandI command)
+  {
+    if (this.historyList != null)
+    {
+      this.historyList.push(command);
+      broadcastCommand(command, false);
+    }
+  }
+
+  protected void broadcastCommand(CommandI command, boolean undo)
+  {
+    getStructureSelectionManager().commandPerformed(command, undo, getVamsasSource());
+  }
+
+  /**
+   * Add one command to the command redo list.
+   * 
+   * @param command
+   */
+  public void addToRedoList(CommandI command)
+  {
+    if (this.redoList != null)
+    {
+      this.redoList.push(command);
+    }
+    broadcastCommand(command, true);
+  }
+
+  /**
+   * Clear the command redo list.
+   */
+  public void clearRedoList()
+  {
+    if (this.redoList != null)
+    {
+      this.redoList.clear();
+    }
+  }
+
+  public void setHistoryList(Deque<CommandI> list)
+  {
+    this.historyList = list;
+  }
+
+  public Deque<CommandI> getHistoryList()
+  {
+    return this.historyList;
+  }
+
+  public void setRedoList(Deque<CommandI> list)
+  {
+    this.redoList = list;
+  }
+
+  public Deque<CommandI> getRedoList()
+  {
+    return this.redoList;
+  }
+
+  @Override
+  public VamsasSource getVamsasSource()
+  {
+    return this;
+  }
 }
index 2a2d2ab..1ef1d07 100644 (file)
@@ -262,4 +262,52 @@ public class SequenceTest
     seq.createDatasetSequence();
     assertEquals("[1, 4, 6, 7, 9, 12]", Arrays.toString(seq.gapMap()));
   }
+
+  /**
+   * Test the method that gets sequence features, either from the sequence or
+   * its dataset.
+   */
+  @Test
+  public void testGetSequenceFeatures()
+  {
+    SequenceI seq = new Sequence("test", "GATCAT");
+    seq.createDatasetSequence();
+
+    assertNull(seq.getSequenceFeatures());
+
+    /*
+     * SequenceFeature on sequence
+     */
+    SequenceFeature sf = new SequenceFeature();
+    seq.addSequenceFeature(sf);
+    SequenceFeature[] sfs = seq.getSequenceFeatures();
+    assertEquals(1, sfs.length);
+    assertSame(sf, sfs[0]);
+
+    /*
+     * SequenceFeature on sequence and dataset sequence; returns that on
+     * sequence
+     */
+    SequenceFeature sf2 = new SequenceFeature();
+    seq.getDatasetSequence().addSequenceFeature(sf2);
+    sfs = seq.getSequenceFeatures();
+    assertEquals(1, sfs.length);
+    assertSame(sf, sfs[0]);
+
+    /*
+     * SequenceFeature on dataset sequence only
+     */
+    seq.setSequenceFeatures(null);
+    sfs = seq.getSequenceFeatures();
+    assertEquals(1, sfs.length);
+    assertSame(sf2, sfs[0]);
+
+    /*
+     * Corrupt case - no SequenceFeature, dataset's dataset is the original
+     * sequence. Test shows no infinite loop results.
+     */
+    seq.getDatasetSequence().setSequenceFeatures(null);
+    seq.getDatasetSequence().setDatasetSequence(seq); // loop!
+    assertNull(seq.getSequenceFeatures());
+  }
 }
index be4e5ea..ad4f997 100644 (file)
@@ -1,13 +1,14 @@
 package jalview.ext.rbvi.chimera;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertTrue;
 
-import java.util.Arrays;
 import java.util.Collection;
 
 import org.junit.Test;
 
-import ext.edu.ucsf.rbvi.strucviz2.*;
+import ext.edu.ucsf.rbvi.strucviz2.ChimeraManager;
+import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel;
+import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 
 public class ChimeraConnect
 {
@@ -15,9 +16,10 @@ public class ChimeraConnect
   @Test
   public void test()
   {
-    StructureManager csm; 
+    StructureManager csm;
             ext.edu.ucsf.rbvi.strucviz2.ChimeraManager cm = new ChimeraManager(csm = new ext.edu.ucsf.rbvi.strucviz2.StructureManager(true));
-    assertTrue("Couldn't launch chimera",cm.launchChimera(csm.getChimeraPaths()));
+    assertTrue("Couldn't launch chimera",
+            cm.launchChimera(StructureManager.getChimeraPaths()));
     int n=0;
     while (n++<100)
     {
index 2737dd0..1da7c8c 100644 (file)
@@ -6,6 +6,7 @@ import static org.junit.Assert.assertTrue;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Component;
 import java.util.List;
@@ -101,7 +102,7 @@ public class PaintRefresherTest
      * and SeqCanvas, IdPanel, AlignmentPanel are all registered under the
      * sequence set id of the viewport.
      */
-    AlignViewport av = new AlignViewport(al);
+    AlignmentViewport av = new AlignViewport(al);
     AlignFrame af = new AlignFrame(al, 4, 1);
     AlignmentPanel ap1 = af.alignPanel;
     AlignmentPanel[] panels = PaintRefresher.getAssociatedPanels(av
diff --git a/test/jalview/util/MappingUtilsTest.java b/test/jalview/util/MappingUtilsTest.java
new file mode 100644 (file)
index 0000000..f0f3be7
--- /dev/null
@@ -0,0 +1,400 @@
+package jalview.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import jalview.api.AlignViewportI;
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.gui.AlignViewport;
+import jalview.io.AppletFormatAdapter;
+import jalview.io.FormatAdapter;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+import org.junit.Test;
+
+public class MappingUtilsTest
+{
+  private AlignViewportI dnaView;
+  private AlignViewportI proteinView;
+
+  /**
+   * Simple test of mapping with no intron involved.
+   */
+  @Test
+  public void testBuildSearchResults()
+  {
+    final Sequence seq1 = new Sequence("Seq1", "C-G-TA-GC");
+    seq1.createDatasetSequence();
+
+    final Sequence aseq1 = new Sequence("Seq1", "-P-R");
+    aseq1.createDatasetSequence();
+
+    /*
+     * Map dna bases 1-6 to protein residues 1-2
+     */
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList map = new MapList(new int[]
+    { 1, 6 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
+    Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
+
+    /*
+     * Check protein residue 1 maps to codon 1-3, 2 to codon 4-6
+     */
+    SearchResults sr = MappingUtils.buildSearchResults(aseq1, 1, acfList);
+    assertEquals(1, sr.getResults().size());
+    Match m = sr.getResults().get(0);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(1, m.getStart());
+    assertEquals(3, m.getEnd());
+    sr = MappingUtils.buildSearchResults(aseq1, 2, acfList);
+    assertEquals(1, sr.getResults().size());
+    m = sr.getResults().get(0);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(4, m.getStart());
+    assertEquals(6, m.getEnd());
+
+    /*
+     * Check inverse mappings, from codons 1-3, 4-6 to protein 1, 2
+     */
+    for (int i = 1; i < 7; i++)
+    {
+      sr = MappingUtils.buildSearchResults(seq1, i, acfList);
+      assertEquals(1, sr.getResults().size());
+      m = sr.getResults().get(0);
+      assertEquals(aseq1.getDatasetSequence(), m.getSequence());
+      int residue = i > 3 ? 2 : 1;
+      assertEquals(residue, m.getStart());
+      assertEquals(residue, m.getEnd());
+    }
+  }
+
+  /**
+   * Simple test of mapping with introns involved.
+   */
+  @Test
+  public void testBuildSearchResults_withIntro()
+  {
+    final Sequence seq1 = new Sequence("Seq1", "C-G-TAGA-GCAGCTT");
+    seq1.createDatasetSequence();
+  
+    final Sequence aseq1 = new Sequence("Seq1", "-P-R");
+    aseq1.createDatasetSequence();
+  
+    /*
+     * Map dna bases [2, 4, 5], [7, 9, 11] to protein residues 1 and 2
+     */
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList map = new MapList(new int[]
+    { 2, 2, 4, 5, 7, 7, 9, 9, 11, 11 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
+    Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
+  
+    /*
+     * Check protein residue 1 maps to [2, 4, 5]
+     */
+    SearchResults sr = MappingUtils.buildSearchResults(aseq1, 1, acfList);
+    assertEquals(2, sr.getResults().size());
+    Match m = sr.getResults().get(0);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(2, m.getStart());
+    assertEquals(2, m.getEnd());
+    m = sr.getResults().get(1);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(4, m.getStart());
+    assertEquals(5, m.getEnd());
+
+    /*
+     * Check protein residue 2 maps to [7, 9, 11]
+     */
+    sr = MappingUtils.buildSearchResults(aseq1, 2, acfList);
+    assertEquals(3, sr.getResults().size());
+    m = sr.getResults().get(0);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(7, m.getStart());
+    assertEquals(7, m.getEnd());
+    m = sr.getResults().get(1);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(9, m.getStart());
+    assertEquals(9, m.getEnd());
+    m = sr.getResults().get(2);
+    assertEquals(seq1.getDatasetSequence(), m.getSequence());
+    assertEquals(11, m.getStart());
+    assertEquals(11, m.getEnd());
+  
+    /*
+     * Check inverse mappings, from codons to protein
+     */
+    for (int i = 1; i < 14; i++)
+    {
+      sr = MappingUtils.buildSearchResults(seq1, i, acfList);
+      int residue = (i == 2 || i == 4 || i == 5) ? 1 : (i == 7 || i == 9
+              || i == 11 ? 2 : 0);
+      if (residue == 0)
+      {
+        assertEquals(0, sr.getResults().size());
+        continue;
+      }
+      assertEquals(1, sr.getResults().size());
+      m = sr.getResults().get(0);
+      assertEquals(aseq1.getDatasetSequence(), m.getSequence());
+      assertEquals(residue, m.getStart());
+      assertEquals(residue, m.getEnd());
+    }
+  }
+
+  /**
+   * Test mapping a sequence group.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapSequenceGroup() throws IOException
+  {
+    /*
+     * Set up dna and protein Seq1/2/3 with mappings (held on the protein
+     * viewport).
+     */
+    AlignmentI cdna = loadAlignment(">Seq1\nACG\n>Seq2\nTGA\n>Seq3\nTAC\n",
+            "FASTA");
+    cdna.setDataset(null);
+    AlignmentI protein = loadAlignment(">Seq1\nK\n>Seq2\nL\n>Seq3\nQ\n",
+            "FASTA");
+    protein.setDataset(null);
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList map = new MapList(new int[]
+    { 1, 3 }, new int[]
+    { 1, 1 }, 3, 1);
+    for (int seq = 0; seq < 3; seq++)
+    {
+      acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
+              .getSequenceAt(seq).getDatasetSequence(), map);
+    }
+    Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
+
+    AlignViewportI dnaView = new AlignViewport(cdna);
+    AlignViewportI proteinView = new AlignViewport(protein);
+    protein.setCodonFrames(acfList);
+
+    /*
+     * Select Seq1 and Seq3 in the protein
+     */
+    SequenceGroup sg = new SequenceGroup();
+    sg.setColourText(true);
+    sg.setIdColour(Color.GREEN);
+    sg.setOutlineColour(Color.LIGHT_GRAY);
+    sg.addSequence(protein.getSequenceAt(0), false);
+    sg.addSequence(protein.getSequenceAt(2), false);
+
+    /*
+     * Verify the mapped sequence group in dna
+     */
+    SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, proteinView, dnaView);
+    assertTrue(mappedGroup.getColourText());
+    assertSame(sg.getIdColour(), mappedGroup.getIdColour());
+    assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
+    assertEquals(2, mappedGroup.getSequences().size());
+    assertSame(cdna.getSequenceAt(0), mappedGroup.getSequences().get(0));
+    assertSame(cdna.getSequenceAt(2), mappedGroup.getSequences().get(1));
+
+    /*
+     * Verify mapping sequence group from dna to protein
+     */
+    sg.clear();
+    sg.addSequence(cdna.getSequenceAt(1), false);
+    sg.addSequence(cdna.getSequenceAt(0), false);
+    mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
+    assertTrue(mappedGroup.getColourText());
+    assertSame(sg.getIdColour(), mappedGroup.getIdColour());
+    assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
+    assertEquals(2, mappedGroup.getSequences().size());
+    assertSame(protein.getSequenceAt(1), mappedGroup.getSequences().get(0));
+    assertSame(protein.getSequenceAt(0), mappedGroup.getSequences().get(1));
+  }
+
+  /**
+   * Helper method to load an alignment and ensure dataset sequences are set up.
+   * 
+   * @param data
+   * @param format
+   *          TODO
+   * @return
+   * @throws IOException
+   */
+  protected AlignmentI loadAlignment(final String data, String format)
+          throws IOException
+  {
+    Alignment a = new FormatAdapter().readFile(data,
+            AppletFormatAdapter.PASTE, format);
+    a.setDataset(null);
+    return a;
+  }
+
+  /**
+   * Test mapping a column selection in protein to its dna equivalent
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapColumnSelection_proteinToDna() throws IOException
+  {
+    setupMappedAlignments();
+  
+    ColumnSelection colsel = new ColumnSelection();
+
+    /*
+     * Column 0 in protein picks up Seq2/L, Seq3/G which map to cols 0-4 and 0-3
+     * in dna respectively, overall 0-4
+     */
+    colsel.addElement(0);
+    ColumnSelection cs = MappingUtils.mapColumnSelection(colsel,
+            proteinView, dnaView);
+    assertEquals("[0, 1, 2, 3, 4]", cs.getSelected().toString());
+
+    /*
+     * Column 1 in protein picks up Seq1/K which maps to cols 0-3 in dna
+     */
+    colsel.clear();
+    colsel.addElement(1);
+    cs = MappingUtils.mapColumnSelection(colsel, proteinView, dnaView);
+    assertEquals("[0, 1, 2, 3]", cs.getSelected().toString());
+
+    /*
+     * Column 2 in protein picks up gaps only - no mapping
+     */
+    colsel.clear();
+    colsel.addElement(2);
+    cs = MappingUtils.mapColumnSelection(colsel, proteinView, dnaView);
+    assertEquals("[]", cs.getSelected().toString());
+
+    /*
+     * Column 3 in protein picks up Seq1/P, Seq2/Q, Seq3/S which map to columns
+     * 6-9, 6-10, 5-8 respectively, overall to 5-10
+     */
+    colsel.clear();
+    colsel.addElement(3);
+    cs = MappingUtils.mapColumnSelection(colsel, proteinView, dnaView);
+    assertEquals("[5, 6, 7, 8, 9, 10]", cs.getSelected().toString());
+
+    /*
+     * Combine selection of columns 1 and 3 to get a discontiguous mapped
+     * selection
+     */
+    colsel.clear();
+    colsel.addElement(1);
+    colsel.addElement(3);
+    cs = MappingUtils.mapColumnSelection(colsel, proteinView, dnaView);
+    assertEquals("[0, 1, 2, 3, 5, 6, 7, 8, 9, 10]", cs.getSelected()
+            .toString());
+  }
+
+  /**
+   * @throws IOException
+   */
+  protected void setupMappedAlignments() throws IOException
+  {
+    /*
+     * Set up dna and protein Seq1/2/3 with mappings (held on the protein
+     * viewport). Lower case for introns.
+     */
+    AlignmentI cdna = loadAlignment(">Seq1\nAC-GctGtC-T\n"
+            + ">Seq2\nTc-GA-G-T-Tc\n" + ">Seq3\nTtTT-AaCGg-\n",
+            "FASTA");
+    cdna.setDataset(null);
+    AlignmentI protein = loadAlignment(
+            ">Seq1\n-K-P\n>Seq2\nL--Q\n>Seq3\nG--S\n",
+            "FASTA");
+    protein.setDataset(null);
+    AlignedCodonFrame acf = new AlignedCodonFrame();
+    MapList map = new MapList(new int[]
+    { 1, 3, 6, 6, 8, 9 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(cdna.getSequenceAt(0).getDatasetSequence(), protein
+            .getSequenceAt(0).getDatasetSequence(), map);
+    map = new MapList(new int[]
+    { 1, 1, 3, 4, 5, 7 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(cdna.getSequenceAt(1).getDatasetSequence(), protein
+            .getSequenceAt(1).getDatasetSequence(), map);
+    map = new MapList(new int[]
+    { 1, 1, 3, 4, 5, 5, 7, 8 }, new int[]
+    { 1, 2 }, 3, 1);
+    acf.addMap(cdna.getSequenceAt(2).getDatasetSequence(), protein
+            .getSequenceAt(2).getDatasetSequence(), map);
+    Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
+  
+    dnaView = new AlignViewport(cdna);
+    proteinView = new AlignViewport(protein);
+    protein.setCodonFrames(acfList);
+  }
+
+  /**
+   * Test mapping a column selection including hidden columns
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapColumnSelection_hiddenColumns() throws IOException
+  {
+    setupMappedAlignments();
+
+    ColumnSelection colsel = new ColumnSelection();
+  
+    /*
+     * Column 0 in protein picks up Seq2/L, Seq3/G which map to cols 0-4 and 0-3
+     * in dna respectively, overall 0-4
+     */
+    colsel.addElement(0);
+    ColumnSelection cs = MappingUtils.mapColumnSelection(colsel,
+            proteinView, dnaView);
+    assertEquals("[0, 1, 2, 3, 4]", cs.getSelected().toString());
+
+    fail("write me");
+  }
+
+  /**
+   * Test mapping a column selection in dna to its protein equivalent
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testMapColumnSelection_dnaToProtein() throws IOException
+  {
+    setupMappedAlignments();
+  
+    ColumnSelection colsel = new ColumnSelection();
+  
+    /*
+     * Column 0 in dna picks up first bases which map to residue 1, columns 0-1
+     * in protein.
+     */
+    colsel.addElement(0);
+    ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, dnaView,
+            proteinView);
+    assertEquals("[0, 1]", cs.getSelected().toString());
+
+    /*
+     * Columns 3-5 in dna map to the first residues in protein Seq1, Seq2, and
+     * the first two in Seq3. Overall to columns 0, 1, 3 (col2 is all gaps).
+     */
+    colsel.addElement(3);
+    colsel.addElement(4);
+    colsel.addElement(5);
+    cs = MappingUtils.mapColumnSelection(colsel, dnaView, proteinView);
+    assertEquals("[0, 1, 3]", cs.getSelected().toString());
+  }
+}