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