Tooltips for features, links for features added
[jalview.git] / src / jalview / appletgui / FeatureRenderer.java
1 \r
2 /*\r
3  * Jalview - A Sequence Alignment Editor and Viewer\r
4  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
5  *\r
6  * This program is free software; you can redistribute it and/or\r
7  * modify it under the terms of the GNU General Public License\r
8  * as published by the Free Software Foundation; either version 2\r
9  * of the License, or (at your option) any later version.\r
10  *\r
11  * This program is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program; if not, write to the Free Software\r
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
19  */\r
20 package jalview.appletgui;\r
21 \r
22 import jalview.datamodel.*;\r
23 \r
24 import java.awt.*;\r
25 \r
26 import java.util.*;\r
27 \r
28 /**\r
29  * DOCUMENT ME!\r
30  *\r
31  * @author $author$\r
32  * @version $Revision$\r
33  */\r
34 public class FeatureRenderer\r
35 {\r
36     AlignViewport av;\r
37 \r
38 \r
39     // A higher level for grouping features of a\r
40     // particular type\r
41     Hashtable featureGroups = null;\r
42 \r
43     // Holds web links for feature groups and feature types\r
44     // in the form label|link\r
45     Hashtable featureLinks = null;\r
46 \r
47 \r
48     // This is actually an Integer held in the hashtable,\r
49     // Retrieved using the key feature type\r
50     Object currentColour;\r
51 \r
52     String [] renderOrder;\r
53 \r
54     FontMetrics fm;\r
55     int charOffset;\r
56 \r
57     float transparency = 1f;\r
58 \r
59     TransparencySetter transparencySetter = null;\r
60 \r
61     /**\r
62      * Creates a new FeatureRenderer object.\r
63      *\r
64      * @param av DOCUMENT ME!\r
65      */\r
66     public FeatureRenderer(AlignViewport av)\r
67     {\r
68         this.av = av;\r
69         initColours();\r
70 \r
71         if(!System.getProperty("java.version").startsWith("1.1"))\r
72              transparencySetter = new TransparencySetter();\r
73     }\r
74 \r
75     public void addFeatureLink(String feature, String link)\r
76     {\r
77       if(featureLinks == null)\r
78         featureLinks = new Hashtable();\r
79 \r
80        featureLinks.put(feature, link);\r
81     }\r
82 \r
83 \r
84     public void transferSettings(FeatureRenderer fr)\r
85     {\r
86       renderOrder = fr.renderOrder;\r
87       featureGroups = fr.featureGroups;\r
88       featureColours = fr.featureColours;\r
89     }\r
90 \r
91 \r
92     public Color findFeatureColour(Color initialCol, SequenceI seq, int i)\r
93     {\r
94       overview = true;\r
95       if(!av.showSequenceFeatures)\r
96         return initialCol;\r
97 \r
98         lastSequence = seq;\r
99         sequenceFeatures = lastSequence.getSequenceFeatures();\r
100         if(sequenceFeatures==null)\r
101           return initialCol;\r
102 \r
103         sfSize = sequenceFeatures.length;\r
104 \r
105       if(jalview.util.Comparison.isGap(lastSequence.getCharAt(i)))\r
106         return Color.white;\r
107 \r
108       currentColour = null;\r
109 \r
110       drawSequence(null, lastSequence, lastSequence.findPosition(i), -1,-1, -1, -1);\r
111 \r
112       if(currentColour==null)\r
113         return initialCol;\r
114 \r
115       return new Color( ((Integer)currentColour).intValue() );\r
116     }\r
117 \r
118     /**\r
119      * This is used by the Molecule Viewer to get the accurate colour\r
120      * of the rendered sequence\r
121      */\r
122     boolean overview = false;\r
123 \r
124     int white = Color.white.getRGB();\r
125     public int findFeatureColour(int initialCol, int seqIndex, int column)\r
126     {\r
127       if(!av.showSequenceFeatures)\r
128         return initialCol;\r
129 \r
130       if(seqIndex!=lastSequenceIndex)\r
131       {\r
132         lastSequence = av.alignment.getSequenceAt(seqIndex);\r
133         lastSequenceIndex = seqIndex;\r
134         sequenceFeatures = lastSequence.getSequenceFeatures();\r
135         if(sequenceFeatures==null)\r
136           return initialCol;\r
137 \r
138         sfSize = sequenceFeatures.length;\r
139       }\r
140 \r
141 \r
142       if(jalview.util.Comparison.isGap(lastSequence.getCharAt(column)))\r
143         return Color.white.getRGB();\r
144 \r
145       currentColour = null;\r
146 \r
147       drawSequence(null, lastSequence, lastSequence.findPosition(column), -1,-1, -1, -1);\r
148 \r
149       if(currentColour==null)\r
150         return initialCol;\r
151 \r
152       return  ((Integer)currentColour).intValue();\r
153     }\r
154 \r
155 \r
156     /**\r
157      * DOCUMENT ME!\r
158      *\r
159      * @param g DOCUMENT ME!\r
160      * @param seq DOCUMENT ME!\r
161      * @param sg DOCUMENT ME!\r
162      * @param start DOCUMENT ME!\r
163      * @param end DOCUMENT ME!\r
164      * @param x1 DOCUMENT ME!\r
165      * @param y1 DOCUMENT ME!\r
166      * @param width DOCUMENT ME!\r
167      * @param height DOCUMENT ME!\r
168      */\r
169    // String type;\r
170    // SequenceFeature sf;\r
171     int lastSequenceIndex=-1;\r
172     SequenceI lastSequence;\r
173     SequenceFeature [] sequenceFeatures;\r
174     int sfSize, sfindex, spos, epos;\r
175 \r
176     public void drawSequence(Graphics g, SequenceI seq,\r
177                              int start, int end, int y1, int width, int height)\r
178     {\r
179       if (   seq.getSequenceFeatures() == null\r
180           || seq.getSequenceFeatures().length==0)\r
181         return;\r
182 \r
183       if(transparencySetter!=null && g!=null)\r
184       {\r
185         transparencySetter.setTransparency(g, transparency);\r
186       }\r
187 \r
188       if (av.featuresDisplayed == null || renderOrder==null)\r
189        {\r
190          findAllFeatures();\r
191          if(av.featuresDisplayed.size()<1)\r
192            return;\r
193 \r
194          sequenceFeatures = seq.getSequenceFeatures();\r
195          sfSize = sequenceFeatures.length;\r
196        }\r
197        if(lastSequence==null || seq!=lastSequence)\r
198       {\r
199         lastSequence = seq;\r
200         sequenceFeatures = seq.getSequenceFeatures();\r
201         sfSize = sequenceFeatures.length;\r
202       }\r
203       if(!overview)\r
204       {\r
205         spos = lastSequence.findPosition(start);\r
206         epos = lastSequence.findPosition(end);\r
207         if(g!=null)\r
208           fm = g.getFontMetrics();\r
209       }\r
210       String type;\r
211       for(int renderIndex=0; renderIndex<renderOrder.length; renderIndex++)\r
212        {\r
213         type =  renderOrder[renderIndex];\r
214         if(!av.featuresDisplayed.containsKey(type))\r
215           continue;\r
216 \r
217         // loop through all features in sequence to find\r
218         // current feature to render\r
219         for (sfindex = 0; sfindex < sfSize; sfindex++)\r
220         {\r
221           if (!sequenceFeatures[sfindex].type.equals(type))\r
222             continue;\r
223 \r
224           if (featureGroups != null\r
225               && sequenceFeatures[sfindex].featureGroup != null\r
226               &&\r
227               featureGroups.containsKey(sequenceFeatures[sfindex].featureGroup)\r
228               &&\r
229               ! ( (Boolean) featureGroups.get(sequenceFeatures[sfindex].featureGroup)).\r
230               booleanValue())\r
231           {\r
232             continue;\r
233           }\r
234 \r
235           if (!overview && (sequenceFeatures[sfindex].getBegin() > epos\r
236                             || sequenceFeatures[sfindex].getEnd() < spos))\r
237             continue;\r
238 \r
239           if (overview)\r
240           {\r
241             if (sequenceFeatures[sfindex].begin <= start &&\r
242                 sequenceFeatures[sfindex].end >= start)\r
243             {\r
244               currentColour = av.featuresDisplayed.get(sequenceFeatures[sfindex].\r
245                   type);\r
246             }\r
247 \r
248           }\r
249           else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))\r
250           {\r
251 \r
252             renderFeature(g, seq,\r
253                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
254                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
255                           new Color( ( (Integer) av.featuresDisplayed.get(\r
256                 sequenceFeatures[sfindex].type)).intValue()),\r
257                           start, end, y1, width, height);\r
258             renderFeature(g, seq,\r
259                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
260                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
261                           new Color( ( (Integer) av.featuresDisplayed.get(\r
262                 sequenceFeatures[sfindex].type)).intValue()),\r
263                           start, end, y1, width, height);\r
264 \r
265           }\r
266           else\r
267             renderFeature(g, seq,\r
268                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
269                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
270                           getColour(sequenceFeatures[sfindex].type),\r
271                           start, end, y1, width, height);\r
272 \r
273         }\r
274       }\r
275 \r
276       if(transparencySetter!=null && g!=null)\r
277       {\r
278         transparencySetter.setTransparency(g, 1.0f);\r
279       }\r
280     }\r
281 \r
282 \r
283     char s;\r
284     int i;\r
285     void renderFeature(Graphics g, SequenceI seq,\r
286                        int fstart, int fend, Color featureColour, int start, int end,  int y1, int width, int height)\r
287     {\r
288 \r
289       if (((fstart <= end) && (fend >= start)))\r
290       {\r
291         if (fstart < start)\r
292         { // fix for if the feature we have starts before the sequence start,\r
293           fstart = start; // but the feature end is still valid!!\r
294         }\r
295 \r
296         if (fend >= end)\r
297         {\r
298           fend = end;\r
299         }\r
300 \r
301           for (i = fstart; i <= fend; i++)\r
302           {\r
303             s = seq.getSequence().charAt(i);\r
304 \r
305             if (jalview.util.Comparison.isGap(s))\r
306             {\r
307               continue;\r
308             }\r
309 \r
310             g.setColor(featureColour);\r
311 \r
312             g.fillRect( (i - start) * width, y1, width, height);\r
313 \r
314             if(!av.validCharWidth)\r
315               continue;\r
316 \r
317             g.setColor(Color.white);\r
318             charOffset = (width - fm.charWidth(s)) / 2;\r
319             g.drawString(String.valueOf(s),\r
320                          charOffset + (width * (i - start)),\r
321                          (y1 + height) - height / 5); //pady = height / 5;\r
322 \r
323           }\r
324       }\r
325     }\r
326 \r
327     void findAllFeatures()\r
328     {\r
329       av.featuresDisplayed = new Hashtable();\r
330       Vector allfeatures = new Vector();\r
331       for (int i = 0; i < av.alignment.getHeight(); i++)\r
332       {\r
333         SequenceFeature [] features = av.alignment.getSequenceAt(i).getSequenceFeatures();\r
334 \r
335         if (features == null)\r
336           continue;\r
337 \r
338         int index = 0;\r
339         while (index < features.length)\r
340         {\r
341           if (!av.featuresDisplayed.containsKey(features[index].getType()))\r
342           {\r
343             av.featuresDisplayed.put(features[index].getType(),\r
344                                   new Integer( getColour(features[index].getType()).getRGB()) );\r
345             allfeatures.addElement(features[index].getType());\r
346           }\r
347           index++;\r
348         }\r
349       }\r
350 \r
351       renderOrder = new String[allfeatures.size()];\r
352       Enumeration en = allfeatures.elements();\r
353       int i = allfeatures.size()-1;\r
354       while(en.hasMoreElements())\r
355       {\r
356         renderOrder[i] = en.nextElement().toString();\r
357         i--;\r
358       }\r
359     }\r
360 \r
361     public Color getColour(String featureType)\r
362     {\r
363       return (Color)featureColours.get(featureType);\r
364     }\r
365 \r
366     public void addNewFeature(String name, Color col)\r
367     {\r
368 \r
369       setColour(name, col);\r
370       if(av.featuresDisplayed==null)\r
371         av.featuresDisplayed = new Hashtable();\r
372 \r
373 \r
374       av.featuresDisplayed.put(name, "NOGROUP");\r
375     }\r
376 \r
377     public void setColour(String featureType, Color col)\r
378     {\r
379       featureColours.put(featureType, col);\r
380     }\r
381 \r
382     public void setFeaturePriority(Object [][] data)\r
383     {\r
384         // The feature table will display high priority\r
385         // features at the top, but theses are the ones\r
386         // we need to render last, so invert the data\r
387         if(av.featuresDisplayed!=null)\r
388           av.featuresDisplayed.clear();\r
389 \r
390         renderOrder = new String[data.length];\r
391 \r
392         if (data.length > 0)\r
393           for (int i = 0; i < data.length; i++)\r
394           {\r
395             String type = data[i][0].toString();\r
396             setColour(type, (Color) data[i][1]);\r
397             if ( ( (Boolean) data[i][2]).booleanValue())\r
398             {\r
399               av.featuresDisplayed.put(type, new Integer(getColour(type).getRGB()));\r
400             }\r
401 \r
402             renderOrder[data.length - i - 1] = type;\r
403           }\r
404     }\r
405 \r
406     Hashtable featureColours = new Hashtable();\r
407     void initColours()\r
408     {\r
409       featureColours.put("active site", new Color(255, 75, 0));\r
410       featureColours.put("binding site", new Color(245, 85, 0));\r
411       featureColours.put("calcium-binding region", new Color(235, 95, 0));\r
412       featureColours.put("chain", new Color(225, 105, 0));\r
413       featureColours.put("coiled-coil region", new Color(215, 115, 0));\r
414       featureColours.put("compositionally biased region", new Color(205, 125, 0));\r
415       featureColours.put("cross-link", new Color(195, 135, 0));\r
416       featureColours.put("disulfide bond", new Color(185, 145, 0));\r
417       featureColours.put("DNA-binding region", new Color(175, 155, 0));\r
418       featureColours.put("domain", new Color(165, 165, 0));\r
419       featureColours.put("glycosylation site", new Color(155, 175, 0));\r
420       featureColours.put("helix", new Color(145, 185, 0));\r
421       featureColours.put("initiator methionine", new Color(135, 195, 5));\r
422       featureColours.put("lipid moiety-binding region", new Color(125, 205, 15));\r
423       featureColours.put("metal ion-binding site", new Color(115, 215, 25));\r
424       featureColours.put("modified residue", new Color(105, 225, 35));\r
425       featureColours.put("mutagenesis site", new Color(95, 235, 45));\r
426       featureColours.put("non-consecutive residues", new Color(85, 245, 55));\r
427       featureColours.put("non-terminal residue", new Color(75, 255, 65));\r
428       featureColours.put("nucleotide phosphate-binding region",\r
429                          new Color(65, 245, 75));\r
430       featureColours.put("peptide", new Color(55, 235, 85));\r
431       featureColours.put("propeptide", new Color(45, 225, 95));\r
432       featureColours.put("region of interest", new Color(35, 215, 105));\r
433       featureColours.put("repeat", new Color(25, 205, 115));\r
434       featureColours.put("selenocysteine", new Color(15, 195, 125));\r
435       featureColours.put("sequence conflict", new Color(5, 185, 135));\r
436       featureColours.put("sequence variant", new Color(0, 175, 145));\r
437       featureColours.put("short sequence motif", new Color(0, 165, 155));\r
438       featureColours.put("signal peptide", new Color(0, 155, 165));\r
439       featureColours.put("site", new Color(0, 145, 175));\r
440       featureColours.put("splice variant", new Color(0, 135, 185));\r
441       featureColours.put("strand", new Color(0, 125, 195));\r
442       featureColours.put("topological domain", new Color(0, 115, 205));\r
443       featureColours.put("transit peptide", new Color(0, 105, 215));\r
444       featureColours.put("transmembrane region", new Color(0, 95, 225));\r
445       featureColours.put("turn", new Color(0, 85, 235));\r
446       featureColours.put("unsure residue", new Color(0, 75, 245));\r
447       featureColours.put("zinc finger region", new Color(0, 65, 255));\r
448     }\r
449 }\r
450 \r
451   class TransparencySetter\r
452   {\r
453     void setTransparency(Graphics g, float value)\r
454     {\r
455       Graphics2D g2 = (Graphics2D) g;\r
456       g2.setComposite(\r
457           AlphaComposite.getInstance(\r
458               AlphaComposite.SRC_OVER, value));\r
459     }\r
460   }\r
461 \r
462 \r
463 \r
464 \r