JAL-891; base-pair logos can now be activated for structure
[jalview.git] / src / jalview / gui / AnnotationPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.gui;
19
20 import java.awt.*;
21 import java.awt.event.*;
22 import java.awt.font.LineMetrics;
23 import java.awt.geom.AffineTransform;
24 import java.awt.image.*;
25 import java.util.Hashtable;
26
27 import javax.swing.*;
28
29 import com.stevesoft.pat.Regex;
30
31 import jalview.analysis.AAFrequency;
32 import jalview.analysis.StructureFrequency;
33 import jalview.datamodel.*;
34 import jalview.schemes.ColourSchemeI;
35
36 /**
37  * DOCUMENT ME!
38  * 
39  * @author $author$
40  * @version $Revision$
41  */
42 public class AnnotationPanel extends JPanel implements MouseListener,
43         MouseMotionListener, ActionListener, AdjustmentListener
44 {
45   final String HELIX = "Helix";
46
47   final String SHEET = "Sheet";
48
49   /**
50    * For RNA secondary structure "stems" aka helices
51    */
52   final String STEM = "RNA Helix";
53
54   final String LABEL = "Label";
55
56   final String REMOVE = "Remove Annotation";
57
58   final String COLOUR = "Colour";
59
60   final Color HELIX_COLOUR = Color.red.darker();
61
62   final Color SHEET_COLOUR = Color.green.darker().darker();
63
64   final Color STEM_COLOUR = Color.blue.darker();
65
66   /** DOCUMENT ME!! */
67   AlignViewport av;
68
69   AlignmentPanel ap;
70
71   int activeRow = -1;
72
73   BufferedImage image;
74
75   BufferedImage fadedImage;
76
77   Graphics2D gg;
78
79   FontMetrics fm;
80
81   int imgWidth = 0;
82
83   boolean fastPaint = false;
84
85   // Used For mouse Dragging and resizing graphs
86   int graphStretch = -1;
87
88   int graphStretchY = -1;
89
90   int min; // used by mouseDragged to see if user
91
92   int max; // used by mouseDragged to see if user
93
94   boolean mouseDragging = false;
95
96   boolean MAC = false;
97
98   // for editing cursor
99   int cursorX = 0;
100
101   int cursorY = 0;
102
103   /**
104    * Creates a new AnnotationPanel object.
105    * 
106    * @param ap
107    *          DOCUMENT ME!
108    */
109   public AnnotationPanel(AlignmentPanel ap)
110   {
111
112     MAC = new jalview.util.Platform().isAMac();
113
114     ToolTipManager.sharedInstance().registerComponent(this);
115     ToolTipManager.sharedInstance().setInitialDelay(0);
116     ToolTipManager.sharedInstance().setDismissDelay(10000);
117     this.ap = ap;
118     av = ap.av;
119     this.setLayout(null);
120     addMouseListener(this);
121     addMouseMotionListener(this);
122     ap.annotationScroller.getVerticalScrollBar()
123             .addAdjustmentListener(this);
124   }
125
126   public AnnotationPanel(AlignViewport av)
127   {
128     this.av = av;
129   }
130
131   /**
132    * DOCUMENT ME!
133    * 
134    * @param evt
135    *          DOCUMENT ME!
136    */
137   public void adjustmentValueChanged(AdjustmentEvent evt)
138   {
139     ap.alabels.setScrollOffset(-evt.getValue());
140   }
141
142   /**
143    * Calculates the height of the annotation displayed in the annotation panel.
144    * Callers should normally call the ap.adjustAnnotationHeight method to ensure
145    * all annotation associated components are updated correctly.
146    * 
147    */
148   public int adjustPanelHeight()
149   {
150     int height = calcPanelHeight();
151     this.setPreferredSize(new Dimension(1, height));
152     if (ap != null)
153     {
154       // revalidate only when the alignment panel is fully constructed
155       ap.validate();
156     }
157
158     return height;
159   }
160
161   /**
162    * calculate the height for visible annotation, revalidating bounds where
163    * necessary ABSTRACT GUI METHOD
164    * 
165    * @return total height of annotation
166    */
167   public int calcPanelHeight()
168   {
169     // setHeight of panels
170     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
171     int height = 0;
172
173     if (aa != null)
174     {
175       for (int i = 0; i < aa.length; i++)
176       {
177         if (aa[i] == null)
178         {
179           System.err.println("Null annotation row: ignoring.");
180           continue;
181         }
182         if (!aa[i].visible)
183         {
184           continue;
185         }
186
187         aa[i].height = 0;
188
189         if (aa[i].hasText)
190         {
191           aa[i].height += av.charHeight;
192         }
193
194         if (aa[i].hasIcons)
195         {
196           aa[i].height += 16;
197         }
198
199         if (aa[i].graph > 0)
200         {
201           aa[i].height += aa[i].graphHeight;
202         }
203
204         if (aa[i].height == 0)
205         {
206           aa[i].height = 20;
207         }
208
209         height += aa[i].height;
210       }
211     }
212     if (height == 0)
213     {
214       // set minimum
215       height = 20;
216     }
217     return height;
218   }
219
220   /**
221    * DOCUMENT ME!
222    * 
223    * @param evt
224    *          DOCUMENT ME!
225    */
226   public void actionPerformed(ActionEvent evt)
227   {
228     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
229     if (aa == null)
230     {
231       return;
232     }
233     Annotation[] anot = aa[activeRow].annotations;
234
235     if (anot.length < av.getColumnSelection().getMax())
236     {
237       Annotation[] temp = new Annotation[av.getColumnSelection().getMax() + 2];
238       System.arraycopy(anot, 0, temp, 0, anot.length);
239       anot = temp;
240       aa[activeRow].annotations = anot;
241     }
242
243     if (evt.getActionCommand().equals(REMOVE))
244     {
245       for (int i = 0; i < av.getColumnSelection().size(); i++)
246       {
247         anot[av.getColumnSelection().columnAt(i)] = null;
248       }
249     }
250     else if (evt.getActionCommand().equals(LABEL))
251     {
252       String exMesg = collectAnnotVals(anot, av.getColumnSelection(), LABEL);
253       String label = JOptionPane.showInputDialog(this, "Enter label",
254               exMesg);
255
256       if (label == null)
257       {
258         return;
259       }
260
261       if ((label.length() > 0) && !aa[activeRow].hasText)
262       {
263         aa[activeRow].hasText = true;
264       }
265
266       for (int i = 0; i < av.getColumnSelection().size(); i++)
267       {
268         int index = av.getColumnSelection().columnAt(i);
269
270         if (!av.colSel.isVisible(index))
271           continue;
272
273         if (anot[index] == null)
274         {
275           anot[index] = new Annotation(label, "", ' ', 0); // TODO: verify that
276           // null exceptions
277           // aren't raised
278           // elsewhere.
279         }
280         else
281         {
282           anot[index].displayCharacter = label;
283         }
284       }
285     }
286     else if (evt.getActionCommand().equals(COLOUR))
287     {
288       Color col = JColorChooser.showDialog(this,
289               "Choose foreground colour", Color.black);
290
291       for (int i = 0; i < av.getColumnSelection().size(); i++)
292       {
293         int index = av.getColumnSelection().columnAt(i);
294
295         if (!av.colSel.isVisible(index))
296           continue;
297
298         if (anot[index] == null)
299         {
300           anot[index] = new Annotation("", "", ' ', 0);
301         }
302
303         anot[index].colour = col;
304       }
305     }
306     else
307     // HELIX OR SHEET
308     {
309       char type = 0;
310       String symbol = "\u03B1";
311
312       if (evt.getActionCommand().equals(HELIX))
313       {
314         type = 'H';
315       }
316       else if (evt.getActionCommand().equals(SHEET))
317       {
318         type = 'E';
319         symbol = "\u03B2";
320       }
321
322       // Added by LML to color stems
323       else if (evt.getActionCommand().equals(STEM))
324       {
325         type = 'S';
326         symbol = "\u03C3";
327       }
328
329       if (!aa[activeRow].hasIcons)
330       {
331         aa[activeRow].hasIcons = true;
332       }
333
334       String label = JOptionPane.showInputDialog(
335               "Enter a label for the structure?", symbol);
336
337       if (label == null)
338       {
339         return;
340       }
341
342       if ((label.length() > 0) && !aa[activeRow].hasText)
343       {
344         aa[activeRow].hasText = true;
345       }
346
347       for (int i = 0; i < av.getColumnSelection().size(); i++)
348       {
349         int index = av.getColumnSelection().columnAt(i);
350
351         if (!av.colSel.isVisible(index))
352           continue;
353
354         if (anot[index] == null)
355         {
356           anot[index] = new Annotation(label, "", type, 0);
357         }
358
359         anot[index].secondaryStructure = type;
360         anot[index].displayCharacter = label;
361       }
362     }
363
364     adjustPanelHeight();
365     repaint();
366
367     return;
368   }
369
370   private String collectAnnotVals(Annotation[] anot,
371           ColumnSelection columnSelection, String label2)
372   {
373     String collatedInput = "";
374     String last = "";
375     for (int i = 0; i < columnSelection.size(); i++)
376     {
377       int index = columnSelection.columnAt(i);
378       // always check for current display state - just in case
379       if (!av.colSel.isVisible(index))
380         continue;
381       String tlabel = null;
382       if (anot[index] != null)
383       { // LML added stem code
384         if (label2.equals(HELIX) || label2.equals(SHEET)
385                 || label2.equals(STEM) || label2.equals(LABEL))
386         {
387           tlabel = anot[index].description;
388           if (tlabel == null || tlabel.length() < 1)
389           {
390             if (label2.equals(HELIX) || label2.equals(SHEET)
391                     || label2.equals(STEM))
392             {
393               tlabel = "" + anot[index].secondaryStructure;
394             }
395             else
396             {
397               tlabel = "" + anot[index].displayCharacter;
398             }
399           }
400         }
401         if (tlabel != null && !tlabel.equals(last))
402         {
403           if (last.length() > 0)
404           {
405             collatedInput += " ";
406           }
407           collatedInput += tlabel;
408         }
409       }
410     }
411     return collatedInput;
412   }
413
414   /**
415    * DOCUMENT ME!
416    * 
417    * @param evt
418    *          DOCUMENT ME!
419    */
420   public void mousePressed(MouseEvent evt)
421   {
422
423     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
424     if (aa == null)
425     {
426       return;
427     }
428
429     int height = 0;
430     activeRow = -1;
431
432     for (int i = 0; i < aa.length; i++)
433     {
434       if (aa[i].visible)
435       {
436         height += aa[i].height;
437       }
438
439       if (evt.getY() < height)
440       {
441         if (aa[i].editable)
442         {
443           activeRow = i;
444         }
445         else if (aa[i].graph > 0)
446         {
447           // Stretch Graph
448           graphStretch = i;
449           graphStretchY = evt.getY();
450         }
451
452         break;
453       }
454     }
455
456     if (SwingUtilities.isRightMouseButton(evt) && activeRow != -1)
457     {
458       if (av.getColumnSelection() == null)
459       {
460         return;
461       }
462
463       JPopupMenu pop = new JPopupMenu("Structure type");
464       JMenuItem item;
465       /*
466        * Just display the needed structure options
467        */
468       if (av.alignment.isNucleotide() == true)
469       {
470         item = new JMenuItem(STEM);
471         item.addActionListener(this);
472         pop.add(item);
473       }
474       else
475       {
476         item = new JMenuItem(HELIX);
477         item.addActionListener(this);
478         pop.add(item);
479         item = new JMenuItem(SHEET);
480         item.addActionListener(this);
481         pop.add(item);
482       }
483       item = new JMenuItem(LABEL);
484       item.addActionListener(this);
485       pop.add(item);
486       item = new JMenuItem(COLOUR);
487       item.addActionListener(this);
488       pop.add(item);
489       item = new JMenuItem(REMOVE);
490       item.addActionListener(this);
491       pop.add(item);
492       pop.show(this, evt.getX(), evt.getY());
493
494       return;
495     }
496
497     if (aa == null)
498     {
499       return;
500     }
501
502     ap.scalePanel.mousePressed(evt);
503
504   }
505
506   /**
507    * DOCUMENT ME!
508    * 
509    * @param evt
510    *          DOCUMENT ME!
511    */
512   public void mouseReleased(MouseEvent evt)
513   {
514     graphStretch = -1;
515     graphStretchY = -1;
516     mouseDragging = false;
517     ap.scalePanel.mouseReleased(evt);
518   }
519
520   /**
521    * DOCUMENT ME!
522    * 
523    * @param evt
524    *          DOCUMENT ME!
525    */
526   public void mouseEntered(MouseEvent evt)
527   {
528     ap.scalePanel.mouseEntered(evt);
529   }
530
531   /**
532    * DOCUMENT ME!
533    * 
534    * @param evt
535    *          DOCUMENT ME!
536    */
537   public void mouseExited(MouseEvent evt)
538   {
539     ap.scalePanel.mouseExited(evt);
540   }
541
542   /**
543    * DOCUMENT ME!
544    * 
545    * @param evt
546    *          DOCUMENT ME!
547    */
548   public void mouseDragged(MouseEvent evt)
549   {
550     if (graphStretch > -1)
551     {
552       av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight += graphStretchY
553               - evt.getY();
554       if (av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight < 0)
555       {
556         av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight = 0;
557       }
558       graphStretchY = evt.getY();
559       adjustPanelHeight();
560       ap.paintAlignment(true);
561     }
562     else
563     {
564       ap.scalePanel.mouseDragged(evt);
565     }
566   }
567
568   /**
569    * DOCUMENT ME!
570    * 
571    * @param evt
572    *          DOCUMENT ME!
573    */
574   public void mouseMoved(MouseEvent evt)
575   {
576     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
577
578     if (aa == null)
579     {
580       this.setToolTipText(null);
581       return;
582     }
583
584     int row = -1;
585     int height = 0;
586
587     for (int i = 0; i < aa.length; i++)
588     {
589       if (aa[i].visible)
590       {
591         height += aa[i].height;
592       }
593
594       if (evt.getY() < height)
595       {
596         row = i;
597
598         break;
599       }
600     }
601
602     if (row == -1)
603     {
604       this.setToolTipText(null);
605       return;
606     }
607
608     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
609
610     if (av.hasHiddenColumns)
611     {
612       res = av.getColumnSelection().adjustForHiddenColumns(res);
613     }
614
615     if (row > -1 && aa[row].annotations != null
616             && res < (int) aa[row].annotations.length)
617     {
618       if (aa[row].graphGroup > -1)
619       {
620         StringBuffer tip = new StringBuffer("<html>");
621         for (int gg = 0; gg < aa.length; gg++)
622         {
623           if (aa[gg].graphGroup == aa[row].graphGroup
624                   && aa[gg].annotations[res] != null)
625           {
626             tip.append(aa[gg].label + " "
627                     + aa[gg].annotations[res].description + "<br>");
628           }
629         }
630         if (tip.length() != 6)
631         {
632           tip.setLength(tip.length() - 4);
633           this.setToolTipText(tip.toString() + "</html>");
634         }
635       }
636       else if (aa[row].annotations[res] != null
637               && aa[row].annotations[res].description != null
638               && aa[row].annotations[res].description.length() > 0)
639       {
640         this.setToolTipText(aa[row].annotations[res].description);
641       }
642       else
643       {
644         // clear the tooltip.
645         this.setToolTipText(null);
646       }
647
648       if (aa[row].annotations[res] != null)
649       {
650         StringBuffer text = new StringBuffer("Sequence position "
651                 + (res + 1));
652
653         if (aa[row].annotations[res].description != null)
654         {
655           text.append("  " + aa[row].annotations[res].description);
656         }
657
658         ap.alignFrame.statusBar.setText(text.toString());
659       }
660     }
661     else
662     {
663       this.setToolTipText(null);
664     }
665   }
666
667   /**
668    * DOCUMENT ME!
669    * 
670    * @param evt
671    *          DOCUMENT ME!
672    */
673   public void mouseClicked(MouseEvent evt)
674   {
675     if (activeRow != -1)
676     {
677       AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
678       AlignmentAnnotation anot = aa[activeRow];
679
680       if (anot.description.equals("secondary structure"))
681       {
682         // System.out.println(anot.description+" "+anot.getRNAStruc());
683       }
684     }
685   }
686
687   // TODO mouseClicked-content and drawCursor are quite experimental!
688   public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
689           int y1)
690   {
691     int pady = av.charHeight / 5;
692     int charOffset = 0;
693     graphics.setColor(Color.black);
694     graphics.fillRect(x1, y1, av.charWidth, av.charHeight);
695
696     if (av.validCharWidth)
697     {
698       graphics.setColor(Color.white);
699
700       char s = seq.getCharAt(res);
701
702       charOffset = (av.charWidth - fm.charWidth(s)) / 2;
703       graphics.drawString(String.valueOf(s), charOffset + x1,
704               (y1 + av.charHeight) - pady);
705     }
706
707   }
708
709   /**
710    * DOCUMENT ME!
711    * 
712    * @param g
713    *          DOCUMENT ME!
714    */
715   public void paintComponent(Graphics g)
716   {
717     g.setColor(Color.white);
718     g.fillRect(0, 0, getWidth(), getHeight());
719
720     if (image != null)
721     {
722       if (fastPaint || (getVisibleRect().width != g.getClipBounds().width)
723               || (getVisibleRect().height != g.getClipBounds().height))
724       {
725         g.drawImage(image, 0, 0, this);
726         fastPaint = false;
727         return;
728       }
729     }
730     imgWidth = (av.endRes - av.startRes + 1) * av.charWidth;
731     if (imgWidth < 1)
732       return;
733     if (image == null || imgWidth != image.getWidth()
734             || image.getHeight(this) != getHeight())
735     {
736       image = new BufferedImage(imgWidth, ap.annotationPanel.getHeight(),
737               BufferedImage.TYPE_INT_RGB);
738       gg = (Graphics2D) image.getGraphics();
739
740       if (av.antiAlias)
741       {
742         gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
743                 RenderingHints.VALUE_ANTIALIAS_ON);
744       }
745
746       gg.setFont(av.getFont());
747       fm = gg.getFontMetrics();
748       gg.setColor(Color.white);
749       gg.fillRect(0, 0, imgWidth, image.getHeight());
750     }
751
752     drawComponent(gg, av.startRes, av.endRes + 1);
753     g.drawImage(image, 0, 0, this);
754   }
755
756   /**
757    * non-Thread safe repaint
758    * 
759    * @param horizontal
760    *          repaint with horizontal shift in alignment
761    */
762   public void fastPaint(int horizontal)
763   {
764
765     if ((horizontal == 0) || gg == null
766             || av.alignment.getAlignmentAnnotation() == null
767             || av.alignment.getAlignmentAnnotation().length < 1
768             || av.updatingConsensus || av.updatingConservation)
769     {
770       repaint();
771       return;
772     }
773     gg.copyArea(0, 0, imgWidth, getHeight(), -horizontal * av.charWidth, 0);
774
775     int sr = av.startRes;
776     int er = av.endRes + 1;
777     int transX = 0;
778
779     if (horizontal > 0) // scrollbar pulled right, image to the left
780     {
781       transX = (er - sr - horizontal) * av.charWidth;
782       sr = er - horizontal;
783     }
784     else if (horizontal < 0)
785     {
786       er = sr - horizontal;
787     }
788
789     gg.translate(transX, 0);
790
791     drawComponent(gg, sr, er);
792
793     gg.translate(-transX, 0);
794
795     fastPaint = true;
796     repaint();
797
798   }
799
800   /**
801    * DOCUMENT ME!
802    * 
803    * @param g
804    *          DOCUMENT ME!
805    * @param startRes
806    *          DOCUMENT ME!
807    * @param endRes
808    *          DOCUMENT ME!
809    */
810   public void drawComponent(Graphics g, int startRes, int endRes)
811   {
812     if (av.updatingConsensus || av.updatingConservation)
813     {
814       if (image == null)
815       {
816         return;
817       }
818       // We'll keep a record of the old image,
819       // and draw a faded image until the calculation
820       // has completed
821       if (fadedImage == null || fadedImage.getWidth() != imgWidth
822               || fadedImage.getHeight() != image.getHeight())
823       {
824         fadedImage = new BufferedImage(imgWidth, image.getHeight(),
825                 BufferedImage.TYPE_INT_RGB);
826
827         Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
828
829         fadedG.setColor(Color.white);
830         fadedG.fillRect(0, 0, imgWidth, image.getHeight());
831
832         fadedG.setComposite(AlphaComposite.getInstance(
833                 AlphaComposite.SRC_OVER, .3f));
834         fadedG.drawImage(image, 0, 0, this);
835
836       }
837
838     }
839     else
840     {
841       fadedImage = null;
842     }
843
844     g.setColor(Color.white);
845     g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getHeight());
846
847     g.setFont(av.getFont());
848     if (fm == null)
849     {
850       fm = g.getFontMetrics();
851     }
852
853     if ((av.alignment.getAlignmentAnnotation() == null)
854             || (av.alignment.getAlignmentAnnotation().length < 1))
855     {
856       g.setColor(Color.white);
857       g.fillRect(0, 0, getWidth(), getHeight());
858       g.setColor(Color.black);
859       if (av.validCharWidth)
860       {
861         g.drawString("Alignment has no annotations", 20, 15);
862       }
863
864       return;
865     }
866
867     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
868
869     int x = 0, y = 0;
870     int column = 0;
871     char lastSS;
872     int lastSSX;
873     int iconOffset = 0;
874     boolean validRes = false;
875     boolean validEnd = false;
876     boolean labelAllCols = false;
877     boolean centreColLabels, centreColLabelsDef = av
878             .getCentreColumnLabels();
879     boolean scaleColLabel = false;
880     boolean[] graphGroupDrawn = new boolean[aa.length];
881     int charOffset = 0; // offset for a label
882     float fmWidth, fmScaling = 1f; // scaling for a label to fit it into a
883     // column.
884     Font ofont = g.getFont();
885     // \u03B2 \u03B1
886     for (int i = 0; i < aa.length; i++)
887     {
888       AlignmentAnnotation row = aa[i];
889
890       if (!row.visible)
891       {
892         continue;
893       }
894       centreColLabels = row.centreColLabels || centreColLabelsDef;
895       labelAllCols = row.showAllColLabels;
896       scaleColLabel = row.scaleColLabel;
897       lastSS = ' ';
898       lastSSX = 0;
899       if (row.graph > 0)
900       {
901         if (row.graphGroup > -1 && graphGroupDrawn[row.graphGroup])
902         {
903           continue;
904         }
905
906         // this is so that we draw the characters below the graph
907         y += row.height;
908
909         if (row.hasText)
910         {
911           iconOffset = av.charHeight - fm.getDescent();
912           y -= av.charHeight;
913         }
914       }
915       else if (row.hasText)
916       {
917         iconOffset = av.charHeight - fm.getDescent();
918
919       }
920       else
921       {
922         iconOffset = 0;
923       }
924
925       if (av.updatingConsensus && aa[i] == av.consensus)
926       {
927         y += av.charHeight;
928
929         g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0, y
930                 - row.height, imgWidth, y, this);
931         g.setColor(Color.black);
932         // g.drawString("Calculating Consensus....",20, y-row.height/2);
933
934         continue;
935       }
936       else if (av.updatingConservation
937               && aa[i].label.equals("Conservation"))
938       {
939
940         y += av.charHeight;
941         g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0, y
942                 - row.height, imgWidth, y, this);
943
944         g.setColor(Color.black);
945         // g.drawString("Calculating Conservation.....",20, y-row.height/2);
946
947         continue;
948       }
949       else if (av.updatingConservation && aa[i].label.equals("Quality"))
950       {
951
952         y += av.charHeight;
953         g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0, y
954                 - row.height, imgWidth, y, this);
955         g.setColor(Color.black);
956         // / g.drawString("Calculating Quality....",20, y-row.height/2);
957
958         continue;
959       }
960
961       // first pass sets up state for drawing continuation from left-hand column
962       // of startRes
963       x = (startRes == 0) ? 0 : -1;
964       while (x < endRes - startRes)
965       {
966         if (av.hasHiddenColumns)
967         {
968           column = av.getColumnSelection().adjustForHiddenColumns(
969                   startRes + x);
970           if (column > row.annotations.length - 1)
971           {
972             break;
973           }
974         }
975         else
976         {
977           column = startRes + x;
978         }
979
980         if ((row.annotations == null) || (row.annotations.length <= column)
981                 || (row.annotations[column] == null))
982         {
983           validRes = false;
984         }
985         else
986         {
987           validRes = true;
988         }
989         if (x > -1)
990         {
991           if (activeRow == i)
992           {
993             g.setColor(Color.red);
994
995             if (av.getColumnSelection() != null)
996             {
997               for (int n = 0; n < av.getColumnSelection().size(); n++)
998               {
999                 int v = av.getColumnSelection().columnAt(n);
1000
1001                 if (v == column)
1002                 {
1003                   g.fillRect(x * av.charWidth, y, av.charWidth,
1004                           av.charHeight);
1005                 }
1006               }
1007             }
1008           }
1009
1010           if (av.validCharWidth
1011                   && validRes
1012                   && row.annotations[column].displayCharacter != null
1013                   && (row.annotations[column].displayCharacter.length() > 0))
1014           {
1015
1016             if (centreColLabels || scaleColLabel)
1017             {
1018               fmWidth = (float) fm.charsWidth(
1019                       row.annotations[column].displayCharacter
1020                               .toCharArray(), 0,
1021                       row.annotations[column].displayCharacter.length());
1022
1023               if (scaleColLabel)
1024               {
1025                 // justify the label and scale to fit in column
1026                 if (fmWidth > av.charWidth)
1027                 {
1028                   // scale only if the current font isn't already small enough
1029                   fmScaling = av.charWidth;
1030                   fmScaling /= fmWidth;
1031                   g.setFont(ofont.deriveFont(AffineTransform
1032                           .getScaleInstance(fmScaling, 1.0)));
1033                   // and update the label's width to reflect the scaling.
1034                   fmWidth = av.charWidth;
1035                 }
1036               }
1037             }
1038             else
1039             {
1040               fmWidth = (float) fm
1041                       .charWidth(row.annotations[column].displayCharacter
1042                               .charAt(0));
1043             }
1044             charOffset = (int) ((av.charWidth - fmWidth) / 2f);
1045
1046             if (row.annotations[column].colour == null)
1047               g.setColor(Color.black);
1048             else
1049               g.setColor(row.annotations[column].colour);
1050
1051             if (column == 0 || row.graph > 0)
1052             {
1053               g.drawString(row.annotations[column].displayCharacter,
1054                       (x * av.charWidth) + charOffset, y + iconOffset);
1055             }
1056             else if (row.annotations[column - 1] == null
1057                     || (labelAllCols
1058                             || !row.annotations[column].displayCharacter
1059                                     .equals(row.annotations[column - 1].displayCharacter) || (row.annotations[column].displayCharacter
1060                             .length() < 2 && row.annotations[column].secondaryStructure == ' ')))
1061             {
1062               g.drawString(row.annotations[column].displayCharacter, x
1063                       * av.charWidth + charOffset, y + iconOffset);
1064             }
1065             g.setFont(ofont);
1066           }
1067         }
1068         if (row.hasIcons)
1069         {
1070           char ss = row.annotations[column].secondaryStructure;
1071           if (ss == 'S')
1072           {
1073             // distinguish between forward/backward base-pairing
1074             if (row.annotations[column].displayCharacter.indexOf(')') > -1)
1075             {
1076               ss = 's';
1077             }
1078           }
1079           if (!validRes || (ss != lastSS))
1080           {
1081             if (x > -1)
1082             {
1083               switch (lastSS)
1084               {
1085               case 'H':
1086                 drawHelixAnnot(g, row, lastSSX, x, y, iconOffset, startRes,
1087                         column, validRes, validEnd);
1088                 break;
1089
1090               case 'E':
1091                 drawSheetAnnot(g, row, lastSSX, x, y, iconOffset, startRes,
1092                         column, validRes, validEnd);
1093                 break;
1094
1095               case 'S': // Stem case for RNA secondary structure
1096               case 's': // and opposite direction
1097                 drawStemAnnot(g, row, lastSSX, x, y, iconOffset, startRes,
1098                         column, validRes, validEnd);
1099                 break;
1100
1101               default:
1102                 g.setColor(Color.gray);
1103                 g.fillRect(lastSSX, y + 6 + iconOffset, (x * av.charWidth)
1104                         - lastSSX, 2);
1105
1106                 break;
1107               }
1108             }
1109             if (validRes)
1110             {
1111               lastSS = ss;
1112             }
1113             else
1114             {
1115               lastSS = ' ';
1116             }
1117             if (x > -1)
1118             {
1119               lastSSX = (x * av.charWidth);
1120             }
1121           }
1122         }
1123
1124         column++;
1125         x++;
1126       }
1127
1128       if (column >= row.annotations.length)
1129       {
1130         column = row.annotations.length - 1;
1131         validEnd = false;
1132       }
1133       else
1134       {
1135         validEnd = true;
1136       }
1137
1138       // x ++;
1139
1140       if (row.hasIcons)
1141       {
1142         switch (lastSS)
1143         {
1144         case 'H':
1145           drawHelixAnnot(g, row, lastSSX, x, y, iconOffset, startRes,
1146                   column, validRes, validEnd);
1147           break;
1148
1149         case 'E':
1150           drawSheetAnnot(g, row, lastSSX, x, y, iconOffset, startRes,
1151                   column, validRes, validEnd);
1152           break;
1153         case 's':
1154         case 'S': // Stem case for RNA secondary structure
1155           drawStemAnnot(g, row, lastSSX, x, y, iconOffset, startRes,
1156                   column, validRes, validEnd);
1157           break;
1158         default:
1159           drawGlyphLine(g, row, lastSSX, x, y, iconOffset, startRes,
1160                   column, validRes, validEnd);
1161           break;
1162         }
1163       }
1164
1165       if (row.graph > 0 && row.graphHeight > 0)
1166       {
1167         if (row.graph == AlignmentAnnotation.LINE_GRAPH)
1168         {
1169           if (row.graphGroup > -1 && !graphGroupDrawn[row.graphGroup])
1170           {
1171             float groupmax = -999999, groupmin = 9999999;
1172             for (int gg = 0; gg < aa.length; gg++)
1173             {
1174               if (aa[gg].graphGroup != row.graphGroup)
1175               {
1176                 continue;
1177               }
1178
1179               if (aa[gg] != row)
1180               {
1181                 aa[gg].visible = false;
1182               }
1183
1184               if (aa[gg].graphMax > groupmax)
1185               {
1186                 groupmax = aa[gg].graphMax;
1187               }
1188               if (aa[gg].graphMin < groupmin)
1189               {
1190                 groupmin = aa[gg].graphMin;
1191               }
1192             }
1193
1194             for (int gg = 0; gg < aa.length; gg++)
1195             {
1196               if (aa[gg].graphGroup == row.graphGroup)
1197               {
1198                 drawLineGraph(g, aa[gg], startRes, endRes, y, groupmin,
1199                         groupmax, row.graphHeight);
1200               }
1201             }
1202
1203             graphGroupDrawn[row.graphGroup] = true;
1204           }
1205           else
1206           {
1207             drawLineGraph(g, row, startRes, endRes, y, row.graphMin,
1208                     row.graphMax, row.graphHeight);
1209           }
1210         }
1211         else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
1212         {
1213           drawBarGraph(g, row, startRes, endRes, row.graphMin,
1214                   row.graphMax, y);
1215         }
1216       }
1217
1218       if (row.graph > 0 && row.hasText)
1219       {
1220         y += av.charHeight;
1221       }
1222
1223       if (row.graph == 0)
1224       {
1225         y += aa[i].height;
1226       }
1227     }
1228   }
1229
1230   private void drawStemAnnot(Graphics g, AlignmentAnnotation row,
1231           int lastSSX, int x, int y, int iconOffset, int startRes,
1232           int column, boolean validRes, boolean validEnd)
1233   {
1234     g.setColor(STEM_COLOUR);
1235     int sCol = (lastSSX / av.charWidth) + startRes;
1236     int x1 = lastSSX;
1237     int x2 = (x * av.charWidth);
1238     Regex closeparen = new Regex("(\\))");
1239
1240     String dc = column == 0 ? ""
1241             : row.annotations[column - 1].displayCharacter;
1242
1243     boolean diffupstream = sCol == 0 || row.annotations[sCol - 1] == null
1244             || !dc.equals(row.annotations[sCol - 1].displayCharacter);
1245     boolean diffdownstream = !validRes || !validEnd
1246             || row.annotations[column] == null
1247             || !dc.equals(row.annotations[column].displayCharacter);
1248     // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
1249     // If a closing base pair half of the stem, display a backward arrow
1250     if (column > 0 && closeparen.search(dc))
1251     {
1252       if (diffupstream)
1253       // if (validRes && column>1 && row.annotations[column-2]!=null &&
1254       // dc.equals(row.annotations[column-2].displayCharacter))
1255       {
1256         g.fillPolygon(new int[]
1257         { lastSSX + 5, lastSSX + 5, lastSSX }, new int[]
1258         { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
1259         x1 += 5;
1260       }
1261       if (diffdownstream)
1262       {
1263         x2 -= 1;
1264       }
1265     }
1266     else
1267     {
1268       // display a forward arrow
1269       if (diffdownstream)
1270       {
1271         g.fillPolygon(new int[]
1272         { x2 - 5, x2 - 5, x2 }, new int[]
1273         { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
1274         x2 -= 5;
1275       }
1276       if (diffupstream)
1277       {
1278         x1 += 1;
1279       }
1280     }
1281     // draw arrow body
1282     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
1283   }
1284
1285   private void drawGlyphLine(Graphics g, AlignmentAnnotation row,
1286           int lastSSX, int x, int y, int iconOffset, int startRes,
1287           int column, boolean validRes, boolean validEnd)
1288   {
1289     g.setColor(Color.gray);
1290     g
1291             .fillRect(lastSSX, y + 6 + iconOffset, (x * av.charWidth)
1292                     - lastSSX, 2);
1293   }
1294
1295   private void drawSheetAnnot(Graphics g, AlignmentAnnotation row,
1296           int lastSSX, int x, int y, int iconOffset, int startRes,
1297           int column, boolean validRes, boolean validEnd)
1298   {
1299     g.setColor(SHEET_COLOUR);
1300
1301     if (!validEnd || !validRes || row.annotations[column] == null
1302             || row.annotations[column].secondaryStructure != 'E')
1303     {
1304       g.fillRect(lastSSX, y + 4 + iconOffset, (x * av.charWidth) - lastSSX
1305               - 4, 7);
1306       g.fillPolygon(
1307               new int[]
1308               { (x * av.charWidth) - 4, (x * av.charWidth) - 4,
1309                   (x * av.charWidth) }, new int[]
1310               { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset },
1311               3);
1312     }
1313     else
1314     {
1315       g.fillRect(lastSSX, y + 4 + iconOffset, (x + 1) * av.charWidth
1316               - lastSSX, 7);
1317     }
1318
1319   }
1320
1321   private void drawHelixAnnot(Graphics g, AlignmentAnnotation row,
1322           int lastSSX, int x, int y, int iconOffset, int startRes,
1323           int column, boolean validRes, boolean validEnd)
1324   {
1325     g.setColor(HELIX_COLOUR);
1326
1327     int sCol = (lastSSX / av.charWidth) + startRes;
1328     int x1 = lastSSX;
1329     int x2 = (x * av.charWidth);
1330
1331     if (MAC)
1332     {
1333       int ofs = av.charWidth / 2;
1334       // Off by 1 offset when drawing rects and ovals
1335       // to offscreen image on the MAC
1336       g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1, 8, 8, 8);
1337       if (sCol == 0 || row.annotations[sCol - 1] == null
1338               || row.annotations[sCol - 1].secondaryStructure != 'H')
1339       {
1340       }
1341       else
1342       {
1343         // g.setColor(Color.orange);
1344         g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1 - ofs + 1, 8,
1345                 0, 0);
1346       }
1347       if (!validRes || row.annotations[column] == null
1348               || row.annotations[column].secondaryStructure != 'H')
1349       {
1350
1351       }
1352       else
1353       {
1354         // g.setColor(Color.magenta);
1355         g.fillRoundRect(lastSSX + ofs, y + 4 + iconOffset, x2 - x1 - ofs
1356                 + 1, 8, 0, 0);
1357
1358       }
1359
1360       return;
1361     }
1362
1363     if (sCol == 0 || row.annotations[sCol - 1] == null
1364             || row.annotations[sCol - 1].secondaryStructure != 'H')
1365     {
1366       g.fillArc(lastSSX, y + 4 + iconOffset, av.charWidth, 8, 90, 180);
1367       x1 += av.charWidth / 2;
1368     }
1369
1370     if (!validRes || row.annotations[column] == null
1371             || row.annotations[column].secondaryStructure != 'H')
1372     {
1373       g.fillArc((x * av.charWidth) - av.charWidth, y + 4 + iconOffset,
1374               av.charWidth, 8, 270, 180);
1375       x2 -= av.charWidth / 2;
1376     }
1377
1378     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
1379   }
1380
1381   public void drawLineGraph(Graphics g, AlignmentAnnotation aa, int sRes,
1382           int eRes, int y, float min, float max, int graphHeight)
1383   {
1384     if (sRes > aa.annotations.length)
1385     {
1386       return;
1387     }
1388
1389     int x = 0;
1390
1391     // Adjustment for fastpaint to left
1392     if (eRes < av.endRes)
1393     {
1394       eRes++;
1395     }
1396
1397     eRes = Math.min(eRes, aa.annotations.length);
1398
1399     if (sRes == 0)
1400     {
1401       x++;
1402     }
1403
1404     int y1 = y, y2 = y;
1405     float range = max - min;
1406
1407     // //Draw origin
1408     if (min < 0)
1409     {
1410       y2 = y - (int) ((0 - min / range) * graphHeight);
1411     }
1412
1413     g.setColor(Color.gray);
1414     g.drawLine(x - av.charWidth, y2, (eRes - sRes + 1) * av.charWidth, y2);
1415
1416     eRes = Math.min(eRes, aa.annotations.length);
1417
1418     int column;
1419     int aaMax = aa.annotations.length - 1;
1420
1421     while (x < eRes - sRes)
1422     {
1423       column = sRes + x;
1424       if (av.hasHiddenColumns)
1425       {
1426         column = av.getColumnSelection().adjustForHiddenColumns(column);
1427       }
1428
1429       if (column > aaMax)
1430       {
1431         break;
1432       }
1433
1434       if (aa.annotations[column] == null
1435               || aa.annotations[column - 1] == null)
1436       {
1437         x++;
1438         continue;
1439       }
1440
1441       if (aa.annotations[column].colour == null)
1442         g.setColor(Color.black);
1443       else
1444         g.setColor(aa.annotations[column].colour);
1445
1446       y1 = y
1447               - (int) (((aa.annotations[column - 1].value - min) / range) * graphHeight);
1448       y2 = y
1449               - (int) (((aa.annotations[column].value - min) / range) * graphHeight);
1450
1451       g.drawLine(x * av.charWidth - av.charWidth / 2, y1, x * av.charWidth
1452               + av.charWidth / 2, y2);
1453       x++;
1454     }
1455
1456     if (aa.threshold != null)
1457     {
1458       g.setColor(aa.threshold.colour);
1459       Graphics2D g2 = (Graphics2D) g;
1460       g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
1461               BasicStroke.JOIN_ROUND, 3f, new float[]
1462               { 5f, 3f }, 0f));
1463
1464       y2 = (int) (y - ((aa.threshold.value - min) / range) * graphHeight);
1465       g.drawLine(0, y2, (eRes - sRes) * av.charWidth, y2);
1466       g2.setStroke(new BasicStroke());
1467     }
1468   }
1469
1470   public void drawBarGraph(Graphics g, AlignmentAnnotation aa, int sRes,
1471           int eRes, float min, float max, int y)
1472   {
1473     ColourSchemeI profcolour = av.getGlobalColourScheme();
1474     if (profcolour == null)
1475     {
1476       profcolour = new jalview.schemes.ZappoColourScheme();
1477     }
1478     if (sRes > aa.annotations.length)
1479     {
1480       return;
1481     }
1482     Font ofont = g.getFont();
1483     eRes = Math.min(eRes, aa.annotations.length);
1484
1485     int x = 0, y1 = y, y2 = y;
1486
1487     float range = max - min;
1488
1489     if (min < 0)
1490     {
1491       y2 = y - (int) ((0 - min / (range)) * aa.graphHeight);
1492     }
1493
1494     g.setColor(Color.gray);
1495
1496     g.drawLine(x, y2, (eRes - sRes) * av.charWidth, y2);
1497
1498     int column;
1499     int aaMax = aa.annotations.length - 1;
1500     boolean renderHistogram = true, renderProfile = true;
1501     if (aa.autoCalculated && aa.label.startsWith("Consensus"))
1502     {
1503       // TODO: generalise this to have render styles for consensus/profile data
1504       if (aa.groupRef != null)
1505       {
1506         renderHistogram = aa.groupRef.isShowConsensusHistogram();
1507         renderProfile = aa.groupRef.isShowSequenceLogo();
1508       }
1509       else
1510       {
1511         renderHistogram = av.isShowConsensusHistogram();
1512         renderProfile = av.isShowSequenceLogo();
1513       }
1514     }
1515     while (x < eRes - sRes)
1516     {
1517       column = sRes + x;
1518       if (av.hasHiddenColumns)
1519       {
1520         column = av.getColumnSelection().adjustForHiddenColumns(column);
1521       }
1522
1523       if (column > aaMax)
1524       {
1525         break;
1526       }
1527
1528       if (aa.annotations[column] == null)
1529       {
1530         x++;
1531         continue;
1532       }
1533       if (aa.annotations[column].colour == null)
1534         g.setColor(Color.black);
1535       else
1536         g.setColor(aa.annotations[column].colour);
1537
1538       y1 = y
1539               - (int) (((aa.annotations[column].value - min) / (range)) * aa.graphHeight);
1540
1541       if (renderHistogram)
1542       {
1543         if (y1 - y2 > 0)
1544         {
1545           g.fillRect(x * av.charWidth, y2, av.charWidth, y1 - y2);
1546         }
1547         else
1548         {
1549           g.fillRect(x * av.charWidth, y1, av.charWidth, y2 - y1);
1550         }
1551       }
1552       // draw profile if available
1553       if (renderProfile && aa.annotations[column].value != 0)
1554       {
1555
1556         int profl[] = getProfileFor(aa, column);
1557         // just try to draw the logo if profl is not null
1558         if (profl != null)
1559         {
1560
1561           /*
1562            * if (profl != null) {
1563            * 
1564            * for (int i = 0; i < profl.length; i++) { System.out.print(profl[i]
1565            * + ","); }
1566            * 
1567            * }
1568            */
1569           int ht = y1, htn = y2 - y1;// aa.graphHeight;
1570           float wdth;
1571           double ht2 = 0;
1572           char[] dc;
1573
1574           /**
1575            * profl.length == 51 indicates that the profile of a secondary
1576            * structure conservation row was accesed.
1577            * Therefore dc gets length 2, to have space for a basepair instead of
1578            * just a single nucleotide
1579            */
1580           if (profl.length == 51)
1581           {
1582             dc = new char[2];
1583           }
1584           else
1585           {
1586             dc = new char[1];
1587           }
1588
1589           LineMetrics lm;
1590           for (int c = 1; profl != null && c < profl[0];)
1591           {
1592             dc[0] = (char) profl[c++];
1593
1594             if (aa.label.startsWith("StrucConsensus"))
1595             {
1596               dc[1] = (char) profl[c++];
1597             }
1598             
1599             wdth = av.charWidth;
1600             wdth /= (float) fm.charsWidth(dc, 0, dc.length);
1601
1602             if (c > 2)
1603             {
1604               ht += (int) ht2;
1605             }
1606             {
1607               // if (aa.annotations[column].value==0) {
1608               // g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(wdth,
1609               // (ht2=(aa.graphHeight*0.1/av.charHeight)))));
1610               // ht = y2-(int)ht2;
1611               // } else {
1612               g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
1613                       wdth, (ht2 = (htn * ((double) profl[c++]) / 100.0))
1614                               / av.charHeight)));
1615               lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
1616               // htn -=ht2;
1617               // }
1618               g.setColor(profcolour.findColour(dc[0])); // (av.globalColourScheme!=null)
1619               // ? );// try to get a
1620               // colourscheme for the
1621               // group(aa.groupRef.cs==null)
1622               // ? av.textColour2 :
1623               // cs.findColour(dc));
1624               // System.out.println(dc[0]);
1625
1626               g.drawChars(dc, 0, dc.length, x * av.charWidth,
1627                       (int) (ht + lm.getHeight()));
1628
1629               // ht+=g.getFontMetrics().getAscent()-g.getFontMetrics().getDescent();
1630             }
1631           }
1632           g.setFont(ofont);
1633         }
1634       }
1635       x++;
1636     }
1637     if (aa.threshold != null)
1638     {
1639       g.setColor(aa.threshold.colour);
1640       Graphics2D g2 = (Graphics2D) g;
1641       g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
1642               BasicStroke.JOIN_ROUND, 3f, new float[]
1643               { 5f, 3f }, 0f));
1644
1645       y2 = (int) (y - ((aa.threshold.value - min) / range) * aa.graphHeight);
1646       g.drawLine(0, y2, (eRes - sRes) * av.charWidth, y2);
1647       g2.setStroke(new BasicStroke());
1648     }
1649   }
1650
1651   private int[] getProfileFor(AlignmentAnnotation aa, int column)
1652   {
1653     if (aa.autoCalculated && aa.label.startsWith("Consensus"))
1654     {
1655       if (aa.groupRef != null && aa.groupRef.consensusData != null
1656               && aa.groupRef.isShowSequenceLogo())
1657       {
1658         return AAFrequency.extractProfile(
1659                 aa.groupRef.consensusData[column], aa.groupRef
1660                         .getIgnoreGapsConsensus());
1661       }
1662       // TODO extend annotation row to enable dynamic and static profile data to
1663       // be stored
1664       if (aa.groupRef == null && aa.sequenceRef == null
1665               && av.isShowSequenceLogo())
1666       {
1667         return AAFrequency.extractProfile(av.hconsensus[column], av
1668                 .getIgnoreGapsConsensus());
1669       }
1670     }
1671     else
1672     {
1673       if (aa.autoCalculated && aa.label.startsWith("StrucConsensus"))
1674       {
1675         if (aa.groupRef != null && aa.groupRef.consensusData != null
1676                 && aa.groupRef.isShowSequenceLogo())
1677         {
1678           //TODO check what happens for group selections
1679           return StructureFrequency.extractProfile(
1680                   aa.groupRef.consensusData[column], aa.groupRef
1681                           .getIgnoreGapsConsensus(),column);
1682         }
1683         // TODO extend annotation row to enable dynamic and static profile data
1684         // to
1685         // be stored
1686         if (aa.groupRef == null && aa.sequenceRef == null
1687                 && av.isShowSequenceLogo())
1688         {
1689           return StructureFrequency.extractProfile(av.hStrucConsensus[column],
1690                   av.getIgnoreGapsConsensus(),column);
1691         }
1692       }
1693
1694     }
1695     return null;
1696   }
1697
1698   // used by overview window
1699   public void drawGraph(Graphics g, AlignmentAnnotation aa, int width,
1700           int y, int sRes, int eRes)
1701   {
1702     eRes = Math.min(eRes, aa.annotations.length);
1703     g.setColor(Color.white);
1704     g.fillRect(0, 0, width, y);
1705     g.setColor(new Color(0, 0, 180));
1706
1707     int x = 0, height;
1708
1709     for (int j = sRes; j < eRes; j++)
1710     {
1711       if (aa.annotations[j] != null)
1712       {
1713         if (aa.annotations[j].colour == null)
1714           g.setColor(Color.black);
1715         else
1716           g.setColor(aa.annotations[j].colour);
1717
1718         height = (int) ((aa.annotations[j].value / aa.graphMax) * y);
1719         if (height > y)
1720         {
1721           height = y;
1722         }
1723
1724         g.fillRect(x, y - height, av.charWidth, height);
1725       }
1726       x += av.charWidth;
1727     }
1728   }
1729
1730 }