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