2 * Jalview - A Sequence Alignment Editor and Viewer
3 * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
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.
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.
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
21 import jalview.datamodel.*;
24 import java.awt.event.*;
25 import java.awt.image.*;
36 public class AnnotationPanel extends JPanel implements MouseListener,
37 MouseMotionListener, ActionListener, AdjustmentListener
39 final String HELIX = "Helix";
40 final String SHEET = "Sheet";
41 final String LABEL = "Label";
42 final String REMOVE = "Remove Annotation";
43 final String COLOUR = "Colour";
44 final Color HELIX_COLOUR = Color.red.darker();
45 final Color SHEET_COLOUR = Color.green.darker().darker();
55 boolean fastPaint = false;
57 //Used For mouse Dragging and resizing graphs
58 int graphStretch = -1;
59 int graphStretchY = -1;
60 int min; //used by mouseDragged to see if user
61 int max; //used by mouseDragged to see if user
62 boolean mouseDragging = false;
67 * Creates a new AnnotationPanel object.
69 * @param ap DOCUMENT ME!
71 public AnnotationPanel(AlignmentPanel ap)
74 if(System.getProperty("os.name").startsWith("Mac"))
77 ToolTipManager.sharedInstance().registerComponent(this);
78 ToolTipManager.sharedInstance().setInitialDelay(0);
79 ToolTipManager.sharedInstance().setDismissDelay(10000);
83 addMouseListener(this);
84 addMouseMotionListener(this);
85 ap.annotationScroller.getVerticalScrollBar().addAdjustmentListener(this);
88 public AnnotationPanel(AlignViewport av)
97 * @param evt DOCUMENT ME!
99 public void adjustmentValueChanged(AdjustmentEvent evt)
101 ap.alabels.setScrollOffset(-evt.getValue());
107 public int adjustPanelHeight()
109 // setHeight of panels
111 AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
116 for (int i = 0; i < aa.length; i++)
127 aa[i].height += av.charHeight;
137 aa[i].height += aa[i].graphHeight;
140 if (aa[i].height == 0)
145 height += aa[i].height;
153 this.setPreferredSize(new Dimension(1, height));
163 * @param evt DOCUMENT ME!
165 public void actionPerformed(ActionEvent evt)
167 AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
168 Annotation[] anot = aa[activeRow].annotations;
170 if (evt.getActionCommand().equals(REMOVE))
172 for (int i = 0; i < av.getColumnSelection().size(); i++)
174 anot[av.getColumnSelection().columnAt(i)] = null;
177 else if (evt.getActionCommand().equals(LABEL))
179 String label = JOptionPane.showInputDialog(this, "Enter Label ",
180 "Enter label", JOptionPane.QUESTION_MESSAGE);
187 if ((label.length() > 0) && !aa[activeRow].hasText)
189 aa[activeRow].hasText = true;
192 for (int i = 0; i < av.getColumnSelection().size(); i++)
194 int index = av.getColumnSelection().columnAt(i);
196 if (anot[index] == null)
198 anot[index] = new Annotation(label, "", ' ', 0);
201 anot[index].displayCharacter = label;
204 else if (evt.getActionCommand().equals(COLOUR))
206 Color col = JColorChooser.showDialog(this,
207 "Choose foreground colour", Color.black);
209 for (int i = 0; i < av.getColumnSelection().size(); i++)
211 int index = av.getColumnSelection().columnAt(i);
213 if (anot[index] == null)
215 anot[index] = new Annotation("", "", ' ', 0);
218 anot[index].colour = col;
221 else // HELIX OR SHEET
224 String symbol = "\u03B1";
226 if (evt.getActionCommand().equals(HELIX))
230 else if (evt.getActionCommand().equals(SHEET))
236 if (!aa[activeRow].hasIcons)
238 aa[activeRow].hasIcons = true;
241 String label = JOptionPane.showInputDialog("Enter a label for the structure?",
249 if ((label.length() > 0) && !aa[activeRow].hasText)
251 aa[activeRow].hasText = true;
254 for (int i = 0; i < av.getColumnSelection().size(); i++)
256 int index = av.getColumnSelection().columnAt(i);
258 if (anot[index] == null)
260 anot[index] = new Annotation(label, "", type, 0);
263 anot[index].secondaryStructure = type;
264 anot[index].displayCharacter = label;
277 * @param evt DOCUMENT ME!
279 public void mousePressed(MouseEvent evt)
283 AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
292 for (int i = 0; i < aa.length; i++)
296 height += aa[i].height;
299 if (evt.getY() < height)
305 else if(aa[i].graph>0)
309 graphStretchY = evt.getY();
317 if (SwingUtilities.isRightMouseButton(evt))
319 if (av.getColumnSelection() == null)
324 JPopupMenu pop = new JPopupMenu("Structure type");
325 JMenuItem item = new JMenuItem(HELIX);
326 item.addActionListener(this);
328 item = new JMenuItem(SHEET);
329 item.addActionListener(this);
331 item = new JMenuItem(LABEL);
332 item.addActionListener(this);
334 item = new JMenuItem(COLOUR);
335 item.addActionListener(this);
337 item = new JMenuItem(REMOVE);
338 item.addActionListener(this);
340 pop.show(this, evt.getX(), evt.getY());
350 int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
355 if (av.getColumnSelection().contains(res))
356 av.getColumnSelection().removeElement(res);
359 av.getColumnSelection().addElement(res);
360 SequenceGroup sg = new SequenceGroup();
362 for (int i = 0; i < av.alignment.getSequences().size(); i++)
364 sg.addSequence(av.alignment.getSequenceAt(i), false);
369 av.setSelectionGroup(sg);
379 * @param evt DOCUMENT ME!
381 public void mouseReleased(MouseEvent evt)
385 mouseDragging = false;
391 * @param evt DOCUMENT ME!
393 public void mouseEntered(MouseEvent evt)
396 ap.seqPanel.scrollCanvas(null);
402 * @param evt DOCUMENT ME!
404 public void mouseExited(MouseEvent evt)
407 ap.seqPanel.scrollCanvas(evt);
414 * @param evt DOCUMENT ME!
416 public void mouseDragged(MouseEvent evt)
420 av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight += graphStretchY - evt.getY();
421 if(av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight <0)
422 av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight = 0;
423 graphStretchY = evt.getY();
429 mouseDragging = true;
431 int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
433 SequenceGroup sg = av.getSelectionGroup();
447 if (!av.getColumnSelection().contains(res))
449 av.getColumnSelection().addElement(res);
452 if (res > sg.getStartRes())
456 else if (res < sg.getStartRes())
461 for (int i = min; i <= max; i++)
463 if ((i < sg.getStartRes()) || (i > sg.getEndRes()))
465 av.getColumnSelection().removeElement(i);
469 av.getColumnSelection().addElement(i);
482 * @param evt DOCUMENT ME!
484 public void mouseMoved(MouseEvent evt)
486 AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
496 for (int i = 0; i < aa.length; i++)
500 height += aa[i].height;
503 if (evt.getY() < height)
511 int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
513 if(av.hasHiddenColumns)
514 res = av.getColumnSelection().adjustForHiddenColumns(res);
516 if (row > -1 && res<aa[row].annotations.length)
518 if(aa[row].graphGroup>-1)
520 StringBuffer tip = new StringBuffer("<html>");
521 for (int gg = 0; gg < aa.length; gg++)
523 if (aa[gg].graphGroup == aa[row].graphGroup && aa[gg].annotations[res]!=null)
524 tip.append(aa[gg].label+" "+aa[gg].annotations[res].description+"<br>" );
528 tip.setLength(tip.length() - 4);
529 this.setToolTipText(tip.toString() + "</html>");
532 else if(aa[row].annotations[res] != null)
533 this.setToolTipText(aa[row].annotations[res].description);
535 if(aa[row].annotations[res]!=null)
537 StringBuffer text = new StringBuffer("Sequence position " +
539 aa[row].annotations[res].description);
541 ap.alignFrame.statusBar.setText(text.toString());
549 * @param evt DOCUMENT ME!
551 public void mouseClicked(MouseEvent evt)
558 * @param g DOCUMENT ME!
560 public void paintComponent(Graphics g)
562 g.setColor(Color.white);
563 g.fillRect(0, 0, getWidth(), getHeight());
567 || (getVisibleRect().width != g.getClipBounds().width)
568 || (getVisibleRect().height != g.getClipBounds().height))
570 g.drawImage(image, 0, 0, this);
575 imgWidth = (av.endRes - av.startRes + 1) * av.charWidth;
577 if (image == null || imgWidth != image.getWidth()
578 || image.getHeight(this) != getHeight())
580 image = new BufferedImage(imgWidth, ap.annotationPanel.getHeight(),
581 BufferedImage.TYPE_INT_RGB);
582 gg = (Graphics2D) image.getGraphics();
585 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
586 RenderingHints.VALUE_ANTIALIAS_ON);
588 gg.setFont(av.getFont());
589 fm = gg.getFontMetrics();
593 drawComponent(gg, av.startRes, av.endRes + 1);
594 g.drawImage(image, 0, 0, this);
600 * @param horizontal DOCUMENT ME!
602 public void fastPaint(int horizontal)
604 if ((horizontal == 0) || gg==null ||
605 (av.alignment.getAlignmentAnnotation() == null) ||
606 (av.alignment.getAlignmentAnnotation().length < 1))
612 gg.copyArea(0, 0, imgWidth, getHeight(), -horizontal * av.charWidth, 0);
614 int sr = av.startRes;
615 int er = av.endRes + 1;
618 if (horizontal > 0) // scrollbar pulled right, image to the left
620 transX = (er - sr - horizontal) * av.charWidth;
621 sr = er - horizontal;
623 else if (horizontal < 0)
625 er = sr - horizontal;
628 gg.translate(transX, 0);
630 drawComponent(gg, sr, er);
632 gg.translate(-transX, 0);
643 * @param g DOCUMENT ME!
644 * @param startRes DOCUMENT ME!
645 * @param endRes DOCUMENT ME!
647 public void drawComponent(Graphics g, int startRes, int endRes)
649 g.setFont(av.getFont());
652 fm = g.getFontMetrics();
655 g.setColor(Color.white);
656 g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getHeight());
658 if ( (av.alignment.getAlignmentAnnotation() == null) ||
659 (av.alignment.getAlignmentAnnotation().length < 1))
661 g.setColor(Color.white);
662 g.fillRect(0, 0, getWidth(), getHeight());
663 g.setColor(Color.black);
664 if(av.validCharWidth)
665 g.drawString("Alignment has no annotations", 20, 15);
670 AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
676 int iconOffset = av.charHeight / 2;
677 boolean validRes = false;
679 boolean [] graphGroupDrawn = new boolean[aa.length];
683 for (int i = 0; i < aa.length; i++)
685 AlignmentAnnotation row = aa[i];
697 if(row.graphGroup>-1 && graphGroupDrawn[ row.graphGroup ] )
700 // this is so that we draw the characters below the graph
711 iconOffset = av.charHeight / 2 + 4;
719 while (x < endRes-startRes)
721 if (av.hasHiddenColumns)
723 column = av.getColumnSelection().adjustForHiddenColumns(startRes+x);
724 if (column > row.annotations.length-1)
733 if ((row.annotations.length <= column) ||
734 (row.annotations[column] == null))
746 g.setColor(Color.red);
748 if (av.getColumnSelection() != null)
750 for (int n = 0; n < av.getColumnSelection().size(); n++)
752 int v = av.getColumnSelection().columnAt(n);
756 g.fillRect(x * av.charWidth, y,
757 av.charWidth, av.charHeight);
763 if (av.validCharWidth && validRes &&
764 (row.annotations[column].displayCharacter.length() > 0))
767 int charOffset = (av.charWidth -
768 fm.charWidth(row.annotations[column].displayCharacter.charAt(
770 g.setColor(row.annotations[column].colour);
772 if (column == 0 || row.graph>0)
774 g.drawString(row.annotations[column].displayCharacter,
775 (x*av.charWidth)+charOffset,
779 row.annotations[column - 1] == null
780 ||(!row.annotations[column].displayCharacter.equals(
781 row.annotations[column - 1].displayCharacter)
783 (row.annotations[column].displayCharacter.length() <2 &&
784 row.annotations[column].secondaryStructure==' ')))
786 g.drawString(row.annotations[column].displayCharacter,
787 x*av.charWidth+charOffset,
795 (row.annotations[column].secondaryStructure != lastSS))
800 g.setColor(HELIX_COLOUR);
803 //Off by 1 offset when drawing rects and ovals
804 //to offscreen image on the MAC
805 g.fillRoundRect(lastSSX, y + 4 + iconOffset,
806 (x*av.charWidth) - lastSSX, 7, 8, 8);
810 int sCol = (lastSSX / av.charWidth) + startRes;
812 int x2 = (x*av.charWidth);
815 row.annotations[sCol-1]==null ||
816 row.annotations[sCol-1].secondaryStructure!='H')
818 g.fillArc(lastSSX, y+4+iconOffset, av.charWidth, 8, 90,180) ;
819 x1 += av.charWidth/2;
822 if(row.annotations[column]==null ||
823 row.annotations[column].secondaryStructure!='H')
825 g.fillArc((x*av.charWidth)-av.charWidth,
826 y+4+iconOffset, av.charWidth, 8, 270,180);
827 x2 -= av.charWidth/2;
830 g.fillRect(x1, y+4+iconOffset, x2-x1, 8);
834 g.setColor(SHEET_COLOUR);
835 g.fillRect(lastSSX, y + 4 + iconOffset,
836 (x*av.charWidth) - lastSSX - 4, 7);
837 g.fillPolygon(new int[] { (x*av.charWidth) - 4,
838 (x*av.charWidth) - 4,
842 y + iconOffset, y + 14 + iconOffset,
850 g.setColor(Color.gray);
851 g.fillRect(lastSSX, y + 6 + iconOffset,
852 (x*av.charWidth) - lastSSX, 2);
859 lastSS = row.annotations[column].secondaryStructure;
866 lastSSX = (x*av.charWidth);
875 if(column>=row.annotations.length)
876 column = row.annotations.length-1;
885 g.setColor(HELIX_COLOUR);
888 //Off by 1 offset when drawing rects and ovals
889 //to offscreen image on the MAC
890 g.fillRoundRect(lastSSX, y + 4 + iconOffset,
891 (x*av.charWidth) - lastSSX, 7, 8, 8);
895 int sCol = (lastSSX / av.charWidth) + startRes;
897 int x2 = (x*av.charWidth);
900 row.annotations[sCol - 1] == null ||
901 row.annotations[sCol - 1].secondaryStructure != 'H')
903 g.fillArc(lastSSX, y + 4 + iconOffset, av.charWidth, 8, 90, 180);
904 x1 += av.charWidth / 2;
907 if (row.annotations[column] == null ||
908 row.annotations[column].secondaryStructure != 'H')
910 g.fillArc((x*av.charWidth) - av.charWidth,
911 y + 4 + iconOffset, av.charWidth, 8, 270,
913 x2 -= av.charWidth / 2;
916 g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
921 g.setColor(SHEET_COLOUR);
923 if (row.annotations[endRes] == null
924 || row.annotations[endRes].secondaryStructure != 'E')
926 g.fillRect(lastSSX, y + 4 + iconOffset,
927 (x*av.charWidth) - lastSSX - 4, 7);
928 g.fillPolygon(new int[]
929 {(x*av.charWidth) - 4,
930 (x*av.charWidth) - 4,
934 y + iconOffset, y + 14 + iconOffset,
940 g.fillRect(lastSSX, y + 4 + iconOffset,
941 (x+1) * av.charWidth - lastSSX, 7);
946 g.setColor(Color.gray);
947 if(!av.wrapAlignment || endRes==av.endRes)
948 g.fillRect(lastSSX, y + 6 + iconOffset,
949 (x*av.charWidth) - lastSSX, 2);
955 if (row.graph>0 && row.graphHeight>0)
957 if(row.graph == AlignmentAnnotation.LINE_GRAPH )
959 if(row.graphGroup>-1 && !graphGroupDrawn[row.graphGroup])
961 float groupmax=-999999, groupmin=9999999;
962 for(int gg=0; gg<aa.length; gg++)
964 if(aa[gg].graphGroup!=row.graphGroup)
968 aa[gg].visible = false;
970 if(aa[gg].graphMax>groupmax)
971 groupmax = aa[gg].graphMax;
972 if(aa[gg].graphMin<groupmin)
973 groupmin = aa[gg].graphMin;
976 for (int gg = 0; gg < aa.length; gg++)
978 if (aa[gg].graphGroup == row.graphGroup)
980 drawLineGraph(g, aa[gg], startRes, endRes, y,
986 graphGroupDrawn[ row.graphGroup ] = true;
989 drawLineGraph(g, row, startRes, endRes,
990 y, row.graphMin, row.graphMax, row.graphHeight );
992 else if(row.graph == AlignmentAnnotation.BAR_GRAPH )
993 drawBarGraph(g, row, startRes, endRes,
994 row.graphMin, row.graphMax, y);
997 if (row.graph>0 && row.hasText)
1009 public void drawLineGraph(Graphics g, AlignmentAnnotation aa,
1012 float min, float max,
1015 if(sRes>aa.annotations.length)
1021 //Adjustment for fastpaint to left
1025 eRes = Math.min(eRes, aa.annotations.length);
1033 float range = max - min;
1037 y2 = y - (int)((0-min / range)*graphHeight);
1039 g.setColor(Color.gray);
1040 g.drawLine(x-av.charWidth,y2,(eRes-sRes+1)*av.charWidth,y2);
1042 eRes = Math.min(eRes, aa.annotations.length);
1045 int aaMax = aa.annotations.length-1;
1047 while( x < eRes - sRes )
1050 if(av.hasHiddenColumns)
1052 column = av.getColumnSelection().adjustForHiddenColumns(column);
1060 if(aa.annotations[column]==null || aa.annotations[column-1]==null)
1067 g.setColor(aa.annotations[column].colour);
1068 y1 = y - (int) (((aa.annotations[column-1].value-min) / range) * graphHeight);
1069 y2 = y - (int) (((aa.annotations[column].value-min) / range) * graphHeight);
1071 g.drawLine(x*av.charWidth-av.charWidth/2, y1, x*av.charWidth+av.charWidth/2, y2);
1075 if(aa.threshold!=null)
1077 g.setColor(aa.threshold.colour);
1078 Graphics2D g2 = (Graphics2D)g;
1079 g2.setStroke(new BasicStroke(1,
1080 BasicStroke.CAP_SQUARE,
1081 BasicStroke.JOIN_ROUND, 3f,
1082 new float[] { 5f, 3f }, 0f));
1084 y2 = (int)(y - ((aa.threshold.value-min) / range)*graphHeight);
1085 g.drawLine(0,y2,(eRes-sRes)*av.charWidth,y2);
1086 g2.setStroke(new BasicStroke());
1090 public void drawBarGraph(Graphics g, AlignmentAnnotation aa,
1092 float min, float max,
1095 if(sRes>aa.annotations.length)
1098 eRes = Math.min(eRes, aa.annotations.length);
1100 int x=0, y1=y, y2=y;
1102 float range = max - min;
1105 y2 = y - (int)((0-min / (range))*aa.graphHeight);
1107 g.setColor(Color.gray);
1109 g.drawLine(x,y2,(eRes-sRes)*av.charWidth,y2);
1112 int aaMax = aa.annotations.length-1;
1114 while( x < eRes-sRes )
1117 if(av.hasHiddenColumns)
1119 column = av.getColumnSelection().adjustForHiddenColumns(column);
1127 if (aa.annotations[column] == null)
1133 g.setColor(aa.annotations[column].colour);
1134 y1 = y - (int) (((aa.annotations[column].value-min) / (range)) * aa.graphHeight);
1137 g.fillRect(x*av.charWidth, y2, av.charWidth, y1-y2 );
1139 g.fillRect(x*av.charWidth, y1, av.charWidth, y2-y1 );
1144 if(aa.threshold!=null)
1146 g.setColor(aa.threshold.colour);
1147 Graphics2D g2 = (Graphics2D)g;
1148 g2.setStroke(new BasicStroke(1,
1149 BasicStroke.CAP_SQUARE,
1150 BasicStroke.JOIN_ROUND, 3f,
1151 new float[] { 5f, 3f }, 0f));
1153 y2 = (int)(y - ((aa.threshold.value-min) / range)*aa.graphHeight);
1154 g.drawLine(0,y2,(eRes-sRes)*av.charWidth,y2);
1155 g2.setStroke(new BasicStroke());
1159 // used by overview window
1160 public void drawGraph(Graphics g, AlignmentAnnotation aa, int width, int y, int sRes, int eRes)
1162 eRes = Math.min(eRes, aa.annotations.length);
1163 g.setColor(Color.white);
1164 g.fillRect(0, 0, width, y);
1165 g.setColor(new Color(0, 0, 180));
1169 for (int j = sRes; j < eRes; j++)
1171 g.setColor(aa.annotations[j].colour);
1173 height = (int) ((aa.annotations[j].value / aa.graphMax) * y);
1177 g.fillRect(x, y - height, av.charWidth, height);