JAL-845 fix bug in 3-to-1 scaling of protein from cDNA
[jalview.git] / src / jalview / gui / SplitFrame.java
index ab4596e..6042efc 100644 (file)
@@ -1,13 +1,14 @@
 package jalview.gui;
 
+import jalview.api.SplitContainerI;
+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.MouseInfo;
-import java.awt.Point;
-import java.awt.Rectangle;
 import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -25,7 +26,19 @@ import javax.swing.KeyStroke;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 
-public class SplitFrame extends GSplitFrame
+/**
+ * An internal frame on the desktop that hosts a horizontally split view of
+ * linked DNA and Protein alignments. Additional views can be created in linked
+ * pairs, expanded to separate split frames, or regathered into a single frame.
+ * <p>
+ * (Some) operations on each alignment are automatically mirrored on the other.
+ * These include mouseover (highlighting), sequence and column selection,
+ * sequence ordering and sorting, and grouping, colouring and sorting by tree.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class SplitFrame extends GSplitFrame implements SplitContainerI
 {
   private static final long serialVersionUID = 1L;
 
@@ -40,13 +53,83 @@ public class SplitFrame extends GSplitFrame
    */
   protected void init()
   {
+    getTopFrame().setSplitFrame(this);
+    getBottomFrame().setSplitFrame(this);
+    getTopFrame().setVisible(true);
+    getBottomFrame().setVisible(true);
+
+    ((AlignFrame) getTopFrame()).getViewport().setCodingComplement(
+            ((AlignFrame) getBottomFrame()).getViewport());
+
     setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 20);
 
+    adjustLayout();
+
     addCloseFrameListener();
     
     addKeyListener();
 
     addKeyBindings();
+
+    addCommandListeners();
+  }
+
+  /**
+   * Set the top and bottom frames to listen to each others Commands (e.g. Edit,
+   * Order).
+   */
+  protected void addCommandListeners()
+  {
+    // TODO if CommandListener is only ever 1:1 for complementary views,
+    // may change broadcast pattern to direct messaging (more efficient)
+    final StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
+    ssm.addCommandListener(((AlignFrame) getTopFrame()).getViewport());
+    ssm.addCommandListener(((AlignFrame) getBottomFrame()).getViewport());
+  }
+
+  /**
+   * Do any tweaking and twerking of the layout wanted.
+   */
+  public void adjustLayout()
+  {
+    /*
+     * Ensure sequence ids are the same width for good alignment.
+     */
+    int w1 = ((AlignFrame) getTopFrame()).getViewport().getIdWidth();
+    int w2 = ((AlignFrame) getBottomFrame()).getViewport().getIdWidth();
+    int w3 = Math.max(w1, w2);
+    if (w1 != w3)
+    {
+      ((AlignFrame) getTopFrame()).getViewport().setIdWidth(w3);
+    }
+    if (w2 != w3)
+    {
+      ((AlignFrame) getBottomFrame()).getViewport().setIdWidth(w3);
+    }
+
+    /*
+     * Set the character width for protein to 3 times that for dna.
+     */
+    boolean scaleThreeToOne = true; // TODO a new Preference option?
+    if (scaleThreeToOne)
+    {
+      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();
+        ViewStyleI vs2 = protein.getViewStyle();
+        vs2.setCharWidth(3 * vs.getCharWidth());
+        protein.setViewStyle(vs2);
+      }
+    }
   }
 
   /**
@@ -165,33 +248,6 @@ public class SplitFrame extends GSplitFrame
   }
 
   /**
-   * Returns the split pane component the mouse is in, or null if neither.
-   * 
-   * @return
-   */
-  protected GAlignFrame getFrameAtMouse()
-  {
-    Point loc = MouseInfo.getPointerInfo().getLocation();
-    
-    if (isIn(loc, getTopFrame()))
-    {
-      return getTopFrame();
-    }
-    else if (isIn(loc, getBottomFrame()))
-    {
-      return getBottomFrame();
-    }
-    return null;
-  }
-
-  private boolean isIn(Point loc, JComponent comp)
-  {
-    Point p = comp.getLocationOnScreen();
-    Rectangle r = new Rectangle(p.x, p.y, comp.getWidth(), comp.getHeight());
-    return r.contains(loc);
-  }
-
-  /**
    * Set key bindings (recommended for Swing over key accelerators).
    */
   private void addKeyBindings()
@@ -324,10 +380,13 @@ public class SplitFrame extends GSplitFrame
    * Note this is _not_ multiple tabs, each hosting a split pane view, rather it
    * is a single split pane with each split holding multiple tabs which are
    * linked in pairs.
+   * <p>
+   * TODO implement instead with a tabbed holder in the SplitView, each tab
+   * holding a single JSplitPane. Would avoid a duplicated tab, at the cost of
+   * some additional coding.
    */
   protected void newView_actionPerformed()
   {
-    System.out.println("newView " + this.hashCode());
     AlignFrame topFrame = (AlignFrame) getTopFrame();
     AlignFrame bottomFrame = (AlignFrame) getBottomFrame();
 
@@ -487,4 +546,59 @@ public class SplitFrame extends GSplitFrame
   {
     Desktop.instance.gatherViews(this);
   }
+
+  /**
+   * Returns the alignment in the complementary frame to the one given.
+   */
+  @Override
+  public AlignmentI getComplement(Object alignFrame)
+  {
+    if (alignFrame == this.getTopFrame())
+    {
+      return ((AlignFrame) getBottomFrame()).viewport.getAlignment();
+    }
+    else if (alignFrame == this.getBottomFrame())
+    {
+      return ((AlignFrame) getTopFrame()).viewport.getAlignment();
+    }
+    return null;
+  }
+
+  /**
+   * Returns the title of the complementary frame to the one given.
+   */
+  @Override
+  public String getComplementTitle(Object alignFrame)
+  {
+    if (alignFrame == this.getTopFrame())
+    {
+      return ((AlignFrame) getBottomFrame()).getTitle();
+    }
+    else if (alignFrame == this.getBottomFrame())
+    {
+      return ((AlignFrame) getTopFrame()).getTitle();
+    }
+    return null;
+  }
+
+  /**
+   * Set the 'other half' to hidden / revealed.
+   */
+  @Override
+  public void setComplementVisible(Object alignFrame, boolean show)
+  {
+    /*
+     * Hiding the AlignPanel suppresses unnecessary repaints
+     */
+    if (alignFrame == getTopFrame())
+    {
+      ((AlignFrame) getBottomFrame()).alignPanel.setVisible(show);
+    }
+    else if (alignFrame == getBottomFrame())
+    {
+      ((AlignFrame) getTopFrame()).alignPanel.setVisible(show);
+    }
+    super.setComplementVisible(alignFrame, show);
+  }
 }
+