Merge branch 'develop' into spike/JAL-4047/JAL-4048_columns_in_sequenceID spike/JAL-4047/JAL-4048_columns_in_sequenceID
authorJames Procter <j.procter@dundee.ac.uk>
Thu, 14 Sep 2023 09:16:21 +0000 (10:16 +0100)
committerJames Procter <j.procter@dundee.ac.uk>
Thu, 14 Sep 2023 09:16:21 +0000 (10:16 +0100)
src/jalview/api/AlignViewportI.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/IdPanel.java
src/jalview/gui/PopupMenu.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/seqfeatures/IdColumn.java [new file with mode: 0644]
src/jalview/viewmodel/seqfeatures/IdColumns.java [new file with mode: 0644]

index 0cfd03d..ba3ed75 100644 (file)
  */
 package jalview.api;
 
+import java.awt.Color;
+import java.awt.Font;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
 import jalview.analysis.Conservation;
 import jalview.analysis.TreeModel;
 import jalview.datamodel.AlignmentAnnotation;
@@ -37,13 +44,7 @@ import jalview.datamodel.SequenceI;
 import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ColourSchemeI;
 import jalview.viewmodel.ViewportRanges;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import jalview.viewmodel.seqfeatures.IdColumns;
 
 /**
  * @author jimp
@@ -566,5 +567,7 @@ public interface AlignViewportI extends ViewStyleI
    */
   Iterator<int[]> getViewAsVisibleContigs(boolean selectedRegionOnly);
 
+  IdColumns getIdColumns();
+
   ContactMatrixI getContactMatrix(AlignmentAnnotation alignmentAnnotation);
 }
index aaded9e..2faea01 100755 (executable)
@@ -37,6 +37,9 @@ import javax.swing.JPanel;
 import jalview.datamodel.SequenceI;
 import jalview.viewmodel.ViewportListenerI;
 import jalview.viewmodel.ViewportRanges;
+import jalview.viewmodel.seqfeatures.IdColumn;
+import jalview.viewmodel.seqfeatures.IdColumns;
+import jalview.viewmodel.seqfeatures.IdColumns.ColumnCell;
 
 /**
  * DOCUMENT ME!
@@ -309,19 +312,27 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     }
 
     // Now draw the id strings
-    int panelWidth = getWidth();
-    int xPos = 0;
+    int fullPanelWidth = getWidth();
+
+    IdColumns id_cols = alignViewport.getIdColumns();
+    List<IdColumn> visible = id_cols.getVisible();
+    /**
+     * width of an idColumn
+     */
+    int colWid = 20;
+    int panelWidth = Math.max(fullPanelWidth / 2,
+            fullPanelWidth - (colWid * visible.size()));
 
     // Now draw the id strings
     for (int i = startSeq; i <= endSeq; i++)
     {
+      int xPos = 0;
       SequenceI sequence = alignViewport.getAlignment().getSequenceAt(i);
 
       if (sequence == null)
       {
         continue;
       }
-
       if (hasHiddenRows || alignViewport.isDisplayReferenceSeq())
       {
         g.setFont(getHiddenFont(sequence, alignViewport));
@@ -365,6 +376,37 @@ public class IdCanvas extends JPanel implements ViewportListenerI
               (((i - startSeq) * charHeight) + charHeight)
                       - (charHeight / 5));
 
+      if (visible != null && visible.size() > 0)
+      {
+        try
+        {
+          xPos = panelWidth + 2;
+          for (IdColumn col : visible)
+          {
+            ColumnCell col_cell = id_cols.getCellFor(sequence, col);
+            if (col_cell == null)
+            {
+              g.setColor(Color.gray);
+              g.fillRect(xPos + 1, (i - startSeq) * charHeight,
+                      xPos + colWid - 3, charHeight);
+            }
+            else
+            {
+              g.setColor(col_cell.bg);
+              g.fillRect(xPos + 1, (i - startSeq) * charHeight,
+                      xPos + colWid - 3, charHeight);
+              g.setColor(col_cell.fg);
+              g.drawString(col_cell.label, xPos,
+                      (((i - startSeq) * charHeight) + charHeight)
+                              - (charHeight / 5));
+            }
+            xPos += colWid;
+            g.setColor(currentTextColor);
+          }
+        } catch (Exception q)
+        {
+        }
+      }
       if (hasHiddenRows && av.getShowHiddenMarkers())
       {
         drawMarker(g, alignViewport, i, startSeq, 0);
index 7b491e4..fdca723 100755 (executable)
@@ -46,6 +46,9 @@ import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportRanges;
+import jalview.viewmodel.seqfeatures.IdColumn;
+import jalview.viewmodel.seqfeatures.IdColumns;
+import jalview.viewmodel.seqfeatures.IdColumns.ColumnCell;
 
 /**
  * This panel hosts alignment sequence ids and responds to mouse clicks on them,
@@ -125,8 +128,17 @@ public class IdPanel extends JPanel
         SequenceI sequence = av.getAlignment().getSequenceAt(seq);
         StringBuilder tip = new StringBuilder(64);
         tip.append(sequence.getDisplayId(true)).append(" ");
-        seqAnnotReport.createTooltipAnnotationReport(tip, sequence,
-                av.isShowDBRefs(), av.isShowNPFeats(), sp.seqCanvas.fr);
+        IdColumn col = locateColumnFor(e);
+        if (col != null)
+        {
+          // tooltip for column
+          tip.append(getTooltipFor(col, sequence));
+        }
+        else
+        {
+          seqAnnotReport.createTooltipAnnotationReport(tip, sequence,
+                  av.isShowDBRefs(), av.isShowNPFeats(), sp.seqCanvas.fr);
+        }
         setToolTipText(JvSwingUtils.wrapTooltip(true, tip.toString()));
 
         StringBuilder text = new StringBuilder();
@@ -137,6 +149,40 @@ public class IdPanel extends JPanel
     }
   }
 
+  private Object getTooltipFor(IdColumn col, SequenceI seq)
+  {
+    ColumnCell cell = av.getIdColumns().getCellFor(seq, col);
+    if (cell != null)
+    {
+      return "" + col.getLabel() + ": " + cell.label;
+    }
+    return "";
+  }
+
+  private IdColumn locateColumnFor(MouseEvent e)
+  {
+    // TODO COMBINE SAME CODE IN IDCANVAS!!!
+
+    IdColumns id_cols = av.getIdColumns();
+    List<IdColumn> visible = id_cols.getVisible();
+    /**
+     * width of an idColumn
+     */
+    int colWid = 20;
+    int panelWidth = Math.max(idCanvas.getWidth() / 2,
+            idCanvas.getWidth() - (colWid * visible.size()));
+    int p = 0;
+    while (panelWidth < idCanvas.getWidth() && p < visible.size())
+    {
+
+      if (e.getX() >= panelWidth && e.getX() < panelWidth + colWid)
+        return visible.get(p);
+      p++;
+      panelWidth += colWid;
+    }
+    return null;
+  }
+
   /**
    * Responds to a mouse drag by selecting the sequences under the dragged
    * region.
index 109c140..ca0fb7f 100644 (file)
@@ -87,6 +87,8 @@ import jalview.util.Platform;
 import jalview.util.StringUtils;
 import jalview.util.UrlLink;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
+import jalview.viewmodel.seqfeatures.IdColumn;
+import jalview.viewmodel.seqfeatures.IdColumns;
 
 /**
  * The popup menu that is displayed on right-click on a sequence id, or in the
@@ -726,9 +728,38 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       rnaStructureMenu.setVisible(false);
     }
 
+    if (forIdPanel)
+    {
+      addDisplayColumnsMenu();
+    }
+
     addLinksAndFeatures(seq, column);
   }
 
+  void addDisplayColumnsMenu()
+  {
+    JMenu dis_cols = new JMenu(
+            MessageManager.getString("action.displayed_columns"));
+    final IdColumns id_cols = ap.av.getIdColumns();
+    id_cols.updateTypeList();
+    for (final IdColumn col : id_cols.getIdColumns())
+    {
+      JMenuItem col_entry = new JCheckBoxMenuItem(col.getLabel(),
+              col.isVisible());
+      col_entry.addActionListener(new ActionListener()
+      {
+
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          id_cols.toggleVisible(col.getLabel());
+        }
+      });
+      dis_cols.add(col_entry);
+    }
+    add(dis_cols);
+  }
+
   /**
    * Adds
    * <ul>
index 42768a6..7e0b6b4 100644 (file)
@@ -72,6 +72,7 @@ import jalview.util.Comparison;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
+import jalview.viewmodel.seqfeatures.IdColumns;
 import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
 import jalview.workers.ComplementConsensusThread;
@@ -3122,6 +3123,28 @@ public abstract class AlignmentViewport
             false));
   }
 
+  /**
+   * ordered list of annotation values displayed per sequence in ID panel
+   */
+  private IdColumns id_columns = null;
+
+  /**
+   * available and currently visible columns for this view
+   */
+  @Override
+  public IdColumns getIdColumns()
+  {
+    if (alignment == null)
+    {
+      return null;
+    }
+    if (id_columns == null)
+    {
+      id_columns = new IdColumns(alignment);
+    }
+    return id_columns;
+  }
+
   public void setSavedUpToDate(boolean s)
   {
     setSavedUpToDate(s, QuitHandler.Message.UNSAVED_CHANGES);
diff --git a/src/jalview/viewmodel/seqfeatures/IdColumn.java b/src/jalview/viewmodel/seqfeatures/IdColumn.java
new file mode 100644 (file)
index 0000000..4b1cfa1
--- /dev/null
@@ -0,0 +1,49 @@
+package jalview.viewmodel.seqfeatures;
+
+public class IdColumn
+{
+  String name;
+
+  String descr;
+
+  boolean visible;
+
+  /**
+   * name of the feature this column shows
+   */
+  String featureTypeName = null;
+
+  /**
+   * group to select feature(s)
+   */
+  String featureGroupName = null;
+
+  /**
+   * Label string for annotation row
+   */
+  String annotationTypeName = null;
+
+  /**
+   * calcId for annotation row
+   */
+  String annotationTypeCalcId = null;
+
+  public String DbRefName = null;
+
+  public IdColumn(String string, String description, boolean b)
+  {
+    name = string;
+    descr = description;
+    visible = b;
+  }
+
+  public String getLabel()
+  {
+    return name;
+  }
+
+  public boolean isVisible()
+  {
+    return visible;
+  }
+}
\ No newline at end of file
diff --git a/src/jalview/viewmodel/seqfeatures/IdColumns.java b/src/jalview/viewmodel/seqfeatures/IdColumns.java
new file mode 100644 (file)
index 0000000..a938d63
--- /dev/null
@@ -0,0 +1,221 @@
+package jalview.viewmodel.seqfeatures;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Vector;
+
+import jalview.api.DBRefEntryI;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.SequenceFeaturesI;
+
+public class IdColumns
+{
+
+  AlignmentI alignment = null;
+
+  public IdColumns(AlignmentI al)
+  {
+    alignment = al;
+    columns.put(STRUCTURES_NUM.getLabel(), STRUCTURES_NUM);
+    updateTypeList();
+  }
+
+  public final static IdColumn STRUCTURES_NUM;
+  static
+  {
+    STRUCTURES_NUM = new IdColumn("3D Structures",
+            "Number of associated structure files", false);
+  }
+
+  LinkedHashMap<String, IdColumn> columns = new LinkedHashMap<String, IdColumn>();
+
+  /**
+   * register a feature on the columnset
+   * 
+   * @param sf
+   * @return true if feature was not seen before
+   */
+  boolean updateListForFeature(SequenceFeature sf)
+  {
+    String colname = sf.getType();
+    if (columns.get(colname) != null)
+    {
+      return false;
+    }
+
+    IdColumn col = new IdColumn(colname,
+            "Nonpositional feature: " + colname, false);
+    col.featureTypeName = sf.getType();
+    // col.featureGroupName = sf.getFeatureGroup();
+    columns.put(colname, col);
+    return true;
+  }
+
+  boolean updateListForDbxref(DBRefEntryI dbref)
+  {
+    String colname = dbref.getSource();
+    if (columns.get(colname) != null)
+    {
+      return false;
+    }
+
+    IdColumn col = new IdColumn(colname,
+            "Database CrossReference: " + colname, false);
+    col.DbRefName = colname;
+    // col.featureGroupName = sf.getFeatureGroup();
+    columns.put(colname, col);
+    return true;
+  }
+
+  public void updateTypeList()
+  {
+    for (SequenceI sq : alignment.getSequences())
+    {
+      SequenceI seq = sq;
+      while (seq.getDatasetSequence() != null)
+      {
+        seq = seq.getDatasetSequence();
+      }
+
+      SequenceFeaturesI sqf = seq.getFeatures();
+      List<SequenceFeature> nonpos = sqf.getNonPositionalFeatures();
+      if (nonpos != null)
+      {
+        for (SequenceFeature sf : nonpos)
+        {
+          updateListForFeature(sf);
+        }
+      }
+      if (seq.getDBRefs() != null)
+      {
+        for (DBRefEntryI dbr : seq.getDBRefs())
+        {
+          updateListForDbxref(dbr);
+        }
+      }
+    }
+  }
+
+  public void toggleVisible(String column)
+  {
+    IdColumn col = columns.get(column);
+    if (col != null)
+    {
+      col.visible = !col.visible;
+    }
+  }
+
+  public List<IdColumn> getVisible()
+  {
+    // probably want to cache this
+    ArrayList<IdColumn> vis = new ArrayList();
+    for (IdColumn col : columns.values())
+    {
+      if (col.visible)
+      {
+        vis.add(col);
+      }
+    }
+    return vis;
+  }
+
+  public final class ColumnCell
+  {
+    public final String label;
+
+    public final Color bg;
+
+    public final Color fg;
+
+    public ColumnCell(final String label, final Color bg, final Color fg)
+    {
+      this.label = label;
+      this.bg = bg;
+      this.fg = fg;
+    }
+  }
+
+  /**
+   * render the column value for this sequence
+   * 
+   * @param seq
+   * @param col
+   * @return
+   */
+  public ColumnCell getCellFor(SequenceI seq, IdColumn col)
+  {
+    ColumnCell cell = null;
+    if (col != null)
+    {
+      SequenceI dseq = seq;
+      while (dseq.getDatasetSequence() != null)
+      {
+        dseq = dseq.getDatasetSequence();
+      }
+      if (col == STRUCTURES_NUM)
+      {
+        Vector pdbE = dseq.getAllPDBEntries();
+        if (pdbE == null)
+        {
+          return null;
+        }
+        return new ColumnCell("" + pdbE.size(), Color.red, Color.white);
+      }
+      if (col.featureTypeName != null)
+      {
+        List<SequenceFeature> np = dseq.getFeatures()
+                .getNonPositionalFeatures(col.featureTypeName);
+        if (np != null)
+        {
+          for (SequenceFeature npfeat : np)
+          {
+            // nb deal with multiplicities!
+            if (col.featureGroupName == null || (npfeat.featureGroup != null
+                    && npfeat.featureGroup.equals(col.featureGroupName)))
+            {
+              Color fg = Color.black;
+              Color bg = Color.white;
+
+              return new ColumnCell(npfeat.description, fg, bg);
+            }
+          }
+        }
+      }
+      if (col.DbRefName != null)
+      {
+        List<DBRefEntryI> refs = new ArrayList<DBRefEntryI>();
+        for (DBRefEntryI dbr : dseq.getDBRefs())
+        {
+          if (dbr.getSource().equals(col.DbRefName))
+          {
+            refs.add(dbr);
+          }
+        }
+        if (refs.size()==0)
+        {
+          return null;
+        }
+        StringBuilder dbr = new StringBuilder();
+        dbr.append(refs.size());
+        for (DBRefEntryI ref : refs)
+        {
+          dbr.append(" "+ref.getAccessionId());
+        }
+        return new ColumnCell(dbr.toString(), Color.black, Color.white);
+
+      }
+    }
+    // no value for this sequence in given column
+    return null;
+  }
+
+  public IdColumn[] getIdColumns()
+  {
+    return columns.values().toArray(new IdColumn[0]);
+  }
+}