use new utility class for platform info
[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
612   /**
613    * DOCUMENT ME!
614    *
615    * @param horizontal DOCUMENT ME!
616    */
617   public void fastPaint(int horizontal)
618   {
619     if ( (horizontal == 0)
620         || gg == null
621         || av.alignment.getAlignmentAnnotation() == null
622         || av.alignment.getAlignmentAnnotation().length < 1
623         || av.updatingConsensus
624         || av.updatingConservation)
625     {
626       repaint();
627       return;
628     }
629
630     gg.copyArea(0, 0, imgWidth, getHeight(), -horizontal * av.charWidth, 0);
631
632     int sr = av.startRes;
633     int er = av.endRes + 1;
634     int transX = 0;
635
636     if (horizontal > 0) // scrollbar pulled right, image to the left
637     {
638       transX = (er - sr - horizontal) * av.charWidth;
639       sr = er - horizontal;
640     }
641     else if (horizontal < 0)
642     {
643       er = sr - horizontal;
644     }
645
646     gg.translate(transX, 0);
647
648     drawComponent(gg, sr, er);
649
650     gg.translate( -transX, 0);
651
652     fastPaint = true;
653
654     repaint();
655
656   }
657
658   /**
659    * DOCUMENT ME!
660    *
661    * @param g DOCUMENT ME!
662    * @param startRes DOCUMENT ME!
663    * @param endRes DOCUMENT ME!
664    */
665   public void drawComponent(Graphics g, int startRes, int endRes)
666   {
667     if (av.updatingConsensus || av.updatingConservation)
668     {
669       if (image == null)
670       {
671         return;
672       }
673       //We'll keep a record of the old image,
674       //and draw a faded image until the calculation
675       //has completed
676       if (fadedImage == null
677           || fadedImage.getWidth() != imgWidth
678           || fadedImage.getHeight() != image.getHeight())
679       {
680         fadedImage = new BufferedImage(
681             imgWidth, image.getHeight(),
682             BufferedImage.TYPE_INT_RGB);
683
684         Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
685
686         fadedG.setColor(Color.white);
687         fadedG.fillRect(0, 0, imgWidth, image.getHeight());
688
689         fadedG.setComposite(
690             AlphaComposite.getInstance(
691                 AlphaComposite.SRC_OVER, .3f));
692         fadedG.drawImage(image, 0, 0, this);
693
694       }
695
696     }
697     else
698     {
699       fadedImage = null;
700     }
701
702     g.setColor(Color.white);
703     g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getHeight());
704
705     g.setFont(av.getFont());
706     if (fm == null)
707     {
708       fm = g.getFontMetrics();
709     }
710
711     if ( (av.alignment.getAlignmentAnnotation() == null) ||
712         (av.alignment.getAlignmentAnnotation().length < 1))
713     {
714       g.setColor(Color.white);
715       g.fillRect(0, 0, getWidth(), getHeight());
716       g.setColor(Color.black);
717       if (av.validCharWidth)
718       {
719         g.drawString("Alignment has no annotations", 20, 15);
720       }
721
722       return;
723     }
724
725     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
726
727     int x = 0, y=0;
728     int column = 0;
729     char lastSS;
730     int lastSSX;
731     int iconOffset = 0;
732     boolean validRes = false;
733
734     boolean[] graphGroupDrawn = new boolean[aa.length];
735
736     //\u03B2 \u03B1
737     for (int i = 0; i < aa.length; i++)
738     {
739       AlignmentAnnotation row = aa[i];
740
741       if (!row.visible)
742       {
743         continue;
744       }
745
746       lastSS = ' ';
747       lastSSX = 0;
748
749       if (row.graph > 0)
750       {
751         if (row.graphGroup > -1 && graphGroupDrawn[row.graphGroup])
752         {
753           continue;
754         }
755
756         // this is so that we draw the characters below the graph
757         y += row.height;
758
759         if (row.hasText)
760         {
761           iconOffset =av.charHeight -fm.getDescent();
762           y -= av.charHeight;
763         }
764       }
765       else if (row.hasText)
766       {
767         iconOffset = av.charHeight -fm.getDescent();
768
769       }
770       else
771       {
772         iconOffset = 0;
773       }
774
775
776       if (av.updatingConsensus && aa[i] == av.consensus)
777       {
778         y += av.charHeight;
779
780         g.drawImage(fadedImage,
781                     0, y - row.height, imgWidth, y,
782                     0, y - row.height, imgWidth, y, this);
783         g.setColor(Color.black);
784         // g.drawString("Calculating Consensus....",20, y-row.height/2);
785
786         continue;
787       }
788       else if (av.updatingConservation && aa[i].label.equals("Conservation"))
789       {
790
791         y += av.charHeight;
792         g.drawImage(fadedImage,
793                     0, y - row.height, imgWidth, y,
794                     0, y - row.height, imgWidth, y, this);
795
796         g.setColor(Color.black);
797         //  g.drawString("Calculating Conservation.....",20, y-row.height/2);
798
799         continue;
800       }
801       else if (av.updatingConservation && aa[i].label.equals("Quality"))
802       {
803
804         y += av.charHeight;
805         g.drawImage(fadedImage,
806                     0, y - row.height, imgWidth, y,
807                     0, y - row.height, imgWidth, y, this);
808         g.setColor(Color.black);
809         /// g.drawString("Calculating Quality....",20, y-row.height/2);
810
811         continue;
812       }
813
814
815
816       x = 0;
817       while (x < endRes - startRes)
818       {
819         if (av.hasHiddenColumns)
820         {
821           column = av.getColumnSelection().adjustForHiddenColumns(startRes + x);
822           if (column > row.annotations.length - 1)
823           {
824             break;
825           }
826         }
827         else
828         {
829           column = startRes + x;
830         }
831
832         if ( (row.annotations==null) 
833                 || (row.annotations.length <= column) 
834                 || (row.annotations[column] == null))
835         {
836           validRes = false;
837         }
838         else
839         {
840           validRes = true;
841         }
842
843         if (activeRow == i)
844         {
845           g.setColor(Color.red);
846
847           if (av.getColumnSelection() != null)
848           {
849             for (int n = 0; n < av.getColumnSelection().size(); n++)
850             {
851               int v = av.getColumnSelection().columnAt(n);
852
853               if (v == column)
854               {
855                 g.fillRect(x * av.charWidth, y,
856                            av.charWidth, av.charHeight);
857               }
858             }
859           }
860         }
861
862         if (av.validCharWidth && validRes
863             && row.annotations[column].displayCharacter != null
864             && (row.annotations[column].displayCharacter.length() > 0))
865         {
866
867           int charOffset = (av.charWidth -
868                             fm.charWidth(row.annotations[column].
869                                          displayCharacter.charAt(
870                                              0))) / 2;
871
872           if (row.annotations[column].colour == null)
873             g.setColor(Color.black);
874           else
875             g.setColor(row.annotations[column].colour);
876
877           if (column == 0 || row.graph > 0)
878           {
879             g.drawString(row.annotations[column].displayCharacter,
880                          (x * av.charWidth) + charOffset,
881                          y + iconOffset);
882           }
883           else if (
884               row.annotations[column - 1] == null
885               || (!row.annotations[column].displayCharacter.equals(
886                   row.annotations[column - 1].displayCharacter)
887                   ||
888                   (row.annotations[column].displayCharacter.length() < 2 &&
889                    row.annotations[column].secondaryStructure == ' ')))
890           {
891             g.drawString(row.annotations[column].displayCharacter,
892                          x * av.charWidth + charOffset,
893                          y + iconOffset);
894           }
895         }
896
897         if (row.hasIcons)
898         {
899           if (!validRes ||
900               (row.annotations[column].secondaryStructure != lastSS))
901           {
902             switch (lastSS)
903             {
904               case 'H':
905                 g.setColor(HELIX_COLOUR);
906                 if (MAC)
907                 {
908                   //Off by 1 offset when drawing rects and ovals
909                   //to offscreen image on the MAC
910                   g.fillRoundRect(lastSSX, y + 4 + iconOffset,
911                                   (x * av.charWidth) - lastSSX, 7, 8, 8);
912                   break;
913                 }
914
915                 int sCol = (lastSSX / av.charWidth) + startRes;
916                 int x1 = lastSSX;
917                 int x2 = (x * av.charWidth);
918
919                 if (sCol == 0 ||
920                     row.annotations[sCol - 1] == null ||
921                     row.annotations[sCol - 1].secondaryStructure != 'H')
922                 {
923                   g.fillArc(lastSSX, y + 4 + iconOffset, av.charWidth, 8, 90,
924                             180);
925                   x1 += av.charWidth / 2;
926                 }
927
928                 if (row.annotations[column] == null ||
929                     row.annotations[column].secondaryStructure != 'H')
930                 {
931                   g.fillArc( (x * av.charWidth) - av.charWidth,
932                             y + 4 + iconOffset, av.charWidth, 8, 270, 180);
933                   x2 -= av.charWidth / 2;
934                 }
935
936                 g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
937                 break;
938
939               case 'E':
940                 g.setColor(SHEET_COLOUR);
941                 g.fillRect(lastSSX, y + 4 + iconOffset,
942                            (x * av.charWidth) - lastSSX - 4, 7);
943                 g.fillPolygon(new int[]
944                               { (x * av.charWidth) - 4,
945                               (x * av.charWidth) - 4,
946                               (x * av.charWidth)},
947                               new int[]
948                               {
949                               y + iconOffset, y + 14 + iconOffset,
950                               y + 8 + iconOffset
951                 }, 3);
952
953                 break;
954
955               default:
956                 g.setColor(Color.gray);
957                 g.fillRect(lastSSX, y + 6 + iconOffset,
958                            (x * av.charWidth) - lastSSX, 2);
959
960                 break;
961             }
962
963             if (validRes)
964             {
965               lastSS = row.annotations[column].secondaryStructure;
966             }
967             else
968             {
969               lastSS = ' ';
970             }
971
972             lastSSX = (x * av.charWidth);
973           }
974         }
975
976         column++;
977         x++;
978       }
979
980       if (column >= row.annotations.length)
981       {
982         column = row.annotations.length - 1;
983       }
984
985       //  x ++;
986
987       if (row.hasIcons)
988       {
989         switch (lastSS)
990         {
991           case 'H':
992             g.setColor(HELIX_COLOUR);
993             if (MAC)
994             {
995               //Off by 1 offset when drawing rects and ovals
996               //to offscreen image on the MAC
997               g.fillRoundRect(lastSSX, y + 4 + iconOffset,
998                               (x * av.charWidth) - lastSSX, 7, 8, 8);
999               break;
1000             }
1001
1002             int sCol = (lastSSX / av.charWidth) + startRes;
1003             int x1 = lastSSX;
1004             int x2 = (x * av.charWidth);
1005
1006             if (sCol == 0 ||
1007                 row.annotations[sCol - 1] == null ||
1008                 row.annotations[sCol - 1].secondaryStructure != 'H')
1009             {
1010               g.fillArc(lastSSX, y + 4 + iconOffset, av.charWidth, 8, 90, 180);
1011               x1 += av.charWidth / 2;
1012             }
1013
1014             if (row.annotations[column] == null ||
1015                 row.annotations[column].secondaryStructure != 'H')
1016             {
1017               g.fillArc( (x * av.charWidth) - av.charWidth,
1018                         y + 4 + iconOffset, av.charWidth, 8, 270,
1019                         180);
1020               x2 -= av.charWidth / 2;
1021             }
1022
1023             g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
1024
1025             break;
1026
1027           case 'E':
1028             g.setColor(SHEET_COLOUR);
1029
1030             if (row.annotations[endRes] == null
1031                 || row.annotations[endRes].secondaryStructure != 'E')
1032             {
1033               g.fillRect(lastSSX, y + 4 + iconOffset,
1034                          (x * av.charWidth) - lastSSX - 4, 7);
1035               g.fillPolygon(new int[]
1036                             { (x * av.charWidth) - 4,
1037                             (x * av.charWidth) - 4,
1038                             (x * av.charWidth)},
1039                             new int[]
1040                             {
1041                             y + iconOffset, y + 14 + iconOffset,
1042                             y + 7 + iconOffset
1043               }, 3);
1044             }
1045             else
1046             {
1047               g.fillRect(lastSSX, y + 4 + iconOffset,
1048                          (x + 1) * av.charWidth - lastSSX, 7);
1049             }
1050             break;
1051
1052           default:
1053             g.setColor(Color.gray);
1054             g.fillRect(lastSSX, y + 6 + iconOffset,
1055                        (x * av.charWidth) - lastSSX, 2);
1056
1057             break;
1058         }
1059       }
1060
1061       if (row.graph > 0 && row.graphHeight > 0)
1062       {
1063         if (row.graph == AlignmentAnnotation.LINE_GRAPH)
1064         {
1065           if (row.graphGroup > -1 && !graphGroupDrawn[row.graphGroup])
1066           {
1067             float groupmax = -999999, groupmin = 9999999;
1068             for (int gg = 0; gg < aa.length; gg++)
1069             {
1070               if (aa[gg].graphGroup != row.graphGroup)
1071               {
1072                 continue;
1073               }
1074
1075               if (aa[gg] != row)
1076               {
1077                 aa[gg].visible = false;
1078               }
1079
1080               if (aa[gg].graphMax > groupmax)
1081               {
1082                 groupmax = aa[gg].graphMax;
1083               }
1084               if (aa[gg].graphMin < groupmin)
1085               {
1086                 groupmin = aa[gg].graphMin;
1087               }
1088             }
1089
1090             for (int gg = 0; gg < aa.length; gg++)
1091             {
1092               if (aa[gg].graphGroup == row.graphGroup)
1093               {
1094                 drawLineGraph(g, aa[gg], startRes, endRes, y,
1095                               groupmin, groupmax,
1096                               row.graphHeight);
1097               }
1098             }
1099
1100             graphGroupDrawn[row.graphGroup] = true;
1101           }
1102           else
1103           {
1104             drawLineGraph(g, row, startRes, endRes,
1105                           y, row.graphMin, row.graphMax, row.graphHeight);
1106           }
1107         }
1108         else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
1109         {
1110           drawBarGraph(g, row, startRes, endRes,
1111                        row.graphMin, row.graphMax, y);
1112         }
1113       }
1114
1115       if (row.graph > 0 && row.hasText)
1116       {
1117         y += av.charHeight;
1118       }
1119
1120       if (row.graph == 0)
1121       {
1122         y += aa[i].height;
1123       }
1124     }
1125   }
1126
1127   public void drawLineGraph(Graphics g, AlignmentAnnotation aa,
1128                             int sRes, int eRes,
1129                             int y,
1130                             float min, float max,
1131                             int graphHeight)
1132   {
1133     if (sRes > aa.annotations.length)
1134     {
1135       return;
1136     }
1137
1138     int x = 0;
1139
1140     //Adjustment for fastpaint to left
1141     if (eRes < av.endRes)
1142     {
1143       eRes++;
1144     }
1145
1146     eRes = Math.min(eRes, aa.annotations.length);
1147
1148     if (sRes == 0)
1149     {
1150       x++;
1151     }
1152
1153     int y1 = y, y2 = y;
1154     float range = max - min;
1155
1156     ////Draw origin
1157     if (min < 0)
1158     {
1159       y2 = y - (int) ( (0 - min / range) * graphHeight);
1160     }
1161
1162     g.setColor(Color.gray);
1163     g.drawLine(x - av.charWidth, y2, (eRes - sRes + 1) * av.charWidth, y2);
1164
1165     eRes = Math.min(eRes, aa.annotations.length);
1166
1167     int column;
1168     int aaMax = aa.annotations.length - 1;
1169
1170     while (x < eRes - sRes)
1171     {
1172       column = sRes + x;
1173       if (av.hasHiddenColumns)
1174       {
1175         column = av.getColumnSelection().adjustForHiddenColumns(column);
1176       }
1177
1178       if (column > aaMax)
1179       {
1180         break;
1181       }
1182
1183       if (aa.annotations[column] == null || aa.annotations[column - 1] == null)
1184       {
1185         x++;
1186         continue;
1187       }
1188
1189       if (aa.annotations[column].colour == null)
1190         g.setColor(Color.black);
1191       else
1192         g.setColor(aa.annotations[column].colour);
1193
1194       y1 = y -
1195           (int) ( ( (aa.annotations[column - 1].value - min) / range) * graphHeight);
1196       y2 = y -
1197           (int) ( ( (aa.annotations[column].value - min) / range) * graphHeight);
1198
1199       g.drawLine(x * av.charWidth - av.charWidth / 2, y1,
1200                  x * av.charWidth + av.charWidth / 2, y2);
1201       x++;
1202     }
1203
1204     if (aa.threshold != null)
1205     {
1206       g.setColor(aa.threshold.colour);
1207       Graphics2D g2 = (Graphics2D) g;
1208       g2.setStroke(new BasicStroke(1,
1209                                    BasicStroke.CAP_SQUARE,
1210                                    BasicStroke.JOIN_ROUND, 3f,
1211                                    new float[]
1212                                    {5f, 3f}, 0f));
1213
1214       y2 = (int) (y - ( (aa.threshold.value - min) / range) * graphHeight);
1215       g.drawLine(0, y2, (eRes - sRes) * av.charWidth, y2);
1216       g2.setStroke(new BasicStroke());
1217     }
1218   }
1219
1220   public void drawBarGraph(Graphics g, AlignmentAnnotation aa,
1221                            int sRes, int eRes,
1222                            float min, float max,
1223                            int y)
1224   {
1225     if (sRes > aa.annotations.length)
1226     {
1227       return;
1228     }
1229
1230     eRes = Math.min(eRes, aa.annotations.length);
1231
1232     int x = 0, y1 = y, y2 = y;
1233
1234     float range = max - min;
1235
1236     if (min < 0)
1237     {
1238       y2 = y - (int) ( (0 - min / (range)) * aa.graphHeight);
1239     }
1240
1241     g.setColor(Color.gray);
1242
1243     g.drawLine(x, y2, (eRes - sRes) * av.charWidth, y2);
1244
1245     int column;
1246     int aaMax = aa.annotations.length - 1;
1247
1248     while (x < eRes - sRes)
1249     {
1250       column = sRes + x;
1251       if (av.hasHiddenColumns)
1252       {
1253         column = av.getColumnSelection().adjustForHiddenColumns(column);
1254       }
1255
1256       if (column > aaMax)
1257       {
1258         break;
1259       }
1260
1261       if (aa.annotations[column] == null)
1262       {
1263         x++;
1264         continue;
1265       }
1266
1267       if (aa.annotations[column].colour == null)
1268         g.setColor(Color.black);
1269       else
1270         g.setColor(aa.annotations[column].colour);
1271
1272       y1 = y -
1273           (int) ( ( (aa.annotations[column].value - min) / (range)) * aa.graphHeight);
1274
1275       if (y1 - y2 > 0)
1276       {
1277         g.fillRect(x * av.charWidth, y2, av.charWidth, y1 - y2);
1278       }
1279       else
1280       {
1281         g.fillRect(x * av.charWidth, y1, av.charWidth, y2 - y1);
1282       }
1283
1284       x++;
1285
1286     }
1287     if (aa.threshold != null)
1288     {
1289       g.setColor(aa.threshold.colour);
1290       Graphics2D g2 = (Graphics2D) g;
1291       g2.setStroke(new BasicStroke(1,
1292                                    BasicStroke.CAP_SQUARE,
1293                                    BasicStroke.JOIN_ROUND, 3f,
1294                                    new float[]
1295                                    {5f, 3f}, 0f));
1296
1297       y2 = (int) (y - ( (aa.threshold.value - min) / range) * aa.graphHeight);
1298       g.drawLine(0, y2, (eRes - sRes) * av.charWidth, y2);
1299       g2.setStroke(new BasicStroke());
1300     }
1301   }
1302
1303   // used by overview window
1304   public void drawGraph(Graphics g, AlignmentAnnotation aa, int width, int y,
1305                         int sRes, int eRes)
1306   {
1307     eRes = Math.min(eRes, aa.annotations.length);
1308     g.setColor(Color.white);
1309     g.fillRect(0, 0, width, y);
1310     g.setColor(new Color(0, 0, 180));
1311
1312     int x = 0, height;
1313
1314     for (int j = sRes; j < eRes; j++)
1315     {
1316       if (aa.annotations[j] != null)
1317       {
1318         if (aa.annotations[j].colour == null)
1319           g.setColor(Color.black);
1320         else
1321           g.setColor(aa.annotations[j].colour);
1322
1323         height = (int) ( (aa.annotations[j].value / aa.graphMax) * y);
1324         if (height > y)
1325         {
1326           height = y;
1327         }
1328
1329         g.fillRect(x, y - height, av.charWidth, height);
1330       }
1331       x += av.charWidth;
1332     }
1333   }
1334
1335 }