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