From f441357783f32298b738d32d5c87000ac0e33768 Mon Sep 17 00:00:00 2001 From: amwaterhouse Date: Wed, 7 Jun 2006 12:52:43 +0000 Subject: [PATCH] Tooltips for features, links for features added --- src/jalview/appletgui/APopupMenu.java | 56 +++++---- src/jalview/appletgui/AlignFrame.java | 68 ++++++++++- src/jalview/appletgui/AlignViewport.java | 2 +- src/jalview/appletgui/FeatureRenderer.java | 42 ++++++- src/jalview/appletgui/FeatureSettings.java | 142 +++++++++++++++++++++- src/jalview/appletgui/IdPanel.java | 36 +++++- src/jalview/appletgui/SeqCanvas.java | 15 +-- src/jalview/appletgui/SeqPanel.java | 179 ++++++++++++++++++---------- 8 files changed, 435 insertions(+), 105 deletions(-) diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java index 8f6e49f..1c9b3cd 100755 --- a/src/jalview/appletgui/APopupMenu.java +++ b/src/jalview/appletgui/APopupMenu.java @@ -87,6 +87,11 @@ public class APopupMenu showText.setState(sg.getDisplayText()); showColourText.setState(sg.getColourText()); showBoxes.setState(sg.getDisplayBoxes()); + if (!ap.av.alignment.getGroups().contains(sg)) + { + groupMenu.remove(unGroupMenuItem); + } + } else { @@ -94,12 +99,7 @@ public class APopupMenu remove(editMenu); } - if (!ap.av.alignment.getGroups().contains(sg)) - { - groupMenu.remove(unGroupMenuItem); - } - - if (seq != null && links!=null) + if (links!=null) { Menu linkMenu = new Menu("Link"); MenuItem item; @@ -109,13 +109,22 @@ public class APopupMenu link = links.elementAt(i).toString(); final String target = link.substring(0, link.indexOf("|")); item = new MenuItem(target); - String id = seq.getName(); - if(id.indexOf("|")>-1) - id = id.substring(id.lastIndexOf("|")+1); - final String url = link.substring(link.indexOf("|")+1, link.indexOf("$SEQUENCE_ID$")) - + id + - link.substring(link.indexOf("$SEQUENCE_ID$") + 13); + final String url; + + if (link.indexOf("$SEQUENCE_ID$") > -1) + { + String id = seq.getName(); + if (id.indexOf("|") > -1) + id = id.substring(id.lastIndexOf("|") + 1); + + url = link.substring(link.indexOf("|") + 1, + link.indexOf("$SEQUENCE_ID$")) + + id + + link.substring(link.indexOf("$SEQUENCE_ID$") + 13); + } + else + url = link.substring(link.lastIndexOf("|")+1); item.addActionListener(new java.awt.event.ActionListener() { @@ -128,18 +137,19 @@ public class APopupMenu } add(linkMenu); - item = new MenuItem("Show PDB Structure"); - item.addActionListener(new java.awt.event.ActionListener() - { - public void actionPerformed(ActionEvent e) - { - addPDB(seq); - } - }); - - add(item); - + if(seq!=null) + { + item = new MenuItem("Show PDB Structure"); + item.addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(ActionEvent e) + { + addPDB(seq); + } + }); + add(item); + } } } diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index 1ecf254..77154e0 100755 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -172,18 +172,26 @@ public class AlignFrame extends Frame implements ActionListener, StringTokenizer st; SequenceFeature sf; FeatureRenderer fr = alignPanel.seqPanel.seqCanvas.getFeatureRenderer(); - int lineNo = 0; - String featureGroup = null; + String featureGroup = null, groupLink = null; + Hashtable typeLink = new Hashtable(); + while ( (line = in.readLine()) != null) { - lineNo++; st = new StringTokenizer(line, "\t"); - if (st.countTokens() == 2) + if(!st.hasMoreTokens()) + continue; + + if (st.countTokens() < 4) { type = st.nextToken(); if(type.equalsIgnoreCase("startgroup")) { featureGroup = st.nextToken(); + if (st.hasMoreElements()) + { + groupLink = st.nextToken(); + fr.addFeatureLink(featureGroup, groupLink); + } } else if(type.equalsIgnoreCase("endgroup")) { @@ -191,11 +199,19 @@ public class AlignFrame extends Frame implements ActionListener, //but at present theres no way of showing more than 1 group st.nextToken(); featureGroup = null; + groupLink = null; } else { UserColourScheme ucs = new UserColourScheme(st.nextToken()); fr.setColour(type, ucs.findColour("A")); + if (st.hasMoreElements()) + { + String link = st.nextToken(); + typeLink.put(type, link); + fr.addFeatureLink(type, link); + } + } continue; } @@ -203,6 +219,7 @@ public class AlignFrame extends Frame implements ActionListener, while (st.hasMoreElements()) { desc = st.nextToken(); + token = st.nextToken(); if (!token.equals("ID_NOT_SPECIFIED")) { @@ -229,9 +246,21 @@ public class AlignFrame extends Frame implements ActionListener, fr.setColour(type, ucs.findColour("A")); } - sf = new SequenceFeature(type, desc, "", start, end, featureGroup); - + sf = new SequenceFeature(type, desc, start, end, 0f, featureGroup); seq.addSequenceFeature(sf); + + if(groupLink!=null) + { + sf.addLink(groupLink); + sf.description += "%LINK%"; + } + if(typeLink.containsKey(type)) + { + sf.addLink(typeLink.get(type).toString()); + sf.description += "%LINK%"; + } + + parseDescriptionHTML(sf); } } @@ -248,6 +277,33 @@ public class AlignFrame extends Frame implements ActionListener, } } + void parseDescriptionHTML(SequenceFeature sf) + { + StringBuffer sb = new StringBuffer(); + StringTokenizer st = new StringTokenizer(sf.getDescription(), "<"); + String token, link; + while(st.hasMoreElements()) + { + token = st.nextToken("<>"); + if(token.equalsIgnoreCase("html") || token.startsWith("/")) + continue; + + if(token.startsWith("a href=")) + { + link = token.substring(token.indexOf("\"")+1, token.length()-1); + String label = st.nextToken("<>"); + sf.addLink(label+"|"+link); + sb.append(label+"%LINK%"); + } + else if(token.equalsIgnoreCase("br")) + sb.append("\n"); + else + sb.append(token); + } + + sf.description = sb.toString(); + } + public void keyPressed(KeyEvent evt) { if (viewport.cursorMode diff --git a/src/jalview/appletgui/AlignViewport.java b/src/jalview/appletgui/AlignViewport.java index 481a657..152966e 100755 --- a/src/jalview/appletgui/AlignViewport.java +++ b/src/jalview/appletgui/AlignViewport.java @@ -79,7 +79,7 @@ public class AlignViewport // The following vector holds the features which are // currently visible, in the correct order or rendering - Hashtable featuresDisplayed = null; + Hashtable featuresDisplayed; public Vector vconsensus; diff --git a/src/jalview/appletgui/FeatureRenderer.java b/src/jalview/appletgui/FeatureRenderer.java index 900de83..aa3f830 100755 --- a/src/jalview/appletgui/FeatureRenderer.java +++ b/src/jalview/appletgui/FeatureRenderer.java @@ -40,6 +40,11 @@ public class FeatureRenderer // particular type Hashtable featureGroups = null; + // Holds web links for feature groups and feature types + // in the form label|link + Hashtable featureLinks = null; + + // This is actually an Integer held in the hashtable, // Retrieved using the key feature type Object currentColour; @@ -49,6 +54,10 @@ public class FeatureRenderer FontMetrics fm; int charOffset; + float transparency = 1f; + + TransparencySetter transparencySetter = null; + /** * Creates a new FeatureRenderer object. * @@ -58,6 +67,17 @@ public class FeatureRenderer { this.av = av; initColours(); + + if(!System.getProperty("java.version").startsWith("1.1")) + transparencySetter = new TransparencySetter(); + } + + public void addFeatureLink(String feature, String link) + { + if(featureLinks == null) + featureLinks = new Hashtable(); + + featureLinks.put(feature, link); } @@ -160,6 +180,10 @@ public class FeatureRenderer || seq.getSequenceFeatures().length==0) return; + if(transparencySetter!=null && g!=null) + { + transparencySetter.setTransparency(g, transparency); + } if (av.featuresDisplayed == null || renderOrder==null) { @@ -248,6 +272,11 @@ public class FeatureRenderer } } + + if(transparencySetter!=null && g!=null) + { + transparencySetter.setTransparency(g, 1.0f); + } } @@ -417,8 +446,19 @@ public class FeatureRenderer featureColours.put("unsure residue", new Color(0, 75, 245)); featureColours.put("zinc finger region", new Color(0, 65, 255)); } - } + class TransparencySetter + { + void setTransparency(Graphics g, float value) + { + Graphics2D g2 = (Graphics2D) g; + g2.setComposite( + AlphaComposite.getInstance( + AlphaComposite.SRC_OVER, value)); + } + } + + diff --git a/src/jalview/appletgui/FeatureSettings.java b/src/jalview/appletgui/FeatureSettings.java index 6710ed4..b642fb3 100755 --- a/src/jalview/appletgui/FeatureSettings.java +++ b/src/jalview/appletgui/FeatureSettings.java @@ -25,7 +25,7 @@ import java.awt.event.*; public class FeatureSettings extends Panel implements ItemListener, - MouseListener, MouseMotionListener + MouseListener, MouseMotionListener, ActionListener, AdjustmentListener { FeatureRenderer fr; AlignmentPanel ap; @@ -35,6 +35,8 @@ public class FeatureSettings extends Panel implements ItemListener, Panel featurePanel = new Panel(); ScrollPane scrollPane; boolean alignmentHasFeatures = false; + Image linkImage; + Scrollbar transparency ; public FeatureSettings(AlignViewport av, final AlignmentPanel ap) { @@ -42,6 +44,26 @@ public class FeatureSettings extends Panel implements ItemListener, this.av = av; fr = ap.seqPanel.seqCanvas.getFeatureRenderer(); + transparency = new Scrollbar(Scrollbar.HORIZONTAL, + 100 - (int)(fr.transparency*100), 1, 1, 100); + + if(fr.transparencySetter!=null) + { + transparency.addAdjustmentListener(this); + } + else + transparency.setEnabled(false); + + java.net.URL url = getClass().getResource("/images/link.gif"); + if (url != null) + { + linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url); + } + + + if(av.featuresDisplayed==null) + fr.findAllFeatures(); + setTableData(); this.setLayout(new BorderLayout()); @@ -50,6 +72,28 @@ public class FeatureSettings extends Panel implements ItemListener, if (alignmentHasFeatures) add(scrollPane, BorderLayout.CENTER); + Button invert = new Button("Invert Selection"); + invert.addActionListener(this); + + Panel lowerPanel = new Panel(new GridLayout(2,1,5,10)); + lowerPanel.add(invert); + + Panel tPanel = new Panel(new BorderLayout()); + + if(fr.transparencySetter!=null) + { + tPanel.add(transparency, BorderLayout.CENTER); + tPanel.add(new Label("Transparency"), BorderLayout.EAST); + } + else + tPanel.add(new Label("Transparency not available in this web browser"), BorderLayout.CENTER); + + lowerPanel.add(tPanel, BorderLayout.SOUTH); + + add(lowerPanel, BorderLayout.SOUTH); + + + if(groupPanel!=null) { groupPanel.setLayout( @@ -119,7 +163,14 @@ public class FeatureSettings extends Panel implements ItemListener, groupPanel = new Panel(); } - Checkbox check = new Checkbox(group, visible); + Checkbox check = new MyCheckbox( + group, + visible, + (fr.featureLinks!=null && fr.featureLinks.containsKey(group)) + ); + + + check.addMouseListener(this); check.setFont(new Font("Serif", Font.BOLD, 12)); check.addItemListener(this); groupPanel.add(check); @@ -206,6 +257,7 @@ public class FeatureSettings extends Panel implements ItemListener, // now add checkboxes which should be visible, // if they have not already been added Enumeration en = visibleChecks.elements(); + while(en.hasMoreElements()) { addCheck(groupsChanged, en.nextElement().toString()); @@ -246,7 +298,11 @@ public class FeatureSettings extends Panel implements ItemListener, selected = true; } - check = new Checkbox(type, selected); + check = new MyCheckbox(type, + selected, + (fr.featureLinks!=null && fr.featureLinks.containsKey(type)) + ); + check.addMouseListener(this); check.addMouseMotionListener(this); check.setBackground(fr.getColour(type)); @@ -255,6 +311,16 @@ public class FeatureSettings extends Panel implements ItemListener, } } + public void actionPerformed(ActionEvent evt) + { + for(int i=0; iselectedCheck.stringWidth+20) + { + evt.consume(); + } + } + } public void mouseDragged(MouseEvent evt) { + if(((Component)evt.getSource()).getParent()!=featurePanel) + return; dragging = true; } public void mouseReleased(MouseEvent evt) { + if(((Component)evt.getSource()).getParent()!=featurePanel) + return; + Component comp = null; Checkbox target = null; @@ -364,7 +449,22 @@ public class FeatureSettings extends Panel implements ItemListener, public void mouseExited(MouseEvent evt){} public void mouseClicked(MouseEvent evt) { - Checkbox check = (Checkbox) evt.getSource(); + MyCheckbox check = (MyCheckbox) evt.getSource(); + + if (fr.featureLinks.containsKey(check.getLabel())) + { + if (evt.getX() > check.stringWidth + 20) + { + evt.consume(); + String link = fr.featureLinks.get(check.getLabel()).toString(); + ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1), + link.substring(0, link.indexOf("|"))); + } + } + + if(check.getParent()!=featurePanel) + return; + if(evt.getClickCount()>1) { new UserDefinedColours(this, check.getLabel(), @@ -372,4 +472,34 @@ public class FeatureSettings extends Panel implements ItemListener, } } public void mouseMoved(MouseEvent evt){} + + public void adjustmentValueChanged(AdjustmentEvent evt) + { + fr.transparency = ( (float) (100 - transparency.getValue()) / 100f); + ap.seqPanel.seqCanvas.repaint(); + + } + + class MyCheckbox extends Checkbox + { + public int stringWidth; + boolean hasLink; + public MyCheckbox(String label, boolean checked, boolean haslink) + { + super(label, checked); + FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont()); + stringWidth = fm.stringWidth(label); + this.hasLink = haslink; + } + + public void paint(Graphics g) + { + if (hasLink) + g.drawImage(linkImage, stringWidth + 25,( + getSize().height-linkImage.getHeight(this))/2, + this); + } + } } + + diff --git a/src/jalview/appletgui/IdPanel.java b/src/jalview/appletgui/IdPanel.java index 71acfa2..1a61429 100755 --- a/src/jalview/appletgui/IdPanel.java +++ b/src/jalview/appletgui/IdPanel.java @@ -69,8 +69,42 @@ public class IdPanel } } + Tooltip tooltip; public void mouseMoved(MouseEvent e) - {} + { + int y = e.getY(); + if (av.getWrapAlignment()) + { + y -= 2 * av.charHeight; + } + + int seq = av.getIndex(y); + if (seq == -1) + { + return; + } + + SequenceI sequence = av.getAlignment().getSequenceAt(seq); + + if(sequence.getDescription()==null) + { + if(tooltip!=null) + tooltip.setVisible(false); + tooltip = null; + return; + } + + if (tooltip == null) + tooltip = new Tooltip( + sequence.getDisplayId(true) + + "\n" + sequence.getDescription(), idCanvas); + else + tooltip.setTip(sequence.getDisplayId(true) + + "\n" + sequence.getDescription()); + + tooltip.repaint(); + + } public void mouseDragged(MouseEvent e) { diff --git a/src/jalview/appletgui/SeqCanvas.java b/src/jalview/appletgui/SeqCanvas.java index 3e47139..02c9990 100755 --- a/src/jalview/appletgui/SeqCanvas.java +++ b/src/jalview/appletgui/SeqCanvas.java @@ -294,6 +294,8 @@ public class SeqCanvas int LABEL_WEST, LABEL_EAST; public int getWrappedCanvasWidth(int cwidth) { + cwidth -= cwidth % av.charWidth; + FontMetrics fm = getFontMetrics(av.getFont()); LABEL_EAST = 0; @@ -334,15 +336,12 @@ public class SeqCanvas FontMetrics fm = getFontMetrics(av.getFont()); - int LABEL_EAST = 0; if (av.scaleRightWrapped) { LABEL_EAST = fm.stringWidth(getMask()); } - int LABEL_WEST = 0; - if (av.scaleLeftWrapped) { LABEL_WEST = fm.stringWidth(getMask()); @@ -496,7 +495,7 @@ public class SeqCanvas groupIndex = 0; } - if ( group != null) + if ( group != null ) { do { @@ -505,6 +504,7 @@ public class SeqCanvas boolean inGroup = false; int top = -1; int bottom = -1; + int alHeight = av.alignment.getHeight()-1; for (i = startSeq; i < endSeq; i++) { @@ -522,9 +522,10 @@ public class SeqCanvas group.sequences.contains(av.alignment.getSequenceAt( i))) { - if ((bottom == -1) && - !group.sequences.contains( - av.alignment.getSequenceAt(i + 1))) + if ( (bottom == -1) && + (i >= alHeight || + !group.sequences.contains( + av.alignment.getSequenceAt(i + 1)))) { bottom = sy + av.charHeight; } diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index 87e9bec..4b7a45e 100755 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -25,6 +25,8 @@ import java.awt.event.*; import jalview.datamodel.*; import jalview.schemes.*; +import java.util.Vector; + public class SeqPanel extends Panel implements MouseMotionListener, MouseListener { @@ -312,6 +314,7 @@ public class SeqPanel ")"); } } + ap.alignFrame.statusBar.setText(text.toString()); } @@ -535,48 +538,71 @@ public class SeqPanel && sequence.getSequenceFeatures()!=null && av.featuresDisplayed!=null) { + StringBuffer featureText = new StringBuffer(); + Vector allFeatures = getAllFeaturesAtRes(sequence, sequence.findPosition(res)); + int index = 0; - sequence.getSequenceFeatures(); - boolean first = true; - while (index < sequence.getSequenceFeatures().length) + while (index < allFeatures.size()) { - SequenceFeature sf = sequence.getSequenceFeatures()[index]; - if (sf.getBegin() <= sequence.findPosition(res) && - sf.getEnd() >= sequence.findPosition(res)) - { - if(!av.featuresDisplayed.containsKey(sf.getType())) - { - index++; - continue; - } + SequenceFeature sf = (SequenceFeature) allFeatures.elementAt(index); - if(first) - { - text.append(" Sequence Feature:"); - first = false; - } - - text.append(" "+sf.getType()); + featureText.append(sf.getType()+" "+sf.begin+":"+sf.end); - if(sf.getDescription()!=null) - text.append(" "+sf.getDescription()); + if (sf.getDescription() != null) + featureText.append(" " + sf.getDescription()); - if (sf.getStatus()!=null && sf.getStatus().length() > 0) - { - text.append(" (" + sf.getStatus() + ")"); - } - text.append("; "); + if (sf.getStatus() != null && sf.getStatus().length() > 0) + { + featureText.append(" (" + sf.getStatus() + ")"); } + featureText.append("\n"); index++; - } + + + if (tooltip == null) + tooltip = new Tooltip(featureText.toString(), seqCanvas); + else + tooltip.setTip(featureText.toString()); + + tooltip.repaint(); + + // text.append(" Sequence Feature:"); + // text.append(featureText); + } + ap.alignFrame.statusBar.setText(text.toString()); } + Vector getAllFeaturesAtRes(SequenceI seq, int res) + { + Vector allFeatures = new Vector(); + int index = 0; + if(seq.getSequenceFeatures()!=null) + { + while (index < seq.getSequenceFeatures().length) + { + SequenceFeature sf = seq.getSequenceFeatures()[index]; + if (sf.getBegin() <= res && + sf.getEnd() >= res) + { + if (av.featuresDisplayed.containsKey(sf.getType())) + { + allFeatures.addElement(sf); + } + } + index++; + } + } + return allFeatures; + } + + Tooltip tooltip; + public void mouseDragged(MouseEvent evt) { if (mouseWheelPressed) @@ -1048,57 +1074,90 @@ public class SeqPanel allGroups[i].getEndRes() >= res) { stretchGroup = allGroups[i]; - av.setSelectionGroup(stretchGroup); break; } } } + av.setSelectionGroup(stretchGroup); } - if (stretchGroup == null) + if (av.cursorMode) { - // define a new group here - SequenceGroup sg = new SequenceGroup(); - sg.setStartRes(res); - sg.setEndRes(res); - sg.addSequence(sequence, false); - av.setSelectionGroup(sg); - stretchGroup = sg; - - if (av.getConservationSelected()) - { - SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(), - "Background"); - } - if (av.getAbovePIDThreshold()) - { - SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(), - "Background"); - } - + seqCanvas.cursorX = findRes(evt); + seqCanvas.cursorY = findSeq(evt); + seqCanvas.repaint(); + return; } + + // DETECT RIGHT MOUSE BUTTON IN AWT - else if ( (evt.getModifiers() & InputEvent.BUTTON3_MASK) == + if ( (evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK) { - APopupMenu popup = new APopupMenu(ap, null, null); + Vector allFeatures = getAllFeaturesAtRes(sequence, + sequence.findPosition(res)); + + Vector links = null; + if(allFeatures!=null) + { + for (int i = 0; i < allFeatures.size(); i++) + { + SequenceFeature sf = (SequenceFeature) allFeatures.elementAt(i); + if (sf.links != null) + { + links = new Vector(); + for (int j = 0; j < sf.links.size(); j++) + { + links.addElement(sf.links.elementAt(j)); + } + } + } + } + APopupMenu popup = new APopupMenu(ap, null, links); this.add(popup); popup.show(this, evt.getX(), evt.getY()); + ap.repaint(); } - - if (stretchGroup != null && stretchGroup.getEndRes() == res) + else { - // Edit end res position of selected group - changeEndRes = true; - } + //Only if left mouse button do we want to change group sizes - else if (stretchGroup != null && stretchGroup.getStartRes() == res) - { - // Edit end res position of selected group - changeStartRes = true; - } + if (stretchGroup == null) + { + // define a new group here + SequenceGroup sg = new SequenceGroup(); + sg.setStartRes(res); + sg.setEndRes(res); + sg.addSequence(sequence, false); + av.setSelectionGroup(sg); + stretchGroup = sg; + + if (av.getConservationSelected()) + { + SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(), + "Background"); + } + if (av.getAbovePIDThreshold()) + { + SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(), + "Background"); + } + } + + if (stretchGroup != null && stretchGroup.getEndRes() == res) + { + // Edit end res position of selected group + changeEndRes = true; + } + + else if (stretchGroup != null && stretchGroup.getStartRes() == res) + { + // Edit end res position of selected group + changeStartRes = true; + } + } } public void doMouseReleasedDefineMode(MouseEvent evt) -- 1.7.10.2