Bamboo test #13
[jalview.git] / src / jalview / gui / AlignmentPanel.java
index 4e5e72b..a553e65 100644 (file)
@@ -23,6 +23,7 @@ package jalview.gui;
 import jalview.analysis.AnnotationSorter;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.SequenceRenderer;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
 import jalview.datamodel.AlignmentI;
@@ -55,6 +56,7 @@ import java.awt.event.AdjustmentEvent;
 import java.awt.event.AdjustmentListener;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
+import java.awt.image.BufferedImage;
 import java.awt.print.PageFormat;
 import java.awt.print.Printable;
 import java.awt.print.PrinterException;
@@ -65,10 +67,17 @@ import java.io.FileWriter;
 import java.io.PrintWriter;
 import java.util.List;
 
-import javax.swing.SwingUtilities;
+import javax.swing.JScrollBar;
 
 /**
- * DOCUMENT ME!
+ * The main panel of an AlignFrame, containing holders for the IdPanel,
+ * SeqPanel, AnnotationLabels (a JPanel), and AnnotationPanel.
+ * 
+ * Additional holders contain an IdPanelWidthAdjuster space above the idPanel,
+ * AnnotationScroller (JScrollPane for AnnotationPanel), and vertical and
+ * horizontal scrollbars.
+ * 
+ * 
  * 
  * @author $author$
  * @version $Revision: 1.161 $
@@ -117,7 +126,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   public AlignmentPanel(AlignFrame af, final AlignViewport av)
   {
-//     setBackground(Color.white);  // BH 2019
+    setBackground(Color.white); // BH 2019
+    setOpaque(true);
     alignFrame = af;
     this.av = av;
     setSeqPanel(new SeqPanel(av, this));
@@ -134,7 +144,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     annotationScroller.setViewportView(getAnnotationPanel());
     annotationSpaceFillerHolder.add(getAlabels(), BorderLayout.CENTER);
-
+    if (!av.isShowAnnotation())
+    {
+      annotationScroller.setVisible(false);
+      annotationSpaceFillerHolder.setVisible(false);
+    }
     scalePanelHolder.add(getScalePanel(), BorderLayout.CENTER);
     seqPanelHolder.add(getSeqPanel(), BorderLayout.CENTER);
 
@@ -143,11 +157,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
     hscroll.addAdjustmentListener(this);
     vscroll.addAdjustmentListener(this);
 
+
     addComponentListener(new ComponentAdapter()
     {
       @Override
       public void componentResized(ComponentEvent evt)
       {
+        System.out.println("AlignmentPanel resized " + evt);
         // reset the viewport ranges when the alignment panel is resized
         // in particular, this initialises the end residue value when Jalview
         // is initialised
@@ -166,6 +182,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
                   / av.getCharHeight();
 
           ranges.setViewportWidth(widthInRes);
+          ViewportRanges.sTest += "AP.resize chht=" + av.getCharHeight()
+                  + "canvHt=" + getSeqPanel().seqCanvas.getHeight() + " "
+                  + heightInSeq + "\n";
           ranges.setViewportHeight(heightInSeq);
         }
       }
@@ -194,6 +213,46 @@ public class AlignmentPanel extends GAlignmentPanel implements
   }
 
   @Override
+  public void setBounds(int x, int y, int width, int height)
+  {
+    System.out.println("?AlignmentPanel.setBounds " + this.getX() + " "
+            + this.getY() + " " + this.getWidth() + " " + getHeight() + " "
+            + x + " " + y + " " + width + " " + height);
+    // BH 2020.03.23 when the Desktop changes its progress bar it re-lays out
+    // all
+    // its
+    // children, for unclear reasons. Maybe because they could be tiled?
+    if (x == this.getX() && y == this.getY() && width == this.getWidth()
+            && height == this.getHeight())
+    {
+      return;
+    }
+    System.out.println("!AlignmentPanel.setBounds " + this.getX() + " "
+            + this.getY() + " " + this.getWidth() + " " + getHeight() + " "
+            + x + " " + y + " " + width + " " + height);
+    super.setBounds(x, y, width, height);
+  }
+
+  // @Override
+  // public void reshape(int x, int y, int width, int height)
+  // {
+  // // BH 2020.03.23 when the Desktop changes its progress bar it relays out
+  // all its
+  // // children, for unclear reasons. Maybe because they could be tiled?
+  // if (x == this.getX() && y == this.getY() && width == this.getWidth()
+  // && height == this.getHeight())
+  // {
+  // return;
+  // }
+  //
+  // System.out.println("AlignmentPanel.reshape " + this.getX() + " "
+  // + this.getY() + " " + this.getWidth() + " " + getHeight() + " "
+  // + x
+  // + " " + y + " " + width + " " + height);
+  // super.reshape(x, y, width, height);
+  // }
+
+  @Override
   public AlignViewportI getAlignViewport()
   {
     return av;
@@ -543,6 +602,15 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   protected void validateAnnotationDimensions(boolean adjustPanelHeight)
   {
+    // BH 2018.04.18 comment: addNotify() is not appropriate here. We
+    // are not changing ancestors, and keyboard action listeners do
+    // not need to be reset. addNotify() is a very expensive operation,
+    // requiring a full re-layout of all parents and children.
+    // Note in JComponent:
+    // This method is called by the toolkit internally and should
+    // not be called directly by programs.
+    // I note that addNotify() is called in several areas of Jalview.
+
     int annotationHeight = getAnnotationPanel().adjustPanelHeight();
     annotationHeight = getAnnotationPanel()
             .adjustForAlignFrame(adjustPanelHeight, annotationHeight);
@@ -554,6 +622,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     Dimension e = idPanel.getSize();
     alabels.setSize(new Dimension(e.width, annotationHeight));
 
+
     annotationSpaceFillerHolder.setPreferredSize(new Dimension(
             annotationSpaceFillerHolder.getWidth(), annotationHeight));
     annotationScroller.validate();
@@ -568,7 +637,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   public void updateLayout()
   {
-    fontChanged();
+    fontChanged(); // fires repaint
     setAnnotationVisible(av.isShowAnnotation());
     boolean wrap = av.getWrapAlignment();
     ViewportRanges ranges = av.getRanges();
@@ -621,64 +690,87 @@ public class AlignmentPanel extends GAlignmentPanel implements
    *          visible column to scroll to
    * @param y
    *          visible row to scroll to
+   *          
+   * @return true if scrollbars will fire property changes
    * 
    */
-  public void setScrollValues(int xpos, int ypos)
+  public boolean setScrollValues(int xpos, int ypos)
   {
     int x = xpos;
     int y = ypos;
 
     if (av == null || av.getAlignment() == null)
     {
-      return;
+      return false;
     }
 
     if (av.getWrapAlignment())
     {
-      setScrollingForWrappedPanel(x);
+      return setScrollingForWrappedPanel(x);
     }
-    else
+    int width = av.getAlignment().getVisibleWidth();
+    int height = av.getAlignment().getHeight();
+
+    hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
+    vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
+
+    if (hextent > width)
     {
-      int width = av.getAlignment().getVisibleWidth();
-      int height = av.getAlignment().getHeight();
+      hextent = width;
+    }
 
-      hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
-      vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
+    if (vextent > height)
+    {
+      vextent = height;
+    }
 
-      if (hextent > width)
-      {
-        hextent = width;
-      }
+    if ((hextent + x) > width)
+    {
+      x = width - hextent;
+    }
 
-      if (vextent > height)
-      {
-        vextent = height;
-      }
+    if ((vextent + y) > height)
+    {
+      y = height - vextent;
+    }
 
-      if ((hextent + x) > width)
-      {
-        x = width - hextent;
-      }
+    if (y < 0)
+    {
+      y = 0;
+    }
 
-      if ((vextent + y) > height)
-      {
-        y = height - vextent;
-      }
+    if (x < 0)
+    {
+      x = 0;
+    }
 
-      if (y < 0)
-      {
-        y = 0;
-      }
+    // update the scroll values and return true if they changed
 
-      if (x < 0)
-      {
-        x = 0;
-      }
+    return (setIfChanged(hscroll, x, hextent, 0, width)
+            + setIfChanged(vscroll, y, vextent, 0, height) != 0);
+
+  }
 
-      // update the scroll values
-      hscroll.setValues(x, hextent, 0, width);
-      vscroll.setValues(y, vextent, 0, height);
+  /**
+   * Update a horizontal or vertical scrollbar and indicate if it is actually changed
+   * (and so will be firing an AdjustmentChangedEvent)
+   * @param sb
+   * @param val
+   * @param extent
+   * @param min
+   * @praam max
+   * @return 1 if JScrollBar is changed; 0 if not
+   */
+  private int setIfChanged(JScrollBar sb, int val, int extent, int min,
+          int max)
+  {
+    if (sb.getValue() == val && sb.getModel().getExtent() == extent
+            && sb.getMinimum() == min && sb.getMaximum() == max)
+    {
+      return 0;
     }
+    sb.setValues(val, extent, min, max);
+    return 1;
   }
 
   /**
@@ -722,6 +814,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       int y = vscroll.getValue();
       int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
 
+
       // if we're scrolling to the position we're already at, stop
       // this prevents infinite recursion of events when the scroll/viewport
       // ranges values are the same
@@ -729,6 +822,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
       {
         return;
       }
+
+      ViewportRanges.sTest += "AP.adjvalChanged chht=" + av.getCharHeight()
+              + "canvHt=" + getSeqPanel().seqCanvas.getHeight() + " newHt="
+              + height + "\n";
+
       ranges.setViewportStartAndHeight(y, height);
     }
     repaint();
@@ -778,7 +876,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       // This is only called if file loaded is a jar file that
       // was wrapped when saved and user has wrap alignment true
       // as preference setting
-      SwingUtilities.invokeLater(new Runnable()
+      Jalview.execRunnable(new Runnable()
       {
         @Override
         public void run()
@@ -810,12 +908,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
             av.isShowAutocalculatedAbove());
     sorter.sort(getAlignment().getAlignmentAnnotation(),
             av.getSortAnnotationsBy());
-    // BH 2019.04.18 this should not be necessary, but
-    // there is something wrong with the fast painting
-    // in that there is a -1 shift that should not be there.
-    // It is being covered in Java by the Consensus and Conservation
-    // threads forcing a full repaint after pasting.
-    seqPanel.seqCanvas.clearFastPaint();
     repaint();
 
     if (updateStructures)
@@ -824,7 +916,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
     if (updateOverview)
     {
-
       if (overviewPanel != null)
       {
         overviewPanel.updateOverviewImage();
@@ -832,6 +923,20 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
   }
 
+  @Override
+  public void invalidate()
+  {
+    System.out.println("AlignmentPanel invalidate");
+    super.invalidate();
+  }
+
+  @Override
+  public void validate()
+  {
+    System.out.println("AlignmentPanel validate");
+    super.validate();
+  }
+
   /**
    * DOCUMENT ME!
    * 
@@ -841,6 +946,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
   @Override
   public void paintComponent(Graphics g)
   {
+
+    System.out.println("AlignmentPanel paintComponent");
+
     invalidate(); // needed so that the id width adjuster works correctly
 
     Dimension d = getIdPanel().getIdCanvas().getPreferredSize();
@@ -854,7 +962,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * though I still think this call should be elsewhere.
      */
     ViewportRanges ranges = av.getRanges();
-    setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
+    if (!setScrollValues(ranges.getStartRes(), ranges.getStartSeq()))
+    {
+      // super.paintComponent(g);
+    }
     super.paintComponent(g);
   }
 
@@ -865,7 +976,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @param topLeftColumn
    *          the column position at top left (0..)
    */
-  private void setScrollingForWrappedPanel(int topLeftColumn)
+  private boolean setScrollingForWrappedPanel(int topLeftColumn)
   {
     ViewportRanges ranges = av.getRanges();
     int scrollPosition = ranges.getWrappedScrollPosition(topLeftColumn);
@@ -876,7 +987,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * so we add extent (1) to the maxScroll value
      */
     vscroll.setUnitIncrement(1);
-    vscroll.setValues(scrollPosition, 1, 0, maxScroll + 1);
+    return (setIfChanged(vscroll, scrollPosition, 1, 0,
+            maxScroll + 1) != 0);
   }
 
   /**
@@ -1061,6 +1173,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public int printWrappedAlignment(int pageWidth, int pageHeight, int pageNumber,
           Graphics g) throws PrinterException
   {
+
     int annotationHeight = 0;
     if (av.isShowAnnotation())
     {
@@ -1085,6 +1198,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     int totalHeight = cHeight * (maxwidth / resWidth + 1);
 
+    g = g.create();
+
     g.setColor(Color.white);
     g.fillRect(0, 0, pageWidth, pageHeight);
     g.setFont(av.getFont());
@@ -1098,7 +1213,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
      */
     g.translate(0, -pageNumber * pageHeight);
 
-    g.setClip(0, pageNumber * pageHeight, pageWidth, pageHeight);
+    // BH 2020.03.19 avoiding g.setClip
+    g.clipRect(0, pageNumber * pageHeight, pageWidth, pageHeight);
 
     /*
      * draw sequence ids and annotation labels (if shown)
@@ -1111,6 +1227,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     getSeqPanel().seqCanvas.drawWrappedPanelForPrinting(g, pageWidth - idWidth,
             totalHeight, 0);
 
+    g.dispose();
     if ((pageNumber * pageHeight) < totalHeight)
     {
       return Printable.PAGE_EXISTS;
@@ -1144,13 +1261,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public int getVisibleIdWidth(boolean onscreen)
   {
     // see if rendering offscreen - check preferences and calc width accordingly
-    if (!onscreen && Cache.getDefault("FIGURE_AUTOIDWIDTH", false))
+    if (!onscreen && Cache.getDefault(Preferences.FIGURE_AUTOIDWIDTH, false))
     {
       return calculateIdWidth(-1).width + 4;
     }
     Integer idwidth = null;
     if (onscreen || (idwidth = Cache
-            .getIntegerProperty("FIGURE_FIXEDIDWIDTH")) == null)
+            .getIntegerProperty(Preferences.FIGURE_FIXEDIDWIDTH)) == null)
     {
       int w = getIdPanel().getWidth();
       return (w > 0 ? w : calculateIdWidth().width + 4);
@@ -1372,8 +1489,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     int seqPanelWidth = getSeqPanel().seqCanvas.getWidth();
 
-    if (System.getProperty("java.awt.headless") != null
-            && System.getProperty("java.awt.headless").equals("true"))
+    if (Jalview.isHeadlessMode())
     {
       seqPanelWidth = alignFrame.getWidth() - getVisibleIdWidth()
               - vscroll.getPreferredSize().width
@@ -1719,4 +1835,154 @@ public class AlignmentPanel extends GAlignmentPanel implements
     return calculationDialog;
   }
 
+  @Override
+  public SequenceRenderer getSequenceRenderer()
+  {
+    return seqPanel.seqCanvas.getSequenceRenderer();
+  }
+
+  public boolean scrollTo(int ostart, int end, int seqIndex,
+          boolean scrollToNearest, boolean redrawOverview)
+  {
+    int startv, endv, starts, ends;// , width;
+
+    int start = -1;
+    if (av.hasHiddenColumns())
+    {
+      AlignmentI al = av.getAlignment();
+      start = al.getHiddenColumns().absoluteToVisibleColumn(ostart);
+      end = al.getHiddenColumns().absoluteToVisibleColumn(end);
+      if (start == end)
+      {
+        if (!scrollToNearest && !al.getHiddenColumns().isVisible(ostart))
+        {
+          // don't scroll - position isn't visible
+          return false;
+        }
+      }
+    }
+    else
+    {
+      start = ostart;
+    }
+
+    ViewportRanges ranges = av.getRanges();
+    if (!av.getWrapAlignment())
+    {
+      /*
+       * int spos=av.getStartRes(),sqpos=av.getStartSeq(); if ((startv =
+       * av.getStartRes()) >= start) { spos=start-1; // seqIn //
+       * setScrollValues(start - 1, seqIndex); } else if ((endv =
+       * av.getEndRes()) <= end) { // setScrollValues(spos=startv + 1 + end -
+       * endv, seqIndex); spos=startv + 1 + end - endv; } else if ((starts =
+       * av.getStartSeq()) > seqIndex) { setScrollValues(av.getStartRes(),
+       * seqIndex); } else if ((ends = av.getEndSeq()) <= seqIndex) {
+       * setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1); }
+       */
+
+      // 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 = ranges.getStartRes()) >= start)
+      {
+        /*
+         * Scroll left to make start of search results visible
+         */
+        setScrollValues(start - 1, seqIndex);
+      }
+      else if ((endv = ranges.getEndRes()) <= end)
+      {
+        /*
+         * Scroll right to make end of search results visible
+         */
+        setScrollValues(startv + 1 + end - endv, seqIndex);
+      }
+      else if ((starts = ranges.getStartSeq()) > seqIndex)
+      {
+        /*
+         * Scroll up to make start of search results visible
+         */
+        setScrollValues(ranges.getStartRes(), seqIndex);
+      }
+      else if ((ends = ranges.getEndSeq()) <= seqIndex)
+      {
+        /*
+         * Scroll down to make end of search results visible
+         */
+        setScrollValues(ranges.getStartRes(), starts + seqIndex - ends + 1);
+      }
+      /*
+       * Else results are already visible - no need to scroll
+       */
+    }
+    else
+    {
+      ranges.scrollToWrappedVisible(start);
+    }
+
+    paintAlignment(redrawOverview, false);
+    return true;
+  }
+
+  @Override
+  public void overviewDone(BufferedImage miniMe)
+  {
+    overviewPanel.canvas.finalizeDraw(miniMe);
+  }
+
+
+  private boolean holdRepaint = false;
+
+  public boolean getHoldRepaint()
+  {
+    return holdRepaint;
+  }
+
+  public void setHoldRepaint(boolean b)
+  {
+    if (holdRepaint == b)
+    {
+      return;
+    }
+    holdRepaint = b;
+    if (!b)
+    {
+      repaint();
+    }
+  }
+
+  @Override
+  public void repaint()
+  {
+    if (holdRepaint)
+    {
+      // System.out.println("AP repaint holding");
+      // Platform.stackTrace();
+      return;
+    }
+    super.repaint();
+  }
+
 }