JAL-244 Adjust wrapped alignment widths to always maintain a visible wrapped sequence...
[jalview.git] / src / jalview / gui / AlignmentPanel.java
index 9fb1a23..63455cb 100644 (file)
@@ -48,7 +48,9 @@ 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;
@@ -57,6 +59,7 @@ 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;
@@ -64,6 +67,7 @@ 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;
 
@@ -229,6 +233,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
     // to prevent drawing old image
     FontMetrics fm = getFontMetrics(av.getFont());
 
+    // update the flag controlling whether the grid is too small to render the
+    // font
+    av.validCharWidth = fm.charWidth('M') <= av.getCharWidth();
+
     scalePanelHolder.setPreferredSize(
             new Dimension(10, av.getCharHeight() + fm.getDescent()));
     idSpaceFillerPanel1.setPreferredSize(
@@ -262,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
@@ -287,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.
@@ -298,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(
@@ -317,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 = this.getAlabels();
+        int stringWidth = aal.drawLabels(null, false, idWidth, 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);
+        }
       }
     }
 
@@ -560,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();
   }
 
   /**
@@ -592,7 +630,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
     ranges.setStartSeq(0);
     scalePanelHolder.setVisible(!wrap);
     hscroll.setVisible(!wrap);
-    idwidthAdjuster.setVisible(!wrap);
+    // Allow idPanel width adjustment in wrap mode
+    idwidthAdjuster.setVisible(true);
 
     if (wrap)
     {
@@ -626,7 +665,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
     }
 
-    idSpaceFillerPanel1.setVisible(!wrap);
+    // idSpaceFillerPanel1.setVisible(!wrap);
 
     repaint();
   }
@@ -804,7 +843,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
           // could not be validated and it is not clear if it is now being
           // called. Log warning here in case it is called and unforeseen
           // problems occur
-          Cache.log.warn(
+          Console.warn(
                   "Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences");
 
           // scroll to start of panel
@@ -847,10 +886,24 @@ 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);
+        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
 
@@ -1041,6 +1094,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
        */
       alignmentGraphics.translate(alignmentGraphicsOffset,
               alignmentDrawnHeight);
+      updateLayout();
       getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), av,
               alignmentGraphics, -1, startRes, endRes + 1);
     }
@@ -1164,21 +1218,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()
     {
@@ -1204,7 +1270,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);
   }
 
   /**
@@ -1249,6 +1316,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   }
 
   public void makePNGImageMap(File imgMapFile, String imageName)
+          throws ImageOutputException
   {
     // /////ONLY WORKS WITH NON WRAPPED ALIGNMENTS
     // ////////////////////////////////////////////
@@ -1373,7 +1441,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
 
@@ -1389,8 +1458,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
@@ -1464,9 +1532,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
     else
     {
-      if (Cache.log.isDebugEnabled())
+      if (Console.isDebugEnabled())
       {
-        Cache.log.warn("Closing alignment panel which is already closed.");
+        Console.warn("Closing alignment panel which is already closed.");
       }
     }
   }
@@ -1541,13 +1609,17 @@ public class AlignmentPanel extends GAlignmentPanel implements
     } catch (Exception ex)
     {
     }
-
     if (b)
     {
-      alignFrame.setDisplayedView(this);
+      setAlignFrameView();
     }
   }
 
+  public void setAlignFrameView()
+  {
+    alignFrame.setDisplayedView(this);
+  }
+
   @Override
   public StructureSelectionManager getStructureSelectionManager()
   {
@@ -1763,4 +1835,16 @@ public class AlignmentPanel extends GAlignmentPanel implements
     return overviewTitle;
   }
 
+  /**
+   * If this alignment panel has an Overview panel open, closes it
+   */
+  public void closeOverviewPanel()
+  {
+    if (overviewPanel != null)
+    {
+      overviewPanel.close();
+      overviewPanel = null;
+    }
+  }
+
 }