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.util.Comparison;
29 import jalview.util.MessageManager;
31 import java.awt.Color;
32 import java.awt.Dimension;
34 import java.awt.FontMetrics;
35 import java.awt.Graphics;
36 import java.awt.Image;
37 import java.awt.MenuItem;
38 import java.awt.Panel;
39 import java.awt.PopupMenu;
40 import java.awt.event.ActionEvent;
41 import java.awt.event.ActionListener;
42 import java.awt.event.AdjustmentEvent;
43 import java.awt.event.AdjustmentListener;
44 import java.awt.event.InputEvent;
45 import java.awt.event.MouseEvent;
46 import java.awt.event.MouseListener;
47 import java.awt.event.MouseMotionListener;
49 public class AnnotationPanel extends Panel implements AwtRenderPanelI,
50 AdjustmentListener, ActionListener, MouseListener,
59 final String HELIX = "Helix";
61 final String SHEET = "Sheet";
64 * For RNA secondary structure "stems" aka helices
66 final String STEM = "RNA Helix";
68 final String LABEL = "Label";
70 final String REMOVE = "Remove Annotation";
72 final String COLOUR = "Colour";
74 final Color HELIX_COLOUR = Color.red.darker();
76 final Color SHEET_COLOUR = Color.green.darker().darker();
86 boolean fastPaint = false;
88 // Used For mouse Dragging and resizing graphs
89 int graphStretch = -1;
91 int graphStretchY = -1;
93 boolean mouseDragging = false;
95 public static int GRAPH_HEIGHT = 40;
99 public final AnnotationRenderer renderer;
101 public AnnotationPanel(AlignmentPanel ap)
103 MAC = new jalview.util.Platform().isAMac();
107 int height = adjustPanelHeight();
108 ap.apvscroll.setValues(0, getSize().height, 0, height);
110 addMouseMotionListener(this);
112 addMouseListener(this);
114 // ap.annotationScroller.getVAdjustable().addAdjustmentListener( this );
115 renderer = new AnnotationRenderer();
118 public AnnotationPanel(AlignViewport av)
121 renderer = new AnnotationRenderer();
125 public void adjustmentValueChanged(AdjustmentEvent evt)
136 public void actionPerformed(ActionEvent evt)
138 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
143 Annotation[] anot = aa[activeRow].annotations;
145 if (anot.length < av.getColumnSelection().getMax())
147 Annotation[] temp = new Annotation[av.getColumnSelection().getMax() + 2];
148 System.arraycopy(anot, 0, temp, 0, anot.length);
150 aa[activeRow].annotations = anot;
154 if (av.getColumnSelection() != null
155 && !av.getColumnSelection().isEmpty()
156 && anot[av.getColumnSelection().getMin()] != null)
158 label = anot[av.getColumnSelection().getMin()].displayCharacter;
161 if (evt.getActionCommand().equals(REMOVE))
163 for (int sel : av.getColumnSelection().getSelected())
165 // TODO: JAL-2001 check if applet has faulty 'REMOVE' selected columns
167 // annotation if selection includes hidden columns
171 else if (evt.getActionCommand().equals(LABEL))
173 label = enterLabel(label, "Enter Label");
180 if ((label.length() > 0) && !aa[activeRow].hasText)
182 aa[activeRow].hasText = true;
185 for (int index : av.getColumnSelection().getSelected())
187 // TODO: JAL-2001 - provide a fast method to list visible selected
189 if (!av.getColumnSelection().isVisible(index))
194 if (anot[index] == null)
196 anot[index] = new Annotation(label, "", ' ', 0);
199 anot[index].displayCharacter = label;
202 else if (evt.getActionCommand().equals(COLOUR))
204 UserDefinedColours udc = new UserDefinedColours(this, Color.black,
207 Color col = udc.getColor();
209 for (int index : av.getColumnSelection().getSelected())
211 if (!av.getColumnSelection().isVisible(index))
216 if (anot[index] == null)
218 anot[index] = new Annotation("", "", ' ', 0);
221 anot[index].colour = col;
228 String symbol = "\u03B1";
230 if (evt.getActionCommand().equals(HELIX))
234 else if (evt.getActionCommand().equals(SHEET))
240 // Added by LML to color stems
241 else if (evt.getActionCommand().equals(STEM))
244 symbol = "(";// "\u03C3"; sigma
247 symbol = getCurrentAnnotationCharacter(anot, symbol);
249 if (!aa[activeRow].hasIcons)
251 aa[activeRow].hasIcons = true;
254 label = enterLabel(symbol, "Enter Label");
261 if ((label.length() > 0) && !aa[activeRow].hasText)
263 aa[activeRow].hasText = true;
264 if (evt.getActionCommand().equals(STEM))
266 aa[activeRow].showAllColLabels = true;
270 for (int index : av.getColumnSelection().getSelected())
272 if (!av.getColumnSelection().isVisible(index))
277 if (anot[index] == null)
279 anot[index] = new Annotation(label, "", type, 0);
282 anot[index].secondaryStructure = type != 'S' ? type : label
283 .length() == 0 ? ' ' : label.charAt(0);
284 anot[index].displayCharacter = label;
288 av.getAlignment().validateAnnotation(aa[activeRow]);
290 ap.alignmentChanged();
297 String enterLabel(String text, String label)
299 EditNameDialog dialog = new EditNameDialog(text, null, label, null,
300 ap.alignFrame, "Enter Label", 400, 200, true);
304 return dialog.getName();
313 public void mousePressed(MouseEvent evt)
315 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
321 int height = -scrollOffset;
324 for (int i = 0; i < aa.length; i++)
328 height += aa[i].height;
331 if (evt.getY() < height)
337 else if (aa[i].graph > 0)
341 graphStretchY = evt.getY();
348 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK
351 if (av.getColumnSelection() == null
352 || av.getColumnSelection().isEmpty())
357 PopupMenu pop = new PopupMenu(
358 MessageManager.getString("label.structure_type"));
361 if (av.getAlignment().isNucleotide())
363 item = new MenuItem(STEM);
364 item.addActionListener(this);
369 item = new MenuItem(HELIX);
370 item.addActionListener(this);
372 item = new MenuItem(SHEET);
373 item.addActionListener(this);
376 item = new MenuItem(LABEL);
377 item.addActionListener(this);
379 item = new MenuItem(COLOUR);
380 item.addActionListener(this);
382 item = new MenuItem(REMOVE);
383 item.addActionListener(this);
385 ap.alignFrame.add(pop);
386 pop.show(this, evt.getX(), evt.getY());
391 ap.scalePanel.mousePressed(evt);
395 public void mouseReleased(MouseEvent evt)
399 mouseDragging = false;
403 needValidating = false;
405 ap.scalePanel.mouseReleased(evt);
409 public void mouseClicked(MouseEvent evt)
413 boolean needValidating = false;
416 public void mouseDragged(MouseEvent evt)
418 if (graphStretch > -1)
420 av.getAlignment().getAlignmentAnnotation()[graphStretch].graphHeight += graphStretchY
422 if (av.getAlignment().getAlignmentAnnotation()[graphStretch].graphHeight < 0)
424 av.getAlignment().getAlignmentAnnotation()[graphStretch].graphHeight = 0;
426 graphStretchY = evt.getY();
427 av.calcPanelHeight();
428 needValidating = true;
429 ap.paintAlignment(true);
433 ap.scalePanel.mouseDragged(evt);
438 public void mouseMoved(MouseEvent evt)
440 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
447 int height = -scrollOffset;
448 for (int i = 0; i < aa.length; i++)
453 height += aa[i].height;
456 if (evt.getY() < height)
463 int column = evt.getX() / av.getCharWidth() + av.getStartRes();
465 if (av.hasHiddenColumns())
467 column = av.getColumnSelection().adjustForHiddenColumns(column);
470 if (row > -1 && column < aa[row].annotations.length
471 && aa[row].annotations[column] != null)
473 StringBuilder text = new StringBuilder();
474 text.append(MessageManager.getString("label.column")).append(" ")
476 if (aa[row].annotations[column].description != null)
478 text.append(" ").append(aa[row].annotations[column].description);
482 * if the annotation is sequence-specific, show the sequence number
483 * in the alignment, and (if not a gap) the residue and position
485 SequenceI seqref = aa[row].sequenceRef;
488 int seqIndex = av.getAlignment().findIndex(seqref);
492 .append(MessageManager.getString("label.sequence"))
493 .append(" ").append(seqIndex + 1);
494 char residue = seqref.getCharAt(column);
495 if (!Comparison.isGap(residue))
497 int residuePos = seqref.findPosition(column);
498 text.append(": ").append(residue).append(" (")
499 .append(residuePos).append(")");
504 ap.alignFrame.statusBar.setText(text.toString());
509 public void mouseEntered(MouseEvent evt)
511 ap.scalePanel.mouseEntered(evt);
515 public void mouseExited(MouseEvent evt)
517 ap.scalePanel.mouseExited(evt);
520 public int adjustPanelHeight()
522 return adjustPanelHeight(true);
525 public int adjustPanelHeight(boolean repaint)
527 int height = av.calcPanelHeight();
528 this.setSize(new Dimension(getSize().width, height));
537 * calculate the height for visible annotation, revalidating bounds where
538 * necessary ABSTRACT GUI METHOD
540 * @return total height of annotation
543 public void addEditableColumn(int i)
547 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
553 for (int j = 0; j < aa.length; j++)
565 public void update(Graphics g)
571 public void paint(Graphics g)
573 Dimension d = getSize();
575 // (av.endRes - av.startRes + 1) * av.charWidth;
576 if (imgWidth < 1 || d.height < 1)
580 if (image == null || imgWidth != image.getWidth(this)
581 || d.height != image.getHeight(this))
583 image = createImage(imgWidth, d.height);
584 gg = image.getGraphics();
585 gg.setFont(av.getFont());
586 fm = gg.getFontMetrics();
592 g.drawImage(image, 0, 0, this);
597 gg.setColor(Color.white);
598 gg.fillRect(0, 0, getSize().width, getSize().height);
599 drawComponent(gg, av.startRes, av.endRes + 1);
601 g.drawImage(image, 0, 0, this);
604 public void fastPaint(int horizontal)
607 || av.getAlignment().getAlignmentAnnotation() == null
608 || av.getAlignment().getAlignmentAnnotation().length < 1)
614 gg.copyArea(0, 0, imgWidth, getSize().height,
615 -horizontal * av.getCharWidth(), 0);
616 int sr = av.startRes, er = av.endRes + 1, transX = 0;
618 if (horizontal > 0) // scrollbar pulled right, image to the left
620 transX = (er - sr - horizontal) * av.getCharWidth();
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);
648 public void drawComponent(Graphics g, int startRes, int endRes)
650 Font ofont = av.getFont();
653 g.setColor(Color.white);
654 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(),
659 fm = g.getFontMetrics();
662 if ((av.getAlignment().getAlignmentAnnotation() == null)
663 || (av.getAlignment().getAlignmentAnnotation().length < 1))
665 g.setColor(Color.white);
666 g.fillRect(0, 0, getSize().width, getSize().height);
667 g.setColor(Color.black);
668 if (av.validCharWidth)
670 g.drawString(MessageManager
671 .getString("label.alignment_has_no_annotations"), 20, 15);
676 g.translate(0, -scrollOffset);
677 renderer.drawComponent(this, av, g, activeRow, startRes, endRes);
678 g.translate(0, +scrollOffset);
681 int scrollOffset = 0;
683 public void setScrollOffset(int value, boolean repaint)
685 scrollOffset = value;
693 public FontMetrics getFontMetrics()
699 public Image getFadedImage()
705 public int getFadedImageWidth()
710 private int[] bounds = new int[2];
713 public int[] getVisibleVRange()
715 if (ap != null && ap.alabels != null)
717 int sOffset = -ap.alabels.scrollOffset;
718 int visHeight = sOffset + ap.annotationPanelHolder.getHeight();
720 bounds[1] = visHeight;
730 * Returns the current annotation symbol (if any) within the visible selected
731 * columns (first symbol found left to right in selection). If none is found,
732 * the supplied default value is returned.
735 * @param defaultValue
738 String getCurrentAnnotationCharacter(Annotation[] annotations,
741 String result = defaultValue;
742 for (int index : av.getColumnSelection().getSelected())
744 if (!av.getColumnSelection().isVisible(index))
749 Annotation annotation = annotations[index];
750 if (annotation != null)
752 String displayed = annotation.displayCharacter;
753 if (displayed != null && displayed.length() > 0)
755 result = displayed.substring(0, 1);