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