ported logo rendering and label fit to column width - but disabled due to 1.1 incompa...
[jalview.git] / src / jalview / appletgui / AnnotationPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 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.appletgui;
20
21 import java.util.*;
22
23 import java.awt.*;
24 import java.awt.event.*;
25 import jalview.datamodel.*;
26 import jalview.schemes.ColourSchemeI;
27
28 public class AnnotationPanel extends Panel implements AdjustmentListener,
29         ActionListener, MouseListener, MouseMotionListener
30 {
31   AlignViewport av;
32
33   AlignmentPanel ap;
34
35   int activeRow = -1;
36
37   Vector activeRes;
38
39   static String HELIX = "Helix";
40
41   static String SHEET = "Sheet";
42
43   static String LABEL = "Label";
44
45   static String REMOVE = "Remove Annotation";
46
47   static String COLOUR = "Colour";
48
49   static Color HELIX_COLOUR = Color.red.darker();
50
51   static Color SHEET_COLOUR = Color.green.darker().darker();
52
53   Image image;
54
55   Graphics gg;
56
57   FontMetrics fm;
58
59   int imgWidth = 0;
60
61   boolean fastPaint = false;
62   //Used For mouse Dragging and resizing graphs
63   int graphStretch = -1;
64
65   int graphStretchY = -1;
66   
67   boolean mouseDragging=false;
68   
69   public static int GRAPH_HEIGHT = 40;
70
71   boolean MAC = false;
72
73   public AnnotationPanel(AlignmentPanel ap)
74   {
75     MAC = new jalview.util.Platform().isAMac();
76     this.ap = ap;
77     av = ap.av;
78     setLayout(null);
79     adjustPanelHeight();
80
81     addMouseMotionListener(this);
82
83     addMouseListener(this);
84
85     // ap.annotationScroller.getVAdjustable().addAdjustmentListener( this );
86   }
87
88   public AnnotationPanel(AlignViewport av)
89   {
90     this.av = av;
91   }
92
93   public void adjustmentValueChanged(AdjustmentEvent evt)
94   {
95     ap.alabels.setScrollOffset(-evt.getValue());
96   }
97
98   /**
99    * DOCUMENT ME!
100    * 
101    * @param evt
102    *                DOCUMENT ME!
103    */
104   public void actionPerformed(ActionEvent evt)
105   {
106     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
107     Annotation[] anot = aa[activeRow].annotations;
108
109     if (anot.length < av.getColumnSelection().getMax())
110     {
111       Annotation[] temp = new Annotation[av.getColumnSelection().getMax() + 2];
112       System.arraycopy(anot, 0, temp, 0, anot.length);
113       anot = temp;
114       aa[activeRow].annotations = anot;
115     }
116
117     String label = "";
118     if (av.colSel != null && av.colSel.size() > 0
119             && anot[av.colSel.getMin()] != null)
120       label = anot[av.getColumnSelection().getMin()].displayCharacter;
121
122     if (evt.getActionCommand().equals(REMOVE))
123     {
124       for (int i = 0; i < av.getColumnSelection().size(); i++)
125       {
126         anot[av.getColumnSelection().columnAt(i)] = null;
127       }
128     }
129     else if (evt.getActionCommand().equals(LABEL))
130     {
131       label = enterLabel(label, "Enter Label");
132
133       if (label == null)
134       {
135         return;
136       }
137
138       if ((label.length() > 0) && !aa[activeRow].hasText)
139       {
140         aa[activeRow].hasText = true;
141       }
142
143       for (int i = 0; i < av.getColumnSelection().size(); i++)
144       {
145         int index = av.getColumnSelection().columnAt(i);
146
147         if (!av.colSel.isVisible(index))
148           continue;
149
150         if (anot[index] == null)
151         {
152           anot[index] = new Annotation(label, "", ' ', 0);
153         }
154
155         anot[index].displayCharacter = label;
156       }
157     }
158     else if (evt.getActionCommand().equals(COLOUR))
159     {
160       UserDefinedColours udc = new UserDefinedColours(this, Color.black,
161               ap.alignFrame);
162
163       Color col = udc.getColor();
164
165       for (int i = 0; i < av.getColumnSelection().size(); i++)
166       {
167         int index = av.getColumnSelection().columnAt(i);
168
169         if (!av.colSel.isVisible(index))
170           continue;
171
172         if (anot[index] == null)
173         {
174           anot[index] = new Annotation("", "", ' ', 0);
175         }
176
177         anot[index].colour = col;
178       }
179     }
180     else
181     // HELIX OR SHEET
182     {
183       char type = 0;
184       String symbol = "\u03B1";
185
186       if (evt.getActionCommand().equals(HELIX))
187       {
188         type = 'H';
189       }
190       else if (evt.getActionCommand().equals(SHEET))
191       {
192         type = 'E';
193         symbol = "\u03B2";
194       }
195
196       if (!aa[activeRow].hasIcons)
197       {
198         aa[activeRow].hasIcons = true;
199       }
200
201       label = enterLabel(symbol, "Enter Label");
202
203       if (label == null)
204       {
205         return;
206       }
207
208       if ((label.length() > 0) && !aa[activeRow].hasText)
209       {
210         aa[activeRow].hasText = true;
211       }
212
213       for (int i = 0; i < av.getColumnSelection().size(); i++)
214       {
215         int index = av.getColumnSelection().columnAt(i);
216
217         if (!av.colSel.isVisible(index))
218           continue;
219
220         if (anot[index] == null)
221         {
222           anot[index] = new Annotation(label, "", type, 0);
223         }
224
225         anot[index].secondaryStructure = type;
226         anot[index].displayCharacter = label;
227       }
228     }
229
230     adjustPanelHeight();
231     repaint();
232
233     return;
234   }
235
236   String enterLabel(String text, String label)
237   {
238     EditNameDialog dialog = new EditNameDialog(text, null, label, null,
239             ap.alignFrame, "Enter Label", 400, 200, true);
240
241     if (dialog.accept)
242       return dialog.getName();
243     else
244       return null;
245   }
246
247   public void mousePressed(MouseEvent evt)
248   {
249     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
250     if (aa == null)
251     {
252       return;
253     }
254
255     int height = 0;
256     activeRow = -1;
257
258     for (int i = 0; i < aa.length; i++)
259     {
260       if (aa[i].visible)
261       {
262         height += aa[i].height;
263       }
264
265       if (evt.getY() < height)
266       {
267         if (aa[i].editable)
268         {
269           activeRow = i;
270         }
271                 else if (aa[i].graph > 0)
272         {
273           // Stretch Graph
274           graphStretch = i;
275           graphStretchY = evt.getY();
276         }
277         
278
279         break;
280       }
281     }
282
283     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK
284             && activeRow != -1)
285     {
286       if (av.getColumnSelection() == null)
287       {
288         return;
289       }
290
291       PopupMenu pop = new PopupMenu("Structure type");
292       MenuItem item = new MenuItem(HELIX);
293       item.addActionListener(this);
294       pop.add(item);
295       item = new MenuItem(SHEET);
296       item.addActionListener(this);
297       pop.add(item);
298       item = new MenuItem(LABEL);
299       item.addActionListener(this);
300       pop.add(item);
301       item = new MenuItem(COLOUR);
302       item.addActionListener(this);
303       pop.add(item);
304       item = new MenuItem(REMOVE);
305       item.addActionListener(this);
306       pop.add(item);
307       ap.alignFrame.add(pop);
308       pop.show(this, evt.getX(), evt.getY());
309
310       return;
311     }
312
313     if (aa == null)
314     {
315       return;
316     }
317
318     ap.scalePanel.mousePressed(evt);
319   }
320
321   public void mouseReleased(MouseEvent evt)
322   {
323     graphStretch = -1;
324     graphStretchY = -1;
325     mouseDragging = false;
326     ap.scalePanel.mouseReleased(evt);
327   }
328
329   public void mouseClicked(MouseEvent evt)
330   {
331   }
332
333   public void mouseDragged(MouseEvent evt)
334   {
335     if (graphStretch > -1)
336     {
337       av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight += graphStretchY
338               - evt.getY();
339       if (av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight < 0)
340       {
341         av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight = 0;
342       }
343       graphStretchY = evt.getY();
344       adjustPanelHeight();
345       ap.paintAlignment(true);
346     }
347     else
348     {
349       ap.scalePanel.mouseDragged(evt);
350     }
351   }
352
353   public void mouseMoved(MouseEvent evt)
354   {
355     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
356     if (aa == null)
357     {
358       return;
359     }
360
361     int row = -1;
362     int height = 0;
363     for (int i = 0; i < aa.length; i++)
364     {
365
366       if (aa[i].visible)
367       {
368         height += aa[i].height;
369       }
370
371       if (evt.getY() < height)
372       {
373         row = i;
374         break;
375       }
376     }
377
378     int res = evt.getX() / av.getCharWidth() + av.getStartRes();
379
380     if (av.hasHiddenColumns)
381     {
382       res = av.getColumnSelection().adjustForHiddenColumns(res);
383     }
384
385     if (row > -1 && res < aa[row].annotations.length
386             && aa[row].annotations[res] != null)
387     {
388       StringBuffer text = new StringBuffer("Sequence position " + (res + 1));
389       if (aa[row].annotations[res].description != null)
390       {
391         text.append("  " + aa[row].annotations[res].description);
392       }
393       ap.alignFrame.statusBar.setText(text.toString());
394     }
395   }
396
397   public void mouseEntered(MouseEvent evt)
398   {
399     ap.scalePanel.mouseEntered(evt);
400   }
401
402   public void mouseExited(MouseEvent evt)
403   {
404     ap.scalePanel.mouseExited(evt);
405   }
406
407   public int adjustPanelHeight()
408   {
409     // setHeight of panels
410     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
411     int height = 0;
412
413     if (aa != null)
414     {
415       for (int i = 0; i < aa.length; i++)
416       {
417         if (!aa[i].visible)
418         {
419           continue;
420         }
421
422         aa[i].height = 0;
423
424         if (aa[i].hasText)
425         {
426           aa[i].height += av.charHeight;
427         }
428
429         if (aa[i].hasIcons)
430         {
431           aa[i].height += 16;
432         }
433
434         if (aa[i].graph > 0)
435         {
436           aa[i].height += aa[i].graphHeight;
437         }
438
439         if (aa[i].height == 0)
440         {
441           aa[i].height = 20;
442         }
443
444         height += aa[i].height;
445       }
446     }
447     else
448     {
449       height = 20;
450     }
451
452     this.setSize(getSize().width, height);
453
454     repaint();
455
456     return height;
457
458   }
459
460   public void addEditableColumn(int i)
461   {
462     if (activeRow == -1)
463     {
464       AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
465       if (aa == null)
466       {
467         return;
468       }
469
470       for (int j = 0; j < aa.length; j++)
471       {
472         if (aa[j].editable)
473         {
474           activeRow = j;
475           break;
476         }
477       }
478     }
479
480     if (activeRes == null)
481     {
482       activeRes = new Vector();
483       activeRes.addElement(String.valueOf(i));
484       return;
485     }
486
487     activeRes.addElement(String.valueOf(i));
488   }
489
490   public void update(Graphics g)
491   {
492     paint(g);
493   }
494
495   public void paint(Graphics g)
496   {
497
498     imgWidth = getSize().width;
499     // (av.endRes - av.startRes + 1) * av.charWidth;
500
501     if (image == null || imgWidth != image.getWidth(this))
502     {
503       image = createImage(imgWidth, ap.annotationPanel.getSize().height);
504       gg = image.getGraphics();
505       gg.setFont(av.getFont());
506       fm = gg.getFontMetrics();
507       fastPaint = false;
508     }
509
510     if (fastPaint)
511     {
512       g.drawImage(image, 0, 0, this);
513       fastPaint = false;
514       return;
515     }
516
517     gg.setColor(Color.white);
518     gg.fillRect(0, 0, getSize().width, getSize().height);
519     drawComponent(gg, av.startRes, av.endRes + 1);
520
521     g.drawImage(image, 0, 0, this);
522   }
523
524   public void fastPaint(int horizontal)
525   {
526     if (horizontal == 0 || av.alignment.getAlignmentAnnotation() == null
527             || av.alignment.getAlignmentAnnotation().length < 1)
528     {
529       repaint();
530       return;
531     }
532
533     gg.copyArea(0, 0, imgWidth, getSize().height, -horizontal
534             * av.charWidth, 0);
535     int sr = av.startRes, er = av.endRes + 1, transX = 0;
536
537     if (horizontal > 0) // scrollbar pulled right, image to the left
538     {
539       transX = (er - sr - horizontal) * av.charWidth;
540       sr = er - horizontal;
541     }
542     else if (horizontal < 0)
543     {
544       er = sr - horizontal;
545     }
546
547     gg.translate(transX, 0);
548
549     drawComponent(gg, sr, er);
550
551     gg.translate(-transX, 0);
552
553     fastPaint = true;
554     repaint();
555   }
556
557   /**
558    * DOCUMENT ME!
559    * 
560    * @param g
561    *                DOCUMENT ME!
562    * @param startRes
563    *                DOCUMENT ME!
564    * @param endRes
565    *                DOCUMENT ME!
566    */
567   public void drawComponent(Graphics g, int startRes, int endRes)
568   {
569     Font ofont = av.getFont();
570     g.setFont(ofont);
571
572     g.setColor(Color.white);
573     g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getSize().height);
574
575     if (fm == null)
576     {
577       fm = g.getFontMetrics();
578     }
579
580     if ((av.alignment.getAlignmentAnnotation() == null)
581             || (av.alignment.getAlignmentAnnotation().length < 1))
582     {
583       g.setColor(Color.white);
584       g.fillRect(0, 0, getSize().width, getSize().height);
585       g.setColor(Color.black);
586       if (av.validCharWidth)
587       {
588         g.drawString("Alignment has no annotations", 20, 15);
589       }
590
591       return;
592     }
593
594     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
595
596     int x = 0;
597     int y = 0;
598     int column = 0;
599     char lastSS;
600     int lastSSX;
601     int iconOffset = av.charHeight / 2;
602     boolean validRes = false;
603     boolean validEnd = false;
604     boolean labelAllCols = false;
605     boolean centreColLabels,centreColLabelsDef = av.getCentreColumnLabels();
606     boolean scaleColLabel=false;
607     boolean[] graphGroupDrawn = new boolean[aa.length];
608     int charOffset = 0; // offset for a label
609     float fmWidth, fmScaling = 1f; // scaling for a label to fit it into a column.
610     // \u03B2 \u03B1
611     for (int i = 0; i < aa.length; i++)
612     {
613       AlignmentAnnotation row = aa[i];
614
615       if (!row.visible)
616       {
617         continue;
618       }
619       centreColLabels = row.centreColLabels || centreColLabelsDef;
620       labelAllCols = row.showAllColLabels;
621       scaleColLabel = row.scaleColLabel;
622       lastSS = ' ';
623       lastSSX = 0;
624
625       if (row.graph > 0)
626       {
627         if (row.graphGroup > -1 && graphGroupDrawn[row.graphGroup])
628         {
629           continue;
630         }
631
632         // this is so that we draw the characters below the graph
633         y += row.height;
634
635         if (row.hasText)
636         {
637           iconOffset = av.charHeight - fm.getDescent();
638           y -= av.charHeight;
639         }
640       }
641       //TODO: else is the logic used in application, applet had no 'else'
642       else if (row.hasText)
643       {
644         iconOffset = av.charHeight - fm.getDescent();
645
646       }
647       else
648       {
649         iconOffset = 0;
650       }
651
652       x = 0;
653       while (x < endRes - startRes)
654       {
655         if (av.hasHiddenColumns)
656         {
657           column = av.getColumnSelection().adjustForHiddenColumns(
658                   startRes + x);
659           if (column > row.annotations.length - 1)
660           {
661             break;
662           }
663         }
664         else
665         {
666           column = startRes + x;
667         }
668
669         if ((row.annotations.length <= column)
670                 || (row.annotations[column] == null))
671         {
672           validRes = false;
673         }
674         else
675         {
676           validRes = true;
677         }
678
679         if (activeRow == i)
680         {
681           g.setColor(Color.red);
682
683           if (av.getColumnSelection() != null)
684           {
685             for (int n = 0; n < av.getColumnSelection().size(); n++)
686             {
687               int v = av.getColumnSelection().columnAt(n);
688
689               if (v == column)
690               {
691                 g
692                         .fillRect(x * av.charWidth, y, av.charWidth,
693                                 av.charHeight);
694               }
695             }
696           }
697         }
698
699         if (av.validCharWidth
700                 && validRes
701                 && (row.annotations[column].displayCharacter != null && row.annotations[column].displayCharacter
702                         .length() > 0))
703         {
704                   
705           if (centreColLabels || scaleColLabel)
706           {
707             fmWidth = (float) fm
708             .charsWidth(row.annotations[column].displayCharacter
709                     .toCharArray(), 0,
710                     row.annotations[column].displayCharacter.length());
711
712             if ( scaleColLabel ) { 
713               // justify the label and scale to fit in column
714               if (fmWidth > av.charWidth) {
715                 // scale only if the current font isn't already small enough
716                 fmScaling = av.charWidth;
717                 fmScaling /= fmWidth;
718                 // not 1.1 // g.setFont(new Font(ofont,AffineTransform.getScaleInstance(fmScaling, 1.0)));
719                 // and update the label's width to reflect the scaling.
720                 fmWidth = av.charWidth;
721               }
722             }
723           } else {
724             fmWidth = (float) fm
725             .charWidth(row.annotations[column].displayCharacter.charAt(0));
726           }
727           charOffset =  (int) ((av.charWidth - fmWidth) / 2f);
728           
729           if (row.annotations[column].colour == null)
730             g.setColor(Color.black);
731           else
732             g.setColor(row.annotations[column].colour);
733
734           if (column == 0 || row.graph > 0)
735           {
736             g.drawString(row.annotations[column].displayCharacter,
737                     (x * av.charWidth) + charOffset, y + iconOffset + 3); // + 3?
738           }
739           else if (row.annotations[column - 1] == null
740                   || (labelAllCols||!row.annotations[column].displayCharacter
741                           .equals(row.annotations[column - 1].displayCharacter) || (row.annotations[column].displayCharacter
742                           .length() < 2 && row.annotations[column].secondaryStructure == ' ')))
743           {
744             g.drawString(row.annotations[column].displayCharacter,
745                     (x * av.charWidth) + charOffset, y + iconOffset + 3); // +3?
746           }
747           g.setFont(ofont);
748         }
749
750         if (row.hasIcons)
751         {
752           if (!validRes
753                   || (row.annotations[column].secondaryStructure != lastSS))
754           {
755             switch (lastSS)
756             {
757             case 'H':
758               g.setColor(HELIX_COLOUR);
759               if (MAC)
760               {
761                 // Off by 1 offset when drawing rects and ovals
762                 // to offscreen image on the MAC
763                 g.fillRoundRect(lastSSX, y + 4 + iconOffset,
764                         (x * av.charWidth) - lastSSX, 7, 8, 8);
765                 break;
766               }
767
768               int sCol = (lastSSX / av.charWidth) + startRes;
769               int x1 = lastSSX;
770               int x2 = (x * av.charWidth);
771
772               if (sCol == 0
773                       || row.annotations[sCol - 1] == null
774                       || row.annotations[sCol - 1].secondaryStructure != 'H')
775               {
776                 g.fillArc(lastSSX, y + 4 + iconOffset, av.charWidth, 8, 90,
777                         180);
778                 x1 += av.charWidth / 2;
779               }
780
781               if (!validRes || row.annotations[column] == null
782                       || row.annotations[column].secondaryStructure != 'H')
783               {
784                 g.fillArc((x * av.charWidth) - av.charWidth, y + 4
785                         + iconOffset, av.charWidth, 8, 270, 180);
786                 x2 -= av.charWidth / 2;
787               }
788
789               g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
790               break;
791
792             case 'E':
793               g.setColor(SHEET_COLOUR);
794               g.fillRect(lastSSX, y + 4 + iconOffset, (x * av.charWidth)
795                       - lastSSX - 4, 7);
796               g.fillPolygon(new int[]
797               { (x * av.charWidth) - 4, (x * av.charWidth) - 4,
798                   (x * av.charWidth) }, new int[]
799               { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset },
800                       3);
801
802               break;
803
804             default:
805               g.setColor(Color.gray);
806               g.fillRect(lastSSX, y + 6 + iconOffset, (x * av.charWidth)
807                       - lastSSX, 2);
808
809               break;
810             }
811
812             if (validRes)
813             {
814               lastSS = row.annotations[column].secondaryStructure;
815             }
816             else
817             {
818               lastSS = ' ';
819             }
820
821             lastSSX = (x * av.charWidth);
822           }
823         }
824
825         column++;
826         x++;
827       }
828
829       if (column >= row.annotations.length)
830       {
831         column = row.annotations.length - 1;
832         validEnd = false;
833       }
834       else
835       {
836         validEnd = true;
837       }
838
839       // x ++;
840
841       if (row.hasIcons)
842       {
843         switch (lastSS)
844         {
845         case 'H':
846           g.setColor(HELIX_COLOUR);
847           if (MAC)
848           {
849             // Off by 1 offset when drawing rects and ovals
850             // to offscreen image on the MAC
851             g.fillRoundRect(lastSSX, y + 4 + iconOffset, (x * av.charWidth)
852                     - lastSSX, 7, 8, 8);
853             break;
854           }
855
856           int sCol = (lastSSX / av.charWidth) + startRes;
857           int x1 = lastSSX;
858           int x2 = (x * av.charWidth);
859
860           if (sCol == 0 || row.annotations[sCol - 1] == null
861                   || row.annotations[sCol - 1].secondaryStructure != 'H')
862           {
863             g
864                     .fillArc(lastSSX, y + 4 + iconOffset, av.charWidth, 8,
865                             90, 180);
866             x1 += av.charWidth / 2;
867           }
868
869           if (row.annotations[column] == null
870                   || row.annotations[column].secondaryStructure != 'H')
871           {
872             g.fillArc((x * av.charWidth) - av.charWidth,
873                     y + 4 + iconOffset, av.charWidth, 8, 270, 180);
874             x2 -= av.charWidth / 2;
875           }
876
877           g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
878
879           break;
880
881         case 'E':
882           g.setColor(SHEET_COLOUR);
883
884           if (!validEnd || row.annotations[endRes] == null
885                   || row.annotations[endRes].secondaryStructure != 'E')
886           {
887             g.fillRect(lastSSX, y + 4 + iconOffset, (x * av.charWidth)
888                     - lastSSX - 4, 7);
889             g.fillPolygon(new int[]
890             { (x * av.charWidth) - 4, (x * av.charWidth) - 4,
891                 (x * av.charWidth) }, new int[]
892             { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset }, 3);
893           }
894           else
895           {
896             g.fillRect(lastSSX, y + 4 + iconOffset, x * av.charWidth
897                     - lastSSX, 7);
898           }
899           break;
900
901         default:
902           g.setColor(Color.gray);
903           if (!av.wrapAlignment || endRes == av.endRes)
904           {
905             g.fillRect(lastSSX, y + 6 + iconOffset, (x * av.charWidth)
906                     - lastSSX, 2);
907           }
908
909           break;
910         }
911       }
912
913       if (row.graph > 0 && row.graphHeight> 0)
914       {
915         if (row.graph == AlignmentAnnotation.LINE_GRAPH)
916         {
917           if (row.graphGroup > -1 && !graphGroupDrawn[row.graphGroup])
918           {
919             float groupmax = -999999, groupmin = 9999999;
920             for (int gg = 0; gg < aa.length; gg++)
921             {
922               if (aa[gg].graphGroup != row.graphGroup)
923               {
924                 continue;
925               }
926
927               if (aa[gg] != row)
928               {
929                 aa[gg].visible = false;
930               }
931
932               if (aa[gg].graphMax > groupmax)
933               {
934                 groupmax = aa[gg].graphMax;
935               }
936               if (aa[gg].graphMin < groupmin)
937               {
938                 groupmin = aa[gg].graphMin;
939               }
940             }
941
942             for (int gg = 0; gg < aa.length; gg++)
943             {
944               if (aa[gg].graphGroup == row.graphGroup)
945               {
946                 drawLineGraph(g, aa[gg], startRes, endRes, y, groupmin,
947                         groupmax, row.graphHeight);
948               }
949             }
950
951             graphGroupDrawn[row.graphGroup] = true;
952           }
953           else
954           {
955             drawLineGraph(g, row, startRes, endRes, y, row.graphMin,
956                     row.graphMax, row.graphHeight);
957           }
958         }
959         else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
960         {
961           drawBarGraph(g, row, startRes, endRes, row.graphMin,
962                   row.graphMax, y);
963         }
964       }
965
966       if (row.graph > 0 && row.hasText)
967       {
968         y += av.charHeight;
969       }
970
971       if (row.graph == 0)
972       {
973         y += aa[i].height;
974       }
975     }
976   }
977
978   public void drawLineGraph(Graphics g, AlignmentAnnotation aa, int sRes,
979           int eRes, int y, float min, float max, int graphHeight)
980   {
981     if (sRes > aa.annotations.length)
982     {
983       return;
984     }
985
986     int x = 0;
987
988     // Adjustment for fastpaint to left
989     if (eRes < av.endRes)
990     {
991       eRes++;
992     }
993
994     eRes = Math.min(eRes, aa.annotations.length);
995
996     int y1 = y, y2 = y;
997     float range = max - min;
998
999     // //Draw origin
1000     if (min < 0)
1001     {
1002       y2 = y - (int) ((0 - min / range) * graphHeight);
1003     }
1004
1005     g.setColor(Color.gray);
1006     g.drawLine(x - av.charWidth, y2, (eRes - sRes) * av.charWidth, y2);
1007
1008     eRes = Math.min(eRes, aa.annotations.length);
1009
1010     int column;
1011     int aaMax = aa.annotations.length - 1;
1012
1013     while (x < eRes - sRes)
1014     {
1015       column = sRes + x;
1016       if (av.hasHiddenColumns)
1017       {
1018         column = av.getColumnSelection().adjustForHiddenColumns(column);
1019       }
1020
1021       if (column > aaMax)
1022       {
1023         break;
1024       }
1025
1026       if (aa.annotations[column] == null) // || coaa.annotations[column - 1] ==
1027                                           // null)
1028       {
1029         x++;
1030         continue;
1031       }
1032
1033       if (aa.annotations[column].colour == null)
1034         g.setColor(Color.black);
1035       else
1036         g.setColor(aa.annotations[column].colour);
1037       if (column == 0 || aa.annotations[column - 1] == null)
1038       {
1039         y1 = y
1040                 - (int) (((aa.annotations[column].value - min) / range) * graphHeight);
1041       }
1042       else
1043       {
1044         y1 = y
1045                 - (int) (((aa.annotations[column - 1].value - min) / range) * graphHeight);
1046       }
1047       y2 = y
1048               - (int) (((aa.annotations[column].value - min) / range) * graphHeight);
1049
1050       g.drawLine(x * av.charWidth - av.charWidth / 2, y1, x * av.charWidth
1051               + av.charWidth / 2, y2);
1052       x++;
1053     }
1054
1055     if (aa.threshold != null)
1056     {
1057       g.setColor(aa.threshold.colour);
1058
1059       y2 = (int) (y - ((aa.threshold.value - min) / range) * graphHeight);
1060       g.drawLine(0, y2, (eRes - sRes) * av.charWidth, y2);
1061     }
1062   }
1063
1064   public void drawBarGraph(Graphics g, AlignmentAnnotation aa, int sRes,
1065           int eRes, float min, float max, int y)
1066   {
1067     ColourSchemeI profcolour = av.getGlobalColourScheme();
1068     if (profcolour==null)
1069     {
1070       profcolour = new jalview.schemes.ZappoColourScheme();
1071     }
1072     if (sRes > aa.annotations.length)
1073     {
1074       return;
1075     }
1076     Font ofont = g.getFont();
1077     eRes = Math.min(eRes, aa.annotations.length);
1078
1079     int x = 0, y1 = y, y2 = y;
1080
1081     float range = max - min;
1082
1083     if (min < 0)
1084     {
1085       y2 = y - (int) ((0 - min / (range)) * aa.graphHeight);
1086     }
1087
1088     g.setColor(Color.gray);
1089
1090     g.drawLine(x, y2, (eRes - sRes) * av.charWidth, y2);
1091
1092     int column;
1093     int aaMax = aa.annotations.length - 1;
1094     boolean renderHistogram = true, renderProfile = false;
1095     /* Logos are disabled for 2.5 release : Bug # 0060064
1096      * if (aa.autoCalculated && aa.label.startsWith("Consensus")) {
1097       // TODO: generalise this to have render styles for consensus/profile data 
1098       if (aa.groupRef!=null)
1099       {
1100         renderHistogram = aa.groupRef.isShowConsensusHistogram();
1101         renderProfile = aa.groupRef.isShowSequenceLogo();
1102       } else {
1103         renderHistogram = av.isShowConsensusHistogram();
1104         renderProfile = av.isShowSequenceLogo();
1105       }
1106     }*/
1107     while (x < eRes - sRes)
1108     {
1109       column = sRes + x;
1110       if (av.hasHiddenColumns)
1111       {
1112         column = av.getColumnSelection().adjustForHiddenColumns(column);
1113       }
1114
1115       if (column > aaMax)
1116       {
1117         break;
1118       }
1119
1120       if (aa.annotations[column] == null)
1121       {
1122         x++;
1123         continue;
1124       }
1125
1126       if (aa.annotations[column].colour == null)
1127         g.setColor(Color.black);
1128       else
1129         g.setColor(aa.annotations[column].colour);
1130
1131       y1 = y
1132               - (int) (((aa.annotations[column].value - min) / (range)) * aa.graphHeight);
1133
1134       if (renderHistogram)
1135       {      
1136       if (y1 - y2 > 0)
1137       {
1138         g.fillRect(x * av.charWidth, y2, av.charWidth, y1 - y2);
1139       }
1140       else
1141       {
1142         g.fillRect(x * av.charWidth, y1, av.charWidth, y2 - y1);
1143       }
1144       }
1145       // draw profile if available
1146       // Disabled for 2.5 release: see bug #0060064
1147       /**
1148       if (aa.annotations[column].value!=0 && renderProfile)
1149       {
1150         int profl[] = getProfileFor(aa,column);
1151         int ht = y1; //,htn=y2-y1;//aa.graphHeight;
1152           float wdth;
1153           double ht2=0;
1154           char[] dc = new char[1];
1155           // LineMetrics lm;
1156           for (int c=1;profl!=null && c<profl[0];)
1157           {
1158             dc[0] = (char) profl[c++];
1159             wdth = av.charWidth;
1160             wdth/=(float) fm
1161                     .charsWidth(dc,0,1);
1162
1163             if (c>2)
1164             {
1165               ht+=(int)ht2;
1166             }
1167             {
1168               // not java 1.1 compatible: Bug # 0060064
1169               g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(wdth, (ht2=(htn*((double)profl[c++])/100.0))/av.charHeight)));
1170               lm = g.getFontMetrics().getLineMetrics(dc,0,1, g);
1171               g.setColor(profcolour.findColour(dc[0]));
1172               g.drawChars(dc,0,1,x*av.charWidth, (int) (ht+lm.getHeight())); 
1173             }
1174           }
1175           g.setFont(ofont);
1176       }
1177       **/
1178       x++;
1179
1180     }
1181     if (aa.threshold != null)
1182     {
1183       g.setColor(aa.threshold.colour);
1184       y2 = (int) (y - ((aa.threshold.value - min) / range) * aa.graphHeight);
1185       g.drawLine(0, y2, (eRes - sRes) * av.charWidth, y2);
1186     }
1187   }
1188
1189   /*
1190    * Disabled for 2.5 release - see bug #0060064
1191   private int[] getProfileFor(AlignmentAnnotation aa, int column)
1192   {
1193 //    if (aa.autoCalculated && aa.label.startsWith("Consensus")) {
1194     if (aa.groupRef!=null && aa.groupRef.consensusData!=null) { // && aa.groupRef.isShowSequenceLogo()) {
1195       return AAFrequency.extractProfile(aa.groupRef.consensusData[column],aa.groupRef.getIgnoreGapsConsensus());
1196     }
1197     // TODO extend annotation row to enable dynamic and static profile data to be stored  
1198     if (aa.groupRef==null && aa.sequenceRef==null) //  && av.isShowSequenceLogo())
1199     {
1200       return AAFrequency.extractProfile(av.hconsensus[column],av.getIgnoreGapsConsensus());
1201     }
1202   //  }
1203     return null;
1204   }
1205    */
1206
1207   // used by overview window
1208   public void drawGraph(Graphics g, AlignmentAnnotation aa, int width,
1209           int y, int sRes, int eRes)
1210   {
1211     eRes = Math.min(eRes, aa.annotations.length);
1212     g.setColor(Color.white);
1213     g.fillRect(0, 0, width, y);
1214     g.setColor(new Color(0, 0, 180));
1215
1216     int x = 0, height;
1217
1218     for (int j = sRes; j < eRes; j++)
1219     {
1220       if (aa.annotations[j].colour == null)
1221         g.setColor(Color.black);
1222       else
1223         g.setColor(aa.annotations[j].colour);
1224
1225       height = (int) ((aa.annotations[j].value / aa.graphMax) * GRAPH_HEIGHT);
1226       if (height > y)
1227       {
1228         height = y;
1229       }
1230       g.fillRect(x, y - height, av.charWidth, height);
1231       x += av.charWidth;
1232     }
1233   }
1234 }