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.*;
29 import jalview.schemes.GraduatedColor;
37 public class FeatureRenderer
41 Hashtable featureColours = new Hashtable();
43 // A higher level for grouping features of a
45 Hashtable featureGroups = null;
47 // Holds web links for feature groups and feature types
48 // in the form label|link
49 Hashtable featureLinks = null;
51 // This is actually an Integer held in the hashtable,
52 // Retrieved using the key feature type
61 float transparency = 1f;
63 TransparencySetter transparencySetter = null;
66 * Creates a new FeatureRenderer object.
71 public FeatureRenderer(AlignViewport av)
75 if (!System.getProperty("java.version").startsWith("1.1"))
77 transparencySetter = new TransparencySetter();
81 public void transferSettings(FeatureRenderer fr)
83 renderOrder = fr.renderOrder;
84 featureGroups = fr.featureGroups;
85 featureColours = fr.featureColours;
86 transparency = fr.transparency;
89 static String lastFeatureAdded;
91 static String lastFeatureGroupAdded;
93 static String lastDescriptionAdded;
97 boolean deleteFeature = false;
101 boolean amendFeatures(final SequenceI[] sequences,
102 final SequenceFeature[] features, boolean newFeatures,
103 final AlignmentPanel ap)
105 Panel bigPanel = new Panel(new BorderLayout());
106 final TextField name = new TextField(16);
107 final TextField source = new TextField(16);
108 final TextArea description = new TextArea(3, 35);
109 final TextField start = new TextField(8);
110 final TextField end = new TextField(8);
111 final Choice overlaps;
112 Button deleteButton = new Button("Delete");
113 deleteFeature = false;
115 colourPanel = new Panel(null);
116 colourPanel.setSize(110, 15);
117 final FeatureRenderer fr = this;
119 Panel panel = new Panel(new GridLayout(3, 1));
123 // /////////////////////////////////////
124 // /MULTIPLE FEATURES AT SELECTED RESIDUE
125 if (!newFeatures && features.length > 1)
127 panel = new Panel(new GridLayout(4, 1));
129 tmp.add(new Label("Select Feature: "));
130 overlaps = new Choice();
131 for (int i = 0; i < features.length; i++)
133 String item = features[i].getType() + "/" + features[i].getBegin()
134 + "-" + features[i].getEnd();
136 if (features[i].getFeatureGroup() != null)
137 item += " (" + features[i].getFeatureGroup() + ")";
139 overlaps.addItem(item);
144 overlaps.addItemListener(new java.awt.event.ItemListener()
146 public void itemStateChanged(java.awt.event.ItemEvent e)
148 int index = overlaps.getSelectedIndex();
151 featureIndex = index;
152 name.setText(features[index].getType());
153 description.setText(features[index].getDescription());
154 source.setText(features[index].getFeatureGroup());
155 start.setText(features[index].getBegin() + "");
156 end.setText(features[index].getEnd() + "");
158 SearchResults highlight = new SearchResults();
159 highlight.addResult(sequences[0], features[index].getBegin(),
160 features[index].getEnd());
162 ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
165 Color col = getColour(name.getText());
168 col = new jalview.schemes.UserColourScheme()
169 .createColourFromName(name.getText());
172 colourPanel.setBackground(col);
179 // ////////////////////////////////////
183 tmp.add(new Label("Name: ", Label.RIGHT));
188 tmp.add(new Label("Group: ", Label.RIGHT));
193 tmp.add(new Label("Colour: ", Label.RIGHT));
194 tmp.add(colourPanel);
196 bigPanel.add(panel, BorderLayout.NORTH);
199 panel.add(new Label("Description: ", Label.RIGHT));
200 panel.add(new ScrollPane().add(description));
204 bigPanel.add(panel, BorderLayout.SOUTH);
207 panel.add(new Label(" Start:", Label.RIGHT));
209 panel.add(new Label(" End:", Label.RIGHT));
211 bigPanel.add(panel, BorderLayout.CENTER);
215 bigPanel.add(panel, BorderLayout.CENTER);
218 if (lastFeatureAdded == null)
220 if (features[0].type != null)
222 lastFeatureAdded = features[0].type;
226 lastFeatureAdded = "feature_1";
230 if (lastFeatureGroupAdded == null)
232 if (features[0].featureGroup != null)
234 lastFeatureGroupAdded = features[0].featureGroup;
238 lastFeatureAdded = "Jalview";
242 String title = newFeatures ? "Create New Sequence Feature(s)"
243 : "Amend/Delete Features for " + sequences[0].getName();
245 final JVDialog dialog = new JVDialog(ap.alignFrame, title, true, 385,
248 dialog.setMainPanel(bigPanel);
252 name.setText(lastFeatureAdded);
253 source.setText(lastFeatureGroupAdded);
257 dialog.ok.setLabel("Amend");
258 dialog.buttonPanel.add(deleteButton, 1);
259 deleteButton.addActionListener(new ActionListener()
261 public void actionPerformed(ActionEvent evt)
263 deleteFeature = true;
264 dialog.setVisible(false);
267 name.setText(features[0].getType());
268 source.setText(features[0].getFeatureGroup());
271 start.setText(features[0].getBegin() + "");
272 end.setText(features[0].getEnd() + "");
273 description.setText(features[0].getDescription());
275 Color col = getColour(name.getText());
278 col = new jalview.schemes.UserColourScheme()
279 .createColourFromName(name.getText());
282 colourPanel.setBackground(col);
284 dialog.setResizable(true);
286 colourPanel.addMouseListener(new java.awt.event.MouseAdapter()
288 public void mousePressed(java.awt.event.MouseEvent evt)
290 new UserDefinedColours(fr, ap.alignFrame);
294 dialog.setVisible(true);
296 jalview.io.FeaturesFile ffile = new jalview.io.FeaturesFile();
300 // This ensures that the last sequence
301 // is refreshed and new features are rendered
303 lastFeatureAdded = name.getText().trim();
304 lastFeatureGroupAdded = source.getText().trim();
305 lastDescriptionAdded = description.getText().replace('\n', ' ');
308 if (lastFeatureGroupAdded != null && lastFeatureGroupAdded.length() < 1)
309 lastFeatureGroupAdded = null;
313 SequenceFeature sf = features[featureIndex];
317 sf.type = lastFeatureAdded;
318 sf.featureGroup = lastFeatureGroupAdded;
319 sf.description = lastDescriptionAdded;
320 setColour(sf.type, colourPanel.getBackground());
323 sf.begin = Integer.parseInt(start.getText());
324 sf.end = Integer.parseInt(end.getText());
325 } catch (NumberFormatException ex)
329 ffile.parseDescriptionHTML(sf, false);
333 sequences[0].deleteFeature(sf);
339 if (dialog.accept && name.getText().length() > 0)
341 for (int i = 0; i < sequences.length; i++)
343 features[i].type = lastFeatureAdded;
344 features[i].featureGroup = lastFeatureGroupAdded;
345 features[i].description = lastDescriptionAdded;
346 sequences[i].addSequenceFeature(features[i]);
347 ffile.parseDescriptionHTML(features[i], false);
350 if (av.featuresDisplayed == null)
352 av.featuresDisplayed = new Hashtable();
355 if (featureGroups == null)
357 featureGroups = new Hashtable();
360 col = colourPanel.getBackground();
361 setColour(lastFeatureAdded, col);
363 if (lastFeatureGroupAdded != null)
365 featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
366 av.featuresDisplayed.put(lastFeatureGroupAdded, new Integer(col
371 String[] tro = new String[renderOrder.length];
372 tro[0] = renderOrder[renderOrder.length - 1];
373 System.arraycopy(renderOrder, 0, tro, 1, renderOrder.length - 1);
376 ap.paintAlignment(true);
388 ap.paintAlignment(true);
393 public Color findFeatureColour(Color initialCol, SequenceI seq, int i)
396 if (!av.showSequenceFeatures)
402 sequenceFeatures = lastSeq.getSequenceFeatures();
403 if (sequenceFeatures == null)
408 sfSize = sequenceFeatures.length;
410 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(i)))
415 currentColour = null;
417 drawSequence(null, lastSeq, lastSeq.findPosition(i), -1, -1);
419 if (currentColour == null)
424 return new Color(((Integer) currentColour).intValue());
428 * This is used by the Molecule Viewer to get the accurate colour of the
431 boolean overview = false;
456 // SequenceFeature sf;
459 SequenceFeature[] sequenceFeatures;
461 int sfSize, sfindex, spos, epos;
463 synchronized public void drawSequence(Graphics g, SequenceI seq,
464 int start, int end, int y1)
466 if (seq.getSequenceFeatures() == null
467 || seq.getSequenceFeatures().length == 0)
472 if (transparencySetter != null && g != null)
474 transparencySetter.setTransparency(g, transparency);
477 if (lastSeq == null || seq != lastSeq
478 || sequenceFeatures != seq.getSequenceFeatures())
481 sequenceFeatures = seq.getSequenceFeatures();
482 sfSize = sequenceFeatures.length;
485 if (av.featuresDisplayed == null || renderOrder == null)
488 if (av.featuresDisplayed.size() < 1)
493 sequenceFeatures = seq.getSequenceFeatures();
494 sfSize = sequenceFeatures.length;
498 spos = lastSeq.findPosition(start);
499 epos = lastSeq.findPosition(end);
502 fm = g.getFontMetrics();
506 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
508 type = renderOrder[renderIndex];
509 if (!av.featuresDisplayed.containsKey(type))
514 // loop through all features in sequence to find
515 // current feature to render
516 for (sfindex = 0; sfindex < sfSize; sfindex++)
518 if (!sequenceFeatures[sfindex].type.equals(type))
523 if (featureGroups != null
524 && sequenceFeatures[sfindex].featureGroup != null
526 .containsKey(sequenceFeatures[sfindex].featureGroup)
527 && !((Boolean) featureGroups
528 .get(sequenceFeatures[sfindex].featureGroup))
535 && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
543 if (sequenceFeatures[sfindex].begin <= start
544 && sequenceFeatures[sfindex].end >= start)
546 currentColour = new Integer(getColour(sequenceFeatures[sfindex]).getRGB());//av.featuresDisplayed
547 //.get(sequenceFeatures[sfindex].type);
551 else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
557 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
558 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
559 new Color(((Integer) av.featuresDisplayed
560 .get(sequenceFeatures[sfindex].type)).intValue()),
565 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
566 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
567 new Color(((Integer) av.featuresDisplayed
568 .get(sequenceFeatures[sfindex].type)).intValue()),
574 renderFeature(g, seq, seq
575 .findIndex(sequenceFeatures[sfindex].begin) - 1, seq
576 .findIndex(sequenceFeatures[sfindex].end) - 1,
577 getColour(sequenceFeatures[sfindex]), start, end, y1);
583 if (transparencySetter != null && g != null)
585 transparencySetter.setTransparency(g, 1.0f);
593 void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
594 Color featureColour, int start, int end, int y1)
597 if (((fstart <= end) && (fend >= start)))
600 { // fix for if the feature we have starts before the sequence start,
601 fstart = start; // but the feature end is still valid!!
609 for (i = fstart; i <= fend; i++)
611 s = seq.getCharAt(i);
613 if (jalview.util.Comparison.isGap(s))
618 g.setColor(featureColour);
620 g.fillRect((i - start) * av.charWidth, y1, av.charWidth,
623 if (!av.validCharWidth)
628 g.setColor(Color.white);
629 charOffset = (av.charWidth - fm.charWidth(s)) / 2;
630 g.drawString(String.valueOf(s), charOffset
631 + (av.charWidth * (i - start)), (y1 + av.charHeight)
632 - av.charHeight / 5); // pady = height / 5;
637 Hashtable minmax=null;
638 void findAllFeatures()
640 jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
642 av.featuresDisplayed = new Hashtable();
643 Vector allfeatures = new Vector();
644 minmax = new Hashtable();
646 for (int i = 0; i < av.alignment.getHeight(); i++)
648 SequenceFeature[] features = av.alignment.getSequenceAt(i)
649 .getSequenceFeatures();
651 if (features == null)
657 while (index < features.length)
659 if (features[index].begin==0 && features[index].end==0) {
663 if (!av.featuresDisplayed.containsKey(features[index].getType()))
665 if (getColour(features[index].getType()) == null)
667 featureColours.put(features[index].getType(), ucs
668 .createColourFromName(features[index].getType()));
671 av.featuresDisplayed.put(features[index].getType(), new Integer(
672 getColour(features[index].getType()).getRGB()));
673 allfeatures.addElement(features[index].getType());
675 if (features[index].score != Float.NaN)
677 int nonpos= features[index].getBegin()>=1 ? 0 : 1;
678 float[][] mm = (float[][]) minmax.get(features[index].getType());
681 mm = new float[][] {null, null };
682 minmax.put(features[index].getType(), mm);
684 if (mm[nonpos]==null)
686 mm[nonpos] = new float[] { features[index].score, features[index].score };
691 if (mm[nonpos][0] > features[index].score)
693 mm[nonpos][0] = features[index].score;
695 if (mm[nonpos][1] < features[index].score)
697 mm[nonpos][1] = features[index].score;
706 renderOrder = new String[allfeatures.size()];
707 Enumeration en = allfeatures.elements();
708 int i = allfeatures.size() - 1;
709 while (en.hasMoreElements())
711 renderOrder[i] = en.nextElement().toString();
716 public Color getColour(String featureType)
718 Object fc = featureColours.get(featureType);
721 jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
722 Color col = ucs.createColourFromName(featureType);
723 featureColours.put(featureType, col);
726 else if (fc instanceof Color)
732 if (fc instanceof GraduatedColor)
734 return ((GraduatedColor) fc).getMinColor();
736 // TODO: raise an implementation error here.
737 return null; // Color.white;
741 * implement graduated colouring for features with scores
744 * @return render colour for the given feature
746 public Color getColour(SequenceFeature feature)
748 Object fc = featureColours.get(feature.type);
751 jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
752 Color col = ucs.createColourFromName(feature.type);
753 featureColours.put(feature.type, col);
756 else if (fc instanceof Color)
762 if (fc instanceof GraduatedColor)
764 return ((GraduatedColor) fc).findColor(feature);
766 // TODO: raise an implementation error here.
767 return null; // Color.white;
771 public void setColour(String featureType, Color col)
773 featureColours.put(featureType, col);
776 public void setFeaturePriority(Object[][] data)
778 // The feature table will display high priority
779 // features at the top, but theses are the ones
780 // we need to render last, so invert the data
781 if (av.featuresDisplayed != null)
783 av.featuresDisplayed.clear();
787 * if (visibleNew) { if (av.featuresDisplayed != null) {
788 * av.featuresDisplayed.clear(); } else { av.featuresDisplayed = new
789 * Hashtable(); } } if (data == null) { return; }
792 renderOrder = new String[data.length];
796 for (int i = 0; i < data.length; i++)
798 String type = data[i][0].toString();
799 setColour(type, (Color) data[i][1]);
800 if (((Boolean) data[i][2]).booleanValue())
802 av.featuresDisplayed.put(type, new Integer(getColour(type)
806 renderOrder[data.length - i - 1] = type;
812 * @return a simple list of feature group names or null
814 public String[] getGroups()
817 if (featureGroups != null)
819 String[] gps = new String[featureGroups.size()];
820 Enumeration gn = featureGroups.keys();
822 while (gn.hasMoreElements())
824 gps[i++] = (String) gn.nextElement();
832 * get visible or invisible groups
835 * true to return visible groups, false to return hidden ones.
836 * @return list of groups
838 public String[] getGroups(boolean visible)
841 if (featureGroups != null)
843 Vector gp = new Vector();
845 Enumeration gn = featureGroups.keys();
846 while (gn.hasMoreElements())
848 String nm = (String) gn.nextElement();
849 Boolean state = (Boolean) featureGroups.get(nm);
850 if (state.booleanValue() == visible)
855 String[] gps = new String[gp.size()];
859 while (gn.hasMoreElements())
861 gps[i++] = (String) gn.nextElement();
869 * set all feature groups in toset to be visible or invisible
874 * the state of the named groups to set
876 public void setGroupState(String[] toset, boolean visible)
879 if (toset != null && toset.length > 0 && featureGroups != null)
881 boolean rdrw = false;
882 for (int i = 0; i < toset.length; i++)
884 Object st = featureGroups.get(toset[i]);
887 featureGroups.put(toset[i], new Boolean(visible));
888 rdrw = rdrw || (visible != ((Boolean) st).booleanValue());
894 if (this.av.featureSettings != null)
896 av.featureSettings.rebuildGroups();
897 this.av.featureSettings.resetTable(true);
905 av.alignmentChanged(null);
912 * analyse alignment for groups and hash tables (used to be embedded in
913 * FeatureSettings.setTableData)
915 * @return true if features are on the alignment
917 public boolean buildGroupHash()
919 boolean alignmentHasFeatures = false;
920 if (featureGroups == null)
922 featureGroups = new Hashtable();
924 Vector allFeatures = new Vector();
925 Vector allGroups = new Vector();
926 SequenceFeature[] tmpfeatures;
928 for (int i = 0; i < av.alignment.getHeight(); i++)
930 if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
935 alignmentHasFeatures = true;
937 tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
939 while (index < tmpfeatures.length)
941 if (tmpfeatures[index].getFeatureGroup() != null)
943 group = tmpfeatures[index].featureGroup;
944 if (!allGroups.contains(group))
946 allGroups.addElement(group);
948 boolean visible = true;
949 if (featureGroups.containsKey(group))
951 visible = ((Boolean) featureGroups.get(group)).booleanValue();
955 featureGroups.put(group, new Boolean(visible));
960 if (!allFeatures.contains(tmpfeatures[index].getType()))
962 allFeatures.addElement(tmpfeatures[index].getType());
968 return alignmentHasFeatures;
972 * rebuild the featuresDisplayed and renderorder list based on the
973 * featureGroups hash and any existing display state and force a repaint if
976 * @return true if alignment has visible features
978 public boolean buildFeatureHash()
980 boolean alignmentHasFeatures = false;
981 if (featureGroups == null)
983 alignmentHasFeatures = buildGroupHash();
985 if (!alignmentHasFeatures)
987 Hashtable fdisp = av.featuresDisplayed;
988 Vector allFeatures = new Vector();
989 SequenceFeature[] tmpfeatures;
991 for (int i = 0; i < av.alignment.getHeight(); i++)
993 if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
998 alignmentHasFeatures = true;
1000 tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
1002 while (index < tmpfeatures.length)
1004 boolean visible = true;
1005 if (tmpfeatures[index].getFeatureGroup() != null)
1007 group = tmpfeatures[index].featureGroup;
1008 if (featureGroups.containsKey(group))
1010 visible = ((Boolean) featureGroups.get(group)).booleanValue();
1014 if (visible && !allFeatures.contains(tmpfeatures[index].getType()))
1016 allFeatures.addElement(tmpfeatures[index].getType());
1021 if (allFeatures.size() > 0)
1023 String[] neworder = new String[allFeatures.size()];
1024 int p = neworder.length - 1;
1025 for (int i = renderOrder.length - 1; i >= 0; i--)
1027 if (allFeatures.contains(renderOrder[i]))
1029 neworder[p--] = renderOrder[i];
1030 allFeatures.removeElement(renderOrder[i]);
1034 av.featuresDisplayed.remove(renderOrder[i]);
1037 for (int i = allFeatures.size() - 1; i > 0; i++)
1039 Object e = allFeatures.elementAt(i);
1042 neworder[p--] = (String) e;
1043 av.featuresDisplayed.put(e, getColour((String) e));
1046 renderOrder = neworder;
1050 return alignmentHasFeatures;
1054 * @return the displayed feature type as an array of strings
1056 protected String[] getDisplayedFeatureTypes()
1058 String[] typ = null;
1059 synchronized (renderOrder)
1061 typ = new String[renderOrder.length];
1062 System.arraycopy(renderOrder, 0, typ, 0, typ.length);
1063 for (int i = 0; i < typ.length; i++)
1065 if (av.featuresDisplayed.get(typ[i]) == null)
1075 class TransparencySetter
1077 void setTransparency(Graphics g, float value)
1079 Graphics2D g2 = (Graphics2D) g;
1080 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,