JAL-1925 update source version in license
[jalview.git] / src / jalview / gui / AlignmentPanel.java
index 851c58b..3031cd5 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b2)
+ * Copyright (C) 2015 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  */
 package jalview.gui;
 
+import jalview.analysis.AnnotationSorter;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.jbgui.GAlignmentPanel;
+import jalview.math.AlignmentDimension;
+import jalview.schemes.ResidueProperties;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Container;
@@ -27,6 +43,7 @@ import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
+import java.awt.Insets;
 import java.awt.event.AdjustmentEvent;
 import java.awt.event.AdjustmentListener;
 import java.awt.print.PageFormat;
@@ -41,21 +58,6 @@ import java.util.List;
 
 import javax.swing.SwingUtilities;
 
-import jalview.analysis.AnnotationSorter;
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.bin.Cache;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.jbgui.GAlignmentPanel;
-import jalview.math.AlignmentDimension;
-import jalview.schemes.ResidueProperties;
-import jalview.structure.StructureSelectionManager;
-import jalview.util.MessageManager;
-
 /**
  * DOCUMENT ME!
  * 
@@ -74,6 +76,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   private IdPanel idPanel;
 
   private boolean headless;
+
   IdwidthAdjuster idwidthAdjuster;
 
   /** DOCUMENT ME!! */
@@ -96,7 +99,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * Flag set while scrolling to follow complementary cDNA/protein scroll. When
    * true, suppresses invoking the same method recursively.
    */
-  private boolean followingComplementScroll;
+  private boolean dontScrollComplement;
 
   /**
    * Creates a new AlignmentPanel object.
@@ -153,6 +156,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     return av;
   }
+
   public void alignmentChanged()
   {
     av.alignmentChanged(this);
@@ -175,8 +179,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     scalePanelHolder.setPreferredSize(new Dimension(10, av.getCharHeight()
             + fm.getDescent()));
     idSpaceFillerPanel1.setPreferredSize(new Dimension(10, av
-            .getCharHeight()
-            + fm.getDescent()));
+            .getCharHeight() + fm.getDescent()));
 
     getIdPanel().getIdCanvas().gg = null;
     getSeqPanel().seqCanvas.img = null;
@@ -304,7 +307,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   public boolean scrollToPosition(SearchResults results)
   {
-    return scrollToPosition(results, true, false);
+    return scrollToPosition(results, 0, true, false);
   }
 
   /**
@@ -315,9 +318,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @param redrawOverview
    * @return
    */
-  public boolean scrollToPosition(SearchResults searchResults, boolean redrawOverview)
+  public boolean scrollToPosition(SearchResults searchResults,
+          boolean redrawOverview)
   {
-    return scrollToPosition(searchResults, redrawOverview, false);
+    return scrollToPosition(searchResults, 0, redrawOverview, false);
   }
 
   /**
@@ -325,6 +329,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * (if any)
    * 
    * @param results
+   * @param verticalOffset
+   *          if greater than zero, allows scrolling to a position below the
+   *          first displayed sequence
    * @param redrawOverview
    *          - when set, the overview will be recalculated (takes longer)
    * @param centre
@@ -332,13 +339,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @return false if results were not found
    */
   public boolean scrollToPosition(SearchResults results,
-          boolean redrawOverview, boolean centre)
+          int verticalOffset, boolean redrawOverview, boolean centre)
   {
     int startv, endv, starts, ends;
     // 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)
     {
@@ -356,8 +363,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
       int start = r[0];
       int end = r[1];
-      // System.err.println("Seq : "+seqIndex+" Scroll to "+start+","+end); //
       // DEBUG
+      // System.err.println(this.av.viewName + " Seq : " + seqIndex
+      // + " Scroll to " + start + "," + end);
 
       /*
        * To centre results, scroll to positions half the visible width
@@ -367,7 +375,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       {
         int offset = (av.getEndRes() - av.getStartRes() + 1) / 2 - 1;
         start = Math.max(start - offset, 0);
-        end = Math.min(end + offset, seq.getEnd() - 1);
+        end = end + offset - 1;
       }
       if (start < 0)
       {
@@ -390,6 +398,15 @@ public class AlignmentPanel extends GAlignmentPanel implements
           }
         }
       }
+
+      /*
+       * allow for offset of target sequence (actually scroll to one above it)
+       */
+      seqIndex = Math.max(0, seqIndex - verticalOffset);
+
+      // System.out.println("start=" + start + ", end=" + end + ", startv="
+      // + av.getStartRes() + ", endv=" + av.getEndRes() + ", starts="
+      // + av.getStartSeq() + ", ends=" + av.getEndSeq());
       if (!av.getWrapAlignment())
       {
         if ((startv = av.getStartRes()) >= start)
@@ -397,14 +414,16 @@ public class AlignmentPanel extends GAlignmentPanel implements
           /*
            * Scroll left to make start of search results visible
            */
-          setScrollValues(start - 1, seqIndex);
+          // setScrollValues(start - 1, seqIndex); // plus one residue
+          setScrollValues(start, seqIndex);
         }
         else if ((endv = av.getEndRes()) <= end)
         {
           /*
            * Scroll right to make end of search results visible
            */
-          setScrollValues(startv + 1 + end - endv, seqIndex);
+          // setScrollValues(startv + 1 + end - endv, seqIndex); // plus one
+          setScrollValues(startv + end - endv, seqIndex);
         }
         else if ((starts = av.getStartSeq()) > seqIndex)
         {
@@ -509,47 +528,50 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   protected void validateAnnotationDimensions(boolean adjustPanelHeight)
   {
-    int height = getAnnotationPanel().adjustPanelHeight();
-
-    int theight = av.getCharHeight()
-            * (av.getAlignment().getHeight() + (!av.hasHiddenRows() ? 0
-                    : av.getAlignment().getHiddenSequences().getSize()));
-    float sscaling = (float) (theight / (1.0 * theight + height));
-    float ascaling = (float) (height * 1.0 / alignFrame.getHeight());
-    int rheight = alignFrame.getHeight() - height - av.getCharHeight();
+    int annotationHeight = getAnnotationPanel().adjustPanelHeight();
+
     if (adjustPanelHeight)
     {
-      // NOTE: this logic is different in the applet. Need a better algorithm to
-      // define behaviour
-      // try and set height according to alignment
-      if (ascaling > 0 && sscaling < 0.5)
-      {
-        // if the alignment is too big then
-        // default is 0.5 split
-        height = alignFrame.getHeight() / 2;
-      }
-      else
+      int rowHeight = av.getCharHeight();
+      int alignmentHeight = rowHeight * av.getAlignment().getHeight();
+
+      /*
+       * Estimate available height in the AlignFrame for alignment +
+       * annotations. Deduct an estimate for title bar, menu bar, scale panel,
+       * hscroll, status bar (as these are not laid out we can't inspect their
+       * actual heights). Insets gives frame borders.
+       */
+      int stuff = Platform.isAMac() ? 80 : 100;
+      Insets insets = alignFrame.getInsets();
+      int availableHeight = alignFrame.getHeight() - stuff - insets.top
+              - insets.bottom;
+
+      /*
+       * If not enough vertical space, maximize annotation height while keeping
+       * at least two rows of alignment visible
+       */
+      if (annotationHeight + alignmentHeight > availableHeight)
       {
-        // if space for more than one sequence row left when annotation is fully
-        // displayed then set height to annotation height
-        // otherwise, leave at least two lines of sequence shown.
-        height = (rheight > av.getCharHeight()) ? height
-                : (-av.getCharHeight() * 3 + (int) (alignFrame.getHeight() * (1 - sscaling)));
+        annotationHeight = Math.min(annotationHeight, availableHeight - 2
+                * rowHeight);
       }
     }
     else
     {
       // maintain same window layout whilst updating sliders
-      height = annotationScroller.getSize().height;
+      annotationHeight = annotationScroller.getSize().height;
     }
     hscroll.addNotify();
 
     annotationScroller.setPreferredSize(new Dimension(annotationScroller
-            .getWidth(), height));
+            .getWidth(), annotationHeight));
+
+    Dimension e = idPanel.getSize();
+    alabels.setSize(new Dimension(e.width, annotationHeight));
 
     annotationSpaceFillerHolder.setPreferredSize(new Dimension(
-            annotationSpaceFillerHolder.getWidth(), height));
-    annotationScroller.validate();// repaint();
+            annotationSpaceFillerHolder.getWidth(), annotationHeight));
+    annotationScroller.validate();
     annotationScroller.addNotify();
   }
 
@@ -654,14 +676,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * Adjust row/column scrollers to show a visible position in the alignment.
    * 
    * @param x
-   *          visible column to scroll to DOCUMENT ME!
+   *          visible column to scroll to
    * @param y
    *          visible row to scroll to
    * 
    */
   public void setScrollValues(int x, int y)
   {
-    // System.err.println("Scroll to "+x+","+y);
+    // System.err.println("Scroll " + this.av.viewName + " to " + x + "," + y);
     if (av == null || av.getAlignment() == null)
     {
       return;
@@ -710,7 +732,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
       x = 0;
     }
 
+    /*
+     * each scroll adjustment triggers adjustmentValueChanged, which resets the
+     * 'do not scroll complement' flag; ensure it is the same for both
+     * operations
+     */
+    boolean flag = isDontScrollComplement();
     hscroll.setValues(x, hextent, 0, width);
+    setDontScrollComplement(flag);
     vscroll.setValues(y, vextent, 0, height);
   }
 
@@ -729,7 +758,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
     {
       int x = hscroll.getValue();
       av.setStartRes(x);
-      av.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av.getCharWidth())) - 1);
+      av.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av
+              .getCharWidth())) - 1);
     }
 
     if (evt.getSource() == vscroll)
@@ -808,13 +838,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * If there is one, scroll the (Protein/cDNA) complementary alignment to
      * match, unless we are ourselves doing that.
      */
-    if (isFollowingComplementScroll())
+    if (isDontScrollComplement())
     {
-      setFollowingComplementScroll(false);
+      setDontScrollComplement(false);
     }
     else
     {
-      av.scrollComplementaryAlignment(evt.getSource() == hscroll);
+      av.scrollComplementaryAlignment();
     }
   }
 
@@ -826,13 +856,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
             av.isShowAutocalculatedAbove());
-    sorter.sort(getAlignment()
-            .getAlignmentAnnotation(),
+    sorter.sort(getAlignment().getAlignmentAnnotation(),
             av.getSortAnnotationsBy());
     repaint();
 
     if (updateOverview)
     {
+      // TODO: determine if this paintAlignment changed structure colours
       av.getStructureSelectionManager().sequenceColoursChanged(this);
 
       if (overviewPanel != null)
@@ -857,6 +887,12 @@ public class AlignmentPanel extends GAlignmentPanel implements
     hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12));
     validate();
 
+    /*
+     * set scroll bar positions; first suppress this being 'followed' in any
+     * complementary split pane
+     */
+    setDontScrollComplement(true);
+
     if (av.getWrapAlignment())
     {
       int maxwidth = av.getAlignment().getWidth();
@@ -1044,9 +1080,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
                 - 4;
       }
 
-      pg.drawString(
-              seq.getDisplayId(av.getShowJVSuffix()),
-              xPos,
+      pg.drawString(seq.getDisplayId(av.getShowJVSuffix()), xPos,
               (((i - startSeq) * av.getCharHeight()) + av.getCharHeight())
                       - (av.getCharHeight() / 5));
     }
@@ -1055,7 +1089,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     // draw main sequence panel
     pg.translate(idWidth, 0);
-    getSeqPanel().seqCanvas.drawPanel(pg, startRes, endRes, startSeq, endSeq, 0);
+    getSeqPanel().seqCanvas.drawPanel(pg, startRes, endRes, startSeq,
+            endSeq, 0);
 
     if (av.isShowAnnotation() && (endSeq == av.getAlignment().getHeight()))
     {
@@ -1157,12 +1192,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
       if (labels != null)
       {
-        pg.translate(-3, ypos
- + (av.getAlignment().getHeight() * av.getCharHeight()));
+        pg.translate(-3,
+                ypos + (av.getAlignment().getHeight() * av.getCharHeight()));
 
         pg.setFont(av.getFont());
         labels.drawComponent(pg, idWidth);
-        pg.translate(+3, -ypos
+        pg.translate(
+                +3,
+                -ypos
                         - (av.getAlignment().getHeight() * av
                                 .getCharHeight()));
       }
@@ -1172,8 +1209,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     pg.translate(idWidth, 0);
 
-    getSeqPanel().seqCanvas.drawWrappedPanel(pg, pwidth - idWidth, totalHeight,
-            0);
+    getSeqPanel().seqCanvas.drawWrappedPanel(pg, pwidth - idWidth,
+            totalHeight, 0);
 
     if ((pi * pheight) < totalHeight)
     {
@@ -1231,8 +1268,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (alignFrame != null && !headless)
     {
       alignFrame.setProgressBar(MessageManager.formatMessage(
-              "status.saving_file", new Object[]
-              { type.getLabel() }), progress);
+              "status.saving_file", new Object[] { type.getLabel() }),
+              progress);
     }
     try
     {
@@ -1293,7 +1330,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
     {
       if (alignFrame != null && !headless)
       {
-        alignFrame.setProgressBar(MessageManager.getString("status.export_complete"), progress);
+        alignFrame.setProgressBar(
+                MessageManager.getString("status.export_complete"),
+                progress);
       }
     }
   }
@@ -1358,6 +1397,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     makeAlignmentImage(jalview.util.ImageMaker.TYPE.SVG, svgFile);
   }
+
   public void makePNGImageMap(File imgMapFile, String imageName)
   {
     // /////ONLY WORKS WITH NONE WRAPPED ALIGNMENTS
@@ -1395,8 +1435,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
             if (av.getAlignment().isNucleotide())
             {
               triplet = ResidueProperties.nucleotideName.get(seq
-                      .getCharAt(res)
-                      + "");
+                      .getCharAt(res) + "");
             }
             else
             {
@@ -1418,8 +1457,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
                 text.append("<area shape=\"rect\" coords=\""
                         + (idWidth + res * av.getCharWidth()) + "," + sy
                         + "," + (idWidth + (res + 1) * av.getCharWidth())
-                        + ","
-                        + (av.getCharHeight() + sy) + "\""
+                        + "," + (av.getCharHeight() + sy) + "\""
                         + " onMouseOver=\"toolTip('" + alIndex + " "
                         + triplet);
               }
@@ -1438,8 +1476,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
                 text.append("<area shape=\"rect\" coords=\""
                         + (idWidth + res * av.getCharWidth()) + "," + sy
                         + "," + (idWidth + (res + 1) * av.getCharWidth())
-                        + ","
-                        + (av.getCharHeight() + sy) + "\""
+                        + "," + (av.getCharHeight() + sy) + "\""
                         + " onMouseOver=\"toolTip('" + alIndex + " "
                         + triplet);
               }
@@ -1595,10 +1632,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
   @Override
   public AlignmentI getAlignment()
   {
-    return av.getAlignment();
+    return av == null ? null : av.getAlignment();
   }
 
-
   @Override
   public String getViewName()
   {
@@ -1614,11 +1650,17 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     try
     {
+      if (alignFrame.getSplitViewContainer() != null)
+      {
+        /*
+         * bring enclosing SplitFrame to front first if there is one
+         */
+        ((SplitFrame) alignFrame.getSplitViewContainer()).setSelected(b);
+      }
       alignFrame.setSelected(b);
     } catch (Exception ex)
     {
     }
-    ;
 
     if (b)
     {
@@ -1644,12 +1686,15 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     return new FeatureRenderer(this);
   }
-  @Override 
+
+  @Override
   public jalview.api.FeatureRenderer getFeatureRenderer()
   {
     return seqPanel.seqCanvas.getFeatureRenderer();
   }
-  public void updateFeatureRenderer(jalview.renderer.seqfeatures.FeatureRenderer fr)
+
+  public void updateFeatureRenderer(
+          jalview.renderer.seqfeatures.FeatureRenderer fr)
   {
     fr.transferSettings(getSeqPanel().seqCanvas.getFeatureRenderer());
   }
@@ -1719,13 +1764,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @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
+   * @param verticalOffset
    *          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 verticalOffset)
   {
     /*
      * To avoid jumpy vertical scrolling (if some sequences are gapped or not
@@ -1739,7 +1781,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * 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)
     {
@@ -1748,36 +1789,30 @@ public class AlignmentPanel extends GAlignmentPanel implements
         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.
+     * Scroll to position but centring the target residue.
      */
-    setFollowingComplementScroll(true);
-    scrollToPosition(sr, true, true);
+    scrollToPosition(sr, verticalOffset, true, true);
   }
 
   /**
-   * Set a flag to say we are scrolling to follow a (cDNA/protein) complement.
+   * Set a flag to say do not scroll any (cDNA/protein) complement.
    * 
    * @param b
    */
-  protected void setFollowingComplementScroll(boolean b)
+  protected void setDontScrollComplement(boolean b)
   {
-    this.followingComplementScroll = b;
+    this.dontScrollComplement = b;
   }
 
-  protected boolean isFollowingComplementScroll()
+  protected boolean isDontScrollComplement()
   {
-    return this.followingComplementScroll;
+    return this.dontScrollComplement;
   }
 }