check for null residues
[jalview.git] / src / jalview / gui / AnnotationPanel.java
1 package jalview.gui;\r
2 \r
3 import jalview.datamodel.*;\r
4 \r
5 import javax.swing.*;\r
6 import java.awt.*;\r
7 import java.awt.event.*;\r
8 import java.util.*;\r
9 import java.awt.image.*;\r
10 \r
11 public class AnnotationPanel extends JPanel implements MouseListener, MouseMotionListener, ActionListener, AdjustmentListener\r
12 {\r
13   AlignViewport av;\r
14   AlignmentPanel ap;\r
15   int activeRow =-1;\r
16 \r
17   ArrayList activeRes;\r
18   static String HELIX ="Helix";\r
19   static String SHEET ="Sheet";\r
20   static String LABEL ="Label";\r
21   static String REMOVE="Remove Annotation";\r
22   static String COLOUR="Colour";\r
23   static Color HELIX_COLOUR = Color.red.darker();\r
24   static Color SHEET_COLOUR = Color.green.darker().darker();\r
25 \r
26 \r
27   BufferedImage image;\r
28   Graphics2D gg;\r
29   FontMetrics fm;\r
30   int imgWidth=0;\r
31 \r
32   boolean fastPaint = false;\r
33 \r
34   public static int GRAPH_HEIGHT = 40;\r
35 \r
36 \r
37 \r
38   public AnnotationPanel(AlignmentPanel ap)\r
39   {\r
40     this.ap = ap;\r
41     av = ap.av;\r
42     this.setLayout(null);\r
43     addMouseListener(this);\r
44     addMouseMotionListener(this);\r
45     adjustPanelHeight();\r
46 \r
47 \r
48     ap.annotationScroller.getVerticalScrollBar().addAdjustmentListener( this );\r
49   }\r
50 \r
51   public void adjustmentValueChanged(AdjustmentEvent evt)\r
52   {\r
53     ap.alabels.setScrollOffset( -evt.getValue() );\r
54   }\r
55 \r
56   public void adjustPanelHeight()\r
57   {\r
58     // setHeight of panels\r
59     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
60     int height = 0;\r
61     if(aa!=null)\r
62     for (int i = 0; i < aa.length; i++)\r
63     {\r
64       if(!aa[i].visible)\r
65         continue;\r
66 \r
67       aa[i].height = 0;\r
68 \r
69       if(aa[i].hasText)\r
70         aa[i].height += av.charHeight;\r
71       if (aa[i].hasIcons)\r
72         aa[i].height += 16;\r
73 \r
74       if (aa[i].isGraph)\r
75         aa[i].height += GRAPH_HEIGHT;\r
76 \r
77       if(aa[i].height==0)\r
78         aa[i].height = 20;\r
79       height += aa[i].height;\r
80     }\r
81   else height=20;\r
82     this.setPreferredSize(new Dimension(1, height));\r
83 \r
84   }\r
85 \r
86   public void addEditableColumn(int i)\r
87   {\r
88     if(activeRow==-1)\r
89     {\r
90       AlignmentAnnotation [] aa = av.alignment.getAlignmentAnnotation();\r
91       for(int j=0; j<aa.length; j++)\r
92         if(aa[j].editable)\r
93         {\r
94           activeRow = j;\r
95           break;\r
96         }\r
97     }\r
98 \r
99     if(activeRes==null)\r
100     {\r
101       activeRes = new ArrayList();\r
102       activeRes.add(String.valueOf(i));\r
103       return;\r
104     }\r
105 \r
106     activeRes.add(String.valueOf(i));\r
107 \r
108   }\r
109 \r
110 \r
111   public void actionPerformed(ActionEvent evt)\r
112   {\r
113 \r
114     AlignmentAnnotation [] aa = av.alignment.getAlignmentAnnotation();\r
115     Annotation [] anot = aa[activeRow].annotations;\r
116 \r
117     if(evt.getActionCommand().equals(REMOVE))\r
118     {\r
119       for(int i=0; i<activeRes.size(); i++)\r
120       {\r
121         anot[Integer.parseInt(activeRes.get(i).toString())] = null;\r
122         anot[Integer.parseInt(activeRes.get(i).toString())] = null;\r
123       }\r
124     }\r
125     else if(evt.getActionCommand().equals(LABEL))\r
126     {\r
127       String label = JOptionPane.showInputDialog(this, "Enter Label ", "Enter label", JOptionPane.QUESTION_MESSAGE );\r
128       if(label==null)\r
129         label = "";\r
130 \r
131       if(label.length()>0 && !aa[activeRow].hasText)\r
132        aa[activeRow].hasText = true;\r
133 \r
134       for(int i=0; i<activeRes.size(); i++)\r
135       {\r
136         int index = Integer.parseInt(activeRes.get(i).toString());\r
137         if(anot[index]==null)\r
138           anot[index] = new Annotation(label, "", ' ',0);\r
139         anot[index].displayCharacter = label;\r
140       }\r
141     }\r
142     else if(evt.getActionCommand().equals(COLOUR))\r
143     {\r
144      Color col = JColorChooser.showDialog(this, "Choose foreground colour", Color.black);\r
145      for (int i = 0; i < activeRes.size(); i++)\r
146      {\r
147        int index = Integer.parseInt(activeRes.get(i).toString());\r
148        if (anot[index] == null)\r
149          anot[index] = new Annotation("", "", ' ', 0);\r
150        anot[index].colour = col;\r
151      }\r
152     }\r
153     else // HELIX OR SHEET\r
154     {\r
155     char type = 0;\r
156     String symbol = "\u03B1";\r
157     if(evt.getActionCommand().equals(HELIX))\r
158        type = 'H';\r
159     else if(evt.getActionCommand().equals(SHEET))\r
160     {\r
161       type = 'E';\r
162       symbol = "\u03B2";\r
163     }\r
164 \r
165     if(!aa[activeRow].hasIcons)\r
166       aa[activeRow].hasIcons = true;\r
167 \r
168 \r
169     String label = JOptionPane.showInputDialog("Enter a label for the structure?", symbol );\r
170     if(label==null)\r
171        label="";\r
172 \r
173     if(label.length()>0 && !aa[activeRow].hasText)\r
174        aa[activeRow].hasText = true;\r
175 \r
176     for(int i=0; i<activeRes.size(); i++)\r
177     {\r
178       int index = Integer.parseInt(activeRes.get(i).toString());\r
179       if (anot[index] == null)\r
180       {\r
181         anot[index] = new Annotation(label, "", type, 0);\r
182       }\r
183 \r
184         anot[ index ].secondaryStructure = type;\r
185         anot[ index ].displayCharacter = label;\r
186     }\r
187     }\r
188 \r
189     adjustPanelHeight();\r
190     activeRes = null;\r
191     repaint();\r
192     return;\r
193 \r
194 \r
195   }\r
196 \r
197   public void mousePressed(MouseEvent evt)\r
198   {\r
199     if (SwingUtilities.isRightMouseButton(evt))\r
200     {\r
201       if(activeRes==null)\r
202         return;\r
203 \r
204       JPopupMenu pop = new JPopupMenu("Structure type");\r
205       JMenuItem item = new JMenuItem(HELIX);\r
206       item.addActionListener(this);\r
207       pop.add(item);\r
208       item = new JMenuItem(SHEET);\r
209       item.addActionListener(this);\r
210       pop.add(item);\r
211       item = new JMenuItem(LABEL);\r
212       item.addActionListener(this);\r
213       pop.add(item);\r
214       item = new JMenuItem(COLOUR);\r
215       item.addActionListener(this);\r
216       pop.add(item);\r
217       item = new JMenuItem(REMOVE);\r
218       item.addActionListener(this);\r
219       pop.add(item);\r
220       pop.show(this, evt.getX(), evt.getY());\r
221       return;\r
222     }\r
223 \r
224 \r
225     AlignmentAnnotation [] aa = av.alignment.getAlignmentAnnotation();\r
226     if(aa==null)\r
227       return;\r
228 \r
229     int height = 0;\r
230     activeRow = -1;\r
231     for(int i=0; i<aa.length; i++)\r
232     {\r
233       height+= aa[i].height;\r
234 \r
235       if(evt.getY()<height)\r
236       {\r
237         if(!aa[i].editable)\r
238         {\r
239           activeRes = null;\r
240           continue;\r
241         }\r
242 \r
243         activeRow = i;\r
244         break;\r
245       }\r
246     }\r
247 \r
248 \r
249     int res = evt.getX() / av.getCharWidth() + av.getStartRes();\r
250 \r
251     if(evt.isControlDown() || evt.isAltDown())\r
252       addEditableColumn(res);\r
253 \r
254     else if(evt.isShiftDown())\r
255     {\r
256 \r
257       if(activeRes==null)\r
258         activeRes=new ArrayList();\r
259       else\r
260       {\r
261           int start =  Integer.parseInt( activeRes.get( activeRes.size()-1 ).toString() );\r
262           int end = res;\r
263           if(end<start)\r
264           {\r
265             int temp = end;\r
266             end = start;\r
267             start = temp;\r
268           }\r
269           for(int n=start; n<=end; n++)\r
270             addEditableColumn(n);\r
271 \r
272       }\r
273     }\r
274     else\r
275     {\r
276         activeRes = new ArrayList();\r
277         activeRes.add( String.valueOf(res) );\r
278     }\r
279 \r
280     repaint();\r
281 \r
282   }\r
283   public void mouseReleased(MouseEvent evt)\r
284   {  }\r
285   public void mouseEntered(MouseEvent evt)\r
286   {  }\r
287   public void mouseExited(MouseEvent evt)\r
288   {  }\r
289   public void mouseDragged(MouseEvent evt)\r
290   {  }\r
291   public void mouseMoved(MouseEvent evt)\r
292   {\r
293     ToolTipManager.sharedInstance().registerComponent(this);\r
294     AlignmentAnnotation [] aa = av.alignment.getAlignmentAnnotation();\r
295     if(aa==null)\r
296       return;\r
297 \r
298     int row = -1;\r
299     int height=0;\r
300     for(int i=0; i<aa.length; i++)\r
301     {\r
302 \r
303       if( aa[i].visible )\r
304         height += aa[i].height;\r
305 \r
306       if(evt.getY()<height)\r
307       {\r
308         row = i;\r
309         break;\r
310       }\r
311     }\r
312 \r
313     int res = evt.getX() / av.getCharWidth() + av.getStartRes();\r
314     if(row>-1 && res<aa[row].annotations.length && aa[row].annotations[res]!=null)\r
315     {\r
316       this.setToolTipText(aa[row].annotations[res].description);\r
317       StringBuffer text = new StringBuffer("Sequence position " + (res + 1) +\r
318                                            "  " +\r
319                                            aa[row].annotations[res].description);\r
320       ap.alignFrame.statusBar.setText(text.toString());\r
321     }\r
322 \r
323 \r
324   }\r
325   public void mouseClicked(MouseEvent evt) {}\r
326 \r
327 \r
328   public void paintComponent(Graphics g)\r
329   {\r
330     g.setColor(Color.white);\r
331     g.fillRect(0,0,getWidth(), getHeight());\r
332 \r
333     if(fastPaint)\r
334     {\r
335       g.drawImage(image, 0, 0, this);\r
336       fastPaint = false;\r
337       return;\r
338     }\r
339 \r
340     imgWidth = (av.endRes-av.startRes+1) *av.charWidth;\r
341 \r
342     image = new BufferedImage(imgWidth,\r
343                                 ap.annotationPanel.getHeight(),\r
344                                 BufferedImage.TYPE_INT_RGB);\r
345       gg = (Graphics2D) image.getGraphics();\r
346       gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,\r
347                           RenderingHints.VALUE_ANTIALIAS_ON);\r
348 \r
349       gg.setFont(av.getFont());\r
350       fm = gg.getFontMetrics();\r
351 \r
352     drawComponent( gg, av.startRes, av.endRes+1);\r
353     g.drawImage( image, 0, 0, this);\r
354 \r
355   }\r
356 \r
357   public void fastPaint(int horizontal)\r
358 {\r
359   if( horizontal == 0\r
360      || av.alignment.getAlignmentAnnotation()==null\r
361      || av.alignment.getAlignmentAnnotation().length<1\r
362     )\r
363   {\r
364     repaint();\r
365     return;\r
366   }\r
367 \r
368   gg.copyArea( 0,0, imgWidth, getHeight(), -horizontal*av.charWidth, 0 );\r
369   int sr=av.startRes, er=av.endRes+1, transX=0;\r
370 \r
371   if(horizontal>0) // scrollbar pulled right, image to the left\r
372   {\r
373     transX =  (er-sr-horizontal)*av.charWidth;\r
374     sr = er - horizontal ;\r
375   }\r
376   else if(horizontal<0)\r
377   {\r
378     er = sr-horizontal;\r
379   }\r
380 \r
381 \r
382   gg.translate(transX, 0);\r
383 \r
384   drawComponent(gg, sr, er);\r
385 \r
386   gg.translate( -transX, 0 );\r
387 \r
388   fastPaint = true;\r
389   repaint();\r
390 }\r
391 \r
392 \r
393   public void drawComponent(Graphics2D g, int startRes, int endRes)\r
394   {\r
395     g.setColor(Color.white);\r
396     g.fillRect(0,0,(endRes-startRes) *av.charWidth, getHeight());\r
397     if(av.alignment.getAlignmentAnnotation()==null || av.alignment.getAlignmentAnnotation().length<1)\r
398     {\r
399       g.setColor(Color.white);\r
400       g.fillRect(0,0,getWidth(), getHeight());\r
401       g.setColor(Color.black);\r
402       g.drawString("Alignment has no annotations",20,15);\r
403       return;\r
404     }\r
405 \r
406     AlignmentAnnotation [] aa = av.alignment.getAlignmentAnnotation();\r
407 \r
408     int j, x=0, y=0;\r
409     char [] lastSS = new char[aa.length];\r
410     int  [] lastSSX= new int[aa.length] ;\r
411     int iconOffset = av.charHeight/2;\r
412     boolean validRes = false;\r
413     //\u03B2 \u03B1\r
414 \r
415     for(int i=0; i<aa.length; i++)\r
416     {\r
417       AlignmentAnnotation row = aa[i];\r
418       if(!row.visible)\r
419         continue;\r
420 \r
421       if(row.isGraph)\r
422       {\r
423         // this is so that we draw the characters below the graph\r
424         y += row.height;\r
425         if(row.hasText)\r
426           y -= av.charHeight;\r
427       }\r
428       if(row.hasText)\r
429         iconOffset = av.charHeight/2;\r
430       else\r
431         iconOffset = 0;\r
432 \r
433       for(j=startRes; j<endRes; j++)\r
434       {\r
435         if(row.annotations.length<=j || row.annotations[j]==null)\r
436         validRes = false;\r
437        else\r
438          validRes = true;\r
439 \r
440        x = (j-startRes)*av.charWidth;\r
441 \r
442 \r
443        if(activeRow==i)\r
444        {\r
445 \r
446          g.setColor(Color.red);\r
447 \r
448          if(activeRes!=null)\r
449            for (int n = 0; n < activeRes.size(); n++)\r
450            {\r
451              int v = Integer.parseInt(activeRes.get(n).toString()) ;\r
452              if (v == j)\r
453                g.fillRect( (j-startRes) * av.charWidth, y, av.charWidth, row.height);\r
454            }\r
455        }\r
456 \r
457 \r
458 \r
459        if(validRes && row.annotations[j].displayCharacter.length()>0)\r
460        {\r
461          int charOffset = (av.charWidth - fm.charWidth(row.annotations[j].displayCharacter.charAt(0)))/2;\r
462          g.setColor( row.annotations[j].colour);\r
463           if(j==0)\r
464           {\r
465             if (row.annotations[0].secondaryStructure == 'H'\r
466                 || row.annotations[0].secondaryStructure == 'E')\r
467               g.drawString(row.annotations[j].displayCharacter, x,\r
468                            y + iconOffset + 2);\r
469           }\r
470          else if( (row.annotations[j].secondaryStructure=='H'\r
471                || row.annotations[j].secondaryStructure=='E') &&\r
472                (row.annotations[j-1]==null ||\r
473                 row.annotations[j].secondaryStructure!=row.annotations[j-1].secondaryStructure))\r
474 \r
475         g.drawString(row.annotations[j].displayCharacter, x, y + iconOffset + 2);\r
476 \r
477         if(!row.hasIcons)\r
478             g.drawString(row.annotations[j].displayCharacter, x + charOffset,\r
479                          y + iconOffset + 2);\r
480        }\r
481 \r
482        if(row.hasIcons)\r
483        if(!validRes || row.annotations[j].secondaryStructure!=lastSS[i])\r
484        {\r
485          switch (lastSS[i])\r
486          {\r
487            case 'H':\r
488              g.setColor(HELIX_COLOUR);\r
489              g.fillRoundRect(lastSSX[i], y+4 + iconOffset, x-lastSSX[i], 7, 8, 8);\r
490              break;\r
491            case 'E':\r
492              g.setColor(SHEET_COLOUR);\r
493              g.fillRect(lastSSX[i], y + 4 + iconOffset, x-lastSSX[i]-4, 7);\r
494              g.fillPolygon(new int[] {x - 4, x- 4, x }\r
495                            , new int[]{y+ iconOffset, y + 14+ iconOffset, y + 8+ iconOffset}, 3);\r
496              break;\r
497            case 'C':\r
498              break;\r
499            default :\r
500              g.setColor(Color.gray);\r
501              g.fillRect(lastSSX[i], y+6+ iconOffset, x-lastSSX[i], 2);\r
502              break;\r
503          }\r
504 \r
505          if(validRes)\r
506            lastSS[i] = row.annotations[j].secondaryStructure;\r
507           else\r
508             lastSS[i] = ' ';\r
509          lastSSX[i] = x;\r
510        }\r
511 \r
512        if (validRes && row.isGraph)\r
513        {\r
514          g.setColor(new Color(0,0,180));\r
515          int height = (int)((row.annotations[j].value / row.graphMax)*GRAPH_HEIGHT);\r
516 \r
517          if(row.windowLength>1)\r
518          {\r
519            int total =0;\r
520            for(int i2=j- (row.windowLength/2); i2<j+(row.windowLength/2); i2++)\r
521            {\r
522              if(i2<0 || i2>=av.alignment.getWidth())\r
523                continue;\r
524 \r
525              total += row.annotations[i2].value;\r
526            }\r
527 \r
528            total/=row.windowLength;\r
529            height = (int)( (total / row.graphMax) *GRAPH_HEIGHT);\r
530 \r
531          }\r
532          g.setColor(row.annotations[j].colour);\r
533          g.fillRect(x, y-height, av.charWidth, height );\r
534        }\r
535 \r
536 \r
537       }\r
538 \r
539       x+=av.charWidth;\r
540 \r
541       if(row.hasIcons)\r
542       switch (lastSS[i])\r
543       {\r
544         case 'H':\r
545           g.setColor(HELIX_COLOUR);\r
546           g.fillRoundRect(lastSSX[i], y+4+ iconOffset, x - lastSSX[i], 7, 8, 8);\r
547           break;\r
548         case 'E':\r
549           g.setColor(SHEET_COLOUR);\r
550           g.fillRect(lastSSX[i], y + 4+ iconOffset, x - lastSSX[i] - 4, 7);\r
551           g.fillPolygon(new int[]\r
552                         {x - 4, x - 4, x}\r
553                         , new int[]\r
554                         {y + iconOffset, y + 14+ iconOffset, y + 7+ iconOffset}\r
555                         , 3);\r
556           break;\r
557         case 'C':\r
558           break;\r
559         default:\r
560           g.setColor(Color.gray);\r
561           g.fillRect(lastSSX[i], y+6+ iconOffset, x-lastSSX[i], 2);\r
562           break;\r
563 \r
564       }\r
565 \r
566        if(row.isGraph && row.hasText)\r
567          y+=av.charHeight;\r
568        if(!row.isGraph)\r
569           y+=aa[i].height;\r
570     }\r
571   }\r
572 \r
573   // used by overview window\r
574   public void drawGraph(Graphics g, AlignmentAnnotation aa,int width, int y)\r
575   {\r
576     g.setColor(Color.white);\r
577     g.fillRect(0,0,width, y);\r
578     g.setColor(new Color(0,0,180));\r
579     int x = 0;\r
580     for(int j=0; j<aa.annotations.length; j++)\r
581     {\r
582       g.setColor(new Color(0, 0, 180));\r
583       int height = (int) ( (aa.annotations[j].value / aa.graphMax) * GRAPH_HEIGHT );\r
584       g.fillRect(x, y - height, av.charWidth, height);\r
585       x+=av.charWidth;\r
586     }\r
587   }\r
588 }\r