2 * Jalview - A Sequence Alignment Editor and Viewer
3 * Copyright (C) 2007 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.*;
28 import jalview.appletgui.FeatureSettings.MyCheckbox;
29 import jalview.datamodel.*;
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
60 float transparency = 1f;
62 TransparencySetter transparencySetter = null;
65 * Creates a new FeatureRenderer object.
67 * @param av DOCUMENT ME!
69 public FeatureRenderer(AlignViewport av)
73 if (!System.getProperty("java.version").startsWith("1.1"))
75 transparencySetter = new TransparencySetter();
79 public void transferSettings(FeatureRenderer fr)
81 renderOrder = fr.renderOrder;
82 featureGroups = fr.featureGroups;
83 featureColours = fr.featureColours;
84 transparency = fr.transparency;
88 static String lastFeatureAdded;
89 static String lastFeatureGroupAdded;
90 static String lastDescriptionAdded;
93 boolean deleteFeature = false;
95 boolean amendFeatures(final SequenceI[] sequences,
96 final SequenceFeature[] features,
98 final AlignmentPanel ap)
100 Panel bigPanel = new Panel(new BorderLayout());
101 final TextField name = new TextField(16);
102 final TextField source = new TextField(16);
103 final TextArea description = new TextArea(3, 35);
104 final TextField start = new TextField(8);
105 final TextField end = new TextField(8);
106 final Choice overlaps;
107 Button deleteButton = new Button("Delete");
108 deleteFeature = false;
110 colourPanel = new Panel(null);
111 colourPanel.setSize(110,15);
112 final FeatureRenderer fr = this;
114 Panel panel = new Panel(new GridLayout(3, 1));
118 ///////////////////////////////////////
119 ///MULTIPLE FEATURES AT SELECTED RESIDUE
120 if(!newFeatures && features.length>1)
122 panel = new Panel(new GridLayout(4, 1));
124 tmp.add(new Label("Select Feature: "));
125 overlaps = new Choice();
126 for(int i=0; i<features.length; i++)
128 String item = features[i].getType()
129 +"/"+features[i].getBegin()+"-"+features[i].getEnd();
131 if(features[i].getFeatureGroup()!=null)
132 item += " ("+features[i].getFeatureGroup()+")";
134 overlaps.addItem(item);
139 overlaps.addItemListener(new java.awt.event.ItemListener()
141 public void itemStateChanged(java.awt.event.ItemEvent e)
143 int index = overlaps.getSelectedIndex();
146 featureIndex = index;
147 name.setText(features[index].getType());
148 description.setText(features[index].getDescription());
149 source.setText(features[index].getFeatureGroup());
150 start.setText(features[index].getBegin()+"");
151 end.setText(features[index].getEnd()+"");
153 SearchResults highlight = new SearchResults();
154 highlight.addResult(sequences[0],
155 features[index].getBegin(),
156 features[index].getEnd());
158 ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
161 Color col = getColour(name.getText());
165 jalview.schemes.UserColourScheme()
166 .createColourFromName(name.getText());
169 colourPanel.setBackground(col);
177 //////////////////////////////////////
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";
242 String title = newFeatures ? "Create New Sequence Feature(s)" :
243 "Amend/Delete Features for "
244 + sequences[0].getName();
246 final JVDialog dialog = new JVDialog(ap.alignFrame,
251 dialog.setMainPanel(bigPanel);
255 name.setText(lastFeatureAdded);
256 source.setText(lastFeatureGroupAdded);
260 dialog.ok.setLabel("Amend");
261 dialog.buttonPanel.add(deleteButton, 1);
262 deleteButton.addActionListener(new ActionListener()
264 public void actionPerformed(ActionEvent evt)
266 deleteFeature = true;
267 dialog.setVisible(false);
270 name.setText(features[0].getType());
271 source.setText(features[0].getFeatureGroup());
274 start.setText(features[0].getBegin()+"");
275 end.setText(features[0].getEnd()+"");
276 description.setText(features[0].getDescription());
278 Color col = getColour(name.getText());
282 jalview.schemes.UserColourScheme()
283 .createColourFromName(name.getText());
286 colourPanel.setBackground(col);
288 dialog.setResizable(true);
291 colourPanel.addMouseListener(new java.awt.event.MouseAdapter()
293 public void mousePressed(java.awt.event.MouseEvent evt)
295 new UserDefinedColours(fr, ap.alignFrame);
299 dialog.setVisible(true);
301 jalview.io.FeaturesFile ffile = new jalview.io.FeaturesFile();
305 //This ensures that the last sequence
306 //is refreshed and new features are rendered
308 lastFeatureAdded = name.getText().trim();
309 lastFeatureGroupAdded = source.getText().trim();
310 lastDescriptionAdded = description.getText().replace('\n', ' ');
313 if(lastFeatureGroupAdded !=null && lastFeatureGroupAdded.length()<1)
314 lastFeatureGroupAdded = null;
319 SequenceFeature sf = features[featureIndex];
323 sf.type = lastFeatureAdded;
324 sf.featureGroup = lastFeatureGroupAdded;
325 sf.description = lastDescriptionAdded;
326 setColour(sf.type, colourPanel.getBackground());
329 sf.begin = Integer.parseInt(start.getText());
330 sf.end = Integer.parseInt(end.getText());
332 catch (NumberFormatException ex)
335 ffile.parseDescriptionHTML(sf, false);
339 sequences[0].deleteFeature(sf);
345 if (dialog.accept && name.getText().length()>0)
347 for (int i = 0; i < sequences.length; i++)
349 features[i].type = lastFeatureAdded;
350 features[i].featureGroup = lastFeatureGroupAdded;
351 features[i].description = lastDescriptionAdded;
352 sequences[i].addSequenceFeature(features[i]);
353 ffile.parseDescriptionHTML(features[i], false);
356 if (av.featuresDisplayed == null)
358 av.featuresDisplayed = new Hashtable();
361 if (featureGroups == null)
363 featureGroups = new Hashtable();
366 col = colourPanel.getBackground();
367 setColour(lastFeatureAdded, col);
369 if(lastFeatureGroupAdded!=null)
371 featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
372 av.featuresDisplayed.put(lastFeatureGroupAdded,
373 new Integer(col.getRGB()));
377 String [] tro = new String[renderOrder.length];
378 tro[0] = renderOrder[renderOrder.length-1];
379 System.arraycopy(renderOrder,0,tro,1,renderOrder.length-1);
382 ap.paintAlignment(true);
394 ap.paintAlignment(true);
400 public Color findFeatureColour(Color initialCol, SequenceI seq, int i)
403 if (!av.showSequenceFeatures)
409 sequenceFeatures = lastSeq.getSequenceFeatures();
410 if (sequenceFeatures == null)
415 sfSize = sequenceFeatures.length;
417 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(i)))
422 currentColour = null;
424 drawSequence(null, lastSeq, lastSeq.findPosition(i), -1, -1);
426 if (currentColour == null)
431 return new Color( ( (Integer) currentColour).intValue());
435 * This is used by the Molecule Viewer to get the accurate colour
436 * of the rendered sequence
438 boolean overview = false;
444 * @param g DOCUMENT ME!
445 * @param seq DOCUMENT ME!
446 * @param sg DOCUMENT ME!
447 * @param start DOCUMENT ME!
448 * @param end DOCUMENT ME!
449 * @param x1 DOCUMENT ME!
450 * @param y1 DOCUMENT ME!
451 * @param width DOCUMENT ME!
452 * @param height DOCUMENT ME!
455 // SequenceFeature sf;
458 SequenceFeature[] sequenceFeatures;
459 int sfSize, sfindex, spos, epos;
461 synchronized public void drawSequence(Graphics g, SequenceI seq,
462 int start, int end, int y1)
464 if (seq.getSequenceFeatures() == null
465 || seq.getSequenceFeatures().length == 0)
470 if (transparencySetter != null && g != null)
472 transparencySetter.setTransparency(g, transparency);
475 if (lastSeq == null || seq != lastSeq || sequenceFeatures!=seq.getSequenceFeatures())
478 sequenceFeatures = seq.getSequenceFeatures();
479 sfSize = sequenceFeatures.length;
482 if (av.featuresDisplayed == null || renderOrder == null)
485 if (av.featuresDisplayed.size() < 1)
490 sequenceFeatures = seq.getSequenceFeatures();
491 sfSize = sequenceFeatures.length;
495 spos = lastSeq.findPosition(start);
496 epos = lastSeq.findPosition(end);
499 fm = g.getFontMetrics();
503 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
505 type = renderOrder[renderIndex];
506 if (!av.featuresDisplayed.containsKey(type))
511 // loop through all features in sequence to find
512 // current feature to render
513 for (sfindex = 0; sfindex < sfSize; sfindex++)
515 if (!sequenceFeatures[sfindex].type.equals(type))
520 if (featureGroups != null
521 && sequenceFeatures[sfindex].featureGroup != null
523 featureGroups.containsKey(sequenceFeatures[sfindex].featureGroup)
525 ! ( (Boolean) featureGroups.get(sequenceFeatures[sfindex].
532 if (!overview && (sequenceFeatures[sfindex].getBegin() > epos
533 || sequenceFeatures[sfindex].getEnd() < spos))
540 if (sequenceFeatures[sfindex].begin <= start &&
541 sequenceFeatures[sfindex].end >= start)
543 currentColour = av.featuresDisplayed.get(sequenceFeatures[sfindex].
548 else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
551 renderFeature(g, seq,
552 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
553 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
554 new Color( ( (Integer) av.featuresDisplayed.get(
555 sequenceFeatures[sfindex].type)).intValue()),
557 renderFeature(g, seq,
558 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
559 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
560 new Color( ( (Integer) av.featuresDisplayed.get(
561 sequenceFeatures[sfindex].type)).intValue()),
567 renderFeature(g, seq,
568 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
569 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
570 getColour(sequenceFeatures[sfindex].type),
577 if (transparencySetter != null && g != null)
579 transparencySetter.setTransparency(g, 1.0f);
585 void renderFeature(Graphics g, SequenceI seq,
586 int fstart, int fend, Color featureColour, int start,
590 if ( ( (fstart <= end) && (fend >= start)))
593 { // fix for if the feature we have starts before the sequence start,
594 fstart = start; // but the feature end is still valid!!
602 for (i = fstart; i <= fend; i++)
604 s = seq.getCharAt(i);
606 if (jalview.util.Comparison.isGap(s))
611 g.setColor(featureColour);
613 g.fillRect( (i - start) * av.charWidth, y1, av.charWidth, av.charHeight);
615 if (!av.validCharWidth)
620 g.setColor(Color.white);
621 charOffset = (av.charWidth - fm.charWidth(s)) / 2;
622 g.drawString(String.valueOf(s),
623 charOffset + (av.charWidth * (i - start)),
624 (y1 + av.charHeight) - av.charHeight / 5); //pady = height / 5;
630 void findAllFeatures()
632 jalview.schemes.UserColourScheme ucs = new
633 jalview.schemes.UserColourScheme();
635 av.featuresDisplayed = new Hashtable();
636 Vector allfeatures = new Vector();
637 for (int i = 0; i < av.alignment.getHeight(); i++)
639 SequenceFeature[] features = av.alignment.getSequenceAt(i).
640 getSequenceFeatures();
642 if (features == null)
648 while (index < features.length)
650 if (!av.featuresDisplayed.containsKey(features[index].getType()))
652 if (getColour(features[index].getType()) == null)
654 featureColours.put(features[index].getType(),
655 ucs.createColourFromName(features[index].
659 av.featuresDisplayed.put(features[index].getType(),
660 new Integer(getColour(features[index].
661 getType()).getRGB()));
662 allfeatures.addElement(features[index].getType());
668 renderOrder = new String[allfeatures.size()];
669 Enumeration en = allfeatures.elements();
670 int i = allfeatures.size() - 1;
671 while (en.hasMoreElements())
673 renderOrder[i] = en.nextElement().toString();
678 public Color getColour(String featureType)
680 if (!featureColours.containsKey(featureType))
682 jalview.schemes.UserColourScheme ucs = new
683 jalview.schemes.UserColourScheme();
684 Color col = ucs.createColourFromName(featureType);
685 featureColours.put(featureType, col);
689 return (Color) featureColours.get(featureType);
693 public void setColour(String featureType, Color col)
695 featureColours.put(featureType, col);
698 public void setFeaturePriority(Object[][] data)
700 // The feature table will display high priority
701 // features at the top, but theses are the ones
702 // we need to render last, so invert the data
703 if (av.featuresDisplayed != null)
705 av.featuresDisplayed.clear();
710 if (av.featuresDisplayed != null)
712 av.featuresDisplayed.clear();
716 av.featuresDisplayed = new Hashtable();
725 renderOrder = new String[data.length];
729 for (int i = 0; i < data.length; i++)
731 String type = data[i][0].toString();
732 setColour(type, (Color) data[i][1]);
733 if ( ( (Boolean) data[i][2]).booleanValue())
735 av.featuresDisplayed.put(type, new Integer(getColour(type).getRGB()));
738 renderOrder[data.length - i - 1] = type;
743 * @return a simple list of feature group names or null
745 public String[] getGroups()
748 if (featureGroups!=null)
750 String[] gps = new String[featureGroups.size()];
751 Enumeration gn = featureGroups.keys();
753 while (gn.hasMoreElements())
755 gps[i++] = (String) gn.nextElement();
762 * get visible or invisible groups
763 * @param visible true to return visible groups, false to return hidden ones.
764 * @return list of groups
766 public String[] getGroups(boolean visible)
769 if (featureGroups!=null)
771 Vector gp = new Vector();
773 Enumeration gn = featureGroups.keys();
774 while (gn.hasMoreElements())
776 String nm = (String) gn.nextElement();
777 Boolean state = (Boolean) featureGroups.get(nm);
778 if (state.booleanValue()==visible)
783 String[] gps = new String[gp.size()];
787 while (gn.hasMoreElements())
789 gps[i++] = (String) gn.nextElement();
796 * set all feature groups in toset to be visible or invisible
797 * @param toset group names
798 * @param visible 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);
827 av.alignmentChanged(null);
833 * analyse alignment for groups and hash tables
834 * (used to be embedded in FeatureSettings.setTableData)
835 * @return true if features are on the alignment
837 public boolean buildGroupHash()
839 boolean alignmentHasFeatures=false;
840 if (featureGroups == null)
842 featureGroups = new Hashtable();
844 Vector allFeatures = new Vector();
845 Vector allGroups = new Vector();
846 SequenceFeature[] tmpfeatures;
848 for (int i = 0; i < av.alignment.getHeight(); i++)
850 if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
855 alignmentHasFeatures = true;
857 tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
859 while (index < tmpfeatures.length)
861 if (tmpfeatures[index].getFeatureGroup() != null)
863 group = tmpfeatures[index].featureGroup;
864 if (!allGroups.contains(group))
866 allGroups.addElement(group);
868 boolean visible = true;
869 if (featureGroups.containsKey(group))
871 visible = ( (Boolean) featureGroups.get(group)).booleanValue();
873 featureGroups.put(group, new Boolean(visible));
878 if (!allFeatures.contains(tmpfeatures[index].getType()))
880 allFeatures.addElement(tmpfeatures[index].getType());
886 return alignmentHasFeatures;
889 * rebuild the featuresDisplayed and renderorder list based on the featureGroups hash and any existing display state
890 * and force a repaint if necessary
891 * @return true if alignment has visible features
893 public boolean buildFeatureHash() {
894 boolean alignmentHasFeatures=false;
895 if (featureGroups==null)
897 alignmentHasFeatures = buildGroupHash();
899 if (!alignmentHasFeatures)
901 Hashtable fdisp = av.featuresDisplayed;
902 Vector allFeatures = new Vector();
903 SequenceFeature[] tmpfeatures;
905 for (int i = 0; i < av.alignment.getHeight(); i++)
907 if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
912 alignmentHasFeatures = true;
914 tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
916 while (index < tmpfeatures.length)
918 boolean visible = true;
919 if (tmpfeatures[index].getFeatureGroup() != null)
921 group = tmpfeatures[index].featureGroup;
922 if (featureGroups.containsKey(group))
924 visible = ( (Boolean) featureGroups.get(group)).booleanValue();
928 if (visible && !allFeatures.contains(tmpfeatures[index].getType()))
930 allFeatures.addElement(tmpfeatures[index].getType());
935 if (allFeatures.size()>0)
937 String[] neworder = new String[allFeatures.size()];
938 int p=neworder.length-1;
939 for (int i=renderOrder.length-1; i>=0; i--)
941 if (allFeatures.contains(renderOrder[i]))
943 neworder[p--] = renderOrder[i];
944 allFeatures.removeElement(renderOrder[i]);
946 av.featuresDisplayed.remove(renderOrder[i]);
949 for (int i=allFeatures.size()-1; i>0; i++)
951 Object e = allFeatures.elementAt(i);
954 neworder[p--] = (String) e;
955 av.featuresDisplayed.put(e, getColour((String)e));
958 renderOrder = neworder;
962 return alignmentHasFeatures;
966 class TransparencySetter
968 void setTransparency(Graphics g, float value)
970 Graphics2D g2 = (Graphics2D) g;
972 AlphaComposite.getInstance(
973 AlphaComposite.SRC_OVER, value));