JAL-2774 Created single event for update to both start residue and seq
[jalview.git] / src / jalview / gui / IdCanvas.java
index 92747a2..235dea0 100755 (executable)
@@ -1,29 +1,41 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
- * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  * Jalview is free software: you can redistribute it and/or
  * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
  *  
  * Jalview is distributed in the hope that it will be useful, but 
  * WITHOUT ANY WARRANTY; without even the implied warranty 
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
  * PURPOSE.  See the GNU General Public License for more details.
  * 
- * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 package jalview.gui;
 
-import java.awt.*;
-import java.awt.image.*;
+import jalview.datamodel.SequenceI;
+import jalview.viewmodel.ViewportListenerI;
+import jalview.viewmodel.ViewportRanges;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeEvent;
 import java.util.List;
 
-import javax.swing.*;
-
-import jalview.datamodel.*;
+import javax.swing.JPanel;
 
 /**
  * DOCUMENT ME!
@@ -31,7 +43,7 @@ import jalview.datamodel.*;
  * @author $author$
  * @version $Revision$
  */
-public class IdCanvas extends JPanel
+public class IdCanvas extends JPanel implements ViewportListenerI
 {
   protected AlignViewport av;
 
@@ -57,7 +69,7 @@ public class IdCanvas extends JPanel
 
   AnnotationPanel ap;
 
-  Font idfont;
+  private Font idfont;
 
   /**
    * Creates a new IdCanvas object.
@@ -70,6 +82,7 @@ public class IdCanvas extends JPanel
     setLayout(new BorderLayout());
     this.av = av;
     PaintRefresher.Register(this, av.getSequenceSetId());
+    av.getRanges().addPropertyChangeListener(this);
   }
 
   /**
@@ -77,6 +90,8 @@ public class IdCanvas extends JPanel
    * 
    * @param gg
    *          DOCUMENT ME!
+   * @param hiddenRows
+   *          true - check and display hidden row marker if need be
    * @param s
    *          DOCUMENT ME!
    * @param i
@@ -86,12 +101,12 @@ public class IdCanvas extends JPanel
    * @param ypos
    *          DOCUMENT ME!
    */
-  public void drawIdString(Graphics2D gg, SequenceI s, int i, int starty,
-          int ypos)
+  public void drawIdString(Graphics2D gg, boolean hiddenRows, SequenceI s,
+          int i, int starty, int ypos)
   {
     int xPos = 0;
     int panelWidth = getWidth();
-    int charHeight = av.charHeight;
+    int charHeight = av.getCharHeight();
 
     if ((searchResults != null) && searchResults.contains(s))
     {
@@ -116,7 +131,7 @@ public class IdCanvas extends JPanel
       gg.setColor(Color.black);
     }
 
-    if (av.rightAlignIds)
+    if (av.isRightAlignIds())
     {
       xPos = panelWidth
               - fm.stringWidth(s.getDisplayId(av.getShowJVSuffix())) - 4;
@@ -125,7 +140,7 @@ public class IdCanvas extends JPanel
     gg.drawString(s.getDisplayId(av.getShowJVSuffix()), xPos,
             (((i - starty + 1) * charHeight) + ypos) - (charHeight / 5));
 
-    if (av.hasHiddenRows() && av.showHiddenMarkers)
+    if (hiddenRows)
     {
       drawMarker(i, starty, ypos);
     }
@@ -140,39 +155,45 @@ public class IdCanvas extends JPanel
    */
   public void fastPaint(int vertical)
   {
-    if (gg == null)
+    /*
+     * for now, not attempting fast paint of wrapped ids...
+     */
+    if (gg == null || av.getWrapAlignment())
     {
       repaint();
 
       return;
     }
 
-    gg.copyArea(0, 0, getWidth(), imgHeight, 0, -vertical * av.charHeight);
+    ViewportRanges ranges = av.getRanges();
+
+    gg.copyArea(0, 0, getWidth(), imgHeight, 0,
+            -vertical * av.getCharHeight());
 
-    int ss = av.startSeq;
-    int es = av.endSeq;
+    int ss = ranges.getStartSeq();
+    int es = ranges.getEndSeq();
     int transY = 0;
 
     if (vertical > 0) // scroll down
     {
       ss = es - vertical;
 
-      if (ss < av.startSeq)
+      if (ss < ranges.getStartSeq())
       { // ie scrolling too fast, more than a page at a time
-        ss = av.startSeq;
+        ss = ranges.getStartSeq();
       }
       else
       {
-        transY = imgHeight - (vertical * av.charHeight);
+        transY = imgHeight - ((vertical + 1) * av.getCharHeight());
       }
     }
-    else if (vertical < 0)
+    else if (vertical < 0) // scroll up
     {
       es = ss - vertical;
 
-      if (es > av.endSeq)
+      if (es > ranges.getEndSeq())
       {
-        es = av.endSeq;
+        es = ranges.getEndSeq();
       }
     }
 
@@ -192,6 +213,7 @@ public class IdCanvas extends JPanel
    * @param g
    *          DOCUMENT ME!
    */
+  @Override
   public void paintComponent(Graphics g)
   {
     g.setColor(Color.white);
@@ -208,7 +230,7 @@ public class IdCanvas extends JPanel
     int oldHeight = imgHeight;
 
     imgHeight = getHeight();
-    imgHeight -= (imgHeight % av.charHeight);
+    imgHeight -= (imgHeight % av.getCharHeight());
 
     if (imgHeight < 1)
     {
@@ -227,7 +249,7 @@ public class IdCanvas extends JPanel
     gg.setColor(Color.white);
     gg.fillRect(0, 0, getWidth(), imgHeight);
 
-    drawIds(av.getStartSeq(), av.endSeq);
+    drawIds(av.getRanges().getStartSeq(), av.getRanges().getEndSeq());
 
     g.drawImage(image, 0, 0, this);
   }
@@ -242,17 +264,17 @@ public class IdCanvas extends JPanel
    */
   void drawIds(int starty, int endy)
   {
-    if (av.seqNameItalics)
+    if (av.isSeqNameItalics())
     {
-      idfont = new Font(av.getFont().getName(), Font.ITALIC, av.getFont()
-              .getSize());
+      setIdfont(new Font(av.getFont().getName(), Font.ITALIC,
+              av.getFont().getSize()));
     }
     else
     {
-      idfont = av.getFont();
+      setIdfont(av.getFont());
     }
 
-    gg.setFont(idfont);
+    gg.setFont(getIdfont());
     fm = gg.getFontMetrics();
 
     if (av.antiAlias)
@@ -264,145 +286,165 @@ public class IdCanvas extends JPanel
     Color currentColor = Color.white;
     Color currentTextColor = Color.black;
 
+    boolean hasHiddenRows = av.hasHiddenRows();
+
     if (av.getWrapAlignment())
     {
-      int maxwidth = av.getAlignment().getWidth();
-      int alheight = av.getAlignment().getHeight();
+      drawIdsWrapped(starty, hasHiddenRows);
+      return;
+    }
 
-      if (av.hasHiddenColumns())
-      {
-        maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
-      }
+    // No need to hang on to labels if we're not wrapped
+    labels = null;
 
-      int annotationHeight = 0;
+    // Now draw the id strings
+    int panelWidth = getWidth();
+    int xPos = 0;
+
+    SequenceI sequence;
+    // Now draw the id strings
+    for (int i = starty; i <= endy; i++)
+    {
+      sequence = av.getAlignment().getSequenceAt(i);
 
-      if (av.showAnnotation)
+      if (sequence == null)
       {
-        if (ap == null)
-        {
-          ap = new AnnotationPanel(av);
-        }
+        continue;
+      }
 
-        annotationHeight = ap.adjustPanelHeight();
-        if (labels == null)
-        {
-          labels = new AnnotationLabels(av);
-        }
+      if (hasHiddenRows || av.isDisplayReferenceSeq())
+      {
+        setHiddenFont(sequence);
       }
 
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      // Selected sequence colours
+      if ((searchResults != null) && searchResults.contains(sequence))
+      {
+        currentColor = Color.black;
+        currentTextColor = Color.white;
+      }
+      else if ((av.getSelectionGroup() != null) && av.getSelectionGroup()
+              .getSequences(null).contains(sequence))
+      {
+        currentColor = Color.lightGray;
+        currentTextColor = Color.black;
+      }
+      else
       {
-        hgap += av.charHeight;
+        currentColor = av.getSequenceColour(sequence);
+        currentTextColor = Color.black;
       }
 
-      int cHeight = alheight * av.charHeight + hgap + annotationHeight;
+      gg.setColor(currentColor);
 
-      int rowSize = av.getEndRes() - av.getStartRes();
+      gg.fillRect(0, (i - starty) * av.getCharHeight(), getWidth(),
+              av.getCharHeight());
 
-      // Draw the rest of the panels
-      for (int ypos = hgap, row = av.startRes; (ypos <= getHeight())
-              && (row < maxwidth); ypos += cHeight, row += rowSize)
+      gg.setColor(currentTextColor);
+
+      String string = sequence.getDisplayId(av.getShowJVSuffix());
+
+      if (av.isRightAlignIds())
       {
-        for (int i = starty; i < alheight; i++)
-        {
-          SequenceI s = av.getAlignment().getSequenceAt(i);
-          if (av.isDisplayReferenceSeq() || av.hasHiddenRows())
-          {
-            setHiddenFont(s);
-          }
-          else
-          {
-            gg.setFont(idfont);
-          }
-
-          drawIdString(gg, s, i, 0, ypos);
-        }
+        xPos = panelWidth - fm.stringWidth(string) - 4;
+      }
 
-        if (labels != null && av.showAnnotation)
-        {
-          gg.translate(0, ypos + (alheight * av.charHeight));
-          labels.drawComponent(gg, getWidth());
-          gg.translate(0, -ypos - (alheight * av.charHeight));
-        }
+      gg.drawString(string, xPos,
+              (((i - starty) * av.getCharHeight()) + av.getCharHeight())
+                      - (av.getCharHeight() / 5));
+
+      if (hasHiddenRows)
+      {
+        drawMarker(i, starty, 0);
       }
     }
-    else
-    {
-      // No need to hang on to labels if we're not wrapped
-      labels = null;
+  }
 
-      // Now draw the id strings
-      int panelWidth = getWidth();
-      int xPos = 0;
+  /**
+   * Draws sequence ids in wrapped mode
+   * 
+   * @param starty
+   * @param hasHiddenRows
+   */
+  protected void drawIdsWrapped(int starty, boolean hasHiddenRows)
+  {
+    int maxwidth = av.getAlignment().getWidth();
+    int alheight = av.getAlignment().getHeight();
 
-      SequenceI sequence;
-      // Now draw the id strings
-      for (int i = starty; i < endy; i++)
-      {
-        sequence = av.getAlignment().getSequenceAt(i);
+    if (av.hasHiddenColumns())
+    {
+      maxwidth = av.getAlignment().getHiddenColumns()
+              .findColumnPosition(maxwidth) - 1;
+    }
 
-        if (sequence == null)
-        {
-          continue;
-        }
+    int annotationHeight = 0;
 
-        if (av.isDisplayReferenceSeq() || av.hasHiddenRows())
-        {
-          setHiddenFont(sequence);
-        }
+    if (av.isShowAnnotation())
+    {
+      if (ap == null)
+      {
+        ap = new AnnotationPanel(av);
+      }
 
-        // Selected sequence colours
-        if ((searchResults != null) && searchResults.contains(sequence))
-        {
-          currentColor = Color.black;
-          currentTextColor = Color.white;
-        }
-        else if ((av.getSelectionGroup() != null)
-                && av.getSelectionGroup().getSequences(null)
-                        .contains(sequence))
-        {
-          currentColor = Color.lightGray;
-          currentTextColor = Color.black;
-        }
-        else
-        {
-          currentColor = av.getSequenceColour(sequence);
-          currentTextColor = Color.black;
-        }
+      annotationHeight = ap.adjustPanelHeight();
+      if (labels == null)
+      {
+        labels = new AnnotationLabels(av);
+      }
+    }
 
-        gg.setColor(currentColor);
+    int hgap = av.getCharHeight();
+    if (av.getScaleAboveWrapped())
+    {
+      hgap += av.getCharHeight();
+    }
 
-        gg.fillRect(0, (i - starty) * av.charHeight, getWidth(),
-                av.charHeight);
+    int cHeight = alheight * av.getCharHeight() + hgap + annotationHeight;
 
-        gg.setColor(currentTextColor);
+    ViewportRanges ranges = av.getRanges();
 
-        String string = sequence.getDisplayId(av.getShowJVSuffix());
+    int rowSize = ranges.getViewportWidth();
 
-        if (av.rightAlignIds)
+    /*
+     * draw repeating sequence ids until out of sequence data or
+     * out of visible space, whichever comes first
+     */
+    int ypos = hgap;
+    int row = ranges.getStartRes();
+    while ((ypos <= getHeight()) && (row < maxwidth))
+    {
+      for (int i = starty; i < alheight; i++)
+      {
+        SequenceI s = av.getAlignment().getSequenceAt(i);
+        if (hasHiddenRows || av.isDisplayReferenceSeq())
         {
-          xPos = panelWidth - fm.stringWidth(string) - 4;
+          setHiddenFont(s);
         }
-
-        gg.drawString(string, xPos,
-                (((i - starty) * av.charHeight) + av.charHeight)
-                        - (av.charHeight / 5));
-
-        if (av.hasHiddenRows() && av.showHiddenMarkers)
+        else
         {
-          drawMarker(i, starty, 0);
+          gg.setFont(getIdfont());
         }
 
+        drawIdString(gg, hasHiddenRows, s, i, 0, ypos);
+      }
+
+      if (labels != null && av.isShowAnnotation())
+      {
+        gg.translate(0, ypos + (alheight * av.getCharHeight()));
+        labels.drawComponent(gg, getWidth());
+        gg.translate(0, -ypos - (alheight * av.getCharHeight()));
       }
 
+      ypos += cHeight;
+      row += rowSize;
     }
   }
 
   void drawMarker(int i, int starty, int yoffset)
   {
 
-    SequenceI[] hseqs = av.getAlignment().getHiddenSequences().hiddenSequences;
+    SequenceI[] hseqs = av.getAlignment()
+            .getHiddenSequences().hiddenSequences;
     // Use this method here instead of calling hiddenSeq adjust
     // 3 times.
     int hSize = hseqs.length;
@@ -438,40 +480,43 @@ public class IdCanvas extends JPanel
     {
       gg.fillPolygon(
               new int[]
-              { getWidth() - av.charHeight, getWidth() - av.charHeight,
-                  getWidth() }, new int[]
-              {
-                  (i - starty) * av.charHeight + yoffset,
-                  (i - starty) * av.charHeight + yoffset + av.charHeight
-                          / 4, (i - starty) * av.charHeight + yoffset }, 3);
+              { getWidth() - av.getCharHeight(),
+                  getWidth() - av.getCharHeight(), getWidth() },
+              new int[]
+              { (i - starty) * av.getCharHeight() + yoffset,
+                  (i - starty) * av.getCharHeight() + yoffset
+                          + av.getCharHeight() / 4,
+                  (i - starty) * av.getCharHeight() + yoffset },
+              3);
     }
     if (above)
     {
       gg.fillPolygon(
               new int[]
-              { getWidth() - av.charHeight, getWidth() - av.charHeight,
-                  getWidth() }, new int[]
-              {
-                  (i - starty + 1) * av.charHeight + yoffset,
-                  (i - starty + 1) * av.charHeight + yoffset
-                          - av.charHeight / 4,
-                  (i - starty + 1) * av.charHeight + yoffset }, 3);
+              { getWidth() - av.getCharHeight(),
+                  getWidth() - av.getCharHeight(), getWidth() },
+              new int[]
+              { (i - starty + 1) * av.getCharHeight() + yoffset,
+                  (i - starty + 1) * av.getCharHeight() + yoffset
+                          - av.getCharHeight() / 4,
+                  (i - starty + 1) * av.getCharHeight() + yoffset },
+              3);
 
     }
   }
 
   void setHiddenFont(SequenceI seq)
   {
-    Font bold = new Font(av.getFont().getName(), Font.BOLD, av.getFont()
-            .getSize());
+    Font bold = new Font(av.getFont().getName(), Font.BOLD,
+            av.getFont().getSize());
 
-    if (av.isHiddenRepSequence(seq))
+    if (av.isReferenceSeq(seq) || av.isHiddenRepSequence(seq))
     {
       gg.setFont(bold);
     }
     else
     {
-      gg.setFont(idfont);
+      gg.setFont(getIdfont());
     }
   }
 
@@ -486,4 +531,43 @@ public class IdCanvas extends JPanel
     searchResults = list;
     repaint();
   }
+
+  public Font getIdfont()
+  {
+    return idfont;
+  }
+
+  public void setIdfont(Font idfont)
+  {
+    this.idfont = idfont;
+  }
+
+  /**
+   * Respond to viewport range changes (e.g. alignment panel was scrolled). Both
+   * scrolling and resizing change viewport ranges. Scrolling changes both start
+   * and end points, but resize only changes end values. Here we only want to
+   * fastpaint on a scroll, with resize using a normal paint, so scroll events
+   * are identified as changes to the horizontal or vertical start value.
+   * <p>
+   * In unwrapped mode, only responds to a vertical scroll, as horizontal scroll
+   * leaves sequence ids unchanged. In wrapped mode, only vertical scroll is
+   * provided, but it generates a change of "startres" which does require an
+   * update here.
+   */
+  @Override
+  public void propertyChange(PropertyChangeEvent evt)
+  {
+    String propertyName = evt.getPropertyName();
+    if (propertyName.equals(ViewportRanges.STARTSEQ)
+            || (av.getWrapAlignment()
+                    && propertyName.equals(ViewportRanges.STARTRES)))
+    {
+      fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
+    }
+    else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ))
+    {
+      fastPaint(((int[]) evt.getNewValue())[1]
+              - ((int[]) evt.getOldValue())[1]);
+    }
+  }
 }