Merge branch 'develop' into update_212_Dec_merge_with_21125_chamges
[jalview.git] / src / jalview / gui / AlignmentPanel.java
index e84aae0..fd53faf 100644 (file)
@@ -41,6 +41,7 @@ import jalview.structure.StructureSelectionManager;
 import jalview.util.Comparison;
 import jalview.util.ImageMaker;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportListenerI;
 import jalview.viewmodel.ViewportRanges;
 
@@ -123,6 +124,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   public AlignmentPanel(AlignFrame af, final AlignViewport av)
   {
+    setName("AligmentPanel");
     // setBackground(Color.white); // BH 2019
     alignFrame = af;
     this.av = av;
@@ -174,6 +176,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
           ranges.setViewportWidth(widthInRes);
           ranges.setViewportHeight(heightInSeq);
         }
+        repaint();
       }
 
     });
@@ -184,10 +187,17 @@ public class AlignmentPanel extends GAlignmentPanel implements
       @Override
       public void propertyChange(PropertyChangeEvent evt)
       {
-        if (evt.getPropertyName().equals("alignment"))
-        {
+        switch (evt.getPropertyName()) {  
+        case AlignmentViewport.PROPERTY_SEQUENCE:
+          updateScrollBarsFromRanges();
+          if (annotationPanel != null)
+            annotationPanel.paintImmediately(0,  0, getWidth(), getHeight());
+          break;
+        case AlignmentViewport.PROPERTY_ALIGNMENT:
+          updateScrollBarsFromRanges();
           PaintRefresher.Refresh(ap, av.getSequenceSetId(), true, true);
           alignmentChanged();
+          break;
         }
       }
     };
@@ -260,32 +270,37 @@ public class AlignmentPanel extends GAlignmentPanel implements
     int oldWidth = av.getIdWidth();
 
     // calculate sensible default width when no preference is available
-    Dimension r = null;
+    Dimension d = null;
     if (av.getIdWidth() < 0)
     {
-      int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
-      int idWidth = Math.min(afwidth - 200, 2 * afwidth / 3);
-      int maxwidth = Math.max(IdwidthAdjuster.MIN_ID_WIDTH, idWidth);
-      r = calculateIdWidth(maxwidth);
-      av.setIdWidth(r.width);
+      int maxWidth = getMaxWidth();
+      d = calculateIdWidth(maxWidth);
+      av.setIdWidth(d.width);
     }
     else
     {
-      r = new Dimension();
-      r.width = av.getIdWidth();
-      r.height = 0;
+      d = new Dimension();
+      d.width = av.getIdWidth();
+      d.height = 0;
     }
 
     /*
      * fudge: if desired width has changed, update layout
      * (see also paintComponent - updates layout on a repaint)
      */
-    if (r.width != oldWidth)
+    if (d.width != oldWidth)
     {
-      idPanelHolder.setPreferredSize(r);
+      idPanelHolder.setPreferredSize(d);
       validate();
     }
-    return r;
+    return d;
+  }
+
+  public int getMaxWidth()
+  {
+    int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
+    int idWidth = Math.min(afwidth - 200, 2 * afwidth / 3);
+    return Math.max(IdwidthAdjuster.MIN_ID_WIDTH, idWidth);
   }
 
   /**
@@ -297,9 +312,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @return Dimension giving the maximum width of the alignment label panel
    *         that should be used.
    */
-  protected Dimension calculateIdWidth(int maxwidth)
+  public Dimension calculateIdWidth(int maxwidth)
   {
-    Container c = new Container();
+    Container c = this;// new Container();
 
     FontMetrics fm = c.getFontMetrics(
             new Font(av.font.getName(), Font.ITALIC, av.font.getSize()));
@@ -308,10 +323,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
     int i = 0;
     int idWidth = 0;
 
+    boolean withSuffix = av.getShowJVSuffix();
     while ((i < al.getHeight()) && (al.getSequenceAt(i) != null))
     {
       SequenceI s = al.getSequenceAt(i);
-      String id = s.getDisplayId(av.getShowJVSuffix());
+      String id = s.getDisplayId(withSuffix);
       int stringWidth = fm.stringWidth(id);
       idWidth = Math.max(idWidth, stringWidth);
       i++;
@@ -554,17 +570,24 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     // 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,
+    // not need to be reset, and most importantly, we can't be sure we are actually
+    // connected to resources. 
+    
+    // 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);
 
+    // BH no!!
     hscroll.addNotify();
     annotationScroller.setPreferredSize(
             new Dimension(annotationScroller.getWidth(), annotationHeight));
@@ -572,6 +595,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();
@@ -580,16 +604,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   /**
    * update alignment layout for viewport settings
-   * 
-   * @param wrap
-   *          DOCUMENT ME!
    */
   public void updateLayout()
   {
+    ViewportRanges ranges = av.getRanges();
     fontChanged();
     setAnnotationVisible(av.isShowAnnotation());
     boolean wrap = av.getWrapAlignment();
-    ViewportRanges ranges = av.getRanges();
     ranges.setStartSeq(0);
     scalePanelHolder.setVisible(!wrap);
     hscroll.setVisible(!wrap);
@@ -604,6 +625,22 @@ public class AlignmentPanel extends GAlignmentPanel implements
     {
       annotationScroller.setVisible(true);
       annotationSpaceFillerHolder.setVisible(true);
+    }
+
+    idSpaceFillerPanel1.setVisible(!wrap);
+
+    /*
+     * defer dimension calculations if panel not yet added to a Window
+     * BH 2020.06.09
+     */
+    if (getTopLevelAncestor() == null)
+    {
+      repaint();
+      return;
+    }
+
+    if (!wrap && av.isShowAnnotation())
+    {
       validateAnnotationDimensions(false);
     }
 
@@ -627,7 +664,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
     }
 
-    idSpaceFillerPanel1.setVisible(!wrap);
+    // System.out.println("ap dim = " + getSize());
+    // these values will go negative if getSize() returns (0,0):
+    // System.out.println("seqpan dim = " + getSeqPanel().getSize());
+    // System.out.println("seqcan dim = " + getSeqPanel().seqCanvas.getSize());
 
     repaint();
   }
@@ -641,10 +681,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
    *          visible row to scroll to
    * 
    */
-  public void setScrollValues(int xpos, int ypos)
+  public void setScrollValues(int x, int y)
   {
-    int x = xpos;
-    int y = ypos;
 
     if (av == null || av.getAlignment() == null)
     {
@@ -660,45 +698,23 @@ public class AlignmentPanel extends GAlignmentPanel implements
       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)
-      {
-        hextent = width;
-      }
-
-      if (vextent > height)
-      {
-        vextent = height;
-      }
-
-      if ((hextent + x) > width)
-      {
-        x = width - hextent;
-      }
-
-      if ((vextent + y) > height)
-      {
-        y = height - vextent;
-      }
-
-      if (y < 0)
-      {
-        y = 0;
-      }
-
-      if (x < 0)
-      {
-        x = 0;
-      }
-
-      // update the scroll values
-      hscroll.setValues(x, hextent, 0, width);
-      vscroll.setValues(y, vextent, 0, height);
+      
+      hextent = Math.min(getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(),  width);
+      vextent = Math.min(getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(),  height);
+  
+      x = Math.max(0, Math.min(x,  width - hextent));
+      y = Math.max(0, Math.min(y,  height - vextent));
+      
+      updateRanges(x, y);
+      updateScrollBars(x, y, width, height);
     }
   }
 
+  private void updateScrollBars(int x, int y, int width, int height) 
+  {
+    hscroll.setValues(x, hextent, 0, width);
+    vscroll.setValues(y, vextent, 0, height);
+  }
   /**
    * Respond to adjustment event when horizontal or vertical scrollbar is
    * changed
@@ -715,41 +731,55 @@ public class AlignmentPanel extends GAlignmentPanel implements
       return;
     }
 
-    ViewportRanges ranges = av.getRanges();
 
     if (evt.getSource() == hscroll)
     {
+      if (!updateRanges(hscroll.getValue(), Integer.MIN_VALUE))
+        return;
+    }
+    else if (evt.getSource() == vscroll)
+    {
+      if (!updateRanges(Integer.MIN_VALUE, vscroll.getValue()))
+        return;
+    }
+    repaint();
+  }
+
+  private boolean updateRanges(int x, int y)
+  {
+    ViewportRanges ranges = av.getRanges();
+    boolean isChanged = false;
+    if (x != Integer.MIN_VALUE)
+    {
       int oldX = ranges.getStartRes();
       int oldwidth = ranges.getViewportWidth();
-      int x = hscroll.getValue();
       int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
 
       // 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
-      if ((x == oldX) && (width == oldwidth))
+      if (width > 0 && (x != oldX || width != oldwidth))
       {
-        return;
+        ranges.setViewportStartAndWidth(x, width);
+        isChanged = true;
       }
-      ranges.setViewportStartAndWidth(x, width);
     }
-    else if (evt.getSource() == vscroll)
+    if (y != Integer.MIN_VALUE)
     {
       int oldY = ranges.getStartSeq();
       int oldheight = ranges.getViewportHeight();
-      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
-      if ((y == oldY) && (height == oldheight))
+      if (height > 0 && (y != oldY || height != oldheight))
       {
-        return;
+        ranges.setViewportStartAndHeight(y, height);
+        isChanged = true;
       }
-      ranges.setViewportStartAndHeight(y, height);
     }
-    repaint();
+    return isChanged;
   }
 
   /**
@@ -828,7 +858,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
             av.isShowAutocalculatedAbove());
     sorter.sort(getAlignment().getAlignmentAnnotation(),
             av.getSortAnnotationsBy());
-    repaint();
 
     if (updateStructures)
     {
@@ -837,16 +866,21 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (updateOverview)
     {
 
+      alignFrame.repaint();
       if (overviewPanel != null)
       {
         overviewPanel.updateOverviewImage();
       }
+    } else {
+      invalidate(); // needed so that the id width adjuster works correctly
+      repaint();
     }
   }
 
   @Override
   public void paintComponent(Graphics g)
   {
+    // BH OUCH!
     invalidate(); // needed so that the id width adjuster works correctly
 
     Dimension d = getIdPanel().getIdCanvas().getPreferredSize();
@@ -860,7 +894,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * though I still think this call should be elsewhere.
      */
     ViewportRanges ranges = av.getRanges();
-    setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
+    // setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
     super.paintComponent(g);
   }
 
@@ -1067,8 +1101,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public int printWrappedAlignment(int pageWidth, int pageHeight,
           int pageNumber, Graphics g) throws PrinterException
   {
-    getSeqPanel().seqCanvas.calculateWrappedGeometry(getWidth(),
-            getHeight());
+    getSeqPanel().seqCanvas.calculateWrappedGeometry();
     int annotationHeight = 0;
     if (av.isShowAnnotation())
     {
@@ -1542,6 +1575,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     } catch (Exception ex)
     {
     }
+
     if (b)
     {
       setAlignFrameView();
@@ -1707,10 +1741,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public void propertyChange(PropertyChangeEvent evt)
   {
     // update this panel's scroll values based on the new viewport ranges values
-    ViewportRanges ranges = av.getRanges();
-    int x = ranges.getStartRes();
-    int y = ranges.getStartSeq();
-    setScrollValues(x, y);
+    updateScrollBarsFromRanges();
 
     // now update any complementary alignment (its viewport ranges object
     // is different so does not get automatically updated)
@@ -1722,6 +1753,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
   }
 
+  void updateScrollBarsFromRanges()
+  {
+    ViewportRanges ranges = av.getRanges();
+    setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
+  }
   /**
    * Set the reference to the PCA/Tree chooser dialog for this panel. This
    * reference should be nulled when the dialog is closed.
@@ -1742,4 +1778,201 @@ public class AlignmentPanel extends GAlignmentPanel implements
     return calculationDialog;
   }
 
+  /**
+   * From appletgui, for JalviewJS JavaScript interface
+   * 
+   * preliminary - untested
+   * 
+   * @param ostart
+   * @param end
+   * @param seqIndex
+   * @param scrollToNearest
+   * @param redrawOverview
+   * @return
+   */
+  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;
+  }
+
+  private boolean holdRepaint = false;
+
+  /**
+   * Called by IdCanvas and SeqPanel to defer painting until after JVP loading.
+   * 
+   * @return true if holding
+   */
+  public boolean getHoldRepaint()
+  {
+    return holdRepaint;
+  }
+
+  /**
+   * Called by Jalview2xml while loading
+   * 
+   * @param tf
+   */
+  public void setHoldRepaint(boolean tf)
+  {
+    if (holdRepaint == tf)
+    {
+      return;
+    }
+    holdRepaint = tf;
+    if (!tf)
+    {
+      repaint();
+    }
+  }
+
+  @Override
+  public void repaint()
+  {
+    if (holdRepaint)
+    {
+      // System.out.println("AP repaint holding");
+      // Platform.stackTrace();
+      return;
+    }
+    super.repaint();
+  }
+
+  public void selectAllSequences()
+  {
+    selectSequences(av.getAlignment().getSequences());
+  }
+
+  public void deselectAllSequences()
+  {
+    if (av.cursorMode)
+    {
+      getSeqPanel().keyboardNo1 = null;
+      getSeqPanel().keyboardNo2 = null;
+    }
+    av.setSelectionGroup(null);
+    av.getColumnSelection().clear();
+    av.setSearchResults(null);
+    getIdPanel().getIdCanvas().searchResults = null;
+    av.sendSelection();
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    paintAlignment(false, false);
+    PaintRefresher.Refresh(this, av.getSequenceSetId());
+  }
+
+  public void selectSequences(List<SequenceI> seqs)
+  {
+    SequenceGroup sg = new SequenceGroup(seqs);
+    sg.setEndRes(av.getAlignment().getWidth() - 1);
+    av.setSelectionGroup(sg);
+    av.isSelectionGroupChanged(true);
+    av.sendSelection();
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    paintAlignment(false, false);
+    PaintRefresher.Refresh(this, av.getSequenceSetId());
+  }
+
 }