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