Merge branch 'develop' into feature/JAL-244_Automatically_adjust_left_margin_to_avoid...
authorBen Soares <b.soares@dundee.ac.uk>
Thu, 27 Jul 2023 19:18:50 +0000 (20:18 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Thu, 27 Jul 2023 19:18:50 +0000 (20:18 +0100)
1  2 
src/jalview/gui/AlignFrame.java
src/jalview/gui/AnnotationLabels.java

@@@ -157,6 -157,7 +157,6 @@@ import jalview.viewmodel.AlignmentViewp
  import jalview.viewmodel.ViewportRanges;
  import jalview.ws.DBRefFetcher;
  import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
 -import jalview.ws.datamodel.alphafold.PAEContactMatrix;
  import jalview.ws.jws1.Discoverer;
  import jalview.ws.jws2.Jws2Discoverer;
  import jalview.ws.jws2.jabaws2.Jws2Instance;
@@@ -1455,11 -1456,9 +1455,11 @@@ public class AlignFrame extends GAlignF
    protected void htmlMenuItem_actionPerformed(ActionEvent e)
    {
      HtmlSvgOutput htmlSVG = new HtmlSvgOutput(alignPanel);
 -    try {
 +    try
 +    {
        htmlSVG.exportHTML(null);
 -    } catch (ImageOutputException x) {
 +    } catch (ImageOutputException x)
 +    {
        // report problem to console and raise dialog
      }
    }
    public void bioJSMenuItem_actionPerformed(ActionEvent e)
    {
      BioJsHTMLOutput bjs = new BioJsHTMLOutput(alignPanel);
 -    try {
 -    bjs.exportHTML(null);
 -  } catch (ImageOutputException x) {
 -    // report problem to console and raise dialog
 -  }
 +    try
 +    {
 +      bjs.exportHTML(null);
 +    } catch (ImageOutputException x)
 +    {
 +      // report problem to console and raise dialog
 +    }
    }
  
    public void createImageMap(File file, String image)
    {
 -    try {
 -    alignPanel.makePNGImageMap(file, image);
 -    } catch (ImageOutputException x) {
 +    try
 +    {
 +      alignPanel.makePNGImageMap(file, image);
 +    } catch (ImageOutputException x)
 +    {
        // report problem to console and raise dialog
      }
    }
  
    @Override
 -  public void createPNG_actionPerformed(ActionEvent e) {
 -    try{
 +  public void createPNG_actionPerformed(ActionEvent e)
 +  {
 +    try
 +    {
        createPNG(null);
      } catch (ImageOutputException ioex)
      {
        // raise dialog, and report via console
      }
    }
 +
    @Override
 -  public void createEPS_actionPerformed(ActionEvent e) {
 -    try{
 +  public void createEPS_actionPerformed(ActionEvent e)
 +  {
 +    try
 +    {
        createEPS(null);
      } catch (ImageOutputException ioex)
      {
        // raise dialog, and report via console
      }
 -    
 +
    }
 +
    @Override
 -  public void createSVG_actionPerformed(ActionEvent e) {
 -    try{
 +  public void createSVG_actionPerformed(ActionEvent e)
 +  {
 +    try
 +    {
        createSVG(null);
      } catch (ImageOutputException ioex)
      {
        // raise dialog, and report via console
      }
 -    
 +
    }
 +
    /**
     * Creates a PNG image of the alignment and writes it to the given file. If
     * the file is null, the user is prompted to choose a file.
      createPNG(f, null, BitmapImageSizing.nullBitmapImageSizing());
    }
  
 -  public void createPNG(File f, String renderer, BitmapImageSizing userBis) throws ImageOutputException
 +  public void createPNG(File f, String renderer, BitmapImageSizing userBis)
 +          throws ImageOutputException
    {
      alignPanel.makeAlignmentImage(TYPE.PNG, f, renderer, userBis);
    }
     * 
     * @param f
     */
 -  public void createEPS(File f)  throws ImageOutputException
 +  public void createEPS(File f) throws ImageOutputException
    {
      createEPS(f, null);
    }
     * 
     * @param f
     */
 -  public void createSVG(File f)  throws ImageOutputException
 +  public void createSVG(File f) throws ImageOutputException
    {
      createSVG(f, null);
    }
              // annotation was duplicated earlier
              alignment.addAnnotation(sequences[i].getAnnotation()[a]);
              // take care of contact matrix too
 -            ContactMatrixI cm=sequences[i].getContactMatrixFor(sequences[i].getAnnotation()[a]);
 -            if (cm!=null)
 +            ContactMatrixI cm = sequences[i]
 +                    .getContactMatrixFor(sequences[i].getAnnotation()[a]);
 +            if (cm != null)
              {
 -              alignment.addContactListFor(sequences[i].getAnnotation()[a], cm);
 +              alignment.addContactListFor(sequences[i].getAnnotation()[a],
 +                      cm);
              }
 -            
 +
              alignment.setAnnotationIndex(sequences[i].getAnnotation()[a],
                      a);
            }
    @Override
    public void wrapMenuItem_actionPerformed(ActionEvent e)
    {
 -    scaleAbove.setVisible(wrapMenuItem.isSelected());
 -    scaleLeft.setVisible(wrapMenuItem.isSelected());
 -    scaleRight.setVisible(wrapMenuItem.isSelected());
 -    viewport.setWrapAlignment(wrapMenuItem.isSelected());
 +    setWrapFormat(wrapMenuItem.isSelected());
 +  }
 +
 +  public void setWrapFormat(boolean b)
 +  {
 +    scaleAbove.setVisible(b);
 +    scaleLeft.setVisible(b);
 +    scaleRight.setVisible(b);
 +    viewport.setWrapAlignment(b);
      alignPanel.updateLayout();
    }
  
      return tp;
    }
  
 -  public void showContactMapTree(AlignmentAnnotation aa,
 -          ContactMatrixI cm)
 +  public void showContactMapTree(AlignmentAnnotation aa, ContactMatrixI cm)
    {
      int x = 4, y = 5;
      int w = 400, h = 500;
      {
        NewickFile fin = new NewickFile(
                new FileParse(cm.getNewick(), DataSourceType.PASTE));
-       String title = cm.getAnnotLabel() + " " + cm.getTreeMethod() + " tree"
-               + aa.sequenceRef != null
+       String title = aa.label + " "
+               + cm.getTreeMethod() + " tree" + (aa.sequenceRef != null
                        ? (" for " + aa.sequenceRef.getDisplayId(false))
-                       : "";
+                       : "");
  
        showColumnWiseTree(fin, aa, title, w, h, x, y);
      } catch (Throwable xx)
        {
          return null;
        }
-       TreePanel tp = new TreePanel(alignPanel, nf, aa, title);
+       TreePanel tp = new TreePanel(alignPanel, nf, aa, treeTitle);
  
        tp.setSize(w, h);
  
          tp.setLocation(x, y);
        }
  
-       Desktop.addInternalFrame(tp, title, w, h);
+       Desktop.addInternalFrame(tp, treeTitle, w, h);
        return tp;
      } catch (Throwable xx)
      {
@@@ -20,7 -20,6 +20,7 @@@
   */
  package jalview.gui;
  
 +import java.awt.Canvas;
  import java.awt.Color;
  import java.awt.Cursor;
  import java.awt.Dimension;
@@@ -51,8 -50,6 +51,8 @@@ import javax.swing.ToolTipManager
  
  import jalview.analysis.AlignSeq;
  import jalview.analysis.AlignmentUtils;
 +import jalview.bin.Cache;
 +import jalview.bin.Jalview;
  import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.Annotation;
@@@ -116,8 -113,6 +116,8 @@@ public class AnnotationLabels extends J
    private static final String COPYCONS_SEQ = MessageManager
            .getString("label.copy_consensus_sequence");
  
 +  private static final String ADJUST_ANNOTATION_LABELS_WIDTH_PREF = "ADJUST_ANNOTATION_LABELS_WIDTH";
 +
    private final boolean debugRedraw = false;
  
    private AlignmentPanel ap;
  
    private boolean resizePanel = false;
  
 +  private int annotationIdWidth = -1;
 +
    /**
     * Creates a new AnnotationLabels object
     * 
     */
    public AnnotationLabels(AlignmentPanel ap)
    {
 -
      this.ap = ap;
      av = ap.av;
      ToolTipManager.sharedInstance().registerComponent(this);
          pop.add(consclipbrd);
        }
  
 -      addColourOrFilterByOptions(ap,aa[selectedRow],pop);
 -      
 +      addColourOrFilterByOptions(ap, aa[selectedRow], pop);
 +
        if (aa[selectedRow].graph == AlignmentAnnotation.CONTACT_MAP)
        {
 -        addContactMatrixOptions(ap,aa[selectedRow],pop);
 -          // Set/adjust threshold for grouping ?
 -          // colour alignment by this [type]
 -          // select/hide columns by this row
 -          
 -        }
 +        addContactMatrixOptions(ap, aa[selectedRow], pop);
 +        // Set/adjust threshold for grouping ?
 +        // colour alignment by this [type]
 +        // select/hide columns by this row
 +
        }
 -    
 +    }
 +
      pop.show(this, evt.getX(), evt.getY());
    }
  
    static void addColourOrFilterByOptions(final AlignmentPanel ap,
 -          final AlignmentAnnotation alignmentAnnotation, final JPopupMenu pop)
 +          final AlignmentAnnotation alignmentAnnotation,
 +          final JPopupMenu pop)
    {
      JMenuItem item;
 -    item = new JMenuItem(MessageManager.getString("label.colour_by_annotation"));
 +    item = new JMenuItem(
 +            MessageManager.getString("label.colour_by_annotation"));
      item.addActionListener(new ActionListener()
      {
 -      
 +
        @Override
        public void actionPerformed(ActionEvent e)
        {
 -        AnnotationColourChooser.displayFor(ap.av, ap,alignmentAnnotation,false);
 +        AnnotationColourChooser.displayFor(ap.av, ap, alignmentAnnotation,
 +                false);
        };
      });
      pop.add(item);
 -    if (alignmentAnnotation.sequenceRef!=null)
 +    if (alignmentAnnotation.sequenceRef != null)
      {
 -      item = new JMenuItem(MessageManager.getString("label.colour_by_annotation")+" ("+MessageManager.getString("label.per_seq")+")");
 +      item = new JMenuItem(
 +              MessageManager.getString("label.colour_by_annotation") + " ("
 +                      + MessageManager.getString("label.per_seq") + ")");
        item.addActionListener(new ActionListener()
        {
          @Override
          public void actionPerformed(ActionEvent e)
          {
 -          AnnotationColourChooser.displayFor(ap.av, ap,alignmentAnnotation,true);
 +          AnnotationColourChooser.displayFor(ap.av, ap, alignmentAnnotation,
 +                  true);
          };
        });
        pop.add(item);
      }
 -    item = new JMenuItem(MessageManager.getString("action.select_by_annotation"));
 +    item = new JMenuItem(
 +            MessageManager.getString("action.select_by_annotation"));
      item.addActionListener(new ActionListener()
      {
 -      
 +
        @Override
        public void actionPerformed(ActionEvent e)
        {
 -        AnnotationColumnChooser.displayFor(ap.av,ap,alignmentAnnotation);
 +        AnnotationColumnChooser.displayFor(ap.av, ap, alignmentAnnotation);
        };
      });
      pop.add(item);
    }
 +
    static void addContactMatrixOptions(final AlignmentPanel ap,
 -          final AlignmentAnnotation alignmentAnnotation, final JPopupMenu pop)
 +          final AlignmentAnnotation alignmentAnnotation,
 +          final JPopupMenu pop)
    {
 -    
 +
      final ContactMatrixI cm = ap.av.getContactMatrix(alignmentAnnotation);
      JMenuItem item;
      if (cm != null)
  
        if (cm.hasGroups())
        {
 -        JCheckBoxMenuItem chitem = new JCheckBoxMenuItem(MessageManager.getString("action.show_groups_on_matrix"));
 -        chitem.setToolTipText(MessageManager.getString("action.show_groups_on_matrix_tooltip"));
 -        boolean showGroups = alignmentAnnotation.isShowGroupsForContactMatrix();
 -        final AlignmentAnnotation sel_row=alignmentAnnotation;
 +        JCheckBoxMenuItem chitem = new JCheckBoxMenuItem(
 +                MessageManager.getString("action.show_groups_on_matrix"));
 +        chitem.setToolTipText(MessageManager
 +                .getString("action.show_groups_on_matrix_tooltip"));
 +        boolean showGroups = alignmentAnnotation
 +                .isShowGroupsForContactMatrix();
 +        final AlignmentAnnotation sel_row = alignmentAnnotation;
          chitem.setState(showGroups);
          chitem.addActionListener(new ActionListener()
          {
            public void actionPerformed(ActionEvent e)
            {
              sel_row.setShowGroupsForContactMatrix(chitem.getState());
-             ap.getAnnotationPanel()
-                     .paint(ap.getAnnotationPanel().getGraphics());
+             // so any annotation colour changes are propagated - though they
+             // probably won't be unless the annotation row colours are removed
+             // too!
+             ap.alignmentChanged();
            }
          });
          pop.add(chitem);
        }
        if (cm.hasTree())
        {
 -        item = new JMenuItem(MessageManager.getString("action.show_tree_for_matrix"));
 -        item.setToolTipText(MessageManager.getString("action.show_tree_for_matrix_tooltip"));
 +        item = new JMenuItem(
 +                MessageManager.getString("action.show_tree_for_matrix"));
 +        item.setToolTipText(MessageManager
 +                .getString("action.show_tree_for_matrix_tooltip"));
          item.addActionListener(new ActionListener()
          {
  
        }
        else
        {
 -        item = new JMenuItem(MessageManager.getString("action.cluster_matrix"));
 -        item.setToolTipText(MessageManager.getString("action.cluster_matrix_tooltip"));
 +        item = new JMenuItem(
 +                MessageManager.getString("action.cluster_matrix"));
 +        item.setToolTipText(
 +                MessageManager.getString("action.cluster_matrix_tooltip"));
          item.addActionListener(new ActionListener()
          {
            @Override
                public void run()
                {
                  final long progBar;
 -                ap.alignFrame.setProgressBar(MessageManager.formatMessage("action.clustering_matrix_for",cm.getAnnotDescr(),5f), progBar = System.currentTimeMillis());
 +                ap.alignFrame.setProgressBar(
 +                        MessageManager.formatMessage(
 +                                "action.clustering_matrix_for",
 +                                cm.getAnnotDescr(), 5f),
 +                        progBar = System.currentTimeMillis());
-                 cm.setGroupSet(GroupSet.makeGroups(cm, 5f, true));
+                 cm.setGroupSet(GroupSet.makeGroups(cm, true));
+                 cm.randomlyReColourGroups();
+                 cm.transferGroupColorsTo(alignmentAnnotation);
+                 ap.alignmentChanged();
                  ap.alignFrame.showContactMapTree(alignmentAnnotation, cm);
                  ap.alignFrame.setProgressBar(null, progBar);
                }
     * @param width
     *          Width for scaling labels
     */
 -  public void drawComponent(Graphics g, boolean clip, int width)
 +  public void drawComponent(Graphics g, boolean clip, int givenWidth)
    {
 -    if (av.getFont().getSize() < 10)
 +    int width = givenWidth;
 +    IdwidthAdjuster iwa = null;
 +    if (ap != null)
      {
 -      g.setFont(font);
 +      iwa = ap.idwidthAdjuster;
 +      if ((Cache.getDefault(ADJUST_ANNOTATION_LABELS_WIDTH_PREF, true)
 +              || Jalview.isHeadlessMode()))
 +      {
 +        Graphics2D g2d = (Graphics2D) g;
 +        Graphics dummy = g2d.create();
 +        int newAnnotationIdWidth = drawLabels(dummy, clip, width, false,
 +                null);
 +        dummy.dispose();
 +        Dimension d = ap.calculateDefaultAlignmentIdWidth();
 +        int alignmentIdWidth = d.width;
 +        if (iwa != null && !iwa.manuallyAdjusted())
 +        {
 +          // If no manual adjustment to ID column with has been made then adjust
 +          // width match widest of alignment or annotation id widths
 +          width = Math.max(alignmentIdWidth, newAnnotationIdWidth);
 +        }
 +        else if (newAnnotationIdWidth != annotationIdWidth
 +                && newAnnotationIdWidth > givenWidth
 +                && newAnnotationIdWidth > alignmentIdWidth)
 +        {
 +          // otherwise if the annotation id width has become larger than the
 +          // current id width, increase
 +          width = newAnnotationIdWidth;
 +          annotationIdWidth = newAnnotationIdWidth;
 +        }
 +        // set the width if it's changed
 +        if (width != ap.av.getIdWidth())
 +        {
 +          iwa.setWidth(width);
 +        }
 +      }
      }
      else
      {
 -      g.setFont(av.getFont());
 +      Graphics2D g2d = (Graphics2D) g;
 +      Graphics dummy = g2d.create();
 +      int newAnnotationIdWidth = drawLabels(dummy, clip, width, false,
 +              null);
 +      width = Math.max(newAnnotationIdWidth, givenWidth);
      }
 +    drawLabels(g, clip, width, true, null);
 +  }
  
 -    FontMetrics fm = g.getFontMetrics(g.getFont());
 -    g.setColor(Color.white);
 -    g.fillRect(0, 0, getWidth(), getHeight());
 +  /**
 +   * Render the full set of annotation Labels for the alignment at the given
 +   * cursor. If actuallyDraw is false or g is null then no actual drawing will
 +   * occur, but the widest label width will be returned. If g is null then
 +   * fmetrics must be supplied.
 +   * 
 +   * Returns the width of the annotation labels.
 +   * 
 +   * @param g
 +   *          Graphics2D instance (needed for font scaling)
 +   * @param clip
 +   *          - true indicates that only current visible area needs to be
 +   *          rendered
 +   * @param width
 +   *          Width for scaling labels
 +   * @param fmetrics
 +   *          FontMetrics if Graphics object g is null
 +   */
 +  public int drawLabels(Graphics g, boolean clip, int width,
 +          boolean actuallyDraw, FontMetrics fmetrics)
 +  {
 +    int actualWidth = 0;
 +    if (g != null)
 +    {
 +      if (av.getFont().getSize() < 10)
 +      {
 +        g.setFont(font);
 +      }
 +      else
 +      {
 +        g.setFont(av.getFont());
 +      }
 +    }
 +
 +    FontMetrics fm = fmetrics == null ? g.getFontMetrics(g.getFont())
 +            : fmetrics;
 +    if (actuallyDraw)
 +    {
 +      g.setColor(Color.white);
 +      g.fillRect(0, 0, getWidth(), getHeight());
 +    }
  
 -    g.translate(0, getScrollOffset());
 -    g.setColor(Color.black);
 +    if (actuallyDraw)
 +    {
 +      g.translate(0, getScrollOffset());
 +      g.setColor(Color.black);
 +    }
      SequenceI lastSeqRef = null;
      String lastLabel = null;
      AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
 -    int fontHeight = g.getFont().getSize();
 +    int fontHeight = g != null ? g.getFont().getSize()
 +            : fm.getFont().getSize();
      int y = 0;
      int x = 0;
      int graphExtras = 0;
      int offset = 0;
 -    Font baseFont = g.getFont();
 +    Font baseFont = g != null ? g.getFont() : fm.getFont();
      FontMetrics baseMetrics = fm;
      int ofontH = fontHeight;
      int sOffset = 0;
              continue;
            }
          }
 -        g.setColor(Color.black);
 -
 +        if (actuallyDraw && g != null)
 +        {
 +          g.setColor(Color.black);
 +        }
          offset = -aa[i].height / 2;
  
          if (aa[i].hasText)
              vertBar = true;
            }
          }
 -        x = width - fm.stringWidth(label) - 3;
 +
 +        int labelWidth = fm.stringWidth(label) + 3;
 +        x = width - labelWidth;
  
          if (aa[i].graphGroup > -1)
          {
                s = ((float) fontHeight) / (float) ofontH;
                Font f = baseFont
                        .deriveFont(AffineTransform.getScaleInstance(s, s));
 -              g.setFont(f);
 -              fm = g.getFontMetrics();
 -              graphExtras = (aa[i].height - (groupSize * (fontHeight + 8)))
 -                      / 2;
 +              Canvas c = new Canvas();
 +              fm = c.getFontMetrics(f);
 +              if (actuallyDraw && g != null)
 +              {
 +                g.setFont(f);
 +                // fm = g.getFontMetrics();
 +                graphExtras = (aa[i].height
 +                        - (groupSize * (fontHeight + 8))) / 2;
 +              }
              }
            }
            if (visible)
              {
                if (aa[gg].graphGroup == aa[i].graphGroup)
                {
 -                x = width - fm.stringWidth(aa[gg].label) - 3;
 -                g.drawString(aa[gg].label, x, y - graphExtras);
 -
 -                if (aa[gg]._linecolour != null)
 +                labelWidth = fm.stringWidth(aa[gg].label) + 3;
 +                x = width - labelWidth;
 +                if (actuallyDraw && g != null)
                  {
 +                  g.drawString(aa[gg].label, x, y - graphExtras);
  
 -                  g.setColor(aa[gg]._linecolour);
 -                  g.drawLine(x, y - graphExtras + 3,
 -                          x + fm.stringWidth(aa[gg].label),
 -                          y - graphExtras + 3);
 -                }
 +                  if (aa[gg]._linecolour != null)
 +                  {
  
 -                g.setColor(Color.black);
 +                    g.setColor(aa[gg]._linecolour);
 +                    g.drawLine(x, y - graphExtras + 3,
 +                            x + fm.stringWidth(aa[gg].label),
 +                            y - graphExtras + 3);
 +                  }
 +
 +                  g.setColor(Color.black);
 +                }
                  graphExtras += fontHeight + 8;
                }
              }
            }
 -          g.setFont(baseFont);
 +          if (actuallyDraw && g != null)
 +          {
 +            g.setFont(baseFont);
 +          }
            fm = baseMetrics;
            fontHeight = ofontH;
          }
          else
          {
 -          if (vertBar)
 +          if (actuallyDraw && g != null)
            {
 -            g.drawLine(width - 3, y + offset - fontHeight, width - 3,
 -                    (int) (y - 1.5 * aa[i].height - offset - fontHeight));
 -            // g.drawLine(20, y + offset, x - 20, y + offset);
 +            if (vertBar)
 +            {
 +              g.drawLine(width - 3, y + offset - fontHeight, width - 3,
 +                      (int) (y - 1.5 * aa[i].height - offset - fontHeight));
 +              // g.drawLine(20, y + offset, x - 20, y + offset);
  
 +            }
 +            g.drawString(label, x, y + offset);
            }
 -          g.drawString(label, x, y + offset);
          }
          lastSeqRef = aa[i].sequenceRef;
 +
 +        if (labelWidth > actualWidth)
 +        {
 +          actualWidth = labelWidth;
 +        }
        }
      }
  
      if (!resizePanel && dragEvent != null && aa != null)
      {
 -      g.setColor(Color.lightGray);
 -      g.drawString(
 -              (aa[selectedRow].sequenceRef == null ? ""
 -                      : aa[selectedRow].sequenceRef.getName())
 -                      + aa[selectedRow].label,
 -              dragEvent.getX(), dragEvent.getY() - getScrollOffset());
 +      if (actuallyDraw && g != null)
 +      {
 +        g.setColor(Color.lightGray);
 +        g.drawString(
 +                (aa[selectedRow].sequenceRef == null ? ""
 +                        : aa[selectedRow].sequenceRef.getName())
 +                        + aa[selectedRow].label,
 +                dragEvent.getX(), dragEvent.getY() - getScrollOffset());
 +      }
      }
  
      if (!av.getWrapAlignment() && ((aa == null) || (aa.length < 1)))
      {
 -      g.drawString(MessageManager.getString("label.right_click"), 2, 8);
 -      g.drawString(MessageManager.getString("label.to_add_annotation"), 2,
 -              18);
 +      if (actuallyDraw && g != null)
 +      {
 +        g.drawString(MessageManager.getString("label.right_click"), 2, 8);
 +        g.drawString(MessageManager.getString("label.to_add_annotation"), 2,
 +                18);
 +      }
      }
 +
 +    return actualWidth;
    }
  
    public int getScrollOffset()