Merge branch 'develop' into spike/JAL-4047/JAL-4048_columns_in_sequenceID
[jalview.git] / src / jalview / gui / AlignmentPanel.java
index 2d057bb..ce87c01 100644 (file)
  */
 package jalview.gui;
 
-import jalview.analysis.AnnotationSorter;
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.bin.Cache;
-import jalview.bin.Console;
-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.viewmodel.ViewportListenerI;
-import jalview.viewmodel.ViewportRanges;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Container;
@@ -68,6 +44,33 @@ import java.util.List;
 
 import javax.swing.SwingUtilities;
 
+import jalview.analysis.AnnotationSorter;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.bin.Jalview;
+import jalview.datamodel.AlignmentAnnotation;
+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.io.exceptions.ImageOutputException;
+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.imagemaker.BitmapImageSizing;
+import jalview.viewmodel.ViewportListenerI;
+import jalview.viewmodel.ViewportRanges;
+
 /**
  * DOCUMENT ME!
  * 
@@ -229,8 +232,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
     // set idCanvas bufferedImage to null
     // to prevent drawing old image
     FontMetrics fm = getFontMetrics(av.getFont());
-    
-    // update the flag controlling whether the grid is too small to render the font
+
+    // update the flag controlling whether the grid is too small to render the
+    // font
     av.validCharWidth = fm.charWidth('M') <= av.getCharWidth();
 
     scalePanelHolder.setPreferredSize(
@@ -266,10 +270,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     Dimension r = 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);
+      r = calculateDefaultAlignmentIdWidth();
       av.setIdWidth(r.width);
     }
     else
@@ -291,6 +292,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
     return r;
   }
 
+  public Dimension calculateDefaultAlignmentIdWidth()
+  {
+    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);
+    return calculateIdWidth(-1, false, false);
+  }
+
   /**
    * Calculate the width of the alignment labels based on the displayed names
    * and any bounds on label width set in preferences.
@@ -302,6 +311,12 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   protected Dimension calculateIdWidth(int maxwidth)
   {
+    return calculateIdWidth(maxwidth, true, false);
+  }
+
+  public Dimension calculateIdWidth(int maxwidth,
+          boolean includeAnnotations, boolean visibleOnly)
+  {
     Container c = new Container();
 
     FontMetrics fm = c.getFontMetrics(
@@ -321,18 +336,29 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
 
     // Also check annotation label widths
-    i = 0;
-
-    if (al.getAlignmentAnnotation() != null)
+    if (includeAnnotations && al.getAlignmentAnnotation() != null)
     {
-      fm = c.getFontMetrics(getAlabels().getFont());
-
-      while (i < al.getAlignmentAnnotation().length)
+      if (Jalview.isHeadlessMode())
       {
-        String label = al.getAlignmentAnnotation()[i].label;
-        int stringWidth = fm.stringWidth(label);
+        AnnotationLabels aal = getAlabels();
+        int stringWidth = aal.drawLabels(null, false, idWidth, false, false, fm);
         idWidth = Math.max(idWidth, stringWidth);
-        i++;
+      }
+      else
+      {
+        fm = c.getFontMetrics(getAlabels().getFont());
+
+        for (i = 0; i < al.getAlignmentAnnotation().length; i++)
+        {
+          AlignmentAnnotation aa = al.getAlignmentAnnotation()[i];
+          if (visibleOnly && !aa.visible)
+          {
+            continue;
+          }
+          String label = aa.label;
+          int stringWidth = fm.stringWidth(label);
+          idWidth = Math.max(idWidth, stringWidth);
+        }
       }
     }
 
@@ -540,7 +566,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     // this is called after loading new annotation onto alignment
     if (alignFrame.getHeight() == 0)
     {
-      System.out.println("NEEDS FIXING");
+      jalview.bin.Console.outPrintln("NEEDS FIXING");
     }
     validateAnnotationDimensions(true);
     addNotify();
@@ -564,21 +590,29 @@ public class AlignmentPanel extends GAlignmentPanel implements
     // 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);
+    AnnotationPanel ap = getAnnotationPanel();
+    int annotationHeight = ap.adjustPanelHeight();
+    annotationHeight = ap.adjustForAlignFrame(adjustPanelHeight,
+            annotationHeight);
 
     hscroll.addNotify();
-    annotationScroller.setPreferredSize(
-            new Dimension(annotationScroller.getWidth(), annotationHeight));
-
     Dimension e = idPanel.getSize();
-    alabels.setSize(new Dimension(e.width, annotationHeight));
+    int idWidth = e.width;
+    boolean manuallyAdjusted = this.getIdPanel().getIdCanvas()
+            .manuallyAdjusted();
+    annotationScroller.setPreferredSize(new Dimension(
+            manuallyAdjusted ? idWidth : annotationScroller.getWidth(),
+            annotationHeight));
+
+    alabels.setPreferredSize(new Dimension(idWidth, annotationHeight));
 
     annotationSpaceFillerHolder.setPreferredSize(new Dimension(
-            annotationSpaceFillerHolder.getWidth(), annotationHeight));
+            manuallyAdjusted ? idWidth
+                    : annotationSpaceFillerHolder.getWidth(),
+            annotationHeight));
     annotationScroller.validate();
     annotationScroller.addNotify();
+    ap.validate();
   }
 
   /**
@@ -594,9 +628,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
     boolean wrap = av.getWrapAlignment();
     ViewportRanges ranges = av.getRanges();
     ranges.setStartSeq(0);
-    scalePanelHolder.setVisible(!wrap);
+    // scalePanelHolder.setVisible(!wrap);
     hscroll.setVisible(!wrap);
-    idwidthAdjuster.setVisible(!wrap);
+    // Allow idPanel width adjustment in wrap mode
+    idwidthAdjuster.setVisible(true);
 
     if (wrap)
     {
@@ -630,7 +665,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
     }
 
-    idSpaceFillerPanel1.setVisible(!wrap);
+    // idSpaceFillerPanel1.setVisible(!wrap);
 
     repaint();
   }
@@ -851,10 +886,27 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public void paintComponent(Graphics g)
   {
     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));
+    int idWidth = d.width;
+
+    // check wrapped alignment as at least 1 residue width
+    if (av.getWrapAlignment())
+    {
+      SeqCanvas sc = this.getSeqPanel().seqCanvas;
+      if (sc != null && sc.getWidth() < sc.getMinimumWrappedCanvasWidth())
+      {
+        // need to make some adjustments
+        idWidth -= (sc.getMinimumWrappedCanvasWidth() - sc.getWidth());
+        av.setIdWidth(idWidth);
+        av.getAlignPanel().getIdPanel().getIdCanvas()
+                .setManuallyAdjusted(true);
+
+        validateAnnotationDimensions(false);
+      }
+    }
+
+    idPanelHolder.setPreferredSize(new Dimension(idWidth, d.height));
+    hscrollFillerPanel.setPreferredSize(new Dimension(idWidth, 12));
 
     validate(); // needed so that the id width adjuster works correctly
 
@@ -1012,7 +1064,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     List<SequenceI> selection = av.getSelectionGroup() == null ? null
             : av.getSelectionGroup().getSequences(null);
     idCanvas.drawIds((Graphics2D) idGraphics, av, startSeq, endSeq - 1,
-            selection);
+            selection, false);
 
     idGraphics.setFont(av.getFont());
     idGraphics.translate(0, -scaleHeight);
@@ -1036,7 +1088,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       int offset = getAlabels().getScrollOffset();
       idGraphics.translate(0, -offset);
       idGraphics.translate(0, alignmentDrawnHeight);
-      getAlabels().drawComponent(idGraphics, idWidth);
+      getAlabels().drawComponentNotGUI(idGraphics, idWidth);
       idGraphics.translate(0, -alignmentDrawnHeight);
 
       /*
@@ -1045,6 +1097,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
        */
       alignmentGraphics.translate(alignmentGraphicsOffset,
               alignmentDrawnHeight);
+      updateLayout();
       getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), av,
               alignmentGraphics, -1, startRes, endRes + 1);
     }
@@ -1116,7 +1169,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * draw sequence ids and annotation labels (if shown)
      */
     IdCanvas idCanvas = getIdPanel().getIdCanvas();
-    idCanvas.drawIdsWrapped((Graphics2D) g, av, 0, totalHeight);
+    idCanvas.drawIdsWrappedNoGUI((Graphics2D) g, av, 0, totalHeight);
 
     g.translate(idWidth, 0);
 
@@ -1168,21 +1221,33 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
 
     int w = getIdPanel().getWidth();
+    w = this.calculateIdWidth(-1, true, true).width;
     return (w > 0 ? w : calculateIdWidth().width);
   }
 
+  void makeAlignmentImage(ImageMaker.TYPE type, File file, String renderer)
+          throws ImageOutputException
+  {
+    makeAlignmentImage(type, file, renderer,
+            BitmapImageSizing.nullBitmapImageSizing());
+  }
+
   /**
    * Builds an image of the alignment of the specified type (EPS/PNG/SVG) and
    * writes it to the specified file
    * 
    * @param type
    * @param file
+   * @param textrenderer
+   * @param bitmapscale
    */
-  void makeAlignmentImage(ImageMaker.TYPE type, File file)
+  void makeAlignmentImage(ImageMaker.TYPE type, File file, String renderer,
+          BitmapImageSizing userBis) throws ImageOutputException
   {
     final int borderBottomOffset = 5;
 
     AlignmentDimension aDimension = getAlignmentDimension();
+
     // todo use a lambda function in place of callback here?
     ImageWriterI writer = new ImageWriterI()
     {
@@ -1208,7 +1273,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
     int imageWidth = aDimension.getWidth();
     int imageHeight = aDimension.getHeight() + borderBottomOffset;
     String of = MessageManager.getString("label.alignment");
-    exporter.doExport(file, this, imageWidth, imageHeight, of);
+    exporter.doExport(file, this, imageWidth, imageHeight, of, renderer,
+            userBis);
   }
 
   /**
@@ -1253,6 +1319,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   }
 
   public void makePNGImageMap(File imgMapFile, String imageName)
+          throws ImageOutputException
   {
     // /////ONLY WORKS WITH NON WRAPPED ALIGNMENTS
     // ////////////////////////////////////////////
@@ -1377,7 +1444,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
       } catch (Exception ex)
       {
-        ex.printStackTrace();
+        throw new ImageOutputException(
+                "couldn't write ImageMap due to unexpected error", ex);
       }
     } // /////////END OF IMAGE MAP
 
@@ -1393,8 +1461,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