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