JAL-2388 Initial refactor of OverviewPanel
authorkiramt <k.mourao@dundee.ac.uk>
Thu, 9 Feb 2017 13:00:32 +0000 (13:00 +0000)
committerkiramt <k.mourao@dundee.ac.uk>
Thu, 9 Feb 2017 13:00:32 +0000 (13:00 +0000)
src/jalview/api/AlignViewportI.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/gui/OverviewPanel.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/OverviewDimensions.java [new file with mode: 0644]

index 72542b3..9e080a0 100644 (file)
@@ -47,6 +47,8 @@ public interface AlignViewportI extends ViewStyleI
 
   int getEndRes();
 
+  int getStartRes();
+
   /**
    * calculate the height for visible annotation, revalidating bounds where
    * necessary ABSTRACT GUI METHOD
@@ -445,4 +447,8 @@ public interface AlignViewportI extends ViewStyleI
    * @return search results or null
    */
   SearchResultsI getSearchResults();
+
+  int getStartSeq();
+
+  int getEndSeq();
 }
index 9b2be4c..743d217 100755 (executable)
@@ -20,7 +20,7 @@
  */
 package jalview.appletgui;
 
-import jalview.datamodel.AlignmentI;
+import jalview.viewmodel.OverviewDimensions;
 
 import java.awt.Color;
 import java.awt.Dimension;
@@ -37,33 +37,29 @@ import java.awt.event.MouseMotionListener;
 public class OverviewPanel extends Panel implements Runnable,
         MouseMotionListener, MouseListener
 {
-  Image miniMe;
+  private OverviewDimensions od;
 
-  Image offscreen;
+  private Image miniMe;
 
-  AlignViewport av;
+  private Image offscreen;
 
-  AlignmentPanel ap;
+  private AlignViewport av;
 
-  float scalew = 1f;
+  private AlignmentPanel ap;
 
-  float scaleh = 1f;
+  private boolean resizing = false;
 
-  public int width, sequencesHeight;
-
-  int graphHeight = 20;
-
-  int boxX = -1, boxY = -1, boxWidth = -1, boxHeight = -1;
-
-  boolean resizing = false;
+  // This is set true if the user resizes whilst
+  // the overview is being calculated
+  private boolean resizeAgain = false;
 
   // Can set different properties in this seqCanvas than
   // main visible SeqCanvas
-  SequenceRenderer sr;
+  private SequenceRenderer sr;
 
-  FeatureRenderer fr;
+  private FeatureRenderer fr;
 
-  Frame nullFrame;
+  private Frame nullFrame;
 
   public OverviewPanel(AlignmentPanel ap)
   {
@@ -79,45 +75,17 @@ public class OverviewPanel extends Panel implements Runnable,
     sr.forOverview = true;
     fr = new FeatureRenderer(av);
 
-    // scale the initial size of overviewpanel to shape of alignment
-    float initialScale = (float) av.getAlignment().getWidth()
-            / (float) av.getAlignment().getHeight();
+    od = new OverviewDimensions(av);
 
-    if (av.getSequenceConsensusHash() == null)
-    {
-      graphHeight = 0;
-    }
-
-    if (av.getAlignment().getWidth() > av.getAlignment().getHeight())
-    {
-      // wider
-      width = 400;
-      sequencesHeight = (int) (400f / initialScale);
-      if (sequencesHeight < 40)
-      {
-        sequencesHeight = 40;
-      }
-    }
-    else
-    {
-      // taller
-      width = (int) (400f * initialScale);
-      sequencesHeight = 300;
-      if (width < 120)
-      {
-        width = 120;
-      }
-    }
-
-    setSize(new Dimension(width, sequencesHeight + graphHeight));
+    setSize(new Dimension(od.getWidth(), od.getHeight()));
     addComponentListener(new ComponentAdapter()
     {
 
       @Override
       public void componentResized(ComponentEvent evt)
       {
-        if (getSize().width != width
-                || getSize().height != sequencesHeight + graphHeight)
+        if ((getWidth() != od.getWidth())
+                || (getHeight() != (od.getHeight())))
         {
           updateOverviewImage();
         }
@@ -155,74 +123,34 @@ public class OverviewPanel extends Panel implements Runnable,
   @Override
   public void mousePressed(MouseEvent evt)
   {
-    boxX = evt.getX();
-    boxY = evt.getY();
+    od.setBoxX(evt.getX());
+    od.setBoxY(evt.getY());
     checkValid();
   }
 
   @Override
   public void mouseReleased(MouseEvent evt)
   {
-    boxX = evt.getX();
-    boxY = evt.getY();
+    od.setBoxX(evt.getX());
+    od.setBoxY(evt.getY());
     checkValid();
   }
 
   @Override
   public void mouseDragged(MouseEvent evt)
   {
-    boxX = evt.getX();
-    boxY = evt.getY();
+    od.setBoxX(evt.getX());
+    od.setBoxY(evt.getY());
     checkValid();
   }
 
-  void checkValid()
+  /**
+   * Check box dimensions and scroll positions and correct if necessary
+   */
+  private void checkValid()
   {
-    if (boxY < 0)
-    {
-      boxY = 0;
-    }
-
-    if (boxY > (sequencesHeight - boxHeight))
-    {
-      boxY = sequencesHeight - boxHeight + 1;
-    }
-
-    if (boxX < 0)
-    {
-      boxX = 0;
-    }
-
-    if (boxX > (width - boxWidth))
-    {
-      if (av.hasHiddenColumns())
-      {
-        // Try smallest possible box
-        boxWidth = (int) ((av.endRes - av.startRes + 1) * av.getCharWidth() * scalew);
-      }
-      boxX = width - boxWidth;
-    }
-
-    int col = (int) (boxX / scalew / av.getCharWidth());
-    int row = (int) (boxY / scaleh / av.getCharHeight());
-
-    if (av.hasHiddenColumns())
-    {
-      if (!av.getColumnSelection().isVisible(col))
-      {
-        return;
-      }
-
-      col = av.getColumnSelection().findColumnPosition(col);
-    }
-
-    if (av.hasHiddenRows())
-    {
-      row = av.getAlignment().getHiddenSequences()
-              .findIndexWithoutHiddenSeqs(row);
-    }
-
-    ap.setScrollValues(col, row);
+    od.checkValid();
+    ap.setScrollValues(od.getScrollCol(), od.getScrollRow());
     ap.paintAlignment(false);
   }
 
@@ -244,29 +172,22 @@ public class OverviewPanel extends Panel implements Runnable,
 
     resizing = true;
 
-    if ((getSize().width > 0) && (getSize().height > 0))
+    if ((getWidth() > 0) && (getHeight() > 0))
     {
-      width = getSize().width;
-      sequencesHeight = getSize().height - graphHeight;
+      od.setWidth(getWidth()); // width = getWidth();
+      od.setHeight(getHeight()); // sequencesHeight = getHeight() - graphHeight;
     }
-    setSize(new Dimension(width, sequencesHeight + graphHeight));
+    setSize(new Dimension(od.getWidth(), od.getHeight()));
 
     Thread thread = new Thread(this);
     thread.start();
     repaint();
   }
 
-  // This is set true if the user resizes whilst
-  // the overview is being calculated
-  boolean resizeAgain = false;
-
   @Override
   public void run()
   {
     miniMe = null;
-    int alwidth = av.getAlignment().getWidth();
-    int alheight = av.getAlignment().getHeight()
-            + av.getAlignment().getHiddenSequences().getSize();
 
     if (av.isShowSequenceFeatures())
     {
@@ -275,135 +196,38 @@ public class OverviewPanel extends Panel implements Runnable,
 
     if (getSize().width > 0 && getSize().height > 0)
     {
-      width = getSize().width;
-      sequencesHeight = getSize().height - graphHeight;
+      od.setWidth(getSize().width);
+      od.setHeight(getSize().height - od.getGraphHeight());
     }
 
-    setSize(new Dimension(width, sequencesHeight + graphHeight));
-
-    int fullsizeWidth = alwidth * av.getCharWidth();
-    int fullsizeHeight = alheight * av.getCharHeight();
+    setSize(new Dimension(od.getWidth(), od.getHeight()));
 
-    scalew = (float) width / (float) fullsizeWidth;
-    scaleh = (float) sequencesHeight / (float) fullsizeHeight;
-
-    miniMe = nullFrame.createImage(width, sequencesHeight + graphHeight);
-    offscreen = nullFrame.createImage(width, sequencesHeight + graphHeight);
+    miniMe = nullFrame.createImage(od.getWidth(), od.getHeight());
+    offscreen = nullFrame.createImage(od.getWidth(), od.getHeight());
 
     Graphics mg = miniMe.getGraphics();
-    float sampleCol = (float) alwidth / (float) width;
-    float sampleRow = (float) alheight / (float) sequencesHeight;
 
-    int lastcol = 0, lastrow = 0;
-    int xstart = 0, ystart = 0;
-    Color color = Color.yellow;
-    int row, col, sameRow = 0, sameCol = 0;
-    jalview.datamodel.SequenceI seq;
-    final boolean hasHiddenRows = av.hasHiddenRows(), hasHiddenCols = av
-            .hasHiddenColumns();
-    boolean hiddenRow = false;
-    AlignmentI alignment = av.getAlignment();
-    for (row = 0; row <= sequencesHeight; row++)
-    {
-      if (resizeAgain)
-      {
-        break;
-      }
-      if ((int) (row * sampleRow) == lastrow)
-      {
-        sameRow++;
-        continue;
-      }
-
-      hiddenRow = false;
-      if (hasHiddenRows)
-      {
-        seq = alignment.getHiddenSequences().getHiddenSequence(lastrow);
-        if (seq == null)
-        {
-          int index = alignment.getHiddenSequences()
-                  .findIndexWithoutHiddenSeqs(lastrow);
-
-          seq = alignment.getSequenceAt(index);
-        }
-        else
-        {
-          hiddenRow = true;
-        }
-      }
-      else
-      {
-        seq = alignment.getSequenceAt(lastrow);
-      }
+    od.updateScales();
 
-      for (col = 0; col < width; col++)
-      {
-        if ((int) (col * sampleCol) == lastcol
-                && (int) (row * sampleRow) == lastrow)
-        {
-          sameCol++;
-          continue;
-        }
-
-        lastcol = (int) (col * sampleCol);
-
-        if (seq.getLength() > lastcol)
-        {
-          color = sr.getResidueBoxColour(seq, lastcol);
-
-          if (av.isShowSequenceFeatures())
-          {
-            color = fr.findFeatureColour(color, seq, lastcol);
-          }
-        }
-        else
-        {
-          color = Color.white; // White
-        }
-
-        if (hiddenRow
-                || (hasHiddenCols && !av.getColumnSelection().isVisible(
-                        lastcol)))
-        {
-          color = color.darker().darker();
-        }
-
-        mg.setColor(color);
-        if (sameCol == 1 && sameRow == 1)
-        {
-          mg.drawLine(xstart, ystart, xstart, ystart);
-        }
-        else
-        {
-          mg.fillRect(xstart, ystart, sameCol, sameRow);
-        }
+    int alwidth = av.getAlignment().getWidth();
+    int alheight = av.getAlignment().getHeight()
+            + av.getAlignment().getHiddenSequences().getSize();
+    float sampleCol = alwidth / (float) od.getWidth();
+    float sampleRow = alheight / (float) od.getSequencesHeight();
 
-        xstart = col;
-        sameCol = 1;
-      }
-      lastrow = (int) (row * sampleRow);
-      ystart = row;
-      sameRow = 1;
-    }
+    buildImage(sampleRow, sampleCol, mg);
 
     if (av.getAlignmentConservationAnnotation() != null)
     {
-      for (col = 0; col < width; col++)
+      for (int col = 0; col < od.getWidth() && !resizeAgain; col++)
       {
-        if (resizeAgain)
-        {
-          break;
-        }
-        lastcol = (int) (col * sampleCol);
-        {
-          mg.translate(col, sequencesHeight);
-          ap.annotationPanel.renderer.drawGraph(mg,
-                  av.getAlignmentConservationAnnotation(),
-                  av.getAlignmentConservationAnnotation().annotations,
-                  (int) (sampleCol) + 1, graphHeight,
-                  (int) (col * sampleCol), (int) (col * sampleCol) + 1);
-          mg.translate(-col, -sequencesHeight);
-        }
+        mg.translate(col, od.getSequencesHeight());
+        ap.annotationPanel.renderer.drawGraph(mg,
+                av.getAlignmentConservationAnnotation(),
+                av.getAlignmentConservationAnnotation().annotations,
+                (int) (sampleCol) + 1, od.getGraphHeight(),
+                (int) (col * sampleCol), (int) (col * sampleCol) + 1);
+        mg.translate(-col, -od.getSequencesHeight());
       }
     }
     System.gc();
@@ -419,52 +243,129 @@ public class OverviewPanel extends Panel implements Runnable,
     }
   }
 
-  public void setBoxPosition()
+  private void buildImage(float sampleRow, float sampleCol, Graphics mg)
   {
-    int fullsizeWidth = av.getAlignment().getWidth() * av.getCharWidth();
-    int fullsizeHeight = (av.getAlignment().getHeight() + av.getAlignment()
-            .getHiddenSequences().getSize())
-            * av.getCharHeight();
-
-    int startRes = av.getStartRes();
-    int endRes = av.getEndRes();
-
-    if (av.hasHiddenColumns())
-    {
-      startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);
-      endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);
-    }
+    int lastcol = 0;
+    int lastrow = 0;
+    int xstart = 0;
+    int ystart = 0;
+    Color color = Color.yellow;
+    int sameRow = 0;
+    int sameCol = 0;
 
-    int startSeq = av.startSeq;
-    int endSeq = av.endSeq;
+    jalview.datamodel.SequenceI seq = null;
+    final boolean hasHiddenRows = av.hasHiddenRows();
+    final boolean hasHiddenCols = av.hasHiddenColumns();
+    boolean hiddenRow = false;
 
-    if (av.hasHiddenRows())
+    for (int row = 0; row <= od.getSequencesHeight() && !resizeAgain; row++)
     {
-      startSeq = av.getAlignment().getHiddenSequences()
-              .adjustForHiddenSeqs(startSeq);
+      if ((int) (row * sampleRow) == lastrow)
+      {
+        sameRow++;
+        continue;
+      }
+      else
+      {
+        // this should largely be a method in Alignment
+        hiddenRow = false;
+        if (hasHiddenRows)
+        {
+          seq = av.getAlignment().getHiddenSequences()
+                  .getHiddenSequence(lastrow);
+          if (seq == null)
+          {
+            int index = av.getAlignment().getHiddenSequences()
+                    .findIndexWithoutHiddenSeqs(lastrow);
 
-      endSeq = av.getAlignment().getHiddenSequences()
-              .adjustForHiddenSeqs(endSeq);
+            seq = av.getAlignment().getSequenceAt(index);
+          }
+          else
+          {
+            hiddenRow = true;
+          }
+        }
+        else
+        {
+          seq = av.getAlignment().getSequenceAt(lastrow);
+        }
+        // end of Alignment method
 
+        for (int col = 0; col < od.getWidth(); col++)
+        {
+          if ((int) (col * sampleCol) == lastcol
+                  && (int) (row * sampleRow) == lastrow)
+          {
+            sameCol++;
+          }
+          else
+          {
+            lastcol = (int) (col * sampleCol);
+
+            color = getColumnColourFromSequence(seq, hiddenRow,
+                    hasHiddenCols, lastcol);
+
+            mg.setColor(color);
+            if (sameCol == 1 && sameRow == 1)
+            {
+              mg.drawLine(xstart, ystart, xstart, ystart);
+            }
+            else
+            {
+              mg.fillRect(xstart, ystart, sameCol, sameRow);
+            }
+
+            xstart = col;
+            sameCol = 1;
+          }
+        }
+        lastrow = (int) (row * sampleRow);
+        ystart = row;
+        sameRow = 1;
+      }
     }
 
-    scalew = (float) width / (float) fullsizeWidth;
-    scaleh = (float) sequencesHeight / (float) fullsizeHeight;
-
-    boxX = (int) (startRes * av.getCharWidth() * scalew);
-    boxY = (int) (startSeq * av.getCharHeight() * scaleh);
+  }
 
-    if (av.hasHiddenColumns())
+  /*
+   * Find the colour of a sequence at a specified column position
+   */
+  private Color getColumnColourFromSequence(
+          jalview.datamodel.SequenceI seq, boolean hiddenRow,
+          boolean hasHiddenCols, int lastcol)
+  {
+    Color color;
+    if (seq.getLength() > lastcol)
     {
-      boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
+      color = sr.getResidueBoxColour(seq, lastcol);
+
+      if (av.isShowSequenceFeatures())
+      {
+        color = fr.findFeatureColour(color, seq, lastcol);
+      }
     }
     else
     {
-      boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
+      color = Color.white; // White
     }
 
-    boxHeight = (int) ((endSeq - startSeq) * av.getCharHeight() * scaleh);
+    if (hiddenRow
+            || (hasHiddenCols && !av.getColumnSelection()
+                    .isVisible(lastcol)))
+    {
+      color = color.darker().darker();
+    }
+    return color;
+  }
 
+  /**
+   * Update the overview panel box when the associated alignment panel is
+   * changed
+   * 
+   */
+  public void setBoxPosition()
+  {
+    od.setBoxPosition();
     repaint();
   }
 
@@ -482,8 +383,10 @@ public class OverviewPanel extends Panel implements Runnable,
     {
       og.drawImage(miniMe, 0, 0, this);
       og.setColor(Color.red);
-      og.drawRect(boxX, boxY, boxWidth, boxHeight);
-      og.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
+      og.drawRect(od.getBoxX(), od.getBoxY(), od.getBoxWidth(),
+              od.getBoxHeight());
+      og.drawRect(od.getBoxX() + 1, od.getBoxY() + 1, od.getBoxWidth() - 2,
+              od.getBoxHeight() - 2);
       g.drawImage(offscreen, 0, 0, this);
     }
   }
index 1c48690..fc3cc4b 100755 (executable)
@@ -21,6 +21,7 @@
 package jalview.gui;
 
 import jalview.renderer.AnnotationRenderer;
+import jalview.viewmodel.OverviewDimensions;
 
 import java.awt.Color;
 import java.awt.Dimension;
@@ -42,45 +43,38 @@ import javax.swing.JPanel;
  */
 public class OverviewPanel extends JPanel implements Runnable
 {
-  BufferedImage miniMe;
+  private static final Color TRANS_GREY = new Color(100, 100, 100, 25);
 
-  AlignViewport av;
+  private final AnnotationRenderer renderer = new AnnotationRenderer();
 
-  AlignmentPanel ap;
+  private OverviewDimensions od;
 
-  final AnnotationRenderer renderer = new AnnotationRenderer();
+  private BufferedImage miniMe;
 
-  float scalew = 1f;
-
-  float scaleh = 1f;
-
-  int width;
-
-  int sequencesHeight;
-
-  int graphHeight = 20;
-
-  int boxX = -1;
+  private BufferedImage lastMiniMe = null;
 
-  int boxY = -1;
+  private AlignViewport av;
 
-  int boxWidth = -1;
+  private AlignmentPanel ap;
 
-  int boxHeight = -1;
+  //
+  private boolean resizing = false;
 
-  boolean resizing = false;
+  // This is set true if the user resizes whilst
+  // the overview is being calculated
+  private boolean resizeAgain = false;
 
   // Can set different properties in this seqCanvas than
   // main visible SeqCanvas
-  SequenceRenderer sr;
+  private SequenceRenderer sr;
 
-  jalview.renderer.seqfeatures.FeatureRenderer fr;
+  private jalview.renderer.seqfeatures.FeatureRenderer fr;
 
   /**
    * Creates a new OverviewPanel object.
    * 
    * @param ap
-   *          DOCUMENT ME!
+   *          The alignment panel which is shown in the overview panel
    */
   public OverviewPanel(AlignmentPanel ap)
   {
@@ -93,44 +87,15 @@ public class OverviewPanel extends JPanel implements Runnable
     sr.forOverview = true;
     fr = new FeatureRenderer(ap);
 
-    // scale the initial size of overviewpanel to shape of alignment
-    float initialScale = (float) av.getAlignment().getWidth()
-            / (float) av.getAlignment().getHeight();
-
-    if (av.getAlignmentConservationAnnotation() == null)
-    {
-      graphHeight = 0;
-    }
-
-    if (av.getAlignment().getWidth() > av.getAlignment().getHeight())
-    {
-      // wider
-      width = 400;
-      sequencesHeight = (int) (400f / initialScale);
-      if (sequencesHeight < 40)
-      {
-        sequencesHeight = 40;
-      }
-    }
-    else
-    {
-      // taller
-      width = (int) (400f * initialScale);
-      sequencesHeight = 300;
-
-      if (width < 120)
-      {
-        width = 120;
-      }
-    }
+    od = new OverviewDimensions(av);
 
     addComponentListener(new ComponentAdapter()
     {
       @Override
       public void componentResized(ComponentEvent evt)
       {
-        if ((getWidth() != width)
-                || (getHeight() != (sequencesHeight + graphHeight)))
+        if ((getWidth() != od.getWidth())
+                || (getHeight() != (od.getHeight())))
         {
           updateOverviewImage();
         }
@@ -146,8 +111,8 @@ public class OverviewPanel extends JPanel implements Runnable
         {
           // TODO: feature: jv2.5 detect shift drag and update selection from
           // it.
-          boxX = evt.getX();
-          boxY = evt.getY();
+          od.setBoxX(evt.getX());
+          od.setBoxY(evt.getY());
           checkValid();
         }
       }
@@ -160,8 +125,8 @@ public class OverviewPanel extends JPanel implements Runnable
       {
         if (!av.getWrapAlignment())
         {
-          boxX = evt.getX();
-          boxY = evt.getY();
+          od.setBoxX(evt.getX());
+          od.setBoxY(evt.getY());
           checkValid();
         }
       }
@@ -171,60 +136,17 @@ public class OverviewPanel extends JPanel implements Runnable
   }
 
   /**
-   * DOCUMENT ME!
+   * Check box dimensions and scroll positions and correct if necessary
    */
-  void checkValid()
+  private void checkValid()
   {
-    if (boxY < 0)
-    {
-      boxY = 0;
-    }
-
-    if (boxY > (sequencesHeight - boxHeight))
-    {
-      boxY = sequencesHeight - boxHeight + 1;
-    }
-
-    if (boxX < 0)
-    {
-      boxX = 0;
-    }
-
-    if (boxX > (width - boxWidth))
-    {
-      if (av.hasHiddenColumns())
-      {
-        // Try smallest possible box
-        boxWidth = (int) ((av.endRes - av.startRes + 1) * av.getCharWidth() * scalew);
-      }
-      boxX = width - boxWidth;
-    }
-
-    int col = (int) (boxX / scalew / av.getCharWidth());
-    int row = (int) (boxY / scaleh / av.getCharHeight());
-
-    if (av.hasHiddenColumns())
-    {
-      if (!av.getColumnSelection().isVisible(col))
-      {
-        return;
-      }
-
-      col = av.getColumnSelection().findColumnPosition(col);
-    }
-
-    if (av.hasHiddenRows())
-    {
-      row = av.getAlignment().getHiddenSequences()
-              .findIndexWithoutHiddenSeqs(row);
-    }
-
-    ap.setScrollValues(col, row);
+    od.checkValid();
+    ap.setScrollValues(od.getScrollCol(), od.getScrollRow());
 
   }
 
   /**
-   * DOCUMENT ME!
+   * Updates the overview image when the related alignment panel is updated
    */
   public void updateOverviewImage()
   {
@@ -238,24 +160,17 @@ public class OverviewPanel extends JPanel implements Runnable
 
     if ((getWidth() > 0) && (getHeight() > 0))
     {
-      width = getWidth();
-      sequencesHeight = getHeight() - graphHeight;
+      od.setWidth(getWidth()); // width = getWidth();
+      od.setHeight(getHeight()); // sequencesHeight = getHeight() - graphHeight;
     }
 
-    setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));
+    setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
 
     Thread thread = new Thread(this);
     thread.start();
     repaint();
   }
 
-  // This is set true if the user resizes whilst
-  // the overview is being calculated
-  boolean resizeAgain = false;
-
-  /**
-   * DOCUMENT ME!
-   */
   @Override
   public void run()
   {
@@ -266,81 +181,114 @@ public class OverviewPanel extends JPanel implements Runnable
       fr.transferSettings(ap.getSeqPanel().seqCanvas.getFeatureRenderer());
     }
 
+    // why do we need to set preferred size again? was set in
+    // updateOverviewImage
+    setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
+
+    miniMe = new BufferedImage(od.getWidth(), od.getHeight(),
+            BufferedImage.TYPE_INT_RGB);
+
+    Graphics mg = miniMe.getGraphics();
+    mg.setColor(Color.orange);
+    mg.fillRect(0, 0, od.getWidth(), miniMe.getHeight());
+
+    // calculate scale based on current alignment width and height
+    od.updateScales();
+
+    // calculate sampleCol and sampleRow
+    // alignment width is max number of residues/bases
+    // alignment height is number of sequences
     int alwidth = av.getAlignment().getWidth();
     int alheight = av.getAlignment().getHeight()
             + av.getAlignment().getHiddenSequences().getSize();
 
-    setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));
+    // sampleCol or sampleRow is the width/height allocated to each residue
+    // in particular, sometimes we may need more than one row/col of the
+    // BufferedImage allocated
+    // sampleCol is how much of a residue to assign to each pixel
+    // sampleRow is how many sequences to assign to each pixel
+    float sampleCol = alwidth / (float) od.getWidth();
+    float sampleRow = alheight / (float) od.getSequencesHeight();
+
+    buildImage(sampleRow, sampleCol);
 
-    int fullsizeWidth = alwidth * av.getCharWidth();
-    int fullsizeHeight = alheight * av.getCharHeight();
+    if (av.getAlignmentConservationAnnotation() != null)
+    {
+      renderer.updateFromAlignViewport(av);
+      for (int col = 0; col < od.getWidth() && !resizeAgain; col++)
+      {
+        mg.translate(col, od.getSequencesHeight());
+        renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(),
+                av.getAlignmentConservationAnnotation().annotations,
+                (int) (sampleCol) + 1, od.getGraphHeight(),
+                (int) (col * sampleCol), (int) (col * sampleCol) + 1);
+        mg.translate(-col, -od.getSequencesHeight());
 
-    scalew = (float) width / (float) fullsizeWidth;
-    scaleh = (float) sequencesHeight / (float) fullsizeHeight;
+      }
+    }
+    System.gc();
 
-    miniMe = new BufferedImage(width, sequencesHeight + graphHeight,
-            BufferedImage.TYPE_INT_RGB);
+    resizing = false;
 
-    Graphics mg = miniMe.getGraphics();
-    mg.setColor(Color.orange);
-    mg.fillRect(0, 0, width, miniMe.getHeight());
+    if (resizeAgain)
+    {
+      resizeAgain = false;
+      updateOverviewImage();
+    }
+    else
+    {
+      lastMiniMe = miniMe;
+    }
 
-    float sampleCol = (float) alwidth / (float) width;
-    float sampleRow = (float) alheight / (float) sequencesHeight;
+    setBoxPosition();
+  }
 
-    int lastcol = -1, lastrow = -1;
+  private void buildImage(float sampleRow, float sampleCol)
+  {
+    int lastcol = -1;
+    int lastrow = -1;
     int color = Color.white.getRGB();
-    int row, col;
-    jalview.datamodel.SequenceI seq;
-    final boolean hasHiddenRows = av.hasHiddenRows(), hasHiddenCols = av
-            .hasHiddenColumns();
+
+    jalview.datamodel.SequenceI seq = null;
+    final boolean hasHiddenRows = av.hasHiddenRows();
+    final boolean hasHiddenCols = av.hasHiddenColumns();
     boolean hiddenRow = false;
     // get hidden row and hidden column map once at beginning.
     // clone featureRenderer settings to avoid race conditions... if state is
     // updated just need to refresh again
-    for (row = 0; row < sequencesHeight; row++)
+    for (int row = 0; row < od.getSequencesHeight() && !resizeAgain; row++)
     {
-      if (resizeAgain)
+      boolean doCopy = true;
+      int currentrow = (int) (row * sampleRow);
+      if (currentrow != lastrow)
       {
-        break;
-      }
-      if ((int) (row * sampleRow) == lastrow)
-      {
-        // No need to recalculate the colours,
-        // Just copy from the row above
-        for (col = 0; col < width; col++)
-        {
-          if (resizeAgain)
-          {
-            break;
-          }
-          miniMe.setRGB(col, row, miniMe.getRGB(col, row - 1));
-        }
-        continue;
-      }
+        doCopy = false;
 
-      lastrow = (int) (row * sampleRow);
+        lastrow = currentrow;
 
-      hiddenRow = false;
-      if (hasHiddenRows)
-      {
-        seq = av.getAlignment().getHiddenSequences()
-                .getHiddenSequence(lastrow);
-        if (seq == null)
+        hiddenRow = false;
+        // should be method(s) in Alignment
+        if (hasHiddenRows)
         {
-          int index = av.getAlignment().getHiddenSequences()
-                  .findIndexWithoutHiddenSeqs(lastrow);
+          seq = av.getAlignment().getHiddenSequences()
+                  .getHiddenSequence(lastrow);
+          if (seq == null)
+          {
+            int index = av.getAlignment().getHiddenSequences()
+                    .findIndexWithoutHiddenSeqs(lastrow);
 
-          seq = av.getAlignment().getSequenceAt(index);
+            seq = av.getAlignment().getSequenceAt(index);
+          }
+          else
+          {
+            hiddenRow = true;
+          }
         }
         else
         {
-          hiddenRow = true;
+          seq = av.getAlignment().getSequenceAt(lastrow);
         }
-      }
-      else
-      {
-        seq = av.getAlignment().getSequenceAt(lastrow);
+        // end of Alignment method(s)
       }
 
       if (seq == null)
@@ -349,144 +297,72 @@ public class OverviewPanel extends JPanel implements Runnable
         continue;
       }
 
-      for (col = 0; col < width; col++)
+      for (int col = 0; col < od.getWidth() && !resizeAgain; col++)
       {
-        if (resizeAgain)
-        {
-          break;
-        }
-        if ((int) (col * sampleCol) == lastcol
-                && (int) (row * sampleRow) == lastrow)
+        if (doCopy)
         {
-          miniMe.setRGB(col, row, color);
-          continue;
+          color = miniMe.getRGB(col, row - 1);
         }
-
-        lastcol = (int) (col * sampleCol);
-
-        if (seq.getLength() > lastcol)
-        {
-          color = sr.getResidueBoxColour(seq, lastcol).getRGB();
-
-          if (av.isShowSequenceFeatures())
-          {
-            color = fr.findFeatureColour(color, seq, lastcol);
-          }
-        }
-        else
-        {
-          color = -1; // White
-        }
-
-        if (hiddenRow
-                || (hasHiddenCols && !av.getColumnSelection().isVisible(
-                        lastcol)))
+        else if ((int) (col * sampleCol) != lastcol
+                || (int) (row * sampleRow) != lastrow)
         {
-          color = new Color(color).darker().darker().getRGB();
+          lastcol = (int) (col * sampleCol);
+          color = getColumnColourFromSequence(seq, hiddenRow, hasHiddenCols,
+                  lastcol);
         }
+        // else if ((int) (col * sampleCol) == lastcol && (int) (row *
+        // sampleRow) == lastrow))
+        // we just use the color we already have , so don't need to set it
 
         miniMe.setRGB(col, row, color);
-
       }
     }
+  }
 
-    if (av.getAlignmentConservationAnnotation() != null)
+  /*
+   * Find the colour of a sequence at a specified column position
+   */
+  private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq,
+          boolean hiddenRow, boolean hasHiddenCols, int lastcol)
+  {
+    int color;
+
+    if (seq.getLength() > lastcol)
     {
-      renderer.updateFromAlignViewport(av);
-      for (col = 0; col < width; col++)
+      color = sr.getResidueBoxColour(seq, lastcol).getRGB();
+
+      if (av.isShowSequenceFeatures())
       {
-        if (resizeAgain)
-        {
-          break;
-        }
-        lastcol = (int) (col * sampleCol);
-        {
-          mg.translate(col, sequencesHeight);
-          renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(),
-                  av.getAlignmentConservationAnnotation().annotations,
-                  (int) (sampleCol) + 1, graphHeight,
-                  (int) (col * sampleCol), (int) (col * sampleCol) + 1);
-          mg.translate(-col, -sequencesHeight);
-        }
+        color = fr.findFeatureColour(color, seq, lastcol);
       }
     }
-    System.gc();
-
-    resizing = false;
-
-    if (resizeAgain)
+    else
     {
-      resizeAgain = false;
-      updateOverviewImage();
+      color = Color.white.getRGB(); // White
     }
-    else
+
+    if (hiddenRow
+            || (hasHiddenCols && !av.getColumnSelection()
+                    .isVisible(lastcol)))
     {
-      lastMiniMe = miniMe;
+      color = new Color(color).darker().darker().getRGB();
     }
 
-    setBoxPosition();
+    return color;
   }
 
   /**
-   * DOCUMENT ME!
+   * Update the overview panel box when the associated alignment panel is
+   * changed
+   * 
    */
   public void setBoxPosition()
   {
-    int fullsizeWidth = av.getAlignment().getWidth() * av.getCharWidth();
-    int fullsizeHeight = (av.getAlignment().getHeight() + av.getAlignment()
-            .getHiddenSequences().getSize())
-            * av.getCharHeight();
-
-    int startRes = av.getStartRes();
-    int endRes = av.getEndRes();
-
-    if (av.hasHiddenColumns())
-    {
-      startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);
-      endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);
-    }
-
-    int startSeq = av.startSeq;
-    int endSeq = av.endSeq;
-
-    if (av.hasHiddenRows())
-    {
-      startSeq = av.getAlignment().getHiddenSequences()
-              .adjustForHiddenSeqs(startSeq);
-
-      endSeq = av.getAlignment().getHiddenSequences()
-              .adjustForHiddenSeqs(endSeq);
-
-    }
-
-    scalew = (float) width / (float) fullsizeWidth;
-    scaleh = (float) sequencesHeight / (float) fullsizeHeight;
-
-    boxX = (int) (startRes * av.getCharWidth() * scalew);
-    boxY = (int) (startSeq * av.getCharHeight() * scaleh);
-
-    if (av.hasHiddenColumns())
-    {
-      boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
-    }
-    else
-    {
-      boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
-    }
-
-    boxHeight = (int) ((endSeq - startSeq) * av.getCharHeight() * scaleh);
-
+    od.setBoxPosition();
     repaint();
   }
 
-  private BufferedImage lastMiniMe = null;
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param g
-   *          DOCUMENT ME!
-   */
   @Override
   public void paintComponent(Graphics g)
   {
@@ -501,7 +377,7 @@ public class OverviewPanel extends JPanel implements Runnable
       {
         g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
       }
-      g.setColor(new Color(100, 100, 100, 25));
+      g.setColor(TRANS_GREY);
       g.fillRect(0, 0, getWidth(), getHeight());
     }
     else if (lastMiniMe != null)
@@ -509,13 +385,15 @@ public class OverviewPanel extends JPanel implements Runnable
       g.drawImage(lastMiniMe, 0, 0, this);
       if (lastMiniMe != miniMe)
       {
-        g.setColor(new Color(100, 100, 100, 25));
+        g.setColor(TRANS_GREY);
         g.fillRect(0, 0, getWidth(), getHeight());
       }
     }
     // TODO: render selected regions
     g.setColor(Color.red);
-    g.drawRect(boxX, boxY, boxWidth, boxHeight);
-    g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
+    g.drawRect(od.getBoxX(), od.getBoxY(), od.getBoxWidth(),
+            od.getBoxHeight());
+    g.drawRect(od.getBoxX() + 1, od.getBoxY() + 1, od.getBoxWidth() - 2,
+            od.getBoxHeight() - 2);
   }
 }
index fecccb0..0891071 100644 (file)
@@ -2651,6 +2651,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     this.followHighlight = b;
   }
 
+  @Override
   public int getStartRes()
   {
     return startRes;
@@ -2662,6 +2663,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return endRes;
   }
 
+  @Override
   public int getStartSeq()
   {
     return startSeq;
@@ -2705,6 +2707,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     this.endSeq = seq;
   }
 
+  @Override
   public int getEndSeq()
   {
     return endSeq;
diff --git a/src/jalview/viewmodel/OverviewDimensions.java b/src/jalview/viewmodel/OverviewDimensions.java
new file mode 100644 (file)
index 0000000..17f56c9
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ *  
+ * 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/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.viewmodel;
+
+import jalview.api.AlignViewportI;
+
+public class OverviewDimensions
+{
+  // Default width and height values
+  private static final int DEFAULT_GRAPH_HEIGHT = 20;
+
+  private static final int MAX_WIDTH = 400;
+
+  private static final int MIN_WIDTH = 120;
+
+  private static final int MIN_SEQ_HEIGHT = 40;
+
+  private static final int MAX_SEQ_HEIGHT = 300;
+
+  private AlignViewportI av;
+
+  private float scalew = 1f;
+
+  private float scaleh = 1f;
+
+  // width of the overview panel
+  private int width;
+
+  // height of sequences part of the overview panel
+  private int sequencesHeight;
+
+  // height of the graphs part of the overview panel
+  private int graphHeight = DEFAULT_GRAPH_HEIGHT;
+
+  // dimensions of box outlining current extent of view in alignment panel
+  // location of left side of box
+  private int boxX = -1;
+
+  // location of bottom of box
+  private int boxY = -1;
+
+  // width of box
+  private int boxWidth = -1;
+
+  // height of box
+  private int boxHeight = -1;
+
+  private int scrollCol = -1;
+
+  private int scrollRow = -1;
+
+  public OverviewDimensions(AlignViewportI avi)
+  {
+    this.av = avi;
+
+    // scale the initial size of overviewpanel to shape of alignment
+    float initialScale = (float) av.getAlignment().getWidth()
+            / (float) av.getAlignment().getHeight();
+
+    // TODO: in applet this was getSequenceConsensusHash()
+    // check if it makes any functional difference
+    if (av.getAlignmentConservationAnnotation() == null)
+    {
+      graphHeight = 0;
+    }
+
+    if (av.getAlignment().getWidth() > av.getAlignment().getHeight())
+    {
+      // wider
+      width = MAX_WIDTH;
+      sequencesHeight = (int) (MAX_WIDTH / initialScale);
+      if (sequencesHeight < MIN_SEQ_HEIGHT)
+      {
+        sequencesHeight = MIN_SEQ_HEIGHT;
+      }
+    }
+    else
+    {
+      // taller
+      width = (int) (MAX_WIDTH * initialScale);
+      sequencesHeight = MAX_SEQ_HEIGHT;
+
+      if (width < MIN_WIDTH)
+      {
+        width = MIN_WIDTH;
+      }
+    }
+  }
+
+  /**
+   * Check box dimensions and scroll positions and correct if necessary
+   */
+  public void checkValid()
+  {
+    if (boxY < 0)
+    {
+      boxY = 0;
+    }
+
+    if (boxY > (sequencesHeight - boxHeight))
+    {
+      boxY = sequencesHeight - boxHeight + 1;
+    }
+
+    if (boxX < 0)
+    {
+      boxX = 0;
+    }
+
+    if (boxX > (width - boxWidth))
+    {
+      if (av.hasHiddenColumns())
+      {
+        // Try smallest possible box
+        boxWidth = (int) ((av.getEndRes() - av.getStartRes() + 1)
+                * av.getCharWidth() * scalew);
+      }
+      boxX = width - boxWidth;
+    }
+
+    scrollCol = (int) (boxX / scalew / av.getCharWidth());
+    scrollRow = (int) (boxY / scaleh / av.getCharHeight());
+
+    if (av.hasHiddenColumns())
+    {
+      if (!av.getColumnSelection().isVisible(scrollCol))
+      {
+        return;
+      }
+
+      scrollCol = av.getColumnSelection().findColumnPosition(scrollCol);
+    }
+
+    if (av.hasHiddenRows())
+    {
+      scrollRow = av.getAlignment().getHiddenSequences()
+              .findIndexWithoutHiddenSeqs(scrollRow);
+    }
+  }
+
+  /**
+   * Update the overview panel box when the associated alignment panel is
+   * changed
+   * 
+   */
+  public void setBoxPosition()
+  {
+    updateScales();
+
+    int startRes = av.getStartRes();
+    int endRes = av.getEndRes();
+
+    if (av.hasHiddenColumns())
+    {
+      startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);
+      endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);
+    }
+
+    int startSeq = av.getStartSeq();
+    int endSeq = av.getEndSeq();
+
+    if (av.hasHiddenRows())
+    {
+      startSeq = av.getAlignment().getHiddenSequences()
+              .adjustForHiddenSeqs(startSeq);
+
+      endSeq = av.getAlignment().getHiddenSequences()
+              .adjustForHiddenSeqs(endSeq);
+
+    }
+
+    boxX = (int) (startRes * av.getCharWidth() * scalew);
+    boxY = (int) (startSeq * av.getCharHeight() * scaleh);
+
+    if (av.hasHiddenColumns())
+    {
+      boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
+    }
+    else
+    {
+      boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
+    }
+
+    boxHeight = (int) ((endSeq - startSeq) * av.getCharHeight() * scaleh);
+  }
+
+  /**
+   * Update width and height scales in terms of the alignment width and height
+   */
+  public void updateScales()
+  {
+    int alwidth = av.getAlignment().getWidth();
+    int alheight = av.getAlignment().getHeight()
+            + av.getAlignment().getHiddenSequences().getSize();
+
+    int fullsizeWidth = alwidth * av.getCharWidth();
+    int fullsizeHeight = alheight * av.getCharHeight();
+
+    scalew = (float) width / fullsizeWidth;
+    scaleh = (float) sequencesHeight / fullsizeHeight;
+  }
+
+  // don't like this, scroll vals are separate from setting code
+  public int getScrollCol()
+  {
+    return scrollCol;
+  }
+
+  public int getScrollRow()
+  {
+    return scrollRow;
+  }
+
+  public int getBoxX()
+  {
+    return boxX;
+  }
+
+  public int getBoxY()
+  {
+    return boxY;
+  }
+
+  public int getBoxWidth()
+  {
+    return boxWidth;
+  }
+
+  public int getBoxHeight()
+  {
+    return boxHeight;
+  }
+
+  public void setBoxX(int x)
+  {
+    boxX = x;
+  }
+
+  public void setBoxY(int y)
+  {
+    boxY = y;
+  }
+
+  public void setWidth(int w)
+  {
+    width = w;
+  }
+
+  public void setHeight(int h)
+  {
+    sequencesHeight = h - graphHeight;
+  }
+
+  public int getWidth()
+  {
+    return width;
+  }
+
+  public int getHeight()
+  {
+    return sequencesHeight + graphHeight;
+  }
+
+  public int getSequencesHeight()
+  {
+    return sequencesHeight;
+  }
+
+  public int getGraphHeight()
+  {
+    return graphHeight;
+  }
+}