recovery of identical dataset sequence object on undo (rather than creation of a...
[jalview.git] / src / jalview / appletgui / AnnotationPanel.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2007 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\r
31     implements AdjustmentListener, ActionListener, MouseListener, MouseMotionListener\r
32 {\r
33   AlignViewport av;\r
34   AlignmentPanel ap;\r
35   int activeRow = -1;\r
36 \r
37   Vector activeRes;\r
38   static String HELIX = "Helix";\r
39   static String SHEET = "Sheet";\r
40   static String LABEL = "Label";\r
41   static String REMOVE = "Remove Annotation";\r
42   static String COLOUR = "Colour";\r
43   static Color HELIX_COLOUR = Color.red.darker();\r
44   static Color SHEET_COLOUR = Color.green.darker().darker();\r
45 \r
46   Image image;\r
47   Graphics gg;\r
48   FontMetrics fm;\r
49   int imgWidth = 0;\r
50 \r
51   boolean fastPaint = false;\r
52 \r
53   public static int GRAPH_HEIGHT = 40;\r
54 \r
55   boolean MAC = false;\r
56 \r
57   public AnnotationPanel(AlignmentPanel ap)\r
58   {\r
59     if (System.getProperty("os.name").startsWith("Mac"))\r
60     {\r
61       MAC = true;\r
62     }\r
63 \r
64     this.ap = ap;\r
65     av = ap.av;\r
66     setLayout(null);\r
67     adjustPanelHeight();\r
68 \r
69     addMouseMotionListener(this);\r
70 \r
71     addMouseListener(this);\r
72 \r
73 \r
74     // ap.annotationScroller.getVAdjustable().addAdjustmentListener( this );\r
75   }\r
76 \r
77   public AnnotationPanel(AlignViewport av)\r
78   {\r
79     this.av = av;\r
80   }\r
81 \r
82   public void adjustmentValueChanged(AdjustmentEvent evt)\r
83   {\r
84     ap.alabels.setScrollOffset( -evt.getValue());\r
85   }\r
86 \r
87   /**\r
88    * DOCUMENT ME!\r
89    *\r
90    * @param evt DOCUMENT ME!\r
91    */\r
92   public void actionPerformed(ActionEvent evt)\r
93   {\r
94     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
95     Annotation[] anot = aa[activeRow].annotations;\r
96 \r
97     if (anot.length < av.getColumnSelection().getMax())\r
98     {\r
99       Annotation[] temp = new Annotation[av.getColumnSelection().getMax() + 2];\r
100       System.arraycopy(anot, 0, temp, 0, anot.length);\r
101       anot = temp;\r
102       aa[activeRow].annotations = anot;\r
103     }\r
104 \r
105     String label = "";\r
106     if (av.colSel != null && av.colSel.size() > 0\r
107         && anot[av.colSel.getMin()] != null)\r
108       label = anot[av.getColumnSelection().getMin()].displayCharacter;\r
109 \r
110 \r
111     if (evt.getActionCommand().equals(REMOVE))\r
112     {\r
113       for (int i = 0; i < av.getColumnSelection().size(); i++)\r
114       {\r
115         anot[av.getColumnSelection().columnAt(i)] = null;\r
116       }\r
117     }\r
118     else if (evt.getActionCommand().equals(LABEL))\r
119     {\r
120       label = enterLabel(label, "Enter Label");\r
121 \r
122       if (label == null)\r
123       {\r
124         return;\r
125       }\r
126 \r
127       if ( (label.length() > 0) && !aa[activeRow].hasText)\r
128       {\r
129         aa[activeRow].hasText = true;\r
130       }\r
131 \r
132       for (int i = 0; i < av.getColumnSelection().size(); i++)\r
133       {\r
134         int index = av.getColumnSelection().columnAt(i);\r
135 \r
136         if(!av.colSel.isVisible(index))\r
137           continue;\r
138 \r
139         if (anot[index] == null)\r
140         {\r
141           anot[index] = new Annotation(label, "", ' ', 0);\r
142         }\r
143 \r
144         anot[index].displayCharacter = label;\r
145       }\r
146     }\r
147     else if (evt.getActionCommand().equals(COLOUR))\r
148     {\r
149       UserDefinedColours udc = new UserDefinedColours(\r
150           this,\r
151          Color.black, ap.alignFrame);\r
152 \r
153       Color col = udc.getColor();\r
154 \r
155       for (int i = 0; i < av.getColumnSelection().size(); i++)\r
156       {\r
157         int index = av.getColumnSelection().columnAt(i);\r
158 \r
159         if(!av.colSel.isVisible(index))\r
160           continue;\r
161 \r
162         if (anot[index] == null)\r
163         {\r
164           anot[index] = new Annotation("", "", ' ', 0);\r
165         }\r
166 \r
167         anot[index].colour = col;\r
168       }\r
169     }\r
170     else // HELIX OR SHEET\r
171     {\r
172       char type = 0;\r
173       String symbol = "\u03B1";\r
174 \r
175       if (evt.getActionCommand().equals(HELIX))\r
176       {\r
177         type = 'H';\r
178       }\r
179       else if (evt.getActionCommand().equals(SHEET))\r
180       {\r
181         type = 'E';\r
182         symbol = "\u03B2";\r
183       }\r
184 \r
185       if (!aa[activeRow].hasIcons)\r
186       {\r
187         aa[activeRow].hasIcons = true;\r
188       }\r
189 \r
190       label = enterLabel(symbol, "Enter Label");\r
191 \r
192       if (label == null)\r
193       {\r
194         return;\r
195       }\r
196 \r
197       if ( (label.length() > 0) && !aa[activeRow].hasText)\r
198       {\r
199         aa[activeRow].hasText = true;\r
200       }\r
201 \r
202       for (int i = 0; i < av.getColumnSelection().size(); i++)\r
203       {\r
204         int index = av.getColumnSelection().columnAt(i);\r
205 \r
206         if(!av.colSel.isVisible(index))\r
207           continue;\r
208 \r
209         if (anot[index] == null)\r
210         {\r
211           anot[index] = new Annotation(label, "", type, 0);\r
212         }\r
213 \r
214         anot[index].secondaryStructure = type;\r
215         anot[index].displayCharacter = label;\r
216       }\r
217     }\r
218 \r
219     adjustPanelHeight();\r
220     repaint();\r
221 \r
222     return;\r
223   }\r
224 \r
225   String enterLabel(String text, String label)\r
226   {\r
227     EditNameDialog dialog = new EditNameDialog(text,null,label,null,\r
228         ap.alignFrame,"Enter Label", 400,200, true);\r
229 \r
230     if(dialog.accept)\r
231       return dialog.getName();\r
232     else\r
233       return null;\r
234   }\r
235 \r
236   public void mousePressed(MouseEvent evt)\r
237   {\r
238       AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
239       if (aa == null)\r
240       {\r
241         return;\r
242       }\r
243 \r
244       int height = 0;\r
245       activeRow = -1;\r
246 \r
247 \r
248       for (int i = 0; i < aa.length; i++)\r
249       {\r
250         if (aa[i].visible)\r
251         {\r
252           height += aa[i].height;\r
253         }\r
254 \r
255         if (evt.getY() < height)\r
256         {\r
257           if (aa[i].editable)\r
258           {\r
259             activeRow = i;\r
260           }\r
261 \r
262           break;\r
263         }\r
264       }\r
265 \r
266       if ( (evt.getModifiers() & InputEvent.BUTTON3_MASK) ==\r
267         InputEvent.BUTTON3_MASK  && activeRow != -1)\r
268       {\r
269         if (av.getColumnSelection() == null)\r
270         {\r
271           return;\r
272         }\r
273 \r
274         PopupMenu pop = new PopupMenu("Structure type");\r
275         MenuItem item = new MenuItem(HELIX);\r
276         item.addActionListener(this);\r
277         pop.add(item);\r
278         item = new MenuItem(SHEET);\r
279         item.addActionListener(this);\r
280         pop.add(item);\r
281         item = new MenuItem(LABEL);\r
282         item.addActionListener(this);\r
283         pop.add(item);\r
284         item = new MenuItem(COLOUR);\r
285         item.addActionListener(this);\r
286         pop.add(item);\r
287         item = new MenuItem(REMOVE);\r
288         item.addActionListener(this);\r
289         pop.add(item);\r
290         ap.alignFrame.add(pop);\r
291         pop.show(this, evt.getX(), evt.getY());\r
292 \r
293         return;\r
294       }\r
295 \r
296       if (aa == null)\r
297       {\r
298         return;\r
299       }\r
300 \r
301       ap.scalePanel.mousePressed(evt);\r
302   }\r
303 \r
304   public void mouseReleased(MouseEvent evt)\r
305   {\r
306     ap.scalePanel.mouseReleased(evt);\r
307   }\r
308 \r
309   public void mouseClicked(MouseEvent evt)\r
310   {}\r
311 \r
312   public void mouseDragged(MouseEvent evt)\r
313   {\r
314     ap.scalePanel.mouseDragged(evt);\r
315   }\r
316 \r
317   public void mouseMoved(MouseEvent evt)\r
318   {\r
319     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
320     if (aa == null)\r
321     {\r
322       return;\r
323     }\r
324 \r
325     int row = -1;\r
326     int height = 0;\r
327     for (int i = 0; i < aa.length; i++)\r
328     {\r
329 \r
330       if (aa[i].visible)\r
331       {\r
332         height += aa[i].height;\r
333       }\r
334 \r
335       if (evt.getY() < height)\r
336       {\r
337         row = i;\r
338         break;\r
339       }\r
340     }\r
341 \r
342     int res = evt.getX() / av.getCharWidth() + av.getStartRes();\r
343 \r
344     if (av.hasHiddenColumns)\r
345     {\r
346       res = av.getColumnSelection().adjustForHiddenColumns(res);\r
347     }\r
348 \r
349     if (row > -1 && res < aa[row].annotations.length && aa[row].annotations[res] != null)\r
350     {\r
351       StringBuffer text = new StringBuffer("Sequence position " + (res + 1));\r
352       if (aa[row].annotations[res].description != null)\r
353       {\r
354         text.append("  " + aa[row].annotations[res].description);\r
355       }\r
356       ap.alignFrame.statusBar.setText(text.toString());\r
357     }\r
358   }\r
359   public void mouseEntered(MouseEvent evt)\r
360   {\r
361     ap.scalePanel.mouseEntered(evt);\r
362   }\r
363   public void mouseExited(MouseEvent evt)\r
364   {\r
365     ap.scalePanel.mouseExited(evt);\r
366   }\r
367 \r
368 \r
369   public int adjustPanelHeight()\r
370   {\r
371     // setHeight of panels\r
372     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
373     int height = 0;\r
374 \r
375     if (aa != null)\r
376     {\r
377       for (int i = 0; i < aa.length; i++)\r
378       {\r
379         if (!aa[i].visible)\r
380         {\r
381           continue;\r
382         }\r
383 \r
384         aa[i].height = 0;\r
385 \r
386         if (aa[i].hasText)\r
387         {\r
388           aa[i].height += av.charHeight;\r
389         }\r
390 \r
391         if (aa[i].hasIcons)\r
392         {\r
393           aa[i].height += 16;\r
394         }\r
395 \r
396         if (aa[i].graph > 0)\r
397         {\r
398           aa[i].height += aa[i].graphHeight;\r
399         }\r
400 \r
401         if (aa[i].height == 0)\r
402         {\r
403           aa[i].height = 20;\r
404         }\r
405 \r
406         height += aa[i].height;\r
407       }\r
408     }\r
409     else\r
410     {\r
411       height = 20;\r
412     }\r
413 \r
414     this.setSize(getSize().width, height);\r
415 \r
416     repaint();\r
417 \r
418     return height;\r
419 \r
420   }\r
421 \r
422   public void addEditableColumn(int i)\r
423   {\r
424     if (activeRow == -1)\r
425     {\r
426       AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
427       if (aa == null)\r
428       {\r
429         return;\r
430       }\r
431 \r
432       for (int j = 0; j < aa.length; j++)\r
433       {\r
434         if (aa[j].editable)\r
435         {\r
436           activeRow = j;\r
437           break;\r
438         }\r
439       }\r
440     }\r
441 \r
442     if (activeRes == null)\r
443     {\r
444       activeRes = new Vector();\r
445       activeRes.addElement(String.valueOf(i));\r
446       return;\r
447     }\r
448 \r
449     activeRes.addElement(String.valueOf(i));\r
450   }\r
451 \r
452 \r
453   public void update(Graphics g)\r
454   {\r
455     paint(g);\r
456   }\r
457 \r
458   public void paint(Graphics g)\r
459   {\r
460 \r
461     imgWidth = getSize().width;\r
462     //(av.endRes - av.startRes + 1) * av.charWidth;\r
463 \r
464     if (image == null || imgWidth != image.getWidth(this))\r
465     {\r
466       image = createImage(imgWidth, ap.annotationPanel.getSize().height);\r
467       gg = image.getGraphics();\r
468       gg.setFont(av.getFont());\r
469       fm = gg.getFontMetrics();\r
470       fastPaint = false;\r
471     }\r
472 \r
473     if (fastPaint)\r
474     {\r
475       g.drawImage(image, 0, 0, this);\r
476       fastPaint = false;\r
477       return;\r
478     }\r
479 \r
480     gg.setColor(Color.white);\r
481     gg.fillRect(0, 0, getSize().width, getSize().height);\r
482     drawComponent(gg, av.startRes, av.endRes + 1);\r
483 \r
484     g.drawImage(image, 0, 0, this);\r
485   }\r
486 \r
487   public void fastPaint(int horizontal)\r
488   {\r
489     if (horizontal == 0\r
490         || av.alignment.getAlignmentAnnotation() == null\r
491         || av.alignment.getAlignmentAnnotation().length < 1\r
492         )\r
493     {\r
494       repaint();\r
495       return;\r
496     }\r
497 \r
498     gg.copyArea(0, 0, imgWidth, getSize().height, -horizontal * av.charWidth, 0);\r
499     int sr = av.startRes, er = av.endRes + 1, transX = 0;\r
500 \r
501     if (horizontal > 0) // scrollbar pulled right, image to the left\r
502     {\r
503       transX = (er - sr - horizontal) * av.charWidth;\r
504       sr = er - horizontal;\r
505     }\r
506     else if (horizontal < 0)\r
507     {\r
508       er = sr - horizontal;\r
509     }\r
510 \r
511     gg.translate(transX, 0);\r
512 \r
513     drawComponent(gg, sr, er);\r
514 \r
515     gg.translate( -transX, 0);\r
516 \r
517     fastPaint = true;\r
518     repaint();\r
519   }\r
520 \r
521   /**\r
522    * DOCUMENT ME!\r
523    *\r
524    * @param g DOCUMENT ME!\r
525    * @param startRes DOCUMENT ME!\r
526    * @param endRes DOCUMENT ME!\r
527    */\r
528   public void drawComponent(Graphics g, int startRes, int endRes)\r
529   {\r
530     g.setFont(av.getFont());\r
531 \r
532     g.setColor(Color.white);\r
533     g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getSize().height);\r
534 \r
535     if (fm == null)\r
536     {\r
537       fm = g.getFontMetrics();\r
538     }\r
539 \r
540     if ( (av.alignment.getAlignmentAnnotation() == null) ||\r
541         (av.alignment.getAlignmentAnnotation().length < 1))\r
542     {\r
543       g.setColor(Color.white);\r
544       g.fillRect(0, 0, getSize().width, getSize().height);\r
545       g.setColor(Color.black);\r
546       if (av.validCharWidth)\r
547       {\r
548         g.drawString("Alignment has no annotations", 20, 15);\r
549       }\r
550 \r
551       return;\r
552     }\r
553 \r
554     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
555 \r
556     int x = 0;\r
557     int y = 0;\r
558     int column = 0;\r
559     char lastSS;\r
560     int lastSSX;\r
561     int iconOffset = av.charHeight / 2;\r
562     boolean validRes = false;\r
563 \r
564     boolean[] graphGroupDrawn = new boolean[aa.length];\r
565 \r
566     //\u03B2 \u03B1\r
567     for (int i = 0; i < aa.length; i++)\r
568     {\r
569       AlignmentAnnotation row = aa[i];\r
570 \r
571       if (!row.visible)\r
572       {\r
573         continue;\r
574       }\r
575 \r
576       lastSS = ' ';\r
577       lastSSX = 0;\r
578 \r
579       if (row.graph > 0)\r
580       {\r
581         if (row.graphGroup > -1 && graphGroupDrawn[row.graphGroup])\r
582         {\r
583           continue;\r
584         }\r
585 \r
586         // this is so that we draw the characters below the graph\r
587         y += row.height;\r
588 \r
589         if (row.hasText)\r
590         {\r
591           y -= av.charHeight;\r
592         }\r
593       }\r
594 \r
595       if (row.hasText)\r
596       {\r
597         iconOffset = av.charHeight / 2;\r
598       }\r
599       else\r
600       {\r
601         iconOffset = 0;\r
602       }\r
603 \r
604       x = 0;\r
605       while (x < endRes - startRes)\r
606       {\r
607         if (av.hasHiddenColumns)\r
608         {\r
609           column = av.getColumnSelection().adjustForHiddenColumns(startRes + x);\r
610           if (column > row.annotations.length - 1)\r
611           {\r
612             break;\r
613           }\r
614         }\r
615         else\r
616         {\r
617           column = startRes + x;\r
618         }\r
619 \r
620         if ( (row.annotations.length <= column) ||\r
621             (row.annotations[column] == null))\r
622         {\r
623           validRes = false;\r
624         }\r
625         else\r
626         {\r
627           validRes = true;\r
628         }\r
629 \r
630         if (activeRow == i)\r
631         {\r
632           g.setColor(Color.red);\r
633 \r
634           if (av.getColumnSelection() != null)\r
635           {\r
636             for (int n = 0; n < av.getColumnSelection().size(); n++)\r
637             {\r
638               int v = av.getColumnSelection().columnAt(n);\r
639 \r
640               if (v == column)\r
641               {\r
642                 g.fillRect(x * av.charWidth, y,\r
643                            av.charWidth, av.charHeight);\r
644               }\r
645             }\r
646           }\r
647         }\r
648 \r
649 \r
650 \r
651         if (av.validCharWidth && validRes &&\r
652             (row.annotations[column].displayCharacter.length() > 0))\r
653         {\r
654           int charOffset = (av.charWidth -\r
655                             fm.charWidth(row.annotations[column].\r
656                                          displayCharacter.charAt(\r
657                                              0))) / 2;\r
658 \r
659           if (row.annotations[column].colour == null)\r
660             g.setColor(Color.black);\r
661           else\r
662             g.setColor(row.annotations[column].colour);\r
663 \r
664           if (column == 0 || row.graph > 0)\r
665           {\r
666             g.drawString(row.annotations[column].displayCharacter,\r
667                          (x * av.charWidth) + charOffset,\r
668                          y + iconOffset + 3);\r
669           }\r
670           else if (\r
671               row.annotations[column - 1] == null\r
672               || (!row.annotations[column].displayCharacter.equals(\r
673                   row.annotations[column - 1].displayCharacter)\r
674                   ||\r
675                   (row.annotations[column].displayCharacter.length() < 2 &&\r
676                    row.annotations[column].secondaryStructure == ' ')))\r
677           {\r
678             g.drawString(row.annotations[column].displayCharacter,\r
679                          (x * av.charWidth) + charOffset,\r
680                          y + iconOffset + 3);\r
681           }\r
682         }\r
683 \r
684         if (row.hasIcons)\r
685         {\r
686           if (!validRes ||\r
687               (row.annotations[column].secondaryStructure != lastSS))\r
688           {\r
689             switch (lastSS)\r
690             {\r
691               case 'H':\r
692                 g.setColor(HELIX_COLOUR);\r
693                 if (MAC)\r
694                 {\r
695                   //Off by 1 offset when drawing rects and ovals\r
696                   //to offscreen image on the MAC\r
697                   g.fillRoundRect(lastSSX, y + 4 + iconOffset,\r
698                                   (x * av.charWidth) - lastSSX, 7, 8, 8);\r
699                   break;\r
700                 }\r
701 \r
702                 int sCol = (lastSSX / av.charWidth) + startRes;\r
703                 int x1 = lastSSX;\r
704                 int x2 = (x * av.charWidth);\r
705 \r
706                 if (sCol == 0 ||\r
707                     row.annotations[sCol - 1] == null ||\r
708                     row.annotations[sCol - 1].secondaryStructure != 'H')\r
709                 {\r
710                   g.fillArc(lastSSX, y + 4 + iconOffset, av.charWidth, 8, 90,\r
711                             180);\r
712                   x1 += av.charWidth / 2;\r
713                 }\r
714 \r
715                 if (row.annotations[column] == null ||\r
716                     row.annotations[column].secondaryStructure != 'H')\r
717                 {\r
718                   g.fillArc( (x * av.charWidth) - av.charWidth,\r
719                             y + 4 + iconOffset, av.charWidth, 8, 270, 180);\r
720                   x2 -= av.charWidth / 2;\r
721                 }\r
722 \r
723                 g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);\r
724                 break;\r
725 \r
726               case 'E':\r
727                 g.setColor(SHEET_COLOUR);\r
728                 g.fillRect(lastSSX, y + 4 + iconOffset,\r
729                            (x * av.charWidth) - lastSSX - 4, 7);\r
730                 g.fillPolygon(new int[]\r
731                               { (x * av.charWidth) - 4,\r
732                               (x * av.charWidth) - 4,\r
733                               (x * av.charWidth)},\r
734                               new int[]\r
735                               {\r
736                               y + iconOffset, y + 14 + iconOffset,\r
737                               y + 8 + iconOffset\r
738                 }, 3);\r
739 \r
740                 break;\r
741 \r
742               default:\r
743                 g.setColor(Color.gray);\r
744                 g.fillRect(lastSSX, y + 6 + iconOffset,\r
745                            (x * av.charWidth) - lastSSX, 2);\r
746 \r
747                 break;\r
748             }\r
749 \r
750             if (validRes)\r
751             {\r
752               lastSS = row.annotations[column].secondaryStructure;\r
753             }\r
754             else\r
755             {\r
756               lastSS = ' ';\r
757             }\r
758 \r
759             lastSSX = (x * av.charWidth);\r
760           }\r
761         }\r
762 \r
763         column++;\r
764         x++;\r
765       }\r
766 \r
767       if (column >= row.annotations.length)\r
768       {\r
769         column = row.annotations.length - 1;\r
770       }\r
771 \r
772       //  x ++;\r
773 \r
774       if (row.hasIcons)\r
775       {\r
776         switch (lastSS)\r
777         {\r
778           case 'H':\r
779             g.setColor(HELIX_COLOUR);\r
780             if (MAC)\r
781             {\r
782               //Off by 1 offset when drawing rects and ovals\r
783               //to offscreen image on the MAC\r
784               g.fillRoundRect(lastSSX, y + 4 + iconOffset,\r
785                               (x * av.charWidth) - lastSSX, 7, 8, 8);\r
786               break;\r
787             }\r
788 \r
789             int sCol = (lastSSX / av.charWidth) + startRes;\r
790             int x1 = lastSSX;\r
791             int x2 = (x * av.charWidth);\r
792 \r
793             if (sCol == 0 ||\r
794                 row.annotations[sCol - 1] == null ||\r
795                 row.annotations[sCol - 1].secondaryStructure != 'H')\r
796             {\r
797               g.fillArc(lastSSX, y + 4 + iconOffset, av.charWidth, 8, 90, 180);\r
798               x1 += av.charWidth / 2;\r
799             }\r
800 \r
801             if (row.annotations[column] == null ||\r
802                 row.annotations[column].secondaryStructure != 'H')\r
803             {\r
804               g.fillArc( (x * av.charWidth) - av.charWidth,\r
805                         y + 4 + iconOffset, av.charWidth, 8, 270,\r
806                         180);\r
807               x2 -= av.charWidth / 2;\r
808             }\r
809 \r
810             g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);\r
811 \r
812             break;\r
813 \r
814           case 'E':\r
815             g.setColor(SHEET_COLOUR);\r
816 \r
817             if (row.annotations[endRes] == null\r
818                 || row.annotations[endRes].secondaryStructure != 'E')\r
819             {\r
820               g.fillRect(lastSSX, y + 4 + iconOffset,\r
821                          (x * av.charWidth) - lastSSX - 4, 7);\r
822               g.fillPolygon(new int[]\r
823                             { (x * av.charWidth) - 4,\r
824                             (x * av.charWidth) - 4,\r
825                             (x * av.charWidth)},\r
826                             new int[]\r
827                             {\r
828                             y + iconOffset, y + 14 + iconOffset,\r
829                             y + 7 + iconOffset\r
830               }, 3);\r
831             }\r
832             else\r
833             {\r
834               g.fillRect(lastSSX, y + 4 + iconOffset,\r
835                          x * av.charWidth - lastSSX, 7);\r
836             }\r
837             break;\r
838 \r
839           default:\r
840             g.setColor(Color.gray);\r
841             if (!av.wrapAlignment || endRes == av.endRes)\r
842             {\r
843               g.fillRect(lastSSX, y + 6 + iconOffset,\r
844                          (x * av.charWidth) - lastSSX, 2);\r
845             }\r
846 \r
847             break;\r
848         }\r
849       }\r
850 \r
851       if (row.graph > 0)\r
852       {\r
853         if (row.graph == AlignmentAnnotation.LINE_GRAPH)\r
854         {\r
855           if (row.graphGroup > -1 && !graphGroupDrawn[row.graphGroup])\r
856           {\r
857             float groupmax = -999999, groupmin = 9999999;\r
858             for (int gg = 0; gg < aa.length; gg++)\r
859             {\r
860               if (aa[gg].graphGroup != row.graphGroup)\r
861               {\r
862                 continue;\r
863               }\r
864 \r
865               if (aa[gg] != row)\r
866               {\r
867                 aa[gg].visible = false;\r
868               }\r
869 \r
870               if (aa[gg].graphMax > groupmax)\r
871               {\r
872                 groupmax = aa[gg].graphMax;\r
873               }\r
874               if (aa[gg].graphMin < groupmin)\r
875               {\r
876                 groupmin = aa[gg].graphMin;\r
877               }\r
878             }\r
879 \r
880             for (int gg = 0; gg < aa.length; gg++)\r
881             {\r
882               if (aa[gg].graphGroup == row.graphGroup)\r
883               {\r
884                 drawLineGraph(g, aa[gg], startRes, endRes, y,\r
885                               groupmin, groupmax,\r
886                               row.graphHeight);\r
887               }\r
888             }\r
889 \r
890             graphGroupDrawn[row.graphGroup] = true;\r
891           }\r
892           else\r
893           {\r
894             drawLineGraph(g, row, startRes, endRes,\r
895                           y, row.graphMin, row.graphMax, row.graphHeight);\r
896           }\r
897         }\r
898         else if (row.graph == AlignmentAnnotation.BAR_GRAPH)\r
899         {\r
900           drawBarGraph(g, row, startRes, endRes,\r
901                        row.graphMin, row.graphMax, y);\r
902         }\r
903       }\r
904 \r
905       if (row.graph > 0 && row.hasText)\r
906       {\r
907         y += av.charHeight;\r
908       }\r
909 \r
910       if (row.graph == 0)\r
911       {\r
912         y += aa[i].height;\r
913       }\r
914     }\r
915   }\r
916 \r
917   public void drawLineGraph(Graphics g, AlignmentAnnotation aa,\r
918                             int sRes, int eRes,\r
919                             int y,\r
920                             float min, float max,\r
921                             int graphHeight)\r
922   {\r
923     if (sRes > aa.annotations.length)\r
924     {\r
925       return;\r
926     }\r
927 \r
928     int x = 0;\r
929 \r
930     //Adjustment for fastpaint to left\r
931     if (eRes < av.endRes)\r
932     {\r
933       eRes++;\r
934     }\r
935 \r
936     eRes = Math.min(eRes, aa.annotations.length);\r
937 \r
938     if (sRes == 0)\r
939     {\r
940       sRes++;\r
941       x += av.charWidth;\r
942     }\r
943 \r
944     int y1 = y, y2 = y;\r
945     float range = max - min;\r
946 \r
947     ////Draw origin\r
948     if (min < 0)\r
949     {\r
950       y2 = y - (int) ( (0 - min / range) * graphHeight);\r
951     }\r
952 \r
953     g.setColor(Color.gray);\r
954     g.drawLine(x - av.charWidth, y2, (eRes - sRes) * av.charWidth, y2);\r
955 \r
956     eRes = Math.min(eRes, aa.annotations.length);\r
957 \r
958     int column;\r
959     int aaMax = aa.annotations.length - 1;\r
960 \r
961     while (x < eRes - sRes)\r
962     {\r
963       column = sRes + x;\r
964       if (av.hasHiddenColumns)\r
965       {\r
966         column = av.getColumnSelection().adjustForHiddenColumns(column);\r
967       }\r
968 \r
969       if (column > aaMax)\r
970       {\r
971         break;\r
972       }\r
973 \r
974       if (aa.annotations[column] == null || aa.annotations[column - 1] == null)\r
975       {\r
976         x++;\r
977         continue;\r
978       }\r
979 \r
980       if (aa.annotations[column].colour == null)\r
981         g.setColor(Color.black);\r
982       else\r
983         g.setColor(aa.annotations[column].colour);\r
984 \r
985       y1 = y -\r
986           (int) ( ( (aa.annotations[column - 1].value - min) / range) * graphHeight);\r
987       y2 = y -\r
988           (int) ( ( (aa.annotations[column].value - min) / range) * graphHeight);\r
989 \r
990       g.drawLine(x * av.charWidth - av.charWidth / 2, y1,\r
991                  x * av.charWidth + av.charWidth / 2, y2);\r
992       x++;\r
993     }\r
994 \r
995     if (aa.threshold != null)\r
996     {\r
997       g.setColor(aa.threshold.colour);\r
998 \r
999       y2 = (int) (y - ( (aa.threshold.value - min) / range) * graphHeight);\r
1000       g.drawLine(0, y2, (eRes - sRes) * av.charWidth, y2);\r
1001     }\r
1002   }\r
1003 \r
1004   public void drawBarGraph(Graphics g, AlignmentAnnotation aa,\r
1005                            int sRes, int eRes,\r
1006                            float min, float max,\r
1007                            int y)\r
1008   {\r
1009     if (sRes > aa.annotations.length)\r
1010     {\r
1011       return;\r
1012     }\r
1013 \r
1014     eRes = Math.min(eRes, aa.annotations.length);\r
1015 \r
1016     int x = 0, y1 = y, y2 = y;\r
1017 \r
1018     float range = max - min;\r
1019 \r
1020     if (min < 0)\r
1021     {\r
1022       y2 = y - (int) ( (0 - min / (range)) * aa.graphHeight);\r
1023     }\r
1024 \r
1025     g.setColor(Color.gray);\r
1026 \r
1027     g.drawLine(x, y2, (eRes - sRes) * av.charWidth, y2);\r
1028 \r
1029     int column;\r
1030     int aaMax = aa.annotations.length - 1;\r
1031 \r
1032     while (x < eRes - sRes)\r
1033     {\r
1034       column = sRes + x;\r
1035       if (av.hasHiddenColumns)\r
1036       {\r
1037         column = av.getColumnSelection().adjustForHiddenColumns(column);\r
1038       }\r
1039 \r
1040       if (column > aaMax)\r
1041       {\r
1042         break;\r
1043       }\r
1044 \r
1045       if (aa.annotations[column] == null)\r
1046       {\r
1047         x++;\r
1048         continue;\r
1049       }\r
1050 \r
1051       if (aa.annotations[column].colour == null)\r
1052         g.setColor(Color.black);\r
1053       else\r
1054         g.setColor(aa.annotations[column].colour);\r
1055 \r
1056       y1 = y -\r
1057           (int) ( ( (aa.annotations[column].value - min) / (range)) * aa.graphHeight);\r
1058 \r
1059       if (y1 - y2 > 0)\r
1060       {\r
1061         g.fillRect(x * av.charWidth, y2, av.charWidth, y1 - y2);\r
1062       }\r
1063       else\r
1064       {\r
1065         g.fillRect(x * av.charWidth, y1, av.charWidth, y2 - y1);\r
1066       }\r
1067 \r
1068       x++;\r
1069 \r
1070     }\r
1071     if (aa.threshold != null)\r
1072     {\r
1073       g.setColor(aa.threshold.colour);\r
1074       y2 = (int) (y - ( (aa.threshold.value - min) / range) * aa.graphHeight);\r
1075       g.drawLine(0, y2, (eRes - sRes) * av.charWidth, y2);\r
1076     }\r
1077   }\r
1078 \r
1079   // used by overview window\r
1080   public void drawGraph(Graphics g, AlignmentAnnotation aa, int width, int y,\r
1081                         int sRes, int eRes)\r
1082   {\r
1083     eRes = Math.min(eRes, aa.annotations.length);\r
1084     g.setColor(Color.white);\r
1085     g.fillRect(0, 0, width, y);\r
1086     g.setColor(new Color(0, 0, 180));\r
1087 \r
1088     int x = 0, height;\r
1089 \r
1090     for (int j = sRes; j < eRes; j++)\r
1091     {\r
1092       if (aa.annotations[j].colour == null)\r
1093         g.setColor(Color.black);\r
1094       else\r
1095         g.setColor(aa.annotations[j].colour);\r
1096 \r
1097       height = (int) ( (aa.annotations[j].value / aa.graphMax) * GRAPH_HEIGHT);\r
1098       if (height > y)\r
1099       {\r
1100         height = y;\r
1101       }\r
1102       g.fillRect(x, y - height, av.charWidth, height);\r
1103       x += av.charWidth;\r
1104     }\r
1105   }\r
1106 }\r