2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3 * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 package jalview.appletgui;
25 import java.awt.event.*;
27 import jalview.appletgui.FeatureSettings.MyCheckbox;
28 import jalview.datamodel.*;
36 public class FeatureRenderer
40 Hashtable featureColours = new Hashtable();
42 // A higher level for grouping features of a
44 Hashtable featureGroups = null;
46 // Holds web links for feature groups and feature types
47 // in the form label|link
48 Hashtable featureLinks = null;
50 // This is actually an Integer held in the hashtable,
51 // Retrieved using the key feature type
60 float transparency = 1f;
62 TransparencySetter transparencySetter = null;
65 * Creates a new FeatureRenderer object.
70 public FeatureRenderer(AlignViewport av)
74 if (!System.getProperty("java.version").startsWith("1.1"))
76 transparencySetter = new TransparencySetter();
80 public void transferSettings(FeatureRenderer fr)
82 renderOrder = fr.renderOrder;
83 featureGroups = fr.featureGroups;
84 featureColours = fr.featureColours;
85 transparency = fr.transparency;
88 static String lastFeatureAdded;
90 static String lastFeatureGroupAdded;
92 static String lastDescriptionAdded;
96 boolean deleteFeature = false;
100 boolean amendFeatures(final SequenceI[] sequences,
101 final SequenceFeature[] features, boolean newFeatures,
102 final AlignmentPanel ap)
104 Panel bigPanel = new Panel(new BorderLayout());
105 final TextField name = new TextField(16);
106 final TextField source = new TextField(16);
107 final TextArea description = new TextArea(3, 35);
108 final TextField start = new TextField(8);
109 final TextField end = new TextField(8);
110 final Choice overlaps;
111 Button deleteButton = new Button("Delete");
112 deleteFeature = false;
114 colourPanel = new Panel(null);
115 colourPanel.setSize(110, 15);
116 final FeatureRenderer fr = this;
118 Panel panel = new Panel(new GridLayout(3, 1));
122 // /////////////////////////////////////
123 // /MULTIPLE FEATURES AT SELECTED RESIDUE
124 if (!newFeatures && features.length > 1)
126 panel = new Panel(new GridLayout(4, 1));
128 tmp.add(new Label("Select Feature: "));
129 overlaps = new Choice();
130 for (int i = 0; i < features.length; i++)
132 String item = features[i].getType() + "/" + features[i].getBegin()
133 + "-" + features[i].getEnd();
135 if (features[i].getFeatureGroup() != null)
136 item += " (" + features[i].getFeatureGroup() + ")";
138 overlaps.addItem(item);
143 overlaps.addItemListener(new java.awt.event.ItemListener()
145 public void itemStateChanged(java.awt.event.ItemEvent e)
147 int index = overlaps.getSelectedIndex();
150 featureIndex = index;
151 name.setText(features[index].getType());
152 description.setText(features[index].getDescription());
153 source.setText(features[index].getFeatureGroup());
154 start.setText(features[index].getBegin() + "");
155 end.setText(features[index].getEnd() + "");
157 SearchResults highlight = new SearchResults();
158 highlight.addResult(sequences[0], features[index].getBegin(),
159 features[index].getEnd());
161 ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
164 Color col = getColour(name.getText());
167 col = new jalview.schemes.UserColourScheme()
168 .createColourFromName(name.getText());
171 colourPanel.setBackground(col);
178 // ////////////////////////////////////
182 tmp.add(new Label("Name: ", Label.RIGHT));
187 tmp.add(new Label("Group: ", Label.RIGHT));
192 tmp.add(new Label("Colour: ", Label.RIGHT));
193 tmp.add(colourPanel);
195 bigPanel.add(panel, BorderLayout.NORTH);
198 panel.add(new Label("Description: ", Label.RIGHT));
199 panel.add(new ScrollPane().add(description));
203 bigPanel.add(panel, BorderLayout.SOUTH);
206 panel.add(new Label(" Start:", Label.RIGHT));
208 panel.add(new Label(" End:", Label.RIGHT));
210 bigPanel.add(panel, BorderLayout.CENTER);
214 bigPanel.add(panel, BorderLayout.CENTER);
217 if (lastFeatureAdded == null)
219 if (features[0].type != null)
221 lastFeatureAdded = features[0].type;
225 lastFeatureAdded = "feature_1";
229 if (lastFeatureGroupAdded == null)
231 if (features[0].featureGroup != null)
233 lastFeatureGroupAdded = features[0].featureGroup;
237 lastFeatureAdded = "Jalview";
241 String title = newFeatures ? "Create New Sequence Feature(s)"
242 : "Amend/Delete Features for " + sequences[0].getName();
244 final JVDialog dialog = new JVDialog(ap.alignFrame, title, true, 385,
247 dialog.setMainPanel(bigPanel);
251 name.setText(lastFeatureAdded);
252 source.setText(lastFeatureGroupAdded);
256 dialog.ok.setLabel("Amend");
257 dialog.buttonPanel.add(deleteButton, 1);
258 deleteButton.addActionListener(new ActionListener()
260 public void actionPerformed(ActionEvent evt)
262 deleteFeature = true;
263 dialog.setVisible(false);
266 name.setText(features[0].getType());
267 source.setText(features[0].getFeatureGroup());
270 start.setText(features[0].getBegin() + "");
271 end.setText(features[0].getEnd() + "");
272 description.setText(features[0].getDescription());
274 Color col = getColour(name.getText());
277 col = new jalview.schemes.UserColourScheme()
278 .createColourFromName(name.getText());
281 colourPanel.setBackground(col);
283 dialog.setResizable(true);
285 colourPanel.addMouseListener(new java.awt.event.MouseAdapter()
287 public void mousePressed(java.awt.event.MouseEvent evt)
289 new UserDefinedColours(fr, ap.alignFrame);
293 dialog.setVisible(true);
295 jalview.io.FeaturesFile ffile = new jalview.io.FeaturesFile();
299 // This ensures that the last sequence
300 // is refreshed and new features are rendered
302 lastFeatureAdded = name.getText().trim();
303 lastFeatureGroupAdded = source.getText().trim();
304 lastDescriptionAdded = description.getText().replace('\n', ' ');
307 if (lastFeatureGroupAdded != null && lastFeatureGroupAdded.length() < 1)
308 lastFeatureGroupAdded = null;
312 SequenceFeature sf = features[featureIndex];
316 sf.type = lastFeatureAdded;
317 sf.featureGroup = lastFeatureGroupAdded;
318 sf.description = lastDescriptionAdded;
319 setColour(sf.type, colourPanel.getBackground());
322 sf.begin = Integer.parseInt(start.getText());
323 sf.end = Integer.parseInt(end.getText());
324 } catch (NumberFormatException ex)
328 ffile.parseDescriptionHTML(sf, false);
332 sequences[0].deleteFeature(sf);
338 if (dialog.accept && name.getText().length() > 0)
340 for (int i = 0; i < sequences.length; i++)
342 features[i].type = lastFeatureAdded;
343 features[i].featureGroup = lastFeatureGroupAdded;
344 features[i].description = lastDescriptionAdded;
345 sequences[i].addSequenceFeature(features[i]);
346 ffile.parseDescriptionHTML(features[i], false);
349 if (av.featuresDisplayed == null)
351 av.featuresDisplayed = new Hashtable();
354 if (featureGroups == null)
356 featureGroups = new Hashtable();
359 col = colourPanel.getBackground();
360 setColour(lastFeatureAdded, col);
362 if (lastFeatureGroupAdded != null)
364 featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
365 av.featuresDisplayed.put(lastFeatureGroupAdded, new Integer(col
370 String[] tro = new String[renderOrder.length];
371 tro[0] = renderOrder[renderOrder.length - 1];
372 System.arraycopy(renderOrder, 0, tro, 1, renderOrder.length - 1);
375 ap.paintAlignment(true);
387 ap.paintAlignment(true);
392 public Color findFeatureColour(Color initialCol, SequenceI seq, int i)
395 if (!av.showSequenceFeatures)
401 sequenceFeatures = lastSeq.getSequenceFeatures();
402 if (sequenceFeatures == null)
407 sfSize = sequenceFeatures.length;
409 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(i)))
414 currentColour = null;
416 drawSequence(null, lastSeq, lastSeq.findPosition(i), -1, -1);
418 if (currentColour == null)
423 return new Color(((Integer) currentColour).intValue());
427 * This is used by the Molecule Viewer to get the accurate colour of the
430 boolean overview = false;
455 // SequenceFeature sf;
458 SequenceFeature[] sequenceFeatures;
460 int sfSize, sfindex, spos, epos;
462 synchronized public void drawSequence(Graphics g, SequenceI seq,
463 int start, int end, int y1)
465 if (seq.getSequenceFeatures() == null
466 || seq.getSequenceFeatures().length == 0)
471 if (transparencySetter != null && g != null)
473 transparencySetter.setTransparency(g, transparency);
476 if (lastSeq == null || seq != lastSeq
477 || sequenceFeatures != seq.getSequenceFeatures())
480 sequenceFeatures = seq.getSequenceFeatures();
481 sfSize = sequenceFeatures.length;
484 if (av.featuresDisplayed == null || renderOrder == null)
487 if (av.featuresDisplayed.size() < 1)
492 sequenceFeatures = seq.getSequenceFeatures();
493 sfSize = sequenceFeatures.length;
497 spos = lastSeq.findPosition(start);
498 epos = lastSeq.findPosition(end);
501 fm = g.getFontMetrics();
505 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
507 type = renderOrder[renderIndex];
508 if (!av.featuresDisplayed.containsKey(type))
513 // loop through all features in sequence to find
514 // current feature to render
515 for (sfindex = 0; sfindex < sfSize; sfindex++)
517 if (!sequenceFeatures[sfindex].type.equals(type))
522 if (featureGroups != null
523 && sequenceFeatures[sfindex].featureGroup != null
525 .containsKey(sequenceFeatures[sfindex].featureGroup)
526 && !((Boolean) featureGroups
527 .get(sequenceFeatures[sfindex].featureGroup))
534 && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
542 if (sequenceFeatures[sfindex].begin <= start
543 && sequenceFeatures[sfindex].end >= start)
545 currentColour = av.featuresDisplayed
546 .get(sequenceFeatures[sfindex].type);
550 else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
556 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
557 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
558 new Color(((Integer) av.featuresDisplayed
559 .get(sequenceFeatures[sfindex].type)).intValue()),
564 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
565 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
566 new Color(((Integer) av.featuresDisplayed
567 .get(sequenceFeatures[sfindex].type)).intValue()),
573 renderFeature(g, seq, seq
574 .findIndex(sequenceFeatures[sfindex].begin) - 1, seq
575 .findIndex(sequenceFeatures[sfindex].end) - 1,
576 getColour(sequenceFeatures[sfindex].type), start, end, y1);
582 if (transparencySetter != null && g != null)
584 transparencySetter.setTransparency(g, 1.0f);
592 void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
593 Color featureColour, int start, int end, int y1)
596 if (((fstart <= end) && (fend >= start)))
599 { // fix for if the feature we have starts before the sequence start,
600 fstart = start; // but the feature end is still valid!!
608 for (i = fstart; i <= fend; i++)
610 s = seq.getCharAt(i);
612 if (jalview.util.Comparison.isGap(s))
617 g.setColor(featureColour);
619 g.fillRect((i - start) * av.charWidth, y1, av.charWidth,
622 if (!av.validCharWidth)
627 g.setColor(Color.white);
628 charOffset = (av.charWidth - fm.charWidth(s)) / 2;
629 g.drawString(String.valueOf(s), charOffset
630 + (av.charWidth * (i - start)), (y1 + av.charHeight)
631 - av.charHeight / 5); // pady = height / 5;
637 void findAllFeatures()
639 jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
641 av.featuresDisplayed = new Hashtable();
642 Vector allfeatures = new Vector();
643 for (int i = 0; i < av.alignment.getHeight(); i++)
645 SequenceFeature[] features = av.alignment.getSequenceAt(i)
646 .getSequenceFeatures();
648 if (features == null)
654 while (index < features.length)
656 if (!av.featuresDisplayed.containsKey(features[index].getType()))
658 if (getColour(features[index].getType()) == null)
660 featureColours.put(features[index].getType(), ucs
661 .createColourFromName(features[index].getType()));
664 av.featuresDisplayed.put(features[index].getType(), new Integer(
665 getColour(features[index].getType()).getRGB()));
666 allfeatures.addElement(features[index].getType());
672 renderOrder = new String[allfeatures.size()];
673 Enumeration en = allfeatures.elements();
674 int i = allfeatures.size() - 1;
675 while (en.hasMoreElements())
677 renderOrder[i] = en.nextElement().toString();
682 public Color getColour(String featureType)
684 if (!featureColours.containsKey(featureType))
686 jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
687 Color col = ucs.createColourFromName(featureType);
688 featureColours.put(featureType, col);
692 return (Color) featureColours.get(featureType);
695 public void setColour(String featureType, Color col)
697 featureColours.put(featureType, col);
700 public void setFeaturePriority(Object[][] data)
702 // The feature table will display high priority
703 // features at the top, but theses are the ones
704 // we need to render last, so invert the data
705 if (av.featuresDisplayed != null)
707 av.featuresDisplayed.clear();
711 * if (visibleNew) { if (av.featuresDisplayed != null) {
712 * av.featuresDisplayed.clear(); } else { av.featuresDisplayed = new
713 * Hashtable(); } } if (data == null) { return; }
716 renderOrder = new String[data.length];
720 for (int i = 0; i < data.length; i++)
722 String type = data[i][0].toString();
723 setColour(type, (Color) data[i][1]);
724 if (((Boolean) data[i][2]).booleanValue())
726 av.featuresDisplayed.put(type, new Integer(getColour(type)
730 renderOrder[data.length - i - 1] = type;
736 * @return a simple list of feature group names or null
738 public String[] getGroups()
741 if (featureGroups != null)
743 String[] gps = new String[featureGroups.size()];
744 Enumeration gn = featureGroups.keys();
746 while (gn.hasMoreElements())
748 gps[i++] = (String) gn.nextElement();
756 * get visible or invisible groups
759 * true to return visible groups, false to return hidden ones.
760 * @return list of groups
762 public String[] getGroups(boolean visible)
765 if (featureGroups != null)
767 Vector gp = new Vector();
769 Enumeration gn = featureGroups.keys();
770 while (gn.hasMoreElements())
772 String nm = (String) gn.nextElement();
773 Boolean state = (Boolean) featureGroups.get(nm);
774 if (state.booleanValue() == visible)
779 String[] gps = new String[gp.size()];
783 while (gn.hasMoreElements())
785 gps[i++] = (String) gn.nextElement();
793 * set all feature groups in toset to be visible or invisible
798 * the state of the named groups to set
800 public void setGroupState(String[] toset, boolean visible)
803 if (toset != null && toset.length > 0 && featureGroups != null)
805 boolean rdrw = false;
806 for (int i = 0; i < toset.length; i++)
808 Object st = featureGroups.get(toset[i]);
811 featureGroups.put(toset[i], new Boolean(visible));
812 rdrw = rdrw || (visible != ((Boolean) st).booleanValue());
818 if (this.av.featureSettings != null)
820 av.featureSettings.rebuildGroups();
821 this.av.featureSettings.resetTable(true);
829 av.alignmentChanged(null);
836 * analyse alignment for groups and hash tables (used to be embedded in
837 * FeatureSettings.setTableData)
839 * @return true if features are on the alignment
841 public boolean buildGroupHash()
843 boolean alignmentHasFeatures = false;
844 if (featureGroups == null)
846 featureGroups = new Hashtable();
848 Vector allFeatures = new Vector();
849 Vector allGroups = new Vector();
850 SequenceFeature[] tmpfeatures;
852 for (int i = 0; i < av.alignment.getHeight(); i++)
854 if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
859 alignmentHasFeatures = true;
861 tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
863 while (index < tmpfeatures.length)
865 if (tmpfeatures[index].getFeatureGroup() != null)
867 group = tmpfeatures[index].featureGroup;
868 if (!allGroups.contains(group))
870 allGroups.addElement(group);
872 boolean visible = true;
873 if (featureGroups.containsKey(group))
875 visible = ((Boolean) featureGroups.get(group)).booleanValue();
879 featureGroups.put(group, new Boolean(visible));
884 if (!allFeatures.contains(tmpfeatures[index].getType()))
886 allFeatures.addElement(tmpfeatures[index].getType());
892 return alignmentHasFeatures;
896 * rebuild the featuresDisplayed and renderorder list based on the
897 * featureGroups hash and any existing display state and force a repaint if
900 * @return true if alignment has visible features
902 public boolean buildFeatureHash()
904 boolean alignmentHasFeatures = false;
905 if (featureGroups == null)
907 alignmentHasFeatures = buildGroupHash();
909 if (!alignmentHasFeatures)
911 Hashtable fdisp = av.featuresDisplayed;
912 Vector allFeatures = new Vector();
913 SequenceFeature[] tmpfeatures;
915 for (int i = 0; i < av.alignment.getHeight(); i++)
917 if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
922 alignmentHasFeatures = true;
924 tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
926 while (index < tmpfeatures.length)
928 boolean visible = true;
929 if (tmpfeatures[index].getFeatureGroup() != null)
931 group = tmpfeatures[index].featureGroup;
932 if (featureGroups.containsKey(group))
934 visible = ((Boolean) featureGroups.get(group)).booleanValue();
938 if (visible && !allFeatures.contains(tmpfeatures[index].getType()))
940 allFeatures.addElement(tmpfeatures[index].getType());
945 if (allFeatures.size() > 0)
947 String[] neworder = new String[allFeatures.size()];
948 int p = neworder.length - 1;
949 for (int i = renderOrder.length - 1; i >= 0; i--)
951 if (allFeatures.contains(renderOrder[i]))
953 neworder[p--] = renderOrder[i];
954 allFeatures.removeElement(renderOrder[i]);
958 av.featuresDisplayed.remove(renderOrder[i]);
961 for (int i = allFeatures.size() - 1; i > 0; i++)
963 Object e = allFeatures.elementAt(i);
966 neworder[p--] = (String) e;
967 av.featuresDisplayed.put(e, getColour((String) e));
970 renderOrder = neworder;
974 return alignmentHasFeatures;
978 class TransparencySetter
980 void setTransparency(Graphics g, float value)
982 Graphics2D g2 = (Graphics2D) g;
983 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,