2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.appletgui;
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.Annotation;
25 import jalview.datamodel.SequenceI;
26 import jalview.renderer.AnnotationRenderer;
27 import jalview.renderer.AwtRenderPanelI;
28 import jalview.schemes.ResidueProperties;
29 import jalview.util.Comparison;
30 import jalview.util.MessageManager;
31 import jalview.util.Platform;
33 import java.awt.Color;
34 import java.awt.Dimension;
36 import java.awt.FontMetrics;
37 import java.awt.Graphics;
38 import java.awt.Image;
39 import java.awt.MenuItem;
40 import java.awt.Panel;
41 import java.awt.PopupMenu;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.awt.event.AdjustmentEvent;
45 import java.awt.event.AdjustmentListener;
46 import java.awt.event.InputEvent;
47 import java.awt.event.MouseEvent;
48 import java.awt.event.MouseListener;
49 import java.awt.event.MouseMotionListener;
51 public class AnnotationPanel extends Panel implements AwtRenderPanelI,
52 AdjustmentListener, ActionListener, MouseListener,
61 final String HELIX = "Helix";
63 final String SHEET = "Sheet";
66 * For RNA secondary structure "stems" aka helices
68 final String STEM = "RNA Helix";
70 final String LABEL = "Label";
72 final String REMOVE = "Remove Annotation";
74 final String COLOUR = "Colour";
76 final Color HELIX_COLOUR = Color.red.darker();
78 final Color SHEET_COLOUR = Color.green.darker().darker();
88 boolean fastPaint = false;
90 // Used For mouse Dragging and resizing graphs
91 int graphStretch = -1;
93 int graphStretchY = -1;
95 boolean mouseDragging = false;
97 public static int GRAPH_HEIGHT = 40;
101 public final AnnotationRenderer renderer;
103 public AnnotationPanel(AlignmentPanel ap)
105 new jalview.util.Platform();
106 MAC = Platform.isAMac();
110 int height = adjustPanelHeight();
111 ap.apvscroll.setValues(0, getSize().height, 0, height);
113 addMouseMotionListener(this);
115 addMouseListener(this);
117 // ap.annotationScroller.getVAdjustable().addAdjustmentListener( this );
118 renderer = new AnnotationRenderer();
121 public AnnotationPanel(AlignViewport av)
124 renderer = new AnnotationRenderer();
128 public void adjustmentValueChanged(AdjustmentEvent evt)
139 public void actionPerformed(ActionEvent evt)
141 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
146 Annotation[] anot = aa[activeRow].annotations;
148 if (anot.length < av.getColumnSelection().getMax())
150 Annotation[] temp = new Annotation[av.getColumnSelection().getMax() + 2];
151 System.arraycopy(anot, 0, temp, 0, anot.length);
153 aa[activeRow].annotations = anot;
157 if (av.getColumnSelection() != null
158 && !av.getColumnSelection().isEmpty()
159 && anot[av.getColumnSelection().getMin()] != null)
161 label = anot[av.getColumnSelection().getMin()].displayCharacter;
164 if (evt.getActionCommand().equals(REMOVE))
166 for (int index : av.getColumnSelection().getSelected())
168 if (av.getAlignment().getHiddenColumns().isVisible(index))
174 else if (evt.getActionCommand().equals(LABEL))
176 label = enterLabel(label, "Enter Label");
183 if ((label.length() > 0) && !aa[activeRow].hasText)
185 aa[activeRow].hasText = true;
188 for (int index : av.getColumnSelection().getSelected())
190 // TODO: JAL-2001 - provide a fast method to list visible selected
192 if (!av.getAlignment().getHiddenColumns().isVisible(index))
197 if (anot[index] == null)
199 anot[index] = new Annotation(label, "", ' ', 0);
202 anot[index].displayCharacter = label;
205 else if (evt.getActionCommand().equals(COLOUR))
207 UserDefinedColours udc = new UserDefinedColours(this, Color.black,
210 Color col = udc.getColor();
212 for (int index : av.getColumnSelection().getSelected())
214 if (!av.getAlignment().getHiddenColumns().isVisible(index))
219 if (anot[index] == null)
221 anot[index] = new Annotation("", "", ' ', 0);
224 anot[index].colour = col;
231 String symbol = "\u03B1";
233 if (evt.getActionCommand().equals(HELIX))
237 else if (evt.getActionCommand().equals(SHEET))
243 // Added by LML to color stems
244 else if (evt.getActionCommand().equals(STEM))
247 int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
248 symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
251 if (!aa[activeRow].hasIcons)
253 aa[activeRow].hasIcons = true;
256 label = enterLabel(symbol, "Enter Label");
263 if ((label.length() > 0) && !aa[activeRow].hasText)
265 aa[activeRow].hasText = true;
266 if (evt.getActionCommand().equals(STEM))
268 aa[activeRow].showAllColLabels = true;
272 for (int index : av.getColumnSelection().getSelected())
274 if (!av.getAlignment().getHiddenColumns().isVisible(index))
279 if (anot[index] == null)
281 anot[index] = new Annotation(label, "", type, 0);
284 anot[index].secondaryStructure = type != 'S' ? type : label
285 .length() == 0 ? ' ' : label.charAt(0);
286 anot[index].displayCharacter = label;
290 av.getAlignment().validateAnnotation(aa[activeRow]);
292 ap.alignmentChanged();
299 String enterLabel(String text, String label)
301 EditNameDialog dialog = new EditNameDialog(text, null, label, null,
302 ap.alignFrame, "Enter Label", 400, 200, true);
306 return dialog.getName();
315 public void mousePressed(MouseEvent evt)
317 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
323 int height = -scrollOffset;
326 for (int i = 0; i < aa.length; i++)
330 height += aa[i].height;
333 if (evt.getY() < height)
339 else if (aa[i].graph > 0)
343 graphStretchY = evt.getY();
350 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK
353 if (av.getColumnSelection() == null
354 || av.getColumnSelection().isEmpty())
359 PopupMenu pop = new PopupMenu(
360 MessageManager.getString("label.structure_type"));
363 if (av.getAlignment().isNucleotide())
365 item = new MenuItem(STEM);
366 item.addActionListener(this);
371 item = new MenuItem(HELIX);
372 item.addActionListener(this);
374 item = new MenuItem(SHEET);
375 item.addActionListener(this);
378 item = new MenuItem(LABEL);
379 item.addActionListener(this);
381 item = new MenuItem(COLOUR);
382 item.addActionListener(this);
384 item = new MenuItem(REMOVE);
385 item.addActionListener(this);
387 ap.alignFrame.add(pop);
388 pop.show(this, evt.getX(), evt.getY());
393 ap.scalePanel.mousePressed(evt);
397 public void mouseReleased(MouseEvent evt)
401 mouseDragging = false;
405 needValidating = false;
407 ap.scalePanel.mouseReleased(evt);
411 public void mouseClicked(MouseEvent evt)
415 boolean needValidating = false;
418 public void mouseDragged(MouseEvent evt)
420 if (graphStretch > -1)
422 av.getAlignment().getAlignmentAnnotation()[graphStretch].graphHeight += graphStretchY
424 if (av.getAlignment().getAlignmentAnnotation()[graphStretch].graphHeight < 0)
426 av.getAlignment().getAlignmentAnnotation()[graphStretch].graphHeight = 0;
428 graphStretchY = evt.getY();
429 av.calcPanelHeight();
430 needValidating = true;
431 ap.paintAlignment(true);
435 ap.scalePanel.mouseDragged(evt);
440 public void mouseMoved(MouseEvent evt)
442 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
449 int height = -scrollOffset;
450 for (int i = 0; i < aa.length; i++)
455 height += aa[i].height;
458 if (evt.getY() < height)
465 int column = evt.getX() / av.getCharWidth()
466 + av.getRanges().getStartRes();
468 if (av.hasHiddenColumns())
470 column = av.getAlignment().getHiddenColumns()
471 .adjustForHiddenColumns(column);
474 if (row > -1 && column < aa[row].annotations.length
475 && aa[row].annotations[column] != null)
477 StringBuilder text = new StringBuilder();
478 text.append(MessageManager.getString("label.column")).append(" ")
480 String description = aa[row].annotations[column].description;
481 if (description != null && description.length() > 0)
483 text.append(" ").append(description);
487 * if the annotation is sequence-specific, show the sequence number
488 * in the alignment, and (if not a gap) the residue and position
490 SequenceI seqref = aa[row].sequenceRef;
493 int seqIndex = av.getAlignment().findIndex(seqref);
497 .append(MessageManager.getString("label.sequence"))
498 .append(" ").append(seqIndex + 1);
499 char residue = seqref.getCharAt(column);
500 if (!Comparison.isGap(residue))
504 if (av.getAlignment().isNucleotide())
506 name = ResidueProperties.nucleotideName.get(String
508 text.append(" Nucleotide: ").append(
509 name != null ? name : residue);
513 name = 'X' == residue ? "X" : ('*' == residue ? "STOP"
514 : ResidueProperties.aa2Triplet.get(String
516 text.append(" Residue: ").append(
517 name != null ? name : residue);
519 int residuePos = seqref.findPosition(column);
520 text.append(" (").append(residuePos).append(")");
521 // int residuePos = seqref.findPosition(column);
522 // text.append(residue).append(" (")
523 // .append(residuePos).append(")");
528 ap.alignFrame.statusBar.setText(text.toString());
533 public void mouseEntered(MouseEvent evt)
535 ap.scalePanel.mouseEntered(evt);
539 public void mouseExited(MouseEvent evt)
541 ap.scalePanel.mouseExited(evt);
544 public int adjustPanelHeight()
546 return adjustPanelHeight(true);
549 public int adjustPanelHeight(boolean repaint)
551 int height = av.calcPanelHeight();
552 this.setSize(new Dimension(getSize().width, height));
561 * calculate the height for visible annotation, revalidating bounds where
562 * necessary ABSTRACT GUI METHOD
564 * @return total height of annotation
567 public void addEditableColumn(int i)
571 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
577 for (int j = 0; j < aa.length; j++)
589 public void update(Graphics g)
595 public void paint(Graphics g)
597 Dimension d = getSize();
599 // (av.endRes - av.startRes + 1) * av.charWidth;
600 if (imgWidth < 1 || d.height < 1)
604 if (image == null || imgWidth != image.getWidth(this)
605 || d.height != image.getHeight(this))
607 image = createImage(imgWidth, d.height);
608 gg = image.getGraphics();
609 gg.setFont(av.getFont());
610 fm = gg.getFontMetrics();
616 g.drawImage(image, 0, 0, this);
621 gg.setColor(Color.white);
622 gg.fillRect(0, 0, getSize().width, getSize().height);
623 drawComponent(gg, av.getRanges().getStartRes(), av.getRanges()
626 g.drawImage(image, 0, 0, this);
629 public void fastPaint(int horizontal)
632 || av.getAlignment().getAlignmentAnnotation() == null
633 || av.getAlignment().getAlignmentAnnotation().length < 1)
639 gg.copyArea(0, 0, imgWidth, getSize().height,
640 -horizontal * av.getCharWidth(), 0);
641 int sr = av.getRanges().getStartRes(), er = av.getRanges().getEndRes() + 1, transX = 0;
643 if (horizontal > 0) // scrollbar pulled right, image to the left
645 transX = (er - sr - horizontal) * av.getCharWidth();
646 sr = er - horizontal;
648 else if (horizontal < 0)
650 er = sr - horizontal;
653 gg.translate(transX, 0);
655 drawComponent(gg, sr, er);
657 gg.translate(-transX, 0);
673 public void drawComponent(Graphics g, int startRes, int endRes)
675 Font ofont = av.getFont();
678 g.setColor(Color.white);
679 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(),
684 fm = g.getFontMetrics();
687 if ((av.getAlignment().getAlignmentAnnotation() == null)
688 || (av.getAlignment().getAlignmentAnnotation().length < 1))
690 g.setColor(Color.white);
691 g.fillRect(0, 0, getSize().width, getSize().height);
692 g.setColor(Color.black);
693 if (av.validCharWidth)
695 g.drawString(MessageManager
696 .getString("label.alignment_has_no_annotations"), 20, 15);
701 g.translate(0, -scrollOffset);
702 renderer.drawComponent(this, av, g, activeRow, startRes, endRes);
703 g.translate(0, +scrollOffset);
706 int scrollOffset = 0;
708 public void setScrollOffset(int value, boolean repaint)
710 scrollOffset = value;
718 public FontMetrics getFontMetrics()
724 public Image getFadedImage()
730 public int getFadedImageWidth()
735 private int[] bounds = new int[2];
738 public int[] getVisibleVRange()
740 if (ap != null && ap.alabels != null)
742 int sOffset = -ap.alabels.scrollOffset;
743 int visHeight = sOffset + ap.annotationPanelHolder.getHeight();
745 bounds[1] = visHeight;