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