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