/* * Jalview - A Sequence Alignment Editor and Viewer * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package jalview.gui; import jalview.datamodel.*; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import javax.swing.*; /** * DOCUMENT ME! * * @author $author$ * @version $Revision$ */ public class AnnotationPanel extends JPanel implements MouseListener, MouseMotionListener, ActionListener, AdjustmentListener { final String HELIX = "Helix"; final String SHEET = "Sheet"; final String LABEL = "Label"; final String REMOVE = "Remove Annotation"; final String COLOUR = "Colour"; final Color HELIX_COLOUR = Color.red.darker(); final Color SHEET_COLOUR = Color.green.darker().darker(); /** DOCUMENT ME!! */ AlignViewport av; AlignmentPanel ap; int activeRow = -1; BufferedImage image; Graphics2D gg; FontMetrics fm; int imgWidth = 0; boolean fastPaint = false; //Used For mouse Dragging and resizing graphs int graphStretch = -1; int graphStretchY = -1; /** * Creates a new AnnotationPanel object. * * @param ap DOCUMENT ME! */ public AnnotationPanel(AlignmentPanel ap) { ToolTipManager.sharedInstance().registerComponent(this); ToolTipManager.sharedInstance().setInitialDelay(0); ToolTipManager.sharedInstance().setDismissDelay(10000); this.ap = ap; av = ap.av; this.setLayout(null); addMouseListener(this); addMouseMotionListener(this); ap.annotationScroller.getVerticalScrollBar().addAdjustmentListener(this); } public AnnotationPanel(AlignViewport av) { this.av = av; } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void adjustmentValueChanged(AdjustmentEvent evt) { ap.alabels.setScrollOffset(-evt.getValue()); } /** * DOCUMENT ME! */ public int adjustPanelHeight() { // setHeight of panels image = null; AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation(); int height = 0; if (aa != null) { for (int i = 0; i < aa.length; i++) { if (!aa[i].visible) { continue; } aa[i].height = 0; if (aa[i].hasText) { aa[i].height += av.charHeight; } if (aa[i].hasIcons) { aa[i].height += 16; } if (aa[i].graph>0) { aa[i].height += aa[i].graphHeight; } if (aa[i].height == 0) { aa[i].height = 20; } height += aa[i].height; } } else { height = 20; } this.setPreferredSize(new Dimension(1, height)); return height; } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void actionPerformed(ActionEvent evt) { AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation(); Annotation[] anot = aa[activeRow].annotations; if (evt.getActionCommand().equals(REMOVE)) { for (int i = 0; i < av.getColumnSelection().size(); i++) { anot[av.getColumnSelection().columnAt(i)] = null; } } else if (evt.getActionCommand().equals(LABEL)) { String label = JOptionPane.showInputDialog(this, "Enter Label ", "Enter label", JOptionPane.QUESTION_MESSAGE); if (label == null) { return; } if ((label.length() > 0) && !aa[activeRow].hasText) { aa[activeRow].hasText = true; } for (int i = 0; i < av.getColumnSelection().size(); i++) { int index = av.getColumnSelection().columnAt(i); if (anot[index] == null) { anot[index] = new Annotation(label, "", ' ', 0); } anot[index].displayCharacter = label; } } else if (evt.getActionCommand().equals(COLOUR)) { Color col = JColorChooser.showDialog(this, "Choose foreground colour", Color.black); for (int i = 0; i < av.getColumnSelection().size(); i++) { int index = av.getColumnSelection().columnAt(i); if (anot[index] == null) { anot[index] = new Annotation("", "", ' ', 0); } anot[index].colour = col; } } else // HELIX OR SHEET { char type = 0; String symbol = "\u03B1"; if (evt.getActionCommand().equals(HELIX)) { type = 'H'; } else if (evt.getActionCommand().equals(SHEET)) { type = 'E'; symbol = "\u03B2"; } if (!aa[activeRow].hasIcons) { aa[activeRow].hasIcons = true; } String label = JOptionPane.showInputDialog("Enter a label for the structure?", symbol); if (label == null) { return; } if ((label.length() > 0) && !aa[activeRow].hasText) { aa[activeRow].hasText = true; } for (int i = 0; i < av.getColumnSelection().size(); i++) { int index = av.getColumnSelection().columnAt(i); if (anot[index] == null) { anot[index] = new Annotation(label, "", type, 0); } anot[index].secondaryStructure = type; anot[index].displayCharacter = label; } } adjustPanelHeight(); repaint(); return; } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void mousePressed(MouseEvent evt) { int height = 0; activeRow = -1; AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation(); if(aa==null) return; for (int i = 0; i < aa.length; i++) { if (aa[i].visible) { height += aa[i].height; } if (evt.getY() < height) { if (aa[i].editable) { activeRow = i; } else if(aa[i].graph>0) { //Stretch Graph graphStretch = i; graphStretchY = evt.getY(); } break; } } if (SwingUtilities.isRightMouseButton(evt)) { if (av.getColumnSelection() == null) { return; } JPopupMenu pop = new JPopupMenu("Structure type"); JMenuItem item = new JMenuItem(HELIX); item.addActionListener(this); pop.add(item); item = new JMenuItem(SHEET); item.addActionListener(this); pop.add(item); item = new JMenuItem(LABEL); item.addActionListener(this); pop.add(item); item = new JMenuItem(COLOUR); item.addActionListener(this); pop.add(item); item = new JMenuItem(REMOVE); item.addActionListener(this); pop.add(item); pop.show(this, evt.getX(), evt.getY()); return; } if (aa == null) { return; } int res = (evt.getX() / av.getCharWidth()) + av.getStartRes(); if (evt.isShiftDown()) { /*int start = Integer.parseInt(activeRes.get(activeRes.size() - 1).toString()); int end = res; if (end < start) { int temp = end; end = start; start = temp; } for (int n = start; n <= end; n++) { addEditableColumn(n); } */ } else { if (av.getColumnSelection().contains(res)) av.getColumnSelection().removeElement(res); else av.getColumnSelection().addElement(res); ap.repaint(); } } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void mouseReleased(MouseEvent evt) { graphStretch = -1; graphStretchY = -1; } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void mouseEntered(MouseEvent evt) { } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void mouseExited(MouseEvent evt) { } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void mouseDragged(MouseEvent evt) { if(graphStretch>-1) { av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight += graphStretchY - evt.getY(); if(av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight <10) av.alignment.getAlignmentAnnotation()[graphStretch].graphHeight = 10; graphStretchY = evt.getY(); adjustPanelHeight(); ap.repaint(); } } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void mouseMoved(MouseEvent evt) { AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation(); if (aa == null) { return; } int row = -1; int height = 0; for (int i = 0; i < aa.length; i++) { if (aa[i].visible) { height += aa[i].height; } if (evt.getY() < height) { row = i; break; } } int res = (evt.getX() / av.getCharWidth()) + av.getStartRes(); if(av.hasHiddenColumns) res = av.getColumnSelection().adjustForHiddenColumns(res); if (row > -1 && res-1) { StringBuffer tip = new StringBuffer(""); for (int gg = 0; gg < aa.length; gg++) { if (aa[gg].graphGroup == aa[row].graphGroup && aa[gg].annotations[res]!=null) tip.append(aa[gg].label+" "+aa[gg].annotations[res].description+"
" ); } if(tip.length()!=6) { tip.setLength(tip.length() - 4); this.setToolTipText(tip.toString() + ""); } } else if(aa[row].annotations[res] != null) this.setToolTipText(aa[row].annotations[res].description); if(aa[row].annotations[res]!=null) { StringBuffer text = new StringBuffer("Sequence position " + (res + 1) + " " + aa[row].annotations[res].description); ap.alignFrame.statusBar.setText(text.toString()); } } } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void mouseClicked(MouseEvent evt) { } /** * DOCUMENT ME! * * @param g DOCUMENT ME! */ public void paintComponent(Graphics g) { g.setColor(Color.white); g.fillRect(0, 0, getWidth(), getHeight()); if(image!=null) {if (fastPaint || (getVisibleRect().width != g.getClipBounds().width) || (getVisibleRect().height != g.getClipBounds().height)) { g.drawImage(image, 0, 0, this); fastPaint = false; return; } } imgWidth = (av.endRes - av.startRes + 1) * av.charWidth; if (image == null || imgWidth != image.getWidth() || image.getHeight(this) != getHeight()) { image = new BufferedImage(imgWidth, ap.annotationPanel.getHeight(), BufferedImage.TYPE_INT_RGB); gg = (Graphics2D) image.getGraphics(); if(av.antiAlias) gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); gg.setFont(av.getFont()); fm = gg.getFontMetrics(); } drawComponent(gg, av.startRes, av.endRes + 1); g.drawImage(image, 0, 0, this); } /** * DOCUMENT ME! * * @param horizontal DOCUMENT ME! */ public void fastPaint(int horizontal) { if ((horizontal == 0) || gg==null || (av.alignment.getAlignmentAnnotation() == null) || (av.alignment.getAlignmentAnnotation().length < 1)) { repaint(); return; } gg.copyArea(0, 0, imgWidth, getHeight(), -horizontal * av.charWidth, 0); int sr = av.startRes; int er = av.endRes + 1; int transX = 0; if (horizontal > 0) // scrollbar pulled right, image to the left { transX = (er - sr - horizontal) * av.charWidth; sr = er - horizontal; } else if (horizontal < 0) { er = sr - horizontal; } gg.translate(transX, 0); drawComponent(gg, sr, er); gg.translate(-transX, 0); fastPaint = true; repaint(); } /** * DOCUMENT ME! * * @param g DOCUMENT ME! * @param startRes DOCUMENT ME! * @param endRes DOCUMENT ME! */ public void drawComponent(Graphics g, int startRes, int endRes) { g.setFont(av.getFont()); if (fm == null) fm = g.getFontMetrics(); g.setColor(Color.white); g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getHeight()); if ((av.alignment.getAlignmentAnnotation() == null) || (av.alignment.getAlignmentAnnotation().length < 1)) { g.setColor(Color.white); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(Color.black); g.drawString("Alignment has no annotations", 20, 15); return; } AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation(); int x = 0; int y = 0; char lastSS; int lastSSX; int iconOffset = av.charHeight / 2; boolean validRes = false; boolean [] graphGroupDrawn = new boolean[aa.length]; //\u03B2 \u03B1 for (int i = 0; i < aa.length; i++) { AlignmentAnnotation row = aa[i]; if (!row.visible) { continue; } lastSS = ' '; lastSSX = 0; if (row.graph>0) { if(row.graphGroup>-1 && graphGroupDrawn[ row.graphGroup ] ) continue; // this is so that we draw the characters below the graph y += row.height; if (row.hasText) { y -= av.charHeight; } } if (row.hasText) { iconOffset = av.charHeight / 2; } else { iconOffset = 0; } int column = startRes; int yPos = startRes; while (yPos < endRes) { if (av.hasHiddenColumns) { column = av.getColumnSelection().adjustForHiddenColumns(yPos); if (column > row.annotations.length-1) { break; } } else column = yPos; if ((row.annotations.length <= column) || (row.annotations[column] == null)) { validRes = false; } else { validRes = true; } x = (yPos - startRes) * av.charWidth; if (activeRow == i) { g.setColor(Color.red); if (av.getColumnSelection() != null) { for (int n = 0; n < av.getColumnSelection().size(); n++) { int v = av.getColumnSelection().columnAt(n); if (v == column) { g.fillRect((column - startRes) * av.charWidth, y, av.charWidth, row.height); } } } } if (validRes && (row.annotations[column].displayCharacter.length() > 0)) { int charOffset = (av.charWidth - fm.charWidth(row.annotations[column].displayCharacter.charAt( 0))) / 2; g.setColor(row.annotations[column].colour); if (column == 0 || row.graph>0) { g.drawString(row.annotations[column].displayCharacter, x+charOffset, y + iconOffset + 3); } else if (((row.annotations[column - 1] == null) || (!row.annotations[column].displayCharacter.equals( row.annotations[column - 1].displayCharacter)))) { g.drawString(row.annotations[column].displayCharacter, x+charOffset, y + iconOffset + 3); } } if (row.hasIcons) { if (!validRes || (row.annotations[column].secondaryStructure != lastSS)) { switch (lastSS) { case 'H': g.setColor(HELIX_COLOUR); g.fillRoundRect(lastSSX, y + 4 + iconOffset, x - lastSSX, 7, 8, 8); break; case 'E': g.setColor(SHEET_COLOUR); g.fillRect(lastSSX, y + 4 + iconOffset, x - lastSSX - 4, 7); g.fillPolygon(new int[] { x - 4, x - 4, x }, new int[] { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3); break; default: g.setColor(Color.gray); g.fillRect(lastSSX, y + 6 + iconOffset, x - lastSSX, 2); break; } if (validRes) { lastSS = row.annotations[column].secondaryStructure; } else { lastSS = ' '; } lastSSX = x; } } yPos++; } x += av.charWidth; if (row.hasIcons) { switch (lastSS) { case 'H': g.setColor(HELIX_COLOUR); g.fillRoundRect(lastSSX, y + 4 + iconOffset, x - lastSSX, 7, 8, 8); break; case 'E': g.setColor(SHEET_COLOUR); if (row.annotations[endRes] !=null && row.annotations[endRes].secondaryStructure != 'E') { g.fillRect(lastSSX, y + 4 + iconOffset, x - lastSSX - 4, 7); g.fillPolygon(new int[] {x - 4, x - 4, x}, new int[] { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset }, 3); } else g.fillRect(lastSSX, y + 4 + iconOffset, x - lastSSX, 7); break; default: g.setColor(Color.gray); g.fillRect(lastSSX, y + 6 + iconOffset, x - lastSSX, 2); break; } } if (row.graph>0) { if(row.graph == AlignmentAnnotation.LINE_GRAPH ) { if(row.graphGroup>-1 && !graphGroupDrawn[row.graphGroup]) { float groupmax=-999999, groupmin=9999999; for(int gg=0; gggroupmax) groupmax = aa[gg].graphMax; if(aa[gg].graphMin0 && row.hasText) { y += av.charHeight; } if (row.graph==0) { y += aa[i].height; } } } public void drawLineGraph(Graphics g, AlignmentAnnotation aa, int sRes, int eRes, int y, float min, float max, int graphHeight) { if(sRes>aa.annotations.length) return; eRes = Math.min(eRes, aa.annotations.length); int x = 0; //Adjustment for fastpaint to left if(eRes aaMax) { break; } } else column = yPos; yPos ++; if(aa.annotations[column]==null || aa.annotations[column-1]==null) { x+=av.charWidth; continue; } g.setColor(aa.annotations[column].colour); y1 = y - (int) (((aa.annotations[column-1].value-min) / range) * graphHeight); y2 = y - (int) (((aa.annotations[column].value-min) / range) * graphHeight); g.drawLine(x-av.charWidth/2, y1, x+av.charWidth/2, y2); x += av.charWidth; } } public void drawBarGraph(Graphics g, AlignmentAnnotation aa, int sRes, int eRes, float min, float max, int y) { if(sRes>aa.annotations.length) return; eRes = Math.min(eRes, aa.annotations.length); int x=0, y1, y2; float range = max - min; if(aa.threshold!=null) { g.setColor(aa.threshold.colour); Graphics2D g2 = (Graphics2D)g; g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 3f, new float[] { 5f, 3f }, 0f)); y2 = (int)(y - ((aa.threshold.value-min) / range)*aa.graphHeight); g.drawLine(x-av.charWidth,y2,(eRes-sRes)*av.charWidth,y2); g2.setStroke(new BasicStroke()); } y1 = y2 = y; if(min<0) y2 = (int)(y - (0-min / (range))*aa.graphHeight); g.setColor(Color.gray); g.drawLine(x,y2,(eRes-sRes)*av.charWidth,y2); int column = sRes; int yPos = sRes; int aaMax = aa.annotations.length-1; while( yPos < eRes ) { if(av.hasHiddenColumns) { column = av.getColumnSelection().adjustForHiddenColumns(yPos); if(column > aaMax) { break; } } else column = yPos; yPos ++; if (aa.annotations[column] == null) { x += av.charWidth; continue; } g.setColor(aa.annotations[column].colour); y1 = y - (int) (((aa.annotations[column].value-min) / (range)) * aa.graphHeight); if(y1-y2>0) g.fillRect(x, y2, av.charWidth, y1-y2 ); else g.fillRect(x, y1, av.charWidth, y2-y1 ); x += av.charWidth; } } // used by overview window public void drawGraph(Graphics g, AlignmentAnnotation aa, int width, int y, int sRes, int eRes) { g.setColor(Color.white); g.fillRect(0, 0, width, y); g.setColor(new Color(0, 0, 180)); int x = 0, height; for (int j = sRes; j < eRes; j++) { g.setColor(aa.annotations[j].colour); height = (int) ((aa.annotations[j].value / aa.graphMax) * y); if(height>y) height = y; g.fillRect(x, y - height, av.charWidth, height); x += av.charWidth; } } }