JAL-4047 - proof of concept for JAL-4048 - display columns of info in sequence ID...
authorJim Procter <j.procter@dundee.ac.uk>
Fri, 22 Jul 2022 15:09:48 +0000 (16:09 +0100)
committerJim Procter <j.procter@dundee.ac.uk>
Fri, 22 Jul 2022 15:09:48 +0000 (16:09 +0100)
src/jalview/api/AlignViewportI.java
src/jalview/gui/AlignViewport.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 d15c5fb..5715a95 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;
@@ -35,13 +42,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
@@ -552,4 +553,6 @@ public interface AlignViewportI extends ViewStyleI
    * @return
    */
   Iterator<int[]> getViewAsVisibleContigs(boolean selectedRegionOnly);
+
+  IdColumns getIdColumns();
 }
index 30ccdbe..a43b89b 100644 (file)
  */
 package jalview.gui;
 
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Rectangle;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.swing.JInternalFrame;
+
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.api.AlignViewportI;
@@ -53,17 +64,6 @@ import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.AutoCalcSetting;
 
-import java.awt.Container;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Rectangle;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.List;
-
-import javax.swing.JInternalFrame;
-
 /**
  * DOCUMENT ME!
  * 
index c94dee0..e284bbe 100755 (executable)
@@ -36,6 +36,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!
@@ -308,7 +311,16 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     }
 
     // Now draw the id strings
-    int panelWidth = getWidth();
+    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()));
     int xPos = 0;
 
     // Now draw the id strings
@@ -320,7 +332,6 @@ public class IdCanvas extends JPanel implements ViewportListenerI
       {
         continue;
       }
-
       if (hasHiddenRows || alignViewport.isDisplayReferenceSeq())
       {
         g.setFont(getHiddenFont(sequence, alignViewport));
@@ -364,6 +375,32 @@ public class IdCanvas extends JPanel implements ViewportListenerI
               (((i - startSeq) * charHeight) + charHeight)
                       - (charHeight / 5));
 
+      if (visible != null && visible.size() > 0)
+      {
+        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, (i - startSeq) * charHeight, xPos + colWid - 4,
+                    charHeight);
+          }
+          else
+          {
+            g.setColor(col_cell.bg);
+            g.fillRect(xPos, (i - startSeq) * charHeight, xPos + colWid - 4,
+                    charHeight);
+            g.setColor(col_cell.fg);
+            g.drawString(col_cell.label, xPos,
+                    (((i - startSeq) * charHeight) + charHeight)
+                            - (charHeight / 5));
+          }
+          xPos += colWid;
+          g.setColor(currentTextColor);
+        }
+      }
       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 6903034..f87d841 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.gui;
 
-import java.util.Locale;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.event.ActionEvent;
@@ -34,6 +32,7 @@ import java.util.Collections;
 import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.SortedMap;
@@ -88,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
@@ -727,9 +728,37 @@ 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();
+    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 08af2ec..0820bcf 100644 (file)
  */
 package jalview.viewmodel;
 
+import java.awt.Color;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.Conservation;
 import jalview.analysis.TreeModel;
@@ -55,24 +67,13 @@ 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;
 import jalview.workers.ConsensusThread;
 import jalview.workers.StrucConsensusThread;
 
-import java.awt.Color;
-import java.beans.PropertyChangeSupport;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
 /**
  * base class holding visualization and analysis attributes and common logic for
  * an active alignment view displayed in the GUI
@@ -3096,4 +3097,27 @@ public abstract class AlignmentViewport
     return (alignment.getHiddenColumns().getVisContigsIterator(start, end,
             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;
+  }
+
 }
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..2f113f2
--- /dev/null
@@ -0,0 +1,204 @@
+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.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);
+        }
+      }
+      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)
+    {
+      if (col == STRUCTURES_NUM)
+      {
+        while (seq.getDatasetSequence() != null)
+        {
+          seq = seq.getDatasetSequence();
+        }
+        Vector pdbE = seq.getAllPDBEntries();
+        if (pdbE == null)
+        {
+          return null;
+        }
+        return new ColumnCell("" + pdbE.size(), Color.red, Color.white);
+      }
+      if (col.featureTypeName != null)
+      {
+        List<SequenceFeature> np = seq.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)
+      {
+        for (DBRefEntryI dbr : seq.getDBRefs())
+        {
+          if (dbr.getSource().equals(col.DbRefName))
+          {
+            return new ColumnCell(dbr.getAccessionId(), Color.black,
+                    Color.white);
+          }
+        }
+      }
+    }
+    // no value for this sequence in given column
+    return null;
+  }
+
+  public IdColumn[] getIdColumns()
+  {
+    return columns.values().toArray(new IdColumn[0]);
+  }
+}