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