JAL-1691 applet SplitFrame scrolling; pull up of 5 fields to
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 8 Apr 2015 14:21:23 +0000 (15:21 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 8 Apr 2015 14:21:23 +0000 (15:21 +0100)
AlignmentViewport

20 files changed:
src/jalview/api/AlignViewportI.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/appletgui/FeatureRenderer.java
src/jalview/appletgui/Finder.java
src/jalview/appletgui/IdPanel.java
src/jalview/appletgui/RotatableCanvas.java
src/jalview/appletgui/SeqCanvas.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/SplitFrame.java
src/jalview/appletgui/TreeCanvas.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/IdPanel.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/SeqPanel.java
src/jalview/util/MappingUtils.java
src/jalview/viewmodel/AlignmentViewport.java

index c49ee39..1a1cba2 100644 (file)
  */
 package jalview.api;
 
+import java.awt.Color;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
 import jalview.analysis.Conservation;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
@@ -31,11 +36,6 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
 
-import java.awt.Color;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
 /**
  * @author jimp
  * 
@@ -413,4 +413,17 @@ public interface AlignViewportI extends ViewStyleI
    * @return
    */
   String getViewId();
+
+  /**
+   * Return true if view should scroll to show the highlighted region of a
+   * sequence
+   * 
+   * @return
+   */
+  boolean isFollowHighlight();
+
+  /**
+   * Set whether view should scroll to show the highlighted region of a sequence
+   */
+  void setFollowHighlight(boolean b);
 }
index 5c8135d..ad1626c 100644 (file)
@@ -103,6 +103,7 @@ import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 public class AlignFrame extends EmbmenuFrame implements ActionListener,
         ItemListener, KeyListener, AlignViewControllerGuiI
@@ -197,7 +198,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.updateConsensus(alignPanel);
 
     displayNonconservedMenuItem.setState(viewport.getShowUnconserved());
-    followMouseOverFlag.setState(viewport.getFollowHighlight());
+    followMouseOverFlag.setState(viewport.isFollowHighlight());
     showGroupConsensus.setState(viewport.isShowGroupConsensus());
     showGroupConservation.setState(viewport.isShowGroupConservation());
     showConsensusHistogram.setState(viewport.isShowConsensusHistogram());
@@ -926,7 +927,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   private void mouseOverFlag_stateChanged()
   {
-    viewport.followHighlight = followMouseOverFlag.getState();
+    viewport.setFollowHighlight(followMouseOverFlag.getState());
     // TODO: could kick the scrollTo mechanism to reset view for current
     // searchresults.
   }
@@ -1594,7 +1595,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.addToRedoList(command);
     command.undoCommand(null);
 
-    AlignViewport originalSource = getOriginatingSource(command);
+    AlignmentViewport originalSource = getOriginatingSource(command);
     // JBPNote Test
     if (originalSource != viewport)
     {
@@ -1626,7 +1627,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.addToHistoryList(command);
     command.doCommand(null);
 
-    AlignViewport originalSource = getOriginatingSource(command);
+    AlignmentViewport originalSource = getOriginatingSource(command);
     // JBPNote Test
     if (originalSource != viewport)
     {
@@ -1642,9 +1643,9 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
             .getAlignment().getSequences());
   }
 
-  AlignViewport getOriginatingSource(CommandI command)
+  AlignmentViewport getOriginatingSource(CommandI command)
   {
-    AlignViewport originalSource = null;
+    AlignmentViewport originalSource = null;
     // For sequence removal and addition, we need to fire
     // the property change event FROM the viewport where the
     // original alignment was altered
index b0e29da..fdc6a0c 100644 (file)
@@ -28,6 +28,7 @@ import jalview.bin.JalviewLite;
 import jalview.commands.CommandI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResults;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -42,14 +43,6 @@ import jalview.viewmodel.AlignmentViewport;
 public class AlignViewport extends AlignmentViewport implements
         AlignViewportI, SelectionSource, VamsasSource, CommandListener
 {
-  int startRes;
-
-  int endRes;
-
-  int startSeq;
-
-  int endSeq;
-
   boolean cursorMode = false;
 
   Font font = new Font("SansSerif", Font.PLAIN, 10);
@@ -103,7 +96,7 @@ public class AlignViewport extends AlignmentViewport implements
                           + widthScale + "). Ignoring.");
           widthScale = 1;
         }
-        if (applet.debug)
+        if (JalviewLite.debug)
         {
           System.err
                   .println("Alignment character width scaling factor is now "
@@ -126,7 +119,7 @@ public class AlignViewport extends AlignmentViewport implements
                           + heightScale + "). Ignoring.");
           heightScale = 1;
         }
-        if (applet.debug)
+        if (JalviewLite.debug)
         {
           System.err
                   .println("Alignment character height scaling factor is now "
@@ -170,9 +163,9 @@ public class AlignViewport extends AlignmentViewport implements
       }
       sortByTree = applet.getDefaultParameter("sortByTree", sortByTree);
 
-      followHighlight = applet.getDefaultParameter("automaticScrolling",
-              followHighlight);
-      followSelection = followHighlight;
+      setFollowHighlight(applet.getDefaultParameter("automaticScrolling",
+              isFollowHighlight()));
+      followSelection = isFollowHighlight();
 
       showSequenceLogo = applet.getDefaultParameter("showSequenceLogo",
               showSequenceLogo);
@@ -262,64 +255,6 @@ public class AlignViewport extends AlignmentViewport implements
     return sq;
   }
 
-  public int getStartRes()
-  {
-    return startRes;
-  }
-
-  public int getEndRes()
-  {
-    return endRes;
-  }
-
-  public int getStartSeq()
-  {
-    return startSeq;
-  }
-
-  public void setStartRes(int res)
-  {
-    this.startRes = res;
-  }
-
-  public void setStartSeq(int seq)
-  {
-    this.startSeq = seq;
-  }
-
-  public void setEndRes(int res)
-  {
-    if (res > alignment.getWidth() - 1)
-    {
-      // log.System.out.println(" Corrected res from " + res + " to maximum " +
-      // (alignment.getWidth()-1));
-      res = alignment.getWidth() - 1;
-    }
-    if (res < 0)
-    {
-      res = 0;
-    }
-    this.endRes = res;
-  }
-
-  public void setEndSeq(int seq)
-  {
-    if (seq > alignment.getHeight())
-    {
-      seq = alignment.getHeight();
-    }
-    if (seq < 0)
-    {
-      seq = 0;
-    }
-    this.endSeq = seq;
-  }
-
-  public int getEndSeq()
-  {
-    return endSeq;
-  }
-
   java.awt.Frame nullFrame;
 
   protected FeatureSettings featureSettings = null;
@@ -376,13 +311,6 @@ public class AlignViewport extends AlignmentViewport implements
     return centreColumnLabels;
   }
 
-  public boolean followHighlight = true;
-
-  public boolean getFollowHighlight()
-  {
-    return followHighlight;
-  }
-
   public boolean followSelection = true;
 
   /**
@@ -514,4 +442,29 @@ public class AlignViewport extends AlignmentViewport implements
     return this;
   }
 
+  /**
+   * If this viewport has a (Protein/cDNA) complement, then scroll the
+   * complementary alignment to match this one.
+   */
+  public void scrollComplementaryAlignment(AlignmentPanel complementPanel)
+  {
+    if (complementPanel == null)
+    {
+      return;
+    }
+
+    /*
+     * Populate a SearchResults object with the mapped location to scroll to. If
+     * there is no complement, or it is not following highlights, or no mapping
+     * is found, the result will be empty.
+     */
+    SearchResults sr = new SearchResults();
+    int seqOffset = findComplementScrollTarget(sr);
+    if (!sr.isEmpty())
+    {
+      complementPanel.setFollowingComplementScroll(true);
+      complementPanel.scrollToCentre(sr, seqOffset);
+    }
+  }
+
 }
index c74914f..5a2c5ef 100644 (file)
@@ -32,10 +32,12 @@ import java.awt.event.AdjustmentEvent;
 import java.awt.event.AdjustmentListener;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
+import java.util.List;
 
 import jalview.analysis.AnnotationSorter;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.bin.JalviewLite;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.SequenceI;
@@ -313,7 +315,21 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
   public boolean scrollToPosition(SearchResults results,
           boolean redrawOverview)
   {
+    return scrollToPosition(results, redrawOverview, false);
+  }
 
+  /**
+   * scroll the view to show the position of the highlighted region in results
+   * (if any)
+   * 
+   * @param results
+   * @param redrawOverview
+   *          - when set, the overview will be recalculated (takes longer)
+   * @return false if results were not found
+   */
+  public boolean scrollToPosition(SearchResults results,
+          boolean redrawOverview, boolean centre)
+  {
     // do we need to scroll the panel?
     if (results != null && results.getSize() > 0)
     {
@@ -327,7 +343,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       int[] r = results.getResults(seq, 0, alignment.getWidth());
       if (r == null)
       {
-        if (av.applet.debug)
+        if (JalviewLite.debug)
         {// DEBUG
           System.out
                   .println("DEBUG: scroll didn't happen - results not within alignment : "
@@ -335,7 +351,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
         }
         return false;
       }
-      if (av.applet.debug)
+      if (JalviewLite.debug)
       {
         // DEBUG
         /*
@@ -347,6 +363,18 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       }
       int start = r[0];
       int end = r[1];
+
+      /*
+       * To centre results, scroll to positions half the visible width
+       * left/right of the start/end positions
+       */
+      if (centre)
+      {
+        int offset = (av.getEndRes() - av.getStartRes() + 1) / 2 - 1;
+        start = Math.max(start - offset, 0);
+        end = Math.min(end + offset, seq.getEnd() - 1);
+      }
+
       if (start < 0)
       {
         return false;
@@ -394,33 +422,64 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
        * av.getStartSeq()) > seqIndex) { setScrollValues(av.getStartRes(),
        * seqIndex); } else if ((ends = av.getEndSeq()) <= seqIndex) {
        * setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1); }
-       * 
-       * /*
        */
-      if ((av.getStartRes() > end)
-              || (av.getEndRes() < start)
-              || ((av.getStartSeq() > seqIndex) || (av.getEndSeq() < seqIndex)))
-      {
-        if (start > av.getAlignment().getWidth() - hextent)
-        {
-          start = av.getAlignment().getWidth() - hextent;
-          if (start < 0)
-          {
-            start = 0;
-          }
 
+      // below is scrolling logic up to Jalview 2.8.2
+      // if ((av.getStartRes() > end)
+      // || (av.getEndRes() < start)
+      // || ((av.getStartSeq() > seqIndex) || (av.getEndSeq() < seqIndex)))
+      // {
+      // if (start > av.getAlignment().getWidth() - hextent)
+      // {
+      // start = av.getAlignment().getWidth() - hextent;
+      // if (start < 0)
+      // {
+      // start = 0;
+      // }
+      //
+      // }
+      // if (seqIndex > av.getAlignment().getHeight() - vextent)
+      // {
+      // seqIndex = av.getAlignment().getHeight() - vextent;
+      // if (seqIndex < 0)
+      // {
+      // seqIndex = 0;
+      // }
+      // }
+      // setScrollValues(start, seqIndex);
+      // }
+      // logic copied from jalview.gui.AlignmentPanel:
+        if ((startv = av.getStartRes()) >= start)
+        {
+          /*
+           * Scroll left to make start of search results visible
+           */
+          setScrollValues(start - 1, seqIndex);
+        }
+        else if ((endv = av.getEndRes()) <= end)
+        {
+          /*
+           * Scroll right to make end of search results visible
+           */
+          setScrollValues(startv + 1 + end - endv, seqIndex);
+        }
+        else if ((starts = av.getStartSeq()) > seqIndex)
+        {
+          /*
+           * Scroll up to make start of search results visible
+           */
+          setScrollValues(av.getStartRes(), seqIndex);
         }
-        if (seqIndex > av.getAlignment().getHeight() - vextent)
+        else if ((ends = av.getEndSeq()) <= seqIndex)
         {
-          seqIndex = av.getAlignment().getHeight() - vextent;
-          if (seqIndex < 0)
-          {
-            seqIndex = 0;
-          }
+          /*
+           * Scroll down to make end of search results visible
+           */
+          setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1);
         }
-        // System.out.println("trying to scroll to: "+start+" "+seqIndex);
-        setScrollValues(start, seqIndex);
-      }/**/
+        /*
+         * Else results are already visible - no need to scroll
+         */
     }
     else
     {
@@ -793,6 +852,95 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     }
     sendViewPosition();
 
+    /*
+     * If there is one, scroll the (Protein/cDNA) complementary alignment to
+     * match, unless we are ourselves doing that.
+     */
+    if (isFollowingComplementScroll())
+    {
+      setFollowingComplementScroll(false);
+    }
+    else
+    {
+      AlignmentPanel ap = getComplementPanel();
+      av.scrollComplementaryAlignment(ap);
+    }
+
+  }
+
+  /**
+   * A helper method to return the AlignmentPanel in the other (complementary)
+   * half of a SplitFrame view. Returns null if not in a SplitFrame.
+   * 
+   * @return
+   */
+  private AlignmentPanel getComplementPanel()
+  {
+    AlignmentPanel ap = null;
+    if (alignFrame != null)
+    {
+      SplitFrame sf = alignFrame.getSplitFrame();
+      if (sf != null)
+      {
+        AlignFrame other = sf.getComplement(alignFrame);
+        if (other != null)
+        {
+          ap = other.alignPanel;
+        }
+      }
+    }
+    return ap;
+  }
+
+  /**
+   * Follow a scrolling change in the (cDNA/Protein) complementary alignment.
+   * The aim is to keep the two alignments 'lined up' on their centre columns.
+   * 
+   * @param sr
+   *          holds mapped region(s) of this alignment that we are scrolling
+   *          'to'; may be modified for sequence offset by this method
+   * @param seqOffset
+   *          the number of visible sequences to show above the mapped region
+   */
+  protected void scrollToCentre(SearchResults sr, int seqOffset)
+  {
+    /*
+     * To avoid jumpy vertical scrolling (if some sequences are gapped or not
+     * mapped), we can make the scroll-to location a sequence above the one
+     * actually mapped.
+     */
+    SequenceI mappedTo = sr.getResultSequence(0);
+    List<SequenceI> seqs = av.getAlignment().getSequences();
+
+    /*
+     * This is like AlignmentI.findIndex(seq) but here we are matching the
+     * dataset sequence not the aligned sequence
+     */
+    int sequenceIndex = 0;
+    boolean matched = false;
+    for (SequenceI seq : seqs)
+    {
+      if (mappedTo == seq.getDatasetSequence())
+      {
+        matched = true;
+        break;
+      }
+      sequenceIndex++;
+    }
+    if (!matched)
+    {
+      return; // failsafe, shouldn't happen
+    }
+    sequenceIndex = Math.max(0, sequenceIndex - seqOffset);
+    sr.getResults().get(0)
+            .setSequence(av.getAlignment().getSequenceAt(sequenceIndex));
+
+    /*
+     * Scroll to position but centring the target residue. Also set a state flag
+     * to prevent adjustmentValueChanged performing this recursively.
+     */
+    setFollowingComplementScroll(true);
+    scrollToPosition(sr, true, true);
   }
 
   private void sendViewPosition()
@@ -884,72 +1032,58 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
 
   protected Panel seqPanelHolder = new Panel();
 
-  BorderLayout borderLayout1 = new BorderLayout();
-
-  BorderLayout borderLayout3 = new BorderLayout();
-
   protected Panel scalePanelHolder = new Panel();
 
   protected Panel idPanelHolder = new Panel();
 
-  BorderLayout borderLayout5 = new BorderLayout();
-
   protected Panel idSpaceFillerPanel1 = new Panel();
 
   public Panel annotationSpaceFillerHolder = new Panel();
 
-  BorderLayout borderLayout6 = new BorderLayout();
-
-  BorderLayout borderLayout7 = new BorderLayout();
-
-  Panel hscrollHolder = new Panel();
-
-  BorderLayout borderLayout10 = new BorderLayout();
-
   protected Panel hscrollFillerPanel = new Panel();
 
-  BorderLayout borderLayout11 = new BorderLayout();
-
-  BorderLayout borderLayout4 = new BorderLayout();
-
-  BorderLayout borderLayout2 = new BorderLayout();
-
   Panel annotationPanelHolder = new Panel();
 
   protected Scrollbar apvscroll = new Scrollbar();
 
-  BorderLayout borderLayout12 = new BorderLayout();
+  /*
+   * Flag set while scrolling to follow complementary cDNA/protein scroll. When
+   * true, suppresses invoking the same method recursively.
+   */
+  private boolean followingComplementScroll;
 
   private void jbInit() throws Exception
   {
     // idPanelHolder.setPreferredSize(new Dimension(70, 10));
-    this.setLayout(borderLayout7);
+    this.setLayout(new BorderLayout());
 
     // sequenceHolderPanel.setPreferredSize(new Dimension(150, 150));
-    sequenceHolderPanel.setLayout(borderLayout3);
-    seqPanelHolder.setLayout(borderLayout1);
+    sequenceHolderPanel.setLayout(new BorderLayout());
+    seqPanelHolder.setLayout(new BorderLayout());
     scalePanelHolder.setBackground(Color.white);
 
     // scalePanelHolder.setPreferredSize(new Dimension(10, 30));
-    scalePanelHolder.setLayout(borderLayout6);
-    idPanelHolder.setLayout(borderLayout5);
+    scalePanelHolder.setLayout(new BorderLayout());
+    idPanelHolder.setLayout(new BorderLayout());
     idSpaceFillerPanel1.setBackground(Color.white);
 
     // idSpaceFillerPanel1.setPreferredSize(new Dimension(10, 30));
-    idSpaceFillerPanel1.setLayout(borderLayout11);
+    idSpaceFillerPanel1.setLayout(new BorderLayout());
     annotationSpaceFillerHolder.setBackground(Color.white);
 
     // annotationSpaceFillerHolder.setPreferredSize(new Dimension(10, 80));
-    annotationSpaceFillerHolder.setLayout(borderLayout4);
+    annotationSpaceFillerHolder.setLayout(new BorderLayout());
     hscroll.setOrientation(Scrollbar.HORIZONTAL);
-    hscrollHolder.setLayout(borderLayout10);
+
+    Panel hscrollHolder = new Panel();
+    hscrollHolder.setLayout(new BorderLayout());
     hscrollFillerPanel.setBackground(Color.white);
     apvscroll.setOrientation(Scrollbar.VERTICAL);
     apvscroll.setVisible(true);
     apvscroll.addAdjustmentListener(this);
 
     annotationPanelHolder.setBackground(Color.white);
-    annotationPanelHolder.setLayout(borderLayout12);
+    annotationPanelHolder.setLayout(new BorderLayout());
     annotationPanelHolder.add(apvscroll, BorderLayout.EAST);
     // hscrollFillerPanel.setPreferredSize(new Dimension(70, 10));
     hscrollHolder.setBackground(Color.white);
@@ -1020,4 +1154,19 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     error.printStackTrace();
   }
 
+  /**
+   * Set a flag to say we are scrolling to follow a (cDNA/protein) complement.
+   * 
+   * @param b
+   */
+  protected void setFollowingComplementScroll(boolean b)
+  {
+    this.followingComplementScroll = b;
+  }
+
+  protected boolean isFollowingComplementScroll()
+  {
+    return this.followingComplementScroll;
+  }
+
 }
index 6963ca7..9751514 100644 (file)
@@ -28,6 +28,7 @@ import jalview.datamodel.*;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.GraduatedColor;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
 
 /**
@@ -49,7 +50,7 @@ public class FeatureRenderer extends jalview.renderer.seqfeatures.FeatureRendere
    * @param av
    *          DOCUMENT ME!
    */
-  public FeatureRenderer(AlignViewport av)
+  public FeatureRenderer(AlignmentViewport av)
   {
     super();
     this.av = av;
index 3f8a54d..5412472 100644 (file)
@@ -24,6 +24,7 @@ import jalview.datamodel.SearchResults;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Button;
 import java.awt.Checkbox;
@@ -43,7 +44,7 @@ import java.util.Vector;
 
 public class Finder extends Panel implements ActionListener
 {
-  AlignViewport av;
+  AlignmentViewport av;
 
   AlignmentPanel ap;
 
index 6cf245f..af0b2fa 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.util.UrlLink;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.BorderLayout;
 import java.awt.Panel;
@@ -41,7 +42,7 @@ public class IdPanel extends Panel implements MouseListener,
 
   protected IdCanvas idCanvas;
 
-  protected AlignViewport av;
+  protected AlignmentViewport av;
 
   protected AlignmentPanel alignPanel;
 
index 760e801..377186a 100755 (executable)
@@ -21,7 +21,6 @@
 package jalview.appletgui;
 
 import java.util.*;
-
 import java.awt.*;
 import java.awt.event.*;
 
@@ -29,6 +28,7 @@ import jalview.api.RotatableCanvasI;
 import jalview.datamodel.*;
 import jalview.math.*;
 import jalview.util.*;
+import jalview.viewmodel.AlignmentViewport;
 
 public class RotatableCanvas extends Panel implements MouseListener,
         MouseMotionListener, KeyListener, RotatableCanvasI
@@ -99,11 +99,11 @@ public class RotatableCanvas extends Panel implements MouseListener,
 
   float scalefactor = 1;
 
-  AlignViewport av;
+  AlignmentViewport av;
 
   boolean showLabels = false;
 
-  public RotatableCanvas(AlignViewport av)
+  public RotatableCanvas(AlignmentViewport av)
   {
     this.av = av;
   }
index bf54c66..522151c 100755 (executable)
@@ -24,6 +24,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Color;
 import java.awt.FontMetrics;
@@ -72,7 +73,7 @@ public class SeqCanvas extends Panel
     avcharWidth = av.getCharWidth();
   }
 
-  public AlignViewport getViewport()
+  public AlignmentViewport getViewport()
   {
     return av;
   }
index ae4ae10..86dedfc 100644 (file)
@@ -50,6 +50,7 @@ import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 public class SeqPanel extends Panel implements MouseMotionListener,
         MouseListener, SequenceListener, SelectionListener
@@ -742,7 +743,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   public void highlightSequence(SearchResults results)
   {
-    if (av.followHighlight)
+    if (av.isFollowHighlight())
     {
       if (ap.scrollToPosition(results, true))
       {
@@ -1797,7 +1798,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     // TODO: extend config options to allow user to control if selections may be
     // shared between viewports.
     if (av != null
-            && (av == source || !av.followSelection || (source instanceof AlignViewport && ((AlignViewport) source)
+            && (av == source || !av.followSelection || (source instanceof AlignViewport && ((AlignmentViewport) source)
                     .getSequenceSetId().equals(av.getSequenceSetId()))))
     {
       return;
index 4fbf65a..836d70c 100644 (file)
@@ -11,6 +11,7 @@ import jalview.api.ViewStyleI;
 import jalview.bin.JalviewLite;
 import jalview.datamodel.AlignmentI;
 import jalview.structure.StructureSelectionManager;
+import jalview.viewmodel.AlignmentViewport;
 
 public class SplitFrame extends EmbmenuFrame
 {
@@ -49,9 +50,9 @@ public class SplitFrame extends EmbmenuFrame
     final AlignViewport bottomViewport = bottomFrame.viewport;
     final AlignmentI topAlignment = topViewport.getAlignment();
     final AlignmentI bottomAlignment = bottomViewport.getAlignment();
-    AlignViewport cdna = topAlignment.isNucleotide() ? topViewport
+    AlignmentViewport cdna = topAlignment.isNucleotide() ? topViewport
             : (bottomAlignment.isNucleotide() ? bottomViewport : null);
-    AlignViewport protein = !topAlignment.isNucleotide() ? topViewport
+    AlignmentViewport protein = !topAlignment.isNucleotide() ? topViewport
             : (!bottomAlignment.isNucleotide() ? bottomViewport : null);
 
     boolean mapped = AlignmentUtils.mapProteinToCdna(
@@ -99,9 +100,9 @@ public class SplitFrame extends EmbmenuFrame
    */
   protected void adjustLayout()
   {
-    AlignViewport cdna = topFrame.getAlignViewport().getAlignment()
+    AlignmentViewport cdna = topFrame.getAlignViewport().getAlignment()
             .isNucleotide() ? topFrame.viewport : bottomFrame.viewport;
-    AlignViewport protein = cdna == topFrame.viewport ? bottomFrame.viewport
+    AlignmentViewport protein = cdna == topFrame.viewport ? bottomFrame.viewport
             : topFrame.viewport;
 
     /*
@@ -190,11 +191,30 @@ public class SplitFrame extends EmbmenuFrame
     else
     {
       this.add(outermost);
-      int width = Math.max(topFrame.frameWidth,
-              bottomFrame.frameWidth);
+      int width = Math.max(topFrame.frameWidth, bottomFrame.frameWidth);
       int height = topFrame.frameHeight + bottomFrame.frameHeight;
       jalview.bin.JalviewLite
               .addFrame(this, this.getTitle(), width, height);
     }
   }
+
+  /**
+   * Returns the contained AlignFrame complementary to the one given (or null if
+   * no match to top or bottom component).
+   * 
+   * @param af
+   * @return
+   */
+  public AlignFrame getComplement(AlignFrame af)
+  {
+    if (topFrame == af)
+    {
+      return bottomFrame;
+    }
+    else if (bottomFrame == af)
+    {
+      return topFrame;
+    }
+    return null;
+  }
 }
index 7ba9977..edcd961 100755 (executable)
@@ -33,6 +33,7 @@ import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
 import jalview.util.Format;
 import jalview.util.MappingUtils;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Color;
 import java.awt.Dimension;
@@ -715,7 +716,7 @@ public class TreeCanvas extends Panel implements MouseListener,
     ap.updateAnnotation();
     if (av.getCodingComplement() != null)
     {
-      ((AlignViewport) av.getCodingComplement()).firePropertyChange(
+      ((AlignmentViewport) av.getCodingComplement()).firePropertyChange(
               "alignment", null, ap.av.getAlignment().getSequences());
     }
   }
index c7792a5..bb8eec3 100644 (file)
@@ -2849,7 +2849,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
      * Set the 'follow' flag on the Viewport (and scroll to position if now
      * true).
      */
-    if (viewport.followHighlight = this.followHighlightMenuItem.getState())
+    final boolean state = this.followHighlightMenuItem.getState();
+    viewport.setFollowHighlight(state);
+    if (state)
     {
       alignPanel.scrollToPosition(
               alignPanel.getSeqPanel().seqCanvas.searchResults, false);
index d84d3a3..5e477c7 100644 (file)
@@ -44,7 +44,6 @@ import java.awt.Font;
 import java.awt.Rectangle;
 import java.util.ArrayList;
 import java.util.Hashtable;
-import java.util.List;
 import java.util.Set;
 import java.util.Vector;
 
@@ -62,7 +61,6 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
-import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.Sequence;
@@ -74,8 +72,6 @@ import jalview.structure.CommandListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
-import jalview.util.Comparison;
-import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.AutoCalcSetting;
@@ -89,14 +85,6 @@ import jalview.ws.params.AutoCalcSetting;
 public class AlignViewport extends AlignmentViewport implements
         SelectionSource, AlignViewportI, CommandListener
 {
-  int startRes;
-
-  int endRes;
-
-  int startSeq;
-
-  int endSeq;
-
   Font font;
 
   NJTree currentTree = null;
@@ -371,112 +359,6 @@ public class AlignViewport extends AlignmentViewport implements
     return sq;
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getStartRes()
-  {
-    return startRes;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getEndRes()
-  {
-    return endRes;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getStartSeq()
-  {
-    return startSeq;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param res
-   *          DOCUMENT ME!
-   */
-  public void setStartRes(int res)
-  {
-    this.startRes = res;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param seq
-   *          DOCUMENT ME!
-   */
-  public void setStartSeq(int seq)
-  {
-    this.startSeq = seq;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param res
-   *          DOCUMENT ME!
-   */
-  public void setEndRes(int res)
-  {
-    if (res > (alignment.getWidth() - 1))
-    {
-      // log.System.out.println(" Corrected res from " + res + " to maximum " +
-      // (alignment.getWidth()-1));
-      res = alignment.getWidth() - 1;
-    }
-
-    if (res < 0)
-    {
-      res = 0;
-    }
-
-    this.endRes = res;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param seq
-   *          DOCUMENT ME!
-   */
-  public void setEndSeq(int seq)
-  {
-    if (seq > alignment.getHeight())
-    {
-      seq = alignment.getHeight();
-    }
-
-    if (seq < 0)
-    {
-      seq = 0;
-    }
-
-    this.endSeq = seq;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getEndSeq()
-  {
-    return endSeq;
-  }
-
   boolean validCharWidth;
 
   /**
@@ -674,21 +556,6 @@ public class AlignViewport extends AlignmentViewport implements
     return false;
   }
 
-  /**
-   * when set, view will scroll to show the highlighted position
-   */
-  public boolean followHighlight = true;
-
-  /**
-   * @return true if view should scroll to show the highlighted region of a
-   *         sequence
-   * @return
-   */
-  public boolean getFollowHighlight()
-  {
-    return followHighlight;
-  }
-
   public boolean followSelection = true;
 
   /**
@@ -1139,70 +1006,22 @@ public class AlignViewport extends AlignmentViewport implements
   /**
    * If this viewport has a (Protein/cDNA) complement, then scroll the
    * complementary alignment to match this one.
-   * 
-   * @param horizontal
-   *          true for horizontal scroll event, false for vertical
    */
-  public void scrollComplementaryAlignment(boolean horizontal)
+  public void scrollComplementaryAlignment()
   {
     /*
-     * If no complement, or it is not following scrolling, do nothing.
+     * Populate a SearchResults object with the mapped location to scroll to. If
+     * there is no complement, or it is not following highlights, or no mapping
+     * is found, the result will be empty.
      */
-    // TODO pull up followHighlight to AlignmentViewport/AlignViewportI
-    final AlignViewport codingComplement = (AlignViewport) getCodingComplement();
-    if (codingComplement == null || !codingComplement.followHighlight)
-    {
-      return;
-    }
-    boolean iAmProtein = !getAlignment().isNucleotide();
-    AlignmentI proteinAlignment = iAmProtein ? getAlignment()
-            : codingComplement.getAlignment();
-    if (proteinAlignment == null)
+    SearchResults sr = new SearchResults();
+    int seqOffset = findComplementScrollTarget(sr);
+    if (!sr.isEmpty())
     {
-      return;
-    }
-    final Set<AlignedCodonFrame> mappings = proteinAlignment
-            .getCodonFrames();
-
-    /*
-     * Heuristic: find the first mapped sequence (if any) with a non-gapped
-     * residue in the middle column of the visible region. Scroll the
-     * complementary alignment to line up the corresponding residue.
-     */
-    int seqOffset = 0;
-    SequenceI sequence = null;
-    int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
-    final HiddenSequences hiddenSequences = getAlignment()
-            .getHiddenSequences();
-    for (int seqNo = getStartSeq(); seqNo < getEndSeq(); seqNo++, seqOffset++)
-    {
-      sequence = getAlignment().getSequenceAt(seqNo);
-      if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
-      {
-        continue;
-      }
-      if (Comparison.isGap(sequence.getCharAt(middleColumn)))
-      {
-        continue;
-      }
-      List<AlignedCodonFrame> seqMappings = MappingUtils
-              .findMappingsForSequence(sequence, mappings);
-      if (!seqMappings.isEmpty())
-      {
-        break;
-      }
-    }
-
-    if (sequence == null)
-    {
-      /*
-       * No ungapped mapped sequence in middle column - do nothing
-       */
-      return;
+      // TODO would like next line without cast but needs more refactoring...
+      final AlignmentPanel complementPanel = ((AlignViewport) getCodingComplement()).getAlignPanel();
+      complementPanel.setFollowingComplementScroll(true);
+      complementPanel.scrollToCentre(sr, seqOffset);
     }
-    SearchResults sr = MappingUtils.buildSearchResults(sequence,
-            sequence.findPosition(middleColumn), mappings);
-    codingComplement.getAlignPanel().scrollAsComplement(sr, seqOffset,
-            horizontal);
   }
 }
index 851c58b..c7ef3ce 100644 (file)
@@ -338,7 +338,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     // TODO: properly locate search results in view when large numbers of hidden
     // columns exist before highlighted region
     // do we need to scroll the panel?
-    // TODO: tons of nullpointereexceptions raised here.
+    // TODO: tons of nullpointerexceptions raised here.
     if (results != null && results.getSize() > 0 && av != null
             && av.getAlignment() != null)
     {
@@ -814,7 +814,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
     else
     {
-      av.scrollComplementaryAlignment(evt.getSource() == hscroll);
+      av.scrollComplementaryAlignment();
     }
   }
 
@@ -1721,11 +1721,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
    *          'to'; may be modified for sequence offset by this method
    * @param seqOffset
    *          the number of visible sequences to show above the mapped region
-   * @param horizontal
-   *          if true, horizontal scrolling, else vertical
    */
-  public void scrollAsComplement(SearchResults sr, int seqOffset,
-          boolean horizontal)
+  public void scrollToCentre(SearchResults sr, int seqOffset)
   {
     /*
      * To avoid jumpy vertical scrolling (if some sequences are gapped or not
@@ -1759,10 +1756,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
             .setSequence(av.getAlignment().getSequenceAt(sequenceIndex));
 
     /*
-     * Scroll to position but centring the target residue. Also set a state flag
-     * to prevent adjustmentValueChanged performing this recursively.
+     * Scroll to position but centring the target residue.
      */
-    setFollowingComplementScroll(true);
     scrollToPosition(sr, true, true);
   }
 
index 4ea35ca..88d0c69 100755 (executable)
@@ -27,6 +27,7 @@ import jalview.datamodel.SequenceI;
 import jalview.io.SequenceAnnotationReport;
 import jalview.util.MessageManager;
 import jalview.util.UrlLink;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.BorderLayout;
 import java.awt.event.MouseEvent;
@@ -54,7 +55,7 @@ public class IdPanel extends JPanel implements MouseListener,
 {
   private IdCanvas idCanvas;
 
-  protected AlignViewport av;
+  protected AlignmentViewport av;
 
   protected AlignmentPanel alignPanel;
 
index 2528841..8e77111 100644 (file)
@@ -1133,7 +1133,7 @@ public class Jalview2XML
       view.setShowGroupConservation(av.isShowGroupConservation());
       view.setShowNPfeatureTooltip(av.isShowNPFeats());
       view.setShowDbRefTooltip(av.isShowDBRefs());
-      view.setFollowHighlight(av.followHighlight);
+      view.setFollowHighlight(av.isFollowHighlight());
       view.setFollowSelection(av.followSelection);
       view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus());
       if (av.getFeaturesDisplayed() != null)
@@ -3844,7 +3844,7 @@ public class Jalview2XML
     }
     if (view.hasFollowHighlight())
     {
-      af.viewport.followHighlight = view.getFollowHighlight();
+      af.viewport.setFollowHighlight(view.getFollowHighlight());
     }
     if (view.hasFollowSelection())
     {
index afd3242..6cd5b70 100644 (file)
  */
 package jalview.gui;
 
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Point;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.ToolTipManager;
+
 import jalview.api.AlignViewportI;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
@@ -43,23 +60,6 @@ import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Point;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.MouseMotionListener;
-import java.awt.event.MouseWheelEvent;
-import java.awt.event.MouseWheelListener;
-import java.util.List;
-import java.util.Vector;
-
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.ToolTipManager;
-
 /**
  * DOCUMENT ME!
  * 
@@ -647,7 +647,7 @@ public class SeqPanel extends JPanel implements MouseListener,
   @Override
   public void highlightSequence(SearchResults results)
   {
-    if (av.followHighlight)
+    if (av.isFollowHighlight())
     {
       if (ap.scrollToPosition(results, false))
       {
index c6cbdc5..4aed258 100644 (file)
@@ -250,8 +250,23 @@ public final class MappingUtils
   public static SearchResults buildSearchResults(SequenceI seq, int index,
           Set<AlignedCodonFrame> seqmappings)
   {
-    SearchResults results;
-    results = new SearchResults();
+    SearchResults results = new SearchResults();
+    addSearchResults(results, seq, index, seqmappings);
+    return results;
+  }
+
+  /**
+   * Adds entries to a SearchResults object describing the mapped region
+   * corresponding to the specified sequence position.
+   * 
+   * @param results
+   * @param seq
+   * @param index
+   * @param seqmappings
+   */
+  public static void addSearchResults(SearchResults results, SequenceI seq,
+          int index, Set<AlignedCodonFrame> seqmappings)
+  {
     if (index >= seq.getStart() && index <= seq.getEnd())
     {
       for (AlignedCodonFrame acf : seqmappings)
@@ -259,7 +274,6 @@ public final class MappingUtils
         acf.markMappedRegion(seq, index, results);
       }
     }
-    return results;
   }
 
   /**
index c0e86cb..d6aa400 100644 (file)
@@ -46,6 +46,8 @@ import jalview.datamodel.AlignmentView;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenSequences;
+import jalview.datamodel.SearchResults;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceGroup;
@@ -57,6 +59,8 @@ import jalview.schemes.ResidueProperties;
 import jalview.structure.CommandListener;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
+import jalview.util.Comparison;
+import jalview.util.MappingUtils;
 import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
 import jalview.workers.ComplementConsensusThread;
@@ -1196,6 +1200,20 @@ public abstract class AlignmentViewport implements AlignViewportI,
   protected boolean showAutocalculatedAbove;
 
   /**
+   * when set, view will scroll to show the highlighted position
+   */
+  private boolean followHighlight = true;
+
+  // TODO private with getters and setters?
+  public int startRes;
+
+  public int endRes;
+
+  public int startSeq;
+
+  public int endSeq;
+
+  /**
    * Property change listener for changes in alignment
    * 
    * @param listener
@@ -2423,4 +2441,145 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     viewStyle.setScaleProteinAsCdna(b);
   }
+
+  /**
+   * @return true if view should scroll to show the highlighted region of a
+   *         sequence
+   * @return
+   */
+  @Override
+  public final boolean isFollowHighlight()
+  {
+    return followHighlight;
+  }
+
+  @Override
+  public final void setFollowHighlight(boolean b)
+  {
+    this.followHighlight = b;
+  }
+
+  public int getStartRes()
+  {
+    return startRes;
+  }
+
+  public int getEndRes()
+  {
+    return endRes;
+  }
+
+  public int getStartSeq()
+  {
+    return startSeq;
+  }
+
+  public void setStartRes(int res)
+  {
+    this.startRes = res;
+  }
+
+  public void setStartSeq(int seq)
+  {
+    this.startSeq = seq;
+  }
+
+  public void setEndRes(int res)
+  {
+    if (res > alignment.getWidth() - 1)
+    {
+      // log.System.out.println(" Corrected res from " + res + " to maximum " +
+      // (alignment.getWidth()-1));
+      res = alignment.getWidth() - 1;
+    }
+    if (res < 0)
+    {
+      res = 0;
+    }
+    this.endRes = res;
+  }
+
+  public void setEndSeq(int seq)
+  {
+    if (seq > alignment.getHeight())
+    {
+      seq = alignment.getHeight();
+    }
+    if (seq < 0)
+    {
+      seq = 0;
+    }
+    this.endSeq = seq;
+  }
+
+  public int getEndSeq()
+  {
+    return endSeq;
+  }
+
+  /**
+   * Helper method to populate the SearchResults with the location in the
+   * complementary alignment to scroll to, in order to match this one.
+   * 
+   * @param sr
+   *          the SearchResults to add to
+   * @return the offset (below top of visible region) of the matched sequence
+   */
+  protected int findComplementScrollTarget(SearchResults sr)
+  {
+    final AlignViewportI codingComplement = getCodingComplement();
+    if (codingComplement == null || !codingComplement.isFollowHighlight())
+    {
+      return 0;
+    }
+    boolean iAmProtein = !getAlignment().isNucleotide();
+    AlignmentI proteinAlignment = iAmProtein ? getAlignment()
+            : codingComplement.getAlignment();
+    if (proteinAlignment == null)
+    {
+      return 0;
+    }
+    final Set<AlignedCodonFrame> mappings = proteinAlignment
+            .getCodonFrames();
+  
+    /*
+     * Heuristic: find the first mapped sequence (if any) with a non-gapped
+     * residue in the middle column of the visible region. Scroll the
+     * complementary alignment to line up the corresponding residue.
+     */
+    int seqOffset = 0;
+    SequenceI sequence = null;
+    int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
+    final HiddenSequences hiddenSequences = getAlignment()
+            .getHiddenSequences();
+    for (int seqNo = getStartSeq(); seqNo < getEndSeq(); seqNo++, seqOffset++)
+    {
+      sequence = getAlignment().getSequenceAt(seqNo);
+      if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
+      {
+        continue;
+      }
+      if (Comparison.isGap(sequence.getCharAt(middleColumn)))
+      {
+        continue;
+      }
+      List<AlignedCodonFrame> seqMappings = MappingUtils
+              .findMappingsForSequence(sequence, mappings);
+      if (!seqMappings.isEmpty())
+      {
+        break;
+      }
+    }
+  
+    if (sequence == null)
+    {
+      /*
+       * No ungapped mapped sequence in middle column - do nothing
+       */
+      return 0;
+    }
+    MappingUtils.addSearchResults(sr, sequence,
+            sequence.findPosition(middleColumn), mappings);
+    return seqOffset;
+  }
 }