From 31714b90648bba773f68736b8c1700ae53805eee Mon Sep 17 00:00:00 2001 From: Jim Procter Date: Fri, 22 Jul 2022 16:09:48 +0100 Subject: [PATCH] JAL-4047 - proof of concept for JAL-4048 - display columns of info in sequence ID panel. --- src/jalview/api/AlignViewportI.java | 17 +- src/jalview/gui/AlignViewport.java | 22 +-- src/jalview/gui/IdCanvas.java | 41 ++++- src/jalview/gui/IdPanel.java | 50 +++++- src/jalview/gui/PopupMenu.java | 33 +++- src/jalview/viewmodel/AlignmentViewport.java | 48 +++-- src/jalview/viewmodel/seqfeatures/IdColumn.java | 49 ++++++ src/jalview/viewmodel/seqfeatures/IdColumns.java | 204 ++++++++++++++++++++++ 8 files changed, 428 insertions(+), 36 deletions(-) create mode 100644 src/jalview/viewmodel/seqfeatures/IdColumn.java create mode 100644 src/jalview/viewmodel/seqfeatures/IdColumns.java diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index d15c5fb..5715a95 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -20,6 +20,13 @@ */ 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 getViewAsVisibleContigs(boolean selectedRegionOnly); + + IdColumns getIdColumns(); } diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 30ccdbe..a43b89b 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -20,6 +20,17 @@ */ 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! * diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java index c94dee0..e284bbe 100755 --- a/src/jalview/gui/IdCanvas.java +++ b/src/jalview/gui/IdCanvas.java @@ -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 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); diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index 7b491e4..fdca723 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -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 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. diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index 6903034..f87d841 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -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 *
    diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 08af2ec..0820bcf 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -20,6 +20,18 @@ */ 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 index 0000000..4b1cfa1 --- /dev/null +++ b/src/jalview/viewmodel/seqfeatures/IdColumn.java @@ -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 index 0000000..2f113f2 --- /dev/null +++ b/src/jalview/viewmodel/seqfeatures/IdColumns.java @@ -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 columns = new LinkedHashMap(); + + /** + * 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 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 getVisible() + { + // probably want to cache this + ArrayList 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 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]); + } +} -- 1.7.10.2