allow middle-button drag to change height of annotation rows in applet
[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
26 import jalview.datamodel.*;
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     g.setFont(av.getFont());
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[] graphGroupDrawn = new boolean[aa.length];
604
605     // \u03B2 \u03B1
606     for (int i = 0; i < aa.length; i++)
607     {
608       AlignmentAnnotation row = aa[i];
609
610       if (!row.visible)
611       {
612         continue;
613       }
614
615       lastSS = ' ';
616       lastSSX = 0;
617
618       if (row.graph > 0)
619       {
620         if (row.graphGroup > -1 && graphGroupDrawn[row.graphGroup])
621         {
622           continue;
623         }
624
625         // this is so that we draw the characters below the graph
626         y += row.height;
627
628         if (row.hasText)
629         {
630           y -= av.charHeight;
631         }
632       }
633
634       if (row.hasText)
635       {
636         iconOffset = av.charHeight / 2;
637       }
638       else
639       {
640         iconOffset = 0;
641       }
642
643       x = 0;
644       while (x < endRes - startRes)
645       {
646         if (av.hasHiddenColumns)
647         {
648           column = av.getColumnSelection().adjustForHiddenColumns(
649                   startRes + x);
650           if (column > row.annotations.length - 1)
651           {
652             break;
653           }
654         }
655         else
656         {
657           column = startRes + x;
658         }
659
660         if ((row.annotations.length <= column)
661                 || (row.annotations[column] == null))
662         {
663           validRes = false;
664         }
665         else
666         {
667           validRes = true;
668         }
669
670         if (activeRow == i)
671         {
672           g.setColor(Color.red);
673
674           if (av.getColumnSelection() != null)
675           {
676             for (int n = 0; n < av.getColumnSelection().size(); n++)
677             {
678               int v = av.getColumnSelection().columnAt(n);
679
680               if (v == column)
681               {
682                 g
683                         .fillRect(x * av.charWidth, y, av.charWidth,
684                                 av.charHeight);
685               }
686             }
687           }
688         }
689
690         if (av.validCharWidth
691                 && validRes
692                 && (row.annotations[column].displayCharacter != null && row.annotations[column].displayCharacter
693                         .length() > 0))
694         {
695           int charOffset = (av.getCentreColumnLabels()) ? ((av.charWidth - fm
696                   .charsWidth(row.annotations[column].displayCharacter
697                           .toCharArray(), 0,
698                           row.annotations[column].displayCharacter.length())) / 2)
699                   : (av.charWidth - fm
700                           .charWidth(row.annotations[column].displayCharacter
701                                   .charAt(0))) / 2;
702
703           if (row.annotations[column].colour == null)
704             g.setColor(Color.black);
705           else
706             g.setColor(row.annotations[column].colour);
707
708           if (column == 0 || row.graph > 0)
709           {
710             g.drawString(row.annotations[column].displayCharacter,
711                     (x * av.charWidth) + charOffset, y + iconOffset + 3);
712           }
713           else if (row.annotations[column - 1] == null
714                   || (!row.annotations[column].displayCharacter
715                           .equals(row.annotations[column - 1].displayCharacter) || (row.annotations[column].displayCharacter
716                           .length() < 2 && row.annotations[column].secondaryStructure == ' ')))
717           {
718             g.drawString(row.annotations[column].displayCharacter,
719                     (x * av.charWidth) + charOffset, y + iconOffset + 3);
720           }
721         }
722
723         if (row.hasIcons)
724         {
725           if (!validRes
726                   || (row.annotations[column].secondaryStructure != lastSS))
727           {
728             switch (lastSS)
729             {
730             case 'H':
731               g.setColor(HELIX_COLOUR);
732               if (MAC)
733               {
734                 // Off by 1 offset when drawing rects and ovals
735                 // to offscreen image on the MAC
736                 g.fillRoundRect(lastSSX, y + 4 + iconOffset,
737                         (x * av.charWidth) - lastSSX, 7, 8, 8);
738                 break;
739               }
740
741               int sCol = (lastSSX / av.charWidth) + startRes;
742               int x1 = lastSSX;
743               int x2 = (x * av.charWidth);
744
745               if (sCol == 0
746                       || row.annotations[sCol - 1] == null
747                       || row.annotations[sCol - 1].secondaryStructure != 'H')
748               {
749                 g.fillArc(lastSSX, y + 4 + iconOffset, av.charWidth, 8, 90,
750                         180);
751                 x1 += av.charWidth / 2;
752               }
753
754               if (row.annotations[column] == null
755                       || row.annotations[column].secondaryStructure != 'H')
756               {
757                 g.fillArc((x * av.charWidth) - av.charWidth, y + 4
758                         + iconOffset, av.charWidth, 8, 270, 180);
759                 x2 -= av.charWidth / 2;
760               }
761
762               g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
763               break;
764
765             case 'E':
766               g.setColor(SHEET_COLOUR);
767               g.fillRect(lastSSX, y + 4 + iconOffset, (x * av.charWidth)
768                       - lastSSX - 4, 7);
769               g.fillPolygon(new int[]
770               { (x * av.charWidth) - 4, (x * av.charWidth) - 4,
771                   (x * av.charWidth) }, new int[]
772               { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset },
773                       3);
774
775               break;
776
777             default:
778               g.setColor(Color.gray);
779               g.fillRect(lastSSX, y + 6 + iconOffset, (x * av.charWidth)
780                       - lastSSX, 2);
781
782               break;
783             }
784
785             if (validRes)
786             {
787               lastSS = row.annotations[column].secondaryStructure;
788             }
789             else
790             {
791               lastSS = ' ';
792             }
793
794             lastSSX = (x * av.charWidth);
795           }
796         }
797
798         column++;
799         x++;
800       }
801
802       if (column >= row.annotations.length)
803       {
804         column = row.annotations.length - 1;
805         validEnd = false;
806       }
807       else
808       {
809         validEnd = true;
810       }
811
812       // x ++;
813
814       if (row.hasIcons)
815       {
816         switch (lastSS)
817         {
818         case 'H':
819           g.setColor(HELIX_COLOUR);
820           if (MAC)
821           {
822             // Off by 1 offset when drawing rects and ovals
823             // to offscreen image on the MAC
824             g.fillRoundRect(lastSSX, y + 4 + iconOffset, (x * av.charWidth)
825                     - lastSSX, 7, 8, 8);
826             break;
827           }
828
829           int sCol = (lastSSX / av.charWidth) + startRes;
830           int x1 = lastSSX;
831           int x2 = (x * av.charWidth);
832
833           if (sCol == 0 || row.annotations[sCol - 1] == null
834                   || row.annotations[sCol - 1].secondaryStructure != 'H')
835           {
836             g
837                     .fillArc(lastSSX, y + 4 + iconOffset, av.charWidth, 8,
838                             90, 180);
839             x1 += av.charWidth / 2;
840           }
841
842           if (row.annotations[column] == null
843                   || row.annotations[column].secondaryStructure != 'H')
844           {
845             g.fillArc((x * av.charWidth) - av.charWidth,
846                     y + 4 + iconOffset, av.charWidth, 8, 270, 180);
847             x2 -= av.charWidth / 2;
848           }
849
850           g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
851
852           break;
853
854         case 'E':
855           g.setColor(SHEET_COLOUR);
856
857           if (!validEnd || row.annotations[endRes] == null
858                   || row.annotations[endRes].secondaryStructure != 'E')
859           {
860             g.fillRect(lastSSX, y + 4 + iconOffset, (x * av.charWidth)
861                     - lastSSX - 4, 7);
862             g.fillPolygon(new int[]
863             { (x * av.charWidth) - 4, (x * av.charWidth) - 4,
864                 (x * av.charWidth) }, new int[]
865             { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset }, 3);
866           }
867           else
868           {
869             g.fillRect(lastSSX, y + 4 + iconOffset, x * av.charWidth
870                     - lastSSX, 7);
871           }
872           break;
873
874         default:
875           g.setColor(Color.gray);
876           if (!av.wrapAlignment || endRes == av.endRes)
877           {
878             g.fillRect(lastSSX, y + 6 + iconOffset, (x * av.charWidth)
879                     - lastSSX, 2);
880           }
881
882           break;
883         }
884       }
885
886       if (row.graph > 0)
887       {
888         if (row.graph == AlignmentAnnotation.LINE_GRAPH)
889         {
890           if (row.graphGroup > -1 && !graphGroupDrawn[row.graphGroup])
891           {
892             float groupmax = -999999, groupmin = 9999999;
893             for (int gg = 0; gg < aa.length; gg++)
894             {
895               if (aa[gg].graphGroup != row.graphGroup)
896               {
897                 continue;
898               }
899
900               if (aa[gg] != row)
901               {
902                 aa[gg].visible = false;
903               }
904
905               if (aa[gg].graphMax > groupmax)
906               {
907                 groupmax = aa[gg].graphMax;
908               }
909               if (aa[gg].graphMin < groupmin)
910               {
911                 groupmin = aa[gg].graphMin;
912               }
913             }
914
915             for (int gg = 0; gg < aa.length; gg++)
916             {
917               if (aa[gg].graphGroup == row.graphGroup)
918               {
919                 drawLineGraph(g, aa[gg], startRes, endRes, y, groupmin,
920                         groupmax, row.graphHeight);
921               }
922             }
923
924             graphGroupDrawn[row.graphGroup] = true;
925           }
926           else
927           {
928             drawLineGraph(g, row, startRes, endRes, y, row.graphMin,
929                     row.graphMax, row.graphHeight);
930           }
931         }
932         else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
933         {
934           drawBarGraph(g, row, startRes, endRes, row.graphMin,
935                   row.graphMax, y);
936         }
937       }
938
939       if (row.graph > 0 && row.hasText)
940       {
941         y += av.charHeight;
942       }
943
944       if (row.graph == 0)
945       {
946         y += aa[i].height;
947       }
948     }
949   }
950
951   public void drawLineGraph(Graphics g, AlignmentAnnotation aa, int sRes,
952           int eRes, int y, float min, float max, int graphHeight)
953   {
954     if (sRes > aa.annotations.length)
955     {
956       return;
957     }
958
959     int x = 0;
960
961     // Adjustment for fastpaint to left
962     if (eRes < av.endRes)
963     {
964       eRes++;
965     }
966
967     eRes = Math.min(eRes, aa.annotations.length);
968
969     int y1 = y, y2 = y;
970     float range = max - min;
971
972     // //Draw origin
973     if (min < 0)
974     {
975       y2 = y - (int) ((0 - min / range) * graphHeight);
976     }
977
978     g.setColor(Color.gray);
979     g.drawLine(x - av.charWidth, y2, (eRes - sRes) * av.charWidth, y2);
980
981     eRes = Math.min(eRes, aa.annotations.length);
982
983     int column;
984     int aaMax = aa.annotations.length - 1;
985
986     while (x < eRes - sRes)
987     {
988       column = sRes + x;
989       if (av.hasHiddenColumns)
990       {
991         column = av.getColumnSelection().adjustForHiddenColumns(column);
992       }
993
994       if (column > aaMax)
995       {
996         break;
997       }
998
999       if (aa.annotations[column] == null) // || coaa.annotations[column - 1] ==
1000                                           // null)
1001       {
1002         x++;
1003         continue;
1004       }
1005
1006       if (aa.annotations[column].colour == null)
1007         g.setColor(Color.black);
1008       else
1009         g.setColor(aa.annotations[column].colour);
1010       if (column == 0 || aa.annotations[column - 1] == null)
1011       {
1012         y1 = y
1013                 - (int) (((aa.annotations[column].value - min) / range) * graphHeight);
1014       }
1015       else
1016       {
1017         y1 = y
1018                 - (int) (((aa.annotations[column - 1].value - min) / range) * graphHeight);
1019       }
1020       y2 = y
1021               - (int) (((aa.annotations[column].value - min) / range) * graphHeight);
1022
1023       g.drawLine(x * av.charWidth - av.charWidth / 2, y1, x * av.charWidth
1024               + av.charWidth / 2, y2);
1025       x++;
1026     }
1027
1028     if (aa.threshold != null)
1029     {
1030       g.setColor(aa.threshold.colour);
1031
1032       y2 = (int) (y - ((aa.threshold.value - min) / range) * graphHeight);
1033       g.drawLine(0, y2, (eRes - sRes) * av.charWidth, y2);
1034     }
1035   }
1036
1037   public void drawBarGraph(Graphics g, AlignmentAnnotation aa, int sRes,
1038           int eRes, float min, float max, int y)
1039   {
1040     if (sRes > aa.annotations.length)
1041     {
1042       return;
1043     }
1044
1045     eRes = Math.min(eRes, aa.annotations.length);
1046
1047     int x = 0, y1 = y, y2 = y;
1048
1049     float range = max - min;
1050
1051     if (min < 0)
1052     {
1053       y2 = y - (int) ((0 - min / (range)) * aa.graphHeight);
1054     }
1055
1056     g.setColor(Color.gray);
1057
1058     g.drawLine(x, y2, (eRes - sRes) * av.charWidth, y2);
1059
1060     int column;
1061     int aaMax = aa.annotations.length - 1;
1062
1063     while (x < eRes - sRes)
1064     {
1065       column = sRes + x;
1066       if (av.hasHiddenColumns)
1067       {
1068         column = av.getColumnSelection().adjustForHiddenColumns(column);
1069       }
1070
1071       if (column > aaMax)
1072       {
1073         break;
1074       }
1075
1076       if (aa.annotations[column] == null)
1077       {
1078         x++;
1079         continue;
1080       }
1081
1082       if (aa.annotations[column].colour == null)
1083         g.setColor(Color.black);
1084       else
1085         g.setColor(aa.annotations[column].colour);
1086
1087       y1 = y
1088               - (int) (((aa.annotations[column].value - min) / (range)) * aa.graphHeight);
1089
1090       if (y1 - y2 > 0)
1091       {
1092         g.fillRect(x * av.charWidth, y2, av.charWidth, y1 - y2);
1093       }
1094       else
1095       {
1096         g.fillRect(x * av.charWidth, y1, av.charWidth, y2 - y1);
1097       }
1098
1099       x++;
1100
1101     }
1102     if (aa.threshold != null)
1103     {
1104       g.setColor(aa.threshold.colour);
1105       y2 = (int) (y - ((aa.threshold.value - min) / range) * aa.graphHeight);
1106       g.drawLine(0, y2, (eRes - sRes) * av.charWidth, y2);
1107     }
1108   }
1109
1110   // used by overview window
1111   public void drawGraph(Graphics g, AlignmentAnnotation aa, int width,
1112           int y, int sRes, int eRes)
1113   {
1114     eRes = Math.min(eRes, aa.annotations.length);
1115     g.setColor(Color.white);
1116     g.fillRect(0, 0, width, y);
1117     g.setColor(new Color(0, 0, 180));
1118
1119     int x = 0, height;
1120
1121     for (int j = sRes; j < eRes; j++)
1122     {
1123       if (aa.annotations[j].colour == null)
1124         g.setColor(Color.black);
1125       else
1126         g.setColor(aa.annotations[j].colour);
1127
1128       height = (int) ((aa.annotations[j].value / aa.graphMax) * GRAPH_HEIGHT);
1129       if (height > y)
1130       {
1131         height = y;
1132       }
1133       g.fillRect(x, y - height, av.charWidth, height);
1134       x += av.charWidth;
1135     }
1136   }
1137 }