Add features from Popupmenu
[jalview.git] / src / jalview / gui / FeatureRenderer.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2006 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 import javax.swing.JComboBox;\r
29 import javax.swing.JPanel;\r
30 import javax.swing.JLabel;\r
31 import javax.swing.JOptionPane;\r
32 \r
33 \r
34 /**\r
35  * DOCUMENT ME!\r
36  *\r
37  * @author $author$\r
38  * @version $Revision$\r
39  */\r
40 public class FeatureRenderer\r
41 {\r
42     AlignViewport av;\r
43     Color resBoxColour;\r
44     float transparency = 1.0f;\r
45     FontMetrics fm;\r
46     int charOffset;\r
47 \r
48     Hashtable featureColours = new Hashtable();\r
49 \r
50 \r
51     // A higher level for grouping features of a\r
52    // particular type\r
53     Hashtable featureGroups = null;\r
54 \r
55     // This is actually an Integer held in the hashtable,\r
56     // Retrieved using the key feature type\r
57     Object currentColour;\r
58 \r
59     String [] renderOrder;\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     }\r
70 \r
71     public void transferSettings(FeatureRenderer fr)\r
72     {\r
73       renderOrder = fr.renderOrder;\r
74       featureGroups = fr.featureGroups;\r
75       featureColours = fr.featureColours;\r
76       transparency =  fr.transparency;\r
77     }\r
78 \r
79     BufferedImage offscreenImage;\r
80     boolean offscreenRender = false;\r
81     public Color findFeatureColour(Color initialCol, SequenceI seq, int res)\r
82     {\r
83       return new Color( findFeatureColour (initialCol.getRGB(),\r
84                         seq, res ));\r
85     }\r
86 \r
87     /**\r
88      * This is used by the Molecule Viewer and Overview to\r
89      * get the accurate colourof the rendered sequence\r
90      */\r
91     public int findFeatureColour(int initialCol, SequenceI seq, int column)\r
92     {\r
93       if(!av.showSequenceFeatures)\r
94         return initialCol;\r
95 \r
96       if(seq!=lastSeq)\r
97       {\r
98         lastSeq = seq;\r
99         sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();\r
100         if(sequenceFeatures==null)\r
101           return initialCol;\r
102 \r
103         sfSize = sequenceFeatures.length;\r
104       }\r
105 \r
106       if(jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))\r
107         return Color.white.getRGB();\r
108 \r
109 \r
110       //Only bother making an offscreen image if transparency is applied\r
111       if(transparency!=1.0f && offscreenImage==null)\r
112       {\r
113         offscreenImage = new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB);\r
114       }\r
115 \r
116       currentColour = null;\r
117 \r
118       offscreenRender = true;\r
119 \r
120       if(offscreenImage!=null)\r
121       {\r
122         offscreenImage.setRGB(0,0,initialCol);\r
123         drawSequence(offscreenImage.getGraphics(),\r
124                      lastSeq,\r
125                      column,column,0);\r
126 \r
127         return offscreenImage.getRGB(0,0);\r
128       }\r
129       else\r
130       {\r
131         drawSequence(null,\r
132                     lastSeq,\r
133                     lastSeq.findPosition(column),\r
134                     -1, -1);\r
135 \r
136         if (currentColour == null)\r
137           return initialCol;\r
138         else\r
139           return  ((Integer)currentColour).intValue();\r
140       }\r
141 \r
142 \r
143     }\r
144 \r
145 \r
146     /**\r
147      * DOCUMENT ME!\r
148      *\r
149      * @param g DOCUMENT ME!\r
150      * @param seq DOCUMENT ME!\r
151      * @param sg DOCUMENT ME!\r
152      * @param start DOCUMENT ME!\r
153      * @param end DOCUMENT ME!\r
154      * @param x1 DOCUMENT ME!\r
155      * @param y1 DOCUMENT ME!\r
156      * @param width DOCUMENT ME!\r
157      * @param height DOCUMENT ME!\r
158      */\r
159    // String type;\r
160    // SequenceFeature sf;\r
161     SequenceI lastSeq;\r
162     SequenceFeature [] sequenceFeatures;\r
163     int sfSize, sfindex, spos, epos;\r
164 \r
165     public void drawSequence(Graphics g, SequenceI seq,\r
166                              int start, int end, int y1)\r
167     {\r
168       if ( seq.getDatasetSequence().getSequenceFeatures() == null\r
169           || seq.getDatasetSequence().getSequenceFeatures().length==0)\r
170         return;\r
171 \r
172       if(g!=null)\r
173         fm = g.getFontMetrics();\r
174 \r
175 \r
176       if (av.featuresDisplayed == null\r
177           || renderOrder==null\r
178           || newFeatureAdded)\r
179        {\r
180          findAllFeatures();\r
181          if(av.featuresDisplayed.size()<1)\r
182            return;\r
183 \r
184          sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();\r
185          sfSize = sequenceFeatures.length;\r
186        }\r
187 \r
188        if(lastSeq==null || seq!=lastSeq)\r
189       {\r
190         lastSeq = seq;\r
191         sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();\r
192         sfSize = sequenceFeatures.length;\r
193       }\r
194 \r
195 \r
196       if (transparency != 1 && g!=null)\r
197       {\r
198         Graphics2D g2 = (Graphics2D) g;\r
199         g2.setComposite(\r
200             AlphaComposite.getInstance(\r
201                 AlphaComposite.SRC_OVER, transparency));\r
202       }\r
203 \r
204       if(!offscreenRender)\r
205       {\r
206         spos = lastSeq.findPosition(start);\r
207         epos = lastSeq.findPosition(end);\r
208       }\r
209 \r
210 \r
211       String type;\r
212       for(int renderIndex=0; renderIndex<renderOrder.length; renderIndex++)\r
213        {\r
214         type =  renderOrder[renderIndex];\r
215 \r
216         if(!av.featuresDisplayed.containsKey(type))\r
217           continue;\r
218 \r
219         // loop through all features in sequence to find\r
220         // current feature to render\r
221         for (sfindex = 0; sfindex < sfSize; sfindex++)\r
222         {\r
223           if(sequenceFeatures.length<=sfindex)\r
224           {\r
225             continue;\r
226           }\r
227           if (!sequenceFeatures[sfindex].type.equals(type))\r
228             continue;\r
229 \r
230           if (featureGroups != null\r
231               && sequenceFeatures[sfindex].featureGroup != null\r
232               &&\r
233               featureGroups.containsKey(sequenceFeatures[sfindex].featureGroup)\r
234               &&\r
235               ! ( (Boolean) featureGroups.get(sequenceFeatures[sfindex].featureGroup)).\r
236               booleanValue())\r
237           {\r
238             continue;\r
239           }\r
240 \r
241           if (!offscreenRender && (sequenceFeatures[sfindex].getBegin() > epos\r
242                             || sequenceFeatures[sfindex].getEnd() < spos))\r
243             continue;\r
244 \r
245           if (offscreenRender && offscreenImage==null)\r
246           {\r
247             if (sequenceFeatures[sfindex].begin <= start &&\r
248                 sequenceFeatures[sfindex].end  >= start)\r
249             {\r
250               currentColour = av.featuresDisplayed.get(sequenceFeatures[sfindex].\r
251                  type);\r
252             }\r
253           }\r
254           else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))\r
255           {\r
256 \r
257             renderFeature(g, seq,\r
258                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
259                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
260                           new Color( ( (Integer) av.featuresDisplayed.get(\r
261                 sequenceFeatures[sfindex].type)).intValue()),\r
262                           start, end, y1);\r
263             renderFeature(g, seq,\r
264                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
265                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
266                           new Color( ( (Integer) av.featuresDisplayed.get(\r
267                 sequenceFeatures[sfindex].type)).intValue()),\r
268                           start, end, y1);\r
269 \r
270           }\r
271           else\r
272             renderFeature(g, seq,\r
273                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
274                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
275                           getColour(sequenceFeatures[sfindex].type),\r
276                           start, end, y1);\r
277 \r
278 \r
279         }\r
280 \r
281       }\r
282 \r
283         if(transparency!=1.0f && g!=null)\r
284         {\r
285           Graphics2D g2 = (Graphics2D) g;\r
286           g2.setComposite(\r
287               AlphaComposite.getInstance(\r
288                   AlphaComposite.SRC_OVER, 1.0f));\r
289         }\r
290     }\r
291 \r
292 \r
293     char s;\r
294     int i;\r
295     void renderFeature(Graphics g, SequenceI seq,\r
296                        int fstart, int fend, Color featureColour, int start, int end,  int y1)\r
297     {\r
298 \r
299       if (((fstart <= end) && (fend >= start)))\r
300       {\r
301         if (fstart < start)\r
302         { // fix for if the feature we have starts before the sequence start,\r
303           fstart = start; // but the feature end is still valid!!\r
304         }\r
305 \r
306         if (fend >= end)\r
307         {\r
308           fend = end;\r
309         }\r
310           int pady = (y1 + av.charHeight) - av.charHeight / 5;\r
311           for (i = fstart; i <= fend; i++)\r
312           {\r
313             s = seq.getSequence().charAt(i);\r
314 \r
315             if (jalview.util.Comparison.isGap(s))\r
316             {\r
317               continue;\r
318             }\r
319 \r
320             g.setColor(featureColour);\r
321 \r
322             g.fillRect( (i - start) * av.charWidth, y1, av.charWidth,av.charHeight);\r
323 \r
324             if(offscreenRender || !av.validCharWidth)\r
325               continue;\r
326 \r
327             g.setColor(Color.white);\r
328             charOffset = (av.charWidth - fm.charWidth(s)) / 2;\r
329             g.drawString(String.valueOf(s),\r
330                          charOffset + (av.charWidth * (i - start)),\r
331                          pady);\r
332 \r
333           }\r
334       }\r
335     }\r
336 \r
337 \r
338     boolean newFeatureAdded = false;\r
339 \r
340     public void featuresAdded()\r
341     {\r
342       findAllFeatures();\r
343     }\r
344 \r
345    boolean findingFeatures = false;\r
346    synchronized void findAllFeatures()\r
347    {\r
348       newFeatureAdded = false;\r
349 \r
350       if(findingFeatures)\r
351       {\r
352         newFeatureAdded = true;\r
353         return;\r
354       }\r
355 \r
356       findingFeatures = true;\r
357       jalview.schemes.UserColourScheme ucs = new\r
358           jalview.schemes.UserColourScheme();\r
359 \r
360       if(av.featuresDisplayed==null)\r
361         av.featuresDisplayed = new Hashtable();\r
362 \r
363       av.featuresDisplayed.clear();\r
364 \r
365       Vector allfeatures = new Vector();\r
366       for (int i = 0; i < av.alignment.getHeight(); i++)\r
367       {\r
368         SequenceFeature [] features\r
369             = av.alignment.getSequenceAt(i).getDatasetSequence().getSequenceFeatures();\r
370 \r
371         if (features == null)\r
372           continue;\r
373 \r
374         int index = 0;\r
375         while (index < features.length)\r
376         {\r
377           if (!av.featuresDisplayed.containsKey(features[index].getType()))\r
378           {\r
379             if(!(features[index].begin == 0 && features[index].end ==0))\r
380             {\r
381               // If beginning and end are 0, the feature is for the whole sequence\r
382               // and we don't want to render the feature in the normal way\r
383 \r
384               if (getColour(features[index].getType()) == null)\r
385               {\r
386                  featureColours.put(features[index].getType(),\r
387                                    ucs.createColourFromName(features[index].\r
388                     getType()));\r
389               }\r
390 \r
391               av.featuresDisplayed.put(features[index].getType(),\r
392                                        new Integer(getColour(features[index].\r
393                   getType()).getRGB()));\r
394               allfeatures.addElement(features[index].getType());\r
395             }\r
396           }\r
397           index++;\r
398         }\r
399       }\r
400 \r
401       renderOrder = new String[allfeatures.size()];\r
402       Enumeration en = allfeatures.elements();\r
403       int i = allfeatures.size()-1;\r
404       while(en.hasMoreElements())\r
405       {\r
406         renderOrder[i] = en.nextElement().toString();\r
407         i--;\r
408       }\r
409 \r
410       findingFeatures = false;\r
411     }\r
412 \r
413     public Color getColour(String featureType)\r
414     {\r
415       Color colour = (Color)featureColours.get(featureType);\r
416       return colour;\r
417     }\r
418 \r
419 \r
420     static String lastFeatureAdded = "feature_1";\r
421     static String lastSourceAdded = "Jalview";\r
422 \r
423     public boolean createNewFeatures(SequenceI[] sequences,\r
424                                   SequenceFeature [] features)\r
425     {\r
426 \r
427       JComboBox name = new JComboBox();\r
428       JComboBox source = new JComboBox();\r
429 \r
430       JPanel panel = new JPanel(new GridLayout(2, 2));\r
431       panel.add(new JLabel("New Sequence Feature Name"));\r
432       panel.add(name);\r
433       panel.add(new JLabel("Source:"));\r
434       panel.add(source);\r
435      // name.setPreferredSize(new Dimension(300, 20));\r
436       name.setEditable(true);\r
437       source.setEditable(true);\r
438 \r
439       Enumeration en;\r
440       if (av.featuresDisplayed != null)\r
441       {\r
442         en = av.featuresDisplayed.keys();\r
443         while (en.hasMoreElements())\r
444         {\r
445           name.addItem(en.nextElement().toString());\r
446         }\r
447       }\r
448 \r
449       if (featureGroups != null)\r
450       {\r
451         en = featureGroups.keys();\r
452         while (en.hasMoreElements())\r
453         {\r
454           source.addItem(en.nextElement().toString());\r
455         }\r
456       }\r
457 \r
458       name.setSelectedItem(lastFeatureAdded);\r
459       source.setSelectedItem(lastSourceAdded);\r
460 \r
461       int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,\r
462           panel, "Create New Sequence Feature(s)",\r
463           JOptionPane.OK_CANCEL_OPTION);\r
464 \r
465 \r
466       if(reply==JOptionPane.OK_OPTION\r
467          && name.getSelectedItem()!=null\r
468          && source.getSelectedItem()!=null)\r
469       {\r
470         lastFeatureAdded = name.getSelectedItem().toString();\r
471         lastSourceAdded = source.getSelectedItem().toString();\r
472         for(int i=0; i<sequences.length; i++)\r
473         {\r
474           features[i].type = lastFeatureAdded;\r
475           features[i].featureGroup = lastSourceAdded;\r
476           sequences[i].addSequenceFeature(features[i]);\r
477         }\r
478 \r
479         if (av.featuresDisplayed == null)\r
480           av.featuresDisplayed = new Hashtable();\r
481 \r
482         if(featureGroups==null)\r
483           featureGroups = new Hashtable();\r
484 \r
485         featureGroups.put(lastSourceAdded, new Boolean(true));\r
486 \r
487         Color col = getColour(lastFeatureAdded);\r
488         if(col==null)\r
489         {\r
490           col = new Color(60, 160, 115);\r
491           setColour(lastFeatureAdded, col);\r
492         }\r
493 \r
494         av.featuresDisplayed.put(lastSourceAdded, new Integer(col.getRGB()));\r
495         findAllFeatures();\r
496         return true;\r
497       }\r
498       else\r
499         return false;\r
500 \r
501     }\r
502 \r
503     public void setColour(String featureType, Color col)\r
504     {\r
505       featureColours.put(featureType, col);\r
506     }\r
507 \r
508     public void setTransparency(float value)\r
509     {\r
510       transparency = value;\r
511     }\r
512 \r
513     public float getTransparency()\r
514     {\r
515       return transparency;\r
516     }\r
517 \r
518     public void setFeaturePriority(Object [][] data)\r
519     {\r
520       // The feature table will display high priority\r
521       // features at the top, but theses are the ones\r
522       // we need to render last, so invert the data\r
523       if(av.featuresDisplayed!=null)\r
524         av.featuresDisplayed.clear();\r
525       else\r
526         av.featuresDisplayed = new Hashtable();\r
527 \r
528       renderOrder = new String[data.length];\r
529 \r
530       if (data.length > 0)\r
531         for (int i = 0; i < data.length; i++)\r
532         {\r
533           String type = data[i][0].toString();\r
534           setColour(type, (Color) data[i][1]);\r
535           if ( ( (Boolean) data[i][2]).booleanValue())\r
536           {\r
537             av.featuresDisplayed.put(type, new Integer(getColour(type).getRGB()));\r
538           }\r
539 \r
540           renderOrder[data.length - i - 1] = type;\r
541         }\r
542 \r
543     }\r
544 \r
545 \r
546 \r
547 \r
548 }\r