Use propertyListener for alignment edits
[jalview.git] / src / jalview / appletgui / AnnotationPanel.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 \r
20 package jalview.appletgui;\r
21 \r
22 import java.util.*;\r
23 \r
24 import java.awt.*;\r
25 import java.awt.event.*;\r
26 \r
27 import jalview.datamodel.*;\r
28 \r
29 public class AnnotationPanel\r
30     extends Panel implements AdjustmentListener\r
31 {\r
32   AlignViewport av;\r
33   AlignmentPanel ap;\r
34   int activeRow = -1;\r
35 \r
36   Vector activeRes;\r
37   static String HELIX = "Helix";\r
38   static String SHEET = "Sheet";\r
39   static String LABEL = "Label";\r
40   static String REMOVE = "Remove Annotation";\r
41   static String COLOUR = "Colour";\r
42   static Color HELIX_COLOUR = Color.red.darker();\r
43   static Color SHEET_COLOUR = Color.green.darker().darker();\r
44 \r
45   Image image;\r
46   Graphics gg;\r
47   FontMetrics fm;\r
48   int imgWidth = 0;\r
49 \r
50   boolean fastPaint = false;\r
51 \r
52   public static int GRAPH_HEIGHT = 40;\r
53 \r
54   public AnnotationPanel(AlignmentPanel ap)\r
55   {\r
56     this.ap = ap;\r
57     av = ap.av;\r
58     this.setLayout(null);\r
59     adjustPanelHeight();\r
60 \r
61     addMouseMotionListener(new MouseMotionAdapter()\r
62     {\r
63       public void mouseMoved(MouseEvent evt)\r
64       {\r
65         doMouseMoved(evt);\r
66       }\r
67     });\r
68 \r
69     // ap.annotationScroller.getVAdjustable().addAdjustmentListener( this );\r
70   }\r
71 \r
72   public void adjustmentValueChanged(AdjustmentEvent evt)\r
73   {\r
74     ap.alabels.setScrollOffset( -evt.getValue());\r
75   }\r
76 \r
77   public void adjustPanelHeight()\r
78   {\r
79     // setHeight of panels\r
80     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
81     int height = 0;\r
82     if (aa != null)\r
83     {\r
84       for (int i = 0; i < aa.length; i++)\r
85       {\r
86         if (!aa[i].visible)\r
87         {\r
88           continue;\r
89         }\r
90 \r
91         aa[i].height = 0;\r
92 \r
93         if (aa[i].hasText)\r
94         {\r
95           aa[i].height += av.charHeight;\r
96         }\r
97         if (aa[i].hasIcons)\r
98         {\r
99           aa[i].height += 16;\r
100         }\r
101 \r
102         if (aa[i].isGraph)\r
103         {\r
104           aa[i].height += GRAPH_HEIGHT;\r
105         }\r
106 \r
107         if (aa[i].height == 0)\r
108         {\r
109           aa[i].height = 20;\r
110         }\r
111         height += aa[i].height;\r
112       }\r
113     }\r
114     else\r
115     {\r
116       height = 20;\r
117     }\r
118 \r
119     this.setSize(getSize().width, height);\r
120     ap.annotationScroller.setSize(getSize().width, height);\r
121 \r
122 //  ap.annotationSpaceFillerHolder.setSize(d.width,annotationPanel.getSize().height);\r
123 \r
124     repaint();\r
125 \r
126   }\r
127 \r
128   public void addEditableColumn(int i)\r
129   {\r
130     if (activeRow == -1)\r
131     {\r
132       AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
133       for (int j = 0; j < aa.length; j++)\r
134       {\r
135         if (aa[j].editable)\r
136         {\r
137           activeRow = j;\r
138           break;\r
139         }\r
140       }\r
141     }\r
142 \r
143     if (activeRes == null)\r
144     {\r
145       activeRes = new Vector();\r
146       activeRes.addElement(String.valueOf(i));\r
147       return;\r
148     }\r
149 \r
150     activeRes.addElement(String.valueOf(i));\r
151   }\r
152 \r
153   public void doMouseMoved(MouseEvent evt)\r
154   {\r
155     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
156     if (aa == null)\r
157     {\r
158       return;\r
159     }\r
160 \r
161     int row = -1;\r
162     int height = 0;\r
163     for (int i = 0; i < aa.length; i++)\r
164     {\r
165 \r
166       if (aa[i].visible)\r
167       {\r
168         height += aa[i].height;\r
169       }\r
170 \r
171       if (evt.getY() < height)\r
172       {\r
173         row = i;\r
174         break;\r
175       }\r
176     }\r
177 \r
178     int res = evt.getX() / av.getCharWidth() + av.getStartRes();\r
179     if (row > -1 && res < aa[row].annotations.length && aa[row].annotations[res] != null)\r
180     {\r
181       StringBuffer text = new StringBuffer("Sequence position " + (res + 1) +\r
182                                            "  " +\r
183                                            aa[row].annotations[res].description);\r
184       ap.alignFrame.statusBar.setText(text.toString());\r
185     }\r
186   }\r
187 \r
188   public void update(Graphics g)\r
189   {\r
190     paint(g);\r
191   }\r
192 \r
193   public void paint(Graphics g)\r
194   {\r
195     g.setColor(Color.white);\r
196     g.fillRect(0,0, getSize().width, getSize().height);\r
197     imgWidth = (av.endRes - av.startRes + 1) * av.charWidth;\r
198 \r
199     if (image == null || imgWidth != image.getWidth(this))\r
200     {\r
201       image = createImage(imgWidth, ap.annotationPanel.getSize().height);\r
202       gg = image.getGraphics();\r
203       gg.setFont(av.getFont());\r
204       fm = gg.getFontMetrics();\r
205       fastPaint = false;\r
206     }\r
207 \r
208     if (fastPaint)\r
209     {\r
210       g.drawImage(image, 0, 0, this);\r
211       fastPaint = false;\r
212       return;\r
213     }\r
214 \r
215     drawComponent(gg, av.startRes, av.endRes + 1);\r
216     g.drawImage(image, 0, 0, this);\r
217 \r
218   }\r
219 \r
220   public void fastPaint(int horizontal)\r
221   {\r
222     if (horizontal == 0\r
223         || av.alignment.getAlignmentAnnotation() == null\r
224         || av.alignment.getAlignmentAnnotation().length < 1\r
225         )\r
226     {\r
227       repaint();\r
228       return;\r
229     }\r
230 \r
231     gg.copyArea(0, 0, imgWidth, getSize().height, -horizontal * av.charWidth, 0);\r
232     int sr = av.startRes, er = av.endRes + 1, transX = 0;\r
233 \r
234     if (horizontal > 0) // scrollbar pulled right, image to the left\r
235     {\r
236       transX = (er - sr - horizontal) * av.charWidth;\r
237       sr = er - horizontal;\r
238     }\r
239     else if (horizontal < 0)\r
240     {\r
241       er = sr - horizontal;\r
242     }\r
243 \r
244     gg.translate(transX, 0);\r
245 \r
246     drawComponent(gg, sr, er);\r
247 \r
248     gg.translate( -transX, 0);\r
249 \r
250     fastPaint = true;\r
251     repaint();\r
252   }\r
253 \r
254   public void drawComponent(Graphics g, int startRes, int endRes)\r
255   {\r
256     g.setColor(Color.white);\r
257     g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getSize().height);\r
258     if (av.alignment.getAlignmentAnnotation() == null ||\r
259         av.alignment.getAlignmentAnnotation().length < 1)\r
260     {\r
261       g.setColor(Color.white);\r
262       g.fillRect(0, 0, getSize().width, getSize().height);\r
263       g.setColor(Color.black);\r
264       g.drawString("Alignment has no annotations", 20, 15);\r
265       return;\r
266     }\r
267 \r
268     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
269 \r
270     int j, x = 0, y = 0;\r
271     char[] lastSS = new char[aa.length];\r
272     int[] lastSSX = new int[aa.length];\r
273     int iconOffset = av.charHeight / 2;\r
274     boolean validRes = false;\r
275     //\u03B2 \u03B1\r
276 \r
277     for (int i = 0; i < aa.length; i++)\r
278     {\r
279       AlignmentAnnotation row = aa[i];\r
280       if (!row.visible)\r
281       {\r
282         continue;\r
283       }\r
284 \r
285       if (row.isGraph)\r
286       {\r
287         // this is so that we draw the characters below the graph\r
288         y += row.height;\r
289         if (row.hasText)\r
290         {\r
291           y -= av.charHeight;\r
292         }\r
293       }\r
294       if (row.hasText)\r
295       {\r
296         iconOffset = av.charHeight / 2;\r
297       }\r
298       else\r
299       {\r
300         iconOffset = 0;\r
301       }\r
302 \r
303       for (j = startRes; j < endRes; j++)\r
304       {\r
305         if (row.annotations.length <= j || row.annotations[j] == null)\r
306         {\r
307           validRes = false;\r
308         }\r
309         else\r
310         {\r
311           validRes = true;\r
312         }\r
313 \r
314         x = (j - startRes) * av.charWidth;\r
315 \r
316         if (activeRow == i)\r
317         {\r
318 \r
319           g.setColor(Color.red);\r
320 \r
321           if (activeRes != null)\r
322           {\r
323             for (int n = 0; n < activeRes.size(); n++)\r
324             {\r
325               int v = Integer.parseInt(activeRes.elementAt(n).toString());\r
326               if (v == j)\r
327               {\r
328                 g.fillRect( (j - startRes) * av.charWidth, y, av.charWidth,\r
329                            row.height);\r
330               }\r
331             }\r
332           }\r
333         }\r
334 \r
335         if (validRes && row.annotations[j].displayCharacter.length() > 0)\r
336         {\r
337           int charOffset = (av.charWidth -\r
338                             fm.charWidth(row.annotations[j].displayCharacter.\r
339                                          charAt(0))) / 2;\r
340           g.setColor(row.annotations[j].colour);\r
341           if (j == 0 || row.isGraph)\r
342           {\r
343             if (row.annotations[0].secondaryStructure == 'H'\r
344                 || row.annotations[0].secondaryStructure == 'E')\r
345             {\r
346               g.drawString(row.annotations[j].displayCharacter, x,\r
347                            y + iconOffset + 3);\r
348             }\r
349           }\r
350           else if ( (row.annotations[j].secondaryStructure == 'H'\r
351                      || row.annotations[j].secondaryStructure == 'E') &&\r
352                    (row.annotations[j - 1] == null ||\r
353                     row.annotations[j].secondaryStructure !=\r
354                     row.annotations[j - 1].secondaryStructure))\r
355           {\r
356 \r
357             g.drawString(row.annotations[j].displayCharacter, x + charOffset,\r
358                          y + iconOffset + 3);\r
359           }\r
360 \r
361           if (!row.hasIcons)\r
362           {\r
363             g.drawString(row.annotations[j].displayCharacter, x + charOffset,\r
364                          y + iconOffset + 3);\r
365           }\r
366         }\r
367 \r
368         if (row.hasIcons)\r
369         {\r
370           if (!validRes || row.annotations[j].secondaryStructure != lastSS[i])\r
371           {\r
372             switch (lastSS[i])\r
373             {\r
374               case 'H':\r
375                 g.setColor(HELIX_COLOUR);\r
376                 g.fillRoundRect(lastSSX[i], y + 4 + iconOffset, x - lastSSX[i],\r
377                                 7, 8, 8);\r
378                 break;\r
379               case 'E':\r
380                 g.setColor(SHEET_COLOUR);\r
381                 g.fillRect(lastSSX[i], y + 4 + iconOffset, x - lastSSX[i] - 4,\r
382                            7);\r
383                 g.fillPolygon(new int[]\r
384                               {x - 4, x - 4, x}\r
385                               , new int[]\r
386                               {y + iconOffset, y + 14 + iconOffset,\r
387                               y + 8 + iconOffset}, 3);\r
388                 break;\r
389               case 'C':\r
390                 break;\r
391               default:\r
392                 g.setColor(Color.gray);\r
393                 g.fillRect(lastSSX[i], y + 6 + iconOffset, x - lastSSX[i], 2);\r
394                 break;\r
395             }\r
396 \r
397             if (validRes)\r
398             {\r
399               lastSS[i] = row.annotations[j].secondaryStructure;\r
400             }\r
401             else\r
402             {\r
403               lastSS[i] = ' ';\r
404             }\r
405             lastSSX[i] = x;\r
406           }\r
407         }\r
408 \r
409         if (validRes && row.isGraph)\r
410         {\r
411           g.setColor(new Color(0, 0, 180));\r
412           int height = (int) ( (row.annotations[j].value / row.graphMax) *\r
413                               GRAPH_HEIGHT);\r
414 \r
415           if (row.windowLength > 1)\r
416           {\r
417             int total = 0;\r
418             for (int i2 = j - (row.windowLength / 2);\r
419                  i2 < j + (row.windowLength / 2); i2++)\r
420             {\r
421               if (i2 < 0 || i2 >= av.alignment.getWidth())\r
422               {\r
423                 continue;\r
424               }\r
425 \r
426               total += row.annotations[i2].value;\r
427             }\r
428 \r
429             total /= row.windowLength;\r
430             height = (int) ( (total / row.graphMax) * GRAPH_HEIGHT);\r
431 \r
432           }\r
433           g.setColor(row.annotations[j].colour);\r
434           g.fillRect(x, y - height, av.charWidth, height);\r
435         }\r
436 \r
437       }\r
438 \r
439       x += av.charWidth;\r
440 \r
441       if (row.hasIcons)\r
442       {\r
443         switch (lastSS[i])\r
444         {\r
445           case 'H':\r
446             g.setColor(HELIX_COLOUR);\r
447             g.fillRoundRect(lastSSX[i], y + 4 + iconOffset, x - lastSSX[i], 7,\r
448                             8, 8);\r
449             break;\r
450           case 'E':\r
451             g.setColor(SHEET_COLOUR);\r
452             g.fillRect(lastSSX[i], y + 4 + iconOffset, x - lastSSX[i] - 4, 7);\r
453             g.fillPolygon(new int[]\r
454                           {x - 4, x - 4, x}\r
455                           , new int[]\r
456                           {y + iconOffset, y + 14 + iconOffset,\r
457                           y + 7 + iconOffset}\r
458                           , 3);\r
459             break;\r
460           case 'C':\r
461             break;\r
462           default:\r
463             g.setColor(Color.gray);\r
464             g.fillRect(lastSSX[i], y + 6 + iconOffset, x - lastSSX[i], 2);\r
465             break;\r
466 \r
467         }\r
468       }\r
469 \r
470       if (row.isGraph && row.hasText)\r
471       {\r
472         y += av.charHeight;\r
473       }\r
474       if (!row.isGraph)\r
475       {\r
476         y += aa[i].height;\r
477       }\r
478     }\r
479   }\r
480 \r
481   // used by overview window\r
482   public void drawGraph(Graphics g, AlignmentAnnotation aa, int width, int y)\r
483   {\r
484     g.setColor(Color.white);\r
485     g.fillRect(0, 0, width, y);\r
486     g.setColor(new Color(0, 0, 180));\r
487     int x = 0;\r
488     for (int j = 0; j < aa.annotations.length; j++)\r
489     {\r
490       g.setColor(new Color(0, 0, 180));\r
491       int height = (int) ( (aa.annotations[j].value / aa.graphMax) *\r
492                           GRAPH_HEIGHT);\r
493       g.fillRect(x, y - height, av.charWidth, height);\r
494       x += av.charWidth;\r
495     }\r
496   }\r
497 }\r