JAL-3026 major update 1/2
[jalview.git] / src / jalview / gui / AlignmentPanel.java
index 9b74007..58aa645 100644 (file)
@@ -24,18 +24,21 @@ import jalview.analysis.AnnotationSorter;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
+import jalview.bin.Jalview;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.gui.ImageExporter.ImageWriterI;
 import jalview.io.HTMLOutput;
 import jalview.jbgui.GAlignmentPanel;
 import jalview.math.AlignmentDimension;
 import jalview.schemes.ResidueProperties;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.Comparison;
+import jalview.util.ImageMaker;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.viewmodel.ViewportListenerI;
@@ -48,7 +51,6 @@ import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
-import java.awt.Insets;
 import java.awt.event.AdjustmentEvent;
 import java.awt.event.AdjustmentListener;
 import java.awt.event.ComponentAdapter;
@@ -82,11 +84,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   private IdPanel idPanel;
 
-  private boolean headless;
-
   IdwidthAdjuster idwidthAdjuster;
 
-  /** DOCUMENT ME!! */
   public AlignFrame alignFrame;
 
   private ScalePanel scalePanel;
@@ -95,9 +94,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   private AnnotationLabels alabels;
 
-  // this value is set false when selection area being dragged
-  boolean fastPaint = true;
-
   private int hextent = 0;
 
   private int vextent = 0;
@@ -212,7 +208,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     alignFrame.updateEditMenuBar();
 
-    paintAlignment(true);
+    // no idea if we need to update structure
+    paintAlignment(true, true);
 
   }
 
@@ -241,11 +238,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
     getIdPanel().getIdCanvas().setPreferredSize(d);
     hscrollFillerPanel.setPreferredSize(d);
 
-    if (this.alignFrame.getSplitViewContainer() != null)
-    {
-      ((SplitFrame) this.alignFrame.getSplitViewContainer()).adjustLayout();
-    }
-
     repaint();
   }
 
@@ -343,9 +335,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     boolean scrolled = scrollToPosition(results, 0, true, false);
 
-    boolean noFastPaint = scrolled && av.getWrapAlignment();
+    boolean fastPaint = !(scrolled && av.getWrapAlignment());
 
-    getSeqPanel().seqCanvas.highlightSearchResults(results, noFastPaint);
+    getSeqPanel().seqCanvas.highlightSearchResults(results, fastPaint);
   }
 
   /**
@@ -426,8 +418,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (av.hasHiddenColumns())
     {
       HiddenColumns hidden = av.getAlignment().getHiddenColumns();
-      start = hidden.findColumnPosition(start);
-      end = hidden.findColumnPosition(end);
+      start = hidden.absoluteToVisibleColumn(start);
+      end = hidden.absoluteToVisibleColumn(end);
       if (start == end)
       {
         if (!hidden.isVisible(r[0]))
@@ -485,7 +477,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       scrollNeeded = ranges.scrollToWrappedVisible(start);
     }
 
-    paintAlignment(redrawOverview);
+    paintAlignment(redrawOverview, false);
 
     return scrollNeeded;
   }
@@ -542,7 +534,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
     validateAnnotationDimensions(true);
     addNotify();
-    paintAlignment(true);
+    // TODO: many places call this method and also paintAlignment with various
+    // different settings. this means multiple redraws are triggered...
+    paintAlignment(true, false);
   }
 
   /**
@@ -561,13 +555,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
       /*
        * Estimate available height in the AlignFrame for alignment +
        * annotations. Deduct an estimate for title bar, menu bar, scale panel,
-       * hscroll, status bar (as these are not laid out we can't inspect their
-       * actual heights). Insets gives frame borders.
+       * hscroll, status bar, insets. 
        */
-      int stuff = Platform.isAMac() ? 80 : 100;
-      Insets insets = alignFrame.getInsets();
-      int availableHeight = alignFrame.getHeight() - stuff - insets.top
-              - insets.bottom;
+      int stuff = Platform.isAMac() ? 120 : 140;
+      int availableHeight = alignFrame.getHeight() - stuff;
 
       /*
        * If not enough vertical space, maximize annotation height while keeping
@@ -637,9 +628,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
       else
       {
-        int widthInRes = (canvasWidth / av.getCharWidth()) - 1;
+        int widthInRes = (canvasWidth / av.getCharWidth());
         int heightInSeq = (getSeqPanel().seqCanvas.getHeight()
-                / av.getCharHeight()) - 1;
+                / av.getCharHeight());
 
         ranges.setViewportWidth(widthInRes);
         ranges.setViewportHeight(heightInSeq);
@@ -683,7 +674,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       {
         // reset the width to exclude hidden columns
         width = av.getAlignment().getHiddenColumns()
-                .findColumnPosition(width);
+                .absoluteToVisibleColumn(width);
       }
 
       hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
@@ -775,10 +766,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
       ranges.setViewportStartAndHeight(y, height);
     }
-    if (!fastPaint)
-    {
-      repaint();
-    }
+    repaint();
   }
 
   /**
@@ -846,12 +834,12 @@ public class AlignmentPanel extends GAlignmentPanel implements
     repaint();
   }
 
-  /**
-   * Repaint the alignment including the annotations and overview panels (if
-   * shown).
+  /* (non-Javadoc)
+   * @see jalview.api.AlignmentViewPanel#paintAlignment(boolean)
    */
   @Override
-  public void paintAlignment(boolean updateOverview)
+  public void paintAlignment(boolean updateOverview,
+          boolean updateStructures)
   {
     final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
             av.isShowAutocalculatedAbove());
@@ -859,10 +847,12 @@ public class AlignmentPanel extends GAlignmentPanel implements
             av.getSortAnnotationsBy());
     repaint();
 
-    if (updateOverview)
+    if (updateStructures)
     {
-      // TODO: determine if this paintAlignment changed structure colours
       av.getStructureSelectionManager().sequenceColoursChanged(this);
+    }
+    if (updateOverview)
+    {
 
       if (overviewPanel != null)
       {
@@ -880,15 +870,17 @@ public class AlignmentPanel extends GAlignmentPanel implements
   @Override
   public void paintComponent(Graphics g)
   {
-    invalidate();
+    invalidate(); // needed so that the id width adjuster works correctly
 
     Dimension d = getIdPanel().getIdCanvas().getPreferredSize();
     idPanelHolder.setPreferredSize(d);
     hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12));
-    validate();
+
+    validate(); // needed so that the id width adjuster works correctly
 
     /*
-     * set scroll bar positions
+     * set scroll bar positions - tried to remove but necessary for split panel to resize correctly
+     * though I still think this call should be elsewhere.
      */
     ViewportRanges ranges = av.getRanges();
     setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
@@ -1180,7 +1172,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
+              .absoluteToVisibleColumn(maxwidth) - 1;
     }
 
     int resWidth = getSeqPanel().seqCanvas
@@ -1286,93 +1278,59 @@ public class AlignmentPanel extends GAlignmentPanel implements
     return idwidth.intValue() + 4;
   }
 
-  void makeAlignmentImage(jalview.util.ImageMaker.TYPE type, File file)
+  /**
+   * Builds an image of the alignment of the specified type (EPS/PNG/SVG) and
+   * writes it to the specified file
+   * 
+   * @param type
+   * @param file
+   */
+  void makeAlignmentImage(ImageMaker.TYPE type, File file)
   {
-    int boarderBottomOffset = 5;
-    long pSessionId = System.currentTimeMillis();
-    headless = (System.getProperty("java.awt.headless") != null
-            && System.getProperty("java.awt.headless").equals("true"));
-    if (alignFrame != null && !headless)
-    {
-      if (file != null)
-      {
-        alignFrame.setProgressBar(MessageManager
-                .formatMessage("status.saving_file", new Object[]
-                { type.getLabel() }), pSessionId);
-      }
-    }
-    try
+    final int borderBottomOffset = 5;
+
+    AlignmentDimension aDimension = getAlignmentDimension();
+    // todo use a lambda function in place of callback here?
+    ImageWriterI writer = new ImageWriterI()
     {
-      AlignmentDimension aDimension = getAlignmentDimension();
-      try
+      @Override
+      public void exportImage(Graphics graphics) throws Exception
       {
-        jalview.util.ImageMaker im;
-        final String imageAction, imageTitle;
-        if (type == jalview.util.ImageMaker.TYPE.PNG)
-        {
-          imageAction = "Create PNG image from alignment";
-          imageTitle = null;
-        }
-        else if (type == jalview.util.ImageMaker.TYPE.EPS)
-        {
-          imageAction = "Create EPS file from alignment";
-          imageTitle = alignFrame.getTitle();
-        }
-        else
-        {
-          imageAction = "Create SVG file from alignment";
-          imageTitle = alignFrame.getTitle();
-        }
-
-        im = new jalview.util.ImageMaker(this, type, imageAction,
-                aDimension.getWidth(),
-                aDimension.getHeight() + boarderBottomOffset, file,
-                imageTitle, alignFrame, pSessionId, headless);
-        Graphics graphics = im.getGraphics();
         if (av.getWrapAlignment())
         {
-          if (graphics != null)
-          {
-            printWrappedAlignment(aDimension.getWidth(),
-                    aDimension.getHeight() + boarderBottomOffset, 0,
-                    graphics);
-            im.writeImage();
-          }
+          printWrappedAlignment(aDimension.getWidth(),
+                  aDimension.getHeight() + borderBottomOffset, 0, graphics);
         }
         else
         {
-          if (graphics != null)
-          {
-            printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
-                    graphics, graphics);
-            im.writeImage();
-          }
+          printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
+                  graphics, graphics);
         }
-
-      } catch (OutOfMemoryError err)
-      {
-        // Be noisy here.
-        System.out.println("########################\n" + "OUT OF MEMORY "
-                + file + "\n" + "########################");
-        new OOMWarning("Creating Image for " + file, err);
-        // System.out.println("Create IMAGE: " + err);
-      } catch (Exception ex)
-      {
-        ex.printStackTrace();
       }
-    } finally
-    {
+    };
 
-    }
+    String fileTitle = alignFrame.getTitle();
+    ImageExporter exporter = new ImageExporter(writer, alignFrame, type,
+            fileTitle);
+    int imageWidth = aDimension.getWidth();
+    int imageHeight = aDimension.getHeight() + borderBottomOffset;
+    String of = MessageManager.getString("label.alignment");
+    exporter.doExport(file, this, imageWidth, imageHeight, of);
   }
 
+  /**
+   * Calculates and returns a suitable width and height (in pixels) for an
+   * exported image
+   * 
+   * @return
+   */
   public AlignmentDimension getAlignmentDimension()
   {
     int maxwidth = av.getAlignment().getWidth();
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth);
+              .absoluteToVisibleColumn(maxwidth);
     }
 
     int height = ((av.getAlignment().getHeight() + 1) * av.getCharHeight())
@@ -1382,7 +1340,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (av.getWrapAlignment())
     {
       height = getWrappedHeight();
-      if (headless)
+      if (Jalview.isHeadlessMode())
       {
         // need to obtain default alignment width and then add in any
         // additional allowance for id margin
@@ -1406,27 +1364,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   }
 
-  /**
-   * DOCUMENT ME!
-   */
-  public void makeEPS(File epsFile)
-  {
-    makeAlignmentImage(jalview.util.ImageMaker.TYPE.EPS, epsFile);
-  }
-
-  /**
-   * DOCUMENT ME!
-   */
-  public void makePNG(File pngFile)
-  {
-    makeAlignmentImage(jalview.util.ImageMaker.TYPE.PNG, pngFile);
-  }
-
-  public void makeSVG(File svgFile)
-  {
-    makeAlignmentImage(jalview.util.ImageMaker.TYPE.SVG, svgFile);
-  }
-
   public void makePNGImageMap(File imgMapFile, String imageName)
   {
     // /////ONLY WORKS WITH NON WRAPPED ALIGNMENTS
@@ -1590,7 +1527,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
+              .absoluteToVisibleColumn(maxwidth) - 1;
     }
 
     int height = ((maxwidth / chunkWidth) + 1) * cHeight;
@@ -1616,13 +1553,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (annotationPanel != null)
     {
       annotationPanel.dispose();
+      annotationPanel = null;
     }
 
     if (av != null)
     {
       av.removePropertyChangeListener(propertyChangeListener);
-      jalview.structure.StructureSelectionManager ssm = av
-              .getStructureSelectionManager();
+      propertyChangeListener = null;
+      StructureSelectionManager ssm = av.getStructureSelectionManager();
       ssm.removeStructureViewerListener(getSeqPanel(), null);
       ssm.removeSelectionListener(getSeqPanel());
       ssm.removeCommandListener(av);
@@ -1645,9 +1583,15 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   protected void closeChildFrames()
   {
+    if (overviewPanel != null)
+    {
+      overviewPanel.dispose();
+      overviewPanel = null;
+    }
     if (calculationDialog != null)
     {
       calculationDialog.closeFrame();
+      calculationDialog = null;
     }
   }
 
@@ -1812,35 +1756,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   protected void scrollToCentre(SearchResultsI sr, int verticalOffset)
   {
-    /*
-     * To avoid jumpy vertical scrolling (if some sequences are gapped or not
-     * mapped), we can make the scroll-to location a sequence above the one
-     * actually mapped.
-     */
-    SequenceI mappedTo = sr.getResults().get(0).getSequence();
-    List<SequenceI> seqs = av.getAlignment().getSequences();
-
-    /*
-     * This is like AlignmentI.findIndex(seq) but here we are matching the
-     * dataset sequence not the aligned sequence
-     */
-    boolean matched = false;
-    for (SequenceI seq : seqs)
-    {
-      if (mappedTo == seq.getDatasetSequence())
-      {
-        matched = true;
-        break;
-      }
-    }
-    if (!matched)
-    {
-      return; // failsafe, shouldn't happen
-    }
-
-    /*
-     * Scroll to position but centring the target residue.
-     */
     scrollToPosition(sr, verticalOffset, true, true);
   }
 
@@ -1877,7 +1792,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (adjustHeight)
     {
       // sort, repaint, update overview
-      paintAlignment(true);
+      paintAlignment(true, false);
     }
     else
     {