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