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.
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.Annotation;
25 import jalview.datamodel.ColumnSelection;
26 import jalview.datamodel.HiddenColumns;
27 import jalview.datamodel.SequenceI;
28 import jalview.gui.JalviewColourChooser.ColourChooserListener;
29 import jalview.renderer.AnnotationRenderer;
30 import jalview.renderer.AwtRenderPanelI;
31 import jalview.schemes.ResidueProperties;
32 import jalview.util.Comparison;
33 import jalview.util.MessageManager;
34 import jalview.util.Platform;
35 import jalview.viewmodel.ViewportListenerI;
36 import jalview.viewmodel.ViewportRanges;
38 import java.awt.AlphaComposite;
39 import java.awt.Color;
40 import java.awt.Dimension;
41 import java.awt.FontMetrics;
42 import java.awt.Graphics;
43 import java.awt.Graphics2D;
44 import java.awt.Image;
45 import java.awt.Rectangle;
46 import java.awt.RenderingHints;
47 import java.awt.event.ActionEvent;
48 import java.awt.event.ActionListener;
49 import java.awt.event.AdjustmentEvent;
50 import java.awt.event.AdjustmentListener;
51 import java.awt.event.MouseEvent;
52 import java.awt.event.MouseListener;
53 import java.awt.event.MouseMotionListener;
54 import java.awt.event.MouseWheelEvent;
55 import java.awt.event.MouseWheelListener;
56 import java.awt.image.BufferedImage;
57 import java.beans.PropertyChangeEvent;
58 import java.util.ArrayList;
59 import java.util.Collections;
60 import java.util.List;
62 import javax.swing.JMenuItem;
63 import javax.swing.JPanel;
64 import javax.swing.JPopupMenu;
65 import javax.swing.Scrollable;
66 import javax.swing.ToolTipManager;
69 * AnnotationPanel displays visible portion of annotation rows below unwrapped
75 public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
76 MouseListener, MouseWheelListener, MouseMotionListener,
77 ActionListener, AdjustmentListener, Scrollable, ViewportListenerI
81 Select, Resize, Undefined
84 String HELIX = MessageManager.getString("label.helix");
86 String SHEET = MessageManager.getString("label.sheet");
89 * For RNA secondary structure "stems" aka helices
91 String STEM = MessageManager.getString("label.rna_helix");
93 String LABEL = MessageManager.getString("label.label");
95 String REMOVE = MessageManager.getString("label.remove_annotation");
97 String COLOUR = MessageManager.getString("action.colour");
99 public final Color HELIX_COLOUR = Color.red.darker();
101 public final Color SHEET_COLOUR = Color.green.darker().darker();
103 public final Color STEM_COLOUR = Color.blue.darker();
106 public AlignViewport av;
110 public int activeRow = -1;
112 public BufferedImage image;
114 public volatile BufferedImage fadedImage;
116 // private Graphics2D gg;
118 public FontMetrics fm;
120 public int imgWidth = 0;
122 boolean fastPaint = false;
124 // Used For mouse Dragging and resizing graphs
125 int graphStretch = -1;
127 int mouseDragLastX = -1;
129 int mouseDragLastY = -1;
131 DragMode dragMode = DragMode.Undefined;
133 boolean mouseDragging = false;
135 // for editing cursor
140 public final AnnotationRenderer renderer;
142 private MouseWheelListener[] _mwl;
145 * Creates a new AnnotationPanel object.
150 public AnnotationPanel(AlignmentPanel ap)
152 ToolTipManager.sharedInstance().registerComponent(this);
153 ToolTipManager.sharedInstance().setInitialDelay(0);
154 ToolTipManager.sharedInstance().setDismissDelay(10000);
157 this.setLayout(null);
158 addMouseListener(this);
159 addMouseMotionListener(this);
160 ap.annotationScroller.getVerticalScrollBar()
161 .addAdjustmentListener(this);
162 // save any wheel listeners on the scroller, so we can propagate scroll
164 _mwl = ap.annotationScroller.getMouseWheelListeners();
165 // and then set our own listener to consume all mousewheel events
166 ap.annotationScroller.addMouseWheelListener(this);
167 renderer = new AnnotationRenderer();
169 av.getRanges().addPropertyChangeListener(this);
172 public AnnotationPanel(AlignViewport av)
175 renderer = new AnnotationRenderer();
179 public void mouseWheelMoved(MouseWheelEvent e)
184 double wheelRotation = e.getPreciseWheelRotation();
185 if (wheelRotation > 0)
187 av.getRanges().scrollRight(true);
189 else if (wheelRotation < 0)
191 av.getRanges().scrollRight(false);
196 // TODO: find the correct way to let the event bubble up to
197 // ap.annotationScroller
198 for (MouseWheelListener mwl : _mwl)
202 mwl.mouseWheelMoved(e);
213 public Dimension getPreferredScrollableViewportSize()
215 Dimension ps = getPreferredSize();
216 return new Dimension(ps.width, adjustForAlignFrame(false, ps.height));
220 public int getScrollableBlockIncrement(Rectangle visibleRect,
221 int orientation, int direction)
227 public boolean getScrollableTracksViewportHeight()
233 public boolean getScrollableTracksViewportWidth()
239 public int getScrollableUnitIncrement(Rectangle visibleRect,
240 int orientation, int direction)
249 * java.awt.event.AdjustmentListener#adjustmentValueChanged(java.awt.event
253 public void adjustmentValueChanged(AdjustmentEvent evt)
255 // update annotation label display
256 ap.getAlabels().setScrollOffset(-evt.getValue());
260 * Calculates the height of the annotation displayed in the annotation panel.
261 * Callers should normally call the ap.adjustAnnotationHeight method to ensure
262 * all annotation associated components are updated correctly.
265 public int adjustPanelHeight()
267 int height = av.calcPanelHeight();
268 this.setPreferredSize(new Dimension(1, height));
271 // revalidate only when the alignment panel is fully constructed
285 public void actionPerformed(ActionEvent evt)
287 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
292 Annotation[] anot = aa[activeRow].annotations;
294 if (anot.length < av.getColumnSelection().getMax())
296 Annotation[] temp = new Annotation[av.getColumnSelection().getMax()
298 System.arraycopy(anot, 0, temp, 0, anot.length);
300 aa[activeRow].annotations = anot;
303 String action = evt.getActionCommand();
304 if (action.equals(REMOVE))
306 for (int index : av.getColumnSelection().getSelected())
308 if (av.getAlignment().getHiddenColumns().isVisible(index))
314 else if (action.equals(LABEL))
316 String exMesg = collectAnnotVals(anot, LABEL);
317 String label = JvOptionPane.showInputDialog(
318 MessageManager.getString("label.enter_label"), exMesg);
325 if ((label.length() > 0) && !aa[activeRow].hasText)
327 aa[activeRow].hasText = true;
330 for (int index : av.getColumnSelection().getSelected())
332 if (!av.getAlignment().getHiddenColumns().isVisible(index))
337 if (anot[index] == null)
339 anot[index] = new Annotation(label, "", ' ', 0);
343 anot[index].displayCharacter = label;
347 else if (action.equals(COLOUR))
349 final Annotation[] fAnot = anot;
350 String title = MessageManager
351 .getString("label.select_foreground_colour");
352 ColourChooserListener listener = new ColourChooserListener()
355 public void colourSelected(Color c)
357 HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
358 for (int index : av.getColumnSelection().getSelected())
360 if (hiddenColumns.isVisible(index))
362 if (fAnot[index] == null)
364 fAnot[index] = new Annotation("", "", ' ', 0);
366 fAnot[index].colour = c;
370 JalviewColourChooser.showColourChooser(this,
371 title, Color.black, listener);
374 // HELIX, SHEET or STEM
377 String symbol = "\u03B1"; // alpha
379 if (action.equals(HELIX))
383 else if (action.equals(SHEET))
386 symbol = "\u03B2"; // beta
389 // Added by LML to color stems
390 else if (action.equals(STEM))
393 int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
394 symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
397 if (!aa[activeRow].hasIcons)
399 aa[activeRow].hasIcons = true;
402 String label = JvOptionPane.showInputDialog(MessageManager
403 .getString("label.enter_label_for_the_structure"), symbol);
410 if ((label.length() > 0) && !aa[activeRow].hasText)
412 aa[activeRow].hasText = true;
413 if (action.equals(STEM))
415 aa[activeRow].showAllColLabels = true;
418 for (int index : av.getColumnSelection().getSelected())
420 if (!av.getAlignment().getHiddenColumns().isVisible(index))
425 if (anot[index] == null)
427 anot[index] = new Annotation(label, "", type, 0);
430 anot[index].secondaryStructure = type != 'S' ? type
431 : label.length() == 0 ? ' ' : label.charAt(0);
432 anot[index].displayCharacter = label;
437 av.getAlignment().validateAnnotation(aa[activeRow]);
438 ap.alignmentChanged();
439 ap.alignFrame.setMenusForViewport();
447 * Returns any existing annotation concatenated as a string. For each
448 * annotation, takes the description, if any, else the secondary structure
449 * character (if type is HELIX, SHEET or STEM), else the display character (if
456 private String collectAnnotVals(Annotation[] anots, String type)
458 // TODO is this method wanted? why? 'last' is not used
460 StringBuilder collatedInput = new StringBuilder(64);
462 ColumnSelection viscols = av.getColumnSelection();
463 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
466 * the selection list (read-only view) is in selection order, not
467 * column order; make a copy so we can sort it
469 List<Integer> selected = new ArrayList<>(viscols.getSelected());
470 Collections.sort(selected);
471 for (int index : selected)
473 // always check for current display state - just in case
474 if (!hidden.isVisible(index))
478 String tlabel = null;
479 if (anots[index] != null)
480 { // LML added stem code
481 if (type.equals(HELIX) || type.equals(SHEET) || type.equals(STEM)
482 || type.equals(LABEL))
484 tlabel = anots[index].description;
485 if (tlabel == null || tlabel.length() < 1)
487 if (type.equals(HELIX) || type.equals(SHEET)
488 || type.equals(STEM))
490 tlabel = "" + anots[index].secondaryStructure;
494 tlabel = "" + anots[index].displayCharacter;
498 if (tlabel != null && !tlabel.equals(last))
500 if (last.length() > 0)
502 collatedInput.append(" ");
504 collatedInput.append(tlabel);
508 return collatedInput.toString();
512 * Action on right mouse pressed on Mac is to show a pop-up menu for the
513 * annotation. Action on left mouse pressed is to find which annotation is
514 * pressed and mark the start of a column selection or graph resize operation.
519 public void mousePressed(MouseEvent evt)
522 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
527 mouseDragLastX = evt.getX();
528 mouseDragLastY = evt.getY();
531 * add visible annotation heights until we reach the y
532 * position, to find which annotation it is in
537 final int y = evt.getY();
538 for (int i = 0; i < aa.length; i++)
542 height += aa[i].height;
551 else if (aa[i].graph > 0)
554 * we have clicked on a resizable graph annotation
563 * isPopupTrigger fires in mousePressed on Mac,
564 * not until mouseRelease on Windows
566 if (evt.isPopupTrigger() && activeRow != -1)
568 showPopupMenu(y, evt.getX());
572 ap.getScalePanel().mousePressed(evt);
576 * Construct and display a context menu at the right-click position
581 void showPopupMenu(final int y, int x)
583 if (av.getColumnSelection() == null
584 || av.getColumnSelection().isEmpty())
589 JPopupMenu pop = new JPopupMenu(
590 MessageManager.getString("label.structure_type"));
593 * Just display the needed structure options
595 if (av.getAlignment().isNucleotide())
597 item = new JMenuItem(STEM);
598 item.addActionListener(this);
603 item = new JMenuItem(HELIX);
604 item.addActionListener(this);
606 item = new JMenuItem(SHEET);
607 item.addActionListener(this);
610 item = new JMenuItem(LABEL);
611 item.addActionListener(this);
613 item = new JMenuItem(COLOUR);
614 item.addActionListener(this);
616 item = new JMenuItem(REMOVE);
617 item.addActionListener(this);
619 pop.show(this, x, y);
623 * Action on mouse up is to clear mouse drag data and call mouseReleased on
624 * ScalePanel, to deal with defining the selection group (if any) defined by
630 public void mouseReleased(MouseEvent evt)
635 mouseDragging = false;
636 dragMode = DragMode.Undefined;
637 ap.getScalePanel().mouseReleased(evt);
640 * isPopupTrigger is set in mouseReleased on Windows
641 * (in mousePressed on Mac)
643 if (evt.isPopupTrigger() && activeRow != -1)
645 showPopupMenu(evt.getY(), evt.getX());
657 public void mouseEntered(MouseEvent evt)
659 this.mouseDragging = false;
660 ap.getScalePanel().mouseEntered(evt);
664 * On leaving the panel, calls ScalePanel.mouseExited to deal with scrolling
665 * with column selection on a mouse drag
670 public void mouseExited(MouseEvent evt)
672 ap.getScalePanel().mouseExited(evt);
682 public void mouseDragged(MouseEvent evt)
685 * todo: if dragMode is Undefined:
686 * - set to Select if dx > dy
687 * - set to Resize if dy > dx
688 * - do nothing if dx == dy
690 final int x = evt.getX();
691 final int y = evt.getY();
692 if (dragMode == DragMode.Undefined)
694 int dx = Math.abs(x - mouseDragLastX);
695 int dy = Math.abs(y - mouseDragLastY);
696 if (graphStretch == -1 || dx > dy)
699 * mostly horizontal drag, or not a graph annotation
701 dragMode = DragMode.Select;
706 * mostly vertical drag
708 dragMode = DragMode.Resize;
712 if (dragMode == DragMode.Undefined)
715 * drag is diagonal - defer deciding whether to
716 * treat as up/down or left/right
723 if (dragMode == DragMode.Resize)
726 * resize graph annotation if mouse was dragged up or down
728 int deltaY = mouseDragLastY - evt.getY();
731 AlignmentAnnotation graphAnnotation = av.getAlignment()
732 .getAlignmentAnnotation()[graphStretch];
733 int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
734 graphAnnotation.graphHeight = newHeight;
736 ap.paintAlignment(false, false);
742 * for mouse drag left or right, delegate to
743 * ScalePanel to adjust the column selection
745 ap.getScalePanel().mouseDragged(evt);
755 * Constructs the tooltip, and constructs and displays a status message, for
756 * the current mouse position
761 public void mouseMoved(MouseEvent evt)
763 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
767 this.setToolTipText(null);
774 for (int i = 0; i < aa.length; i++)
778 height += aa[i].height;
781 if (evt.getY() < height)
790 this.setToolTipText(null);
794 int column = (evt.getX() / av.getCharWidth())
795 + av.getRanges().getStartRes();
797 if (av.hasHiddenColumns())
799 column = av.getAlignment().getHiddenColumns()
800 .visibleToAbsoluteColumn(column);
803 AlignmentAnnotation ann = aa[row];
804 if (row > -1 && ann.annotations != null
805 && column < ann.annotations.length)
807 buildToolTip(ann, column, aa);
808 setStatusMessage(column, ann);
812 this.setToolTipText(null);
813 ap.alignFrame.setStatus(" ");
818 * Builds a tooltip for the annotation at the current mouse position.
824 void buildToolTip(AlignmentAnnotation ann, int column,
825 AlignmentAnnotation[] anns)
827 if (ann.graphGroup > -1)
829 StringBuilder tip = new StringBuilder(32);
830 tip.append("<html>");
831 for (int i = 0; i < anns.length; i++)
833 if (anns[i].graphGroup == ann.graphGroup
834 && anns[i].annotations[column] != null)
836 tip.append(anns[i].label);
837 String description = anns[i].annotations[column].description;
838 if (description != null && description.length() > 0)
840 tip.append(" ").append(description);
845 if (tip.length() != 6)
847 tip.setLength(tip.length() - 4);
848 this.setToolTipText(tip.toString() + "</html>");
851 else if (ann.annotations[column] != null)
853 String description = ann.annotations[column].description;
854 if (description != null && description.length() > 0)
856 this.setToolTipText(JvSwingUtils.wrapTooltip(true, description));
860 this.setToolTipText(null); // no tooltip if null or empty description
865 // clear the tooltip.
866 this.setToolTipText(null);
871 * Constructs and displays the status bar message
876 void setStatusMessage(int column, AlignmentAnnotation ann)
879 * show alignment column and annotation description if any
881 StringBuilder text = new StringBuilder(32);
882 text.append(MessageManager.getString("label.column")).append(" ")
885 if (ann.annotations[column] != null)
887 String description = ann.annotations[column].description;
888 if (description != null && description.trim().length() > 0)
890 text.append(" ").append(description);
895 * if the annotation is sequence-specific, show the sequence number
896 * in the alignment, and (if not a gap) the residue and position
898 SequenceI seqref = ann.sequenceRef;
901 int seqIndex = av.getAlignment().findIndex(seqref);
904 text.append(", ").append(MessageManager.getString("label.sequence"))
905 .append(" ").append(seqIndex + 1);
906 char residue = seqref.getCharAt(column);
907 if (!Comparison.isGap(residue))
911 if (av.getAlignment().isNucleotide())
913 name = ResidueProperties.nucleotideName
914 .get(String.valueOf(residue));
915 text.append(" Nucleotide: ")
916 .append(name != null ? name : residue);
920 name = 'X' == residue ? "X"
921 : ('*' == residue ? "STOP"
922 : ResidueProperties.aa2Triplet
923 .get(String.valueOf(residue)));
924 text.append(" Residue: ").append(name != null ? name : residue);
926 int residuePos = seqref.findPosition(column);
927 text.append(" (").append(residuePos).append(")");
932 ap.alignFrame.setStatus(text.toString());
942 public void mouseClicked(MouseEvent evt)
944 // if (activeRow != -1)
946 // AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
947 // AlignmentAnnotation anot = aa[activeRow];
951 // TODO mouseClicked-content and drawCursor are quite experimental!
952 public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
955 int pady = av.getCharHeight() / 5;
957 graphics.setColor(Color.black);
958 graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
960 if (av.validCharWidth)
962 graphics.setColor(Color.white);
964 char s = seq.getCharAt(res);
966 charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
967 graphics.drawString(String.valueOf(s), charOffset + x1,
968 (y1 + av.getCharHeight()) - pady);
973 private volatile boolean imageFresh = false;
974 private Rectangle visibleRect = new Rectangle(), clipBounds = new Rectangle();
983 public void paintComponent(Graphics g)
986 // BH: note that this method is generally recommended to
987 // call super.paintComponent(g). Otherwise, the children of this
988 // component will not be rendered. That is not needed here
989 // because AnnotationPanel does not have any children. It is
990 // just a JPanel contained in a JViewPort.
992 computeVisibleRect(visibleRect);
994 g.setColor(Color.white);
995 g.fillRect(0, 0, visibleRect.width, visibleRect.height);
999 // BH 2018 optimizing generation of new Rectangle().
1000 if (fastPaint || (visibleRect.width != (clipBounds = g.getClipBounds(clipBounds)).width)
1001 || (visibleRect.height != clipBounds.height))
1005 g.drawImage(image, 0, 0, this);
1010 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
1011 + 1) * av.getCharWidth();
1017 if (image == null || imgWidth != image.getWidth(this)
1018 || image.getHeight(this) != getHeight())
1022 image = new BufferedImage(imgWidth,
1023 ap.getAnnotationPanel().getHeight(),
1024 BufferedImage.TYPE_INT_RGB);
1025 } catch (OutOfMemoryError oom)
1030 } catch (Exception x)
1035 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1039 gg = (Graphics2D) image.getGraphics();
1043 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1044 RenderingHints.VALUE_ANTIALIAS_ON);
1047 gg.setFont(av.getFont());
1048 fm = gg.getFontMetrics();
1049 gg.setColor(Color.white);
1050 gg.fillRect(0, 0, imgWidth, image.getHeight());
1053 gg = (Graphics2D) image.getGraphics();
1057 drawComponent(gg, av.getRanges().getStartRes(),
1058 av.getRanges().getEndRes() + 1);
1061 g.drawImage(image, 0, 0, this);
1065 * set true to enable redraw timing debug output on stderr
1067 private final boolean debugRedraw = false;
1070 * non-Thread safe repaint
1073 * repaint with horizontal shift in alignment
1075 public void fastPaint(int horizontal)
1077 if ((horizontal == 0) || image == null
1078 || av.getAlignment().getAlignmentAnnotation() == null
1079 || av.getAlignment().getAlignmentAnnotation().length < 1
1080 || av.isCalcInProgress())
1086 int sr = av.getRanges().getStartRes();
1087 int er = av.getRanges().getEndRes() + 1;
1090 Graphics2D gg = (Graphics2D) image.getGraphics();
1092 gg.copyArea(0, 0, imgWidth, getHeight(),
1093 -horizontal * av.getCharWidth(), 0);
1095 if (horizontal > 0) // scrollbar pulled right, image to the left
1097 transX = (er - sr - horizontal) * av.getCharWidth();
1098 sr = er - horizontal;
1100 else if (horizontal < 0)
1102 er = sr - horizontal;
1105 gg.translate(transX, 0);
1107 drawComponent(gg, sr, er);
1109 gg.translate(-transX, 0);
1115 // Call repaint on alignment panel so that repaints from other alignment
1116 // panel components can be aggregated. Otherwise performance of the overview
1117 // window and others may be adversely affected.
1118 av.getAlignPanel().repaint();
1121 private volatile boolean lastImageGood = false;
1133 public void drawComponent(Graphics g, int startRes, int endRes)
1135 BufferedImage oldFaded = fadedImage;
1136 if (av.isCalcInProgress())
1140 lastImageGood = false;
1143 // We'll keep a record of the old image,
1144 // and draw a faded image until the calculation
1147 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1148 || fadedImage.getHeight() != image.getHeight()))
1150 // System.err.println("redraw faded image ("+(fadedImage==null ?
1151 // "null image" : "") + " lastGood="+lastImageGood+")");
1152 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1153 BufferedImage.TYPE_INT_RGB);
1155 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1157 fadedG.setColor(Color.white);
1158 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1160 fadedG.setComposite(
1161 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1162 fadedG.drawImage(image, 0, 0, this);
1165 // make sure we don't overwrite the last good faded image until all
1166 // calculations have finished
1167 lastImageGood = false;
1172 if (fadedImage != null)
1174 oldFaded = fadedImage;
1179 g.setColor(Color.white);
1180 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1182 g.setFont(av.getFont());
1185 fm = g.getFontMetrics();
1188 if ((av.getAlignment().getAlignmentAnnotation() == null)
1189 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1191 g.setColor(Color.white);
1192 g.fillRect(0, 0, getWidth(), getHeight());
1193 g.setColor(Color.black);
1194 if (av.validCharWidth)
1196 g.drawString(MessageManager
1197 .getString("label.alignment_has_no_annotations"), 20, 15);
1202 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1204 if (!lastImageGood && fadedImage == null)
1206 fadedImage = oldFaded;
1211 public FontMetrics getFontMetrics()
1217 public Image getFadedImage()
1223 public int getFadedImageWidth()
1228 private int[] bounds = new int[2];
1231 public int[] getVisibleVRange()
1233 if (ap != null && ap.getAlabels() != null)
1235 int sOffset = -ap.getAlabels().getScrollOffset();
1236 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1237 bounds[0] = sOffset;
1238 bounds[1] = visHeight;
1248 * Try to ensure any references held are nulled
1250 public void dispose()
1260 * I created the renderer so I will dispose of it
1262 if (renderer != null)
1269 public void propertyChange(PropertyChangeEvent evt)
1271 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1272 // Both scrolling and resizing change viewport ranges: scrolling changes
1273 // both start and end points, but resize only changes end values.
1274 // Here we only want to fastpaint on a scroll, with resize using a normal
1275 // paint, so scroll events are identified as changes to the horizontal or
1276 // vertical start value.
1277 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1279 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1281 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1283 fastPaint(((int[]) evt.getNewValue())[0]
1284 - ((int[]) evt.getOldValue())[0]);
1286 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
1293 * computes the visible height of the annotation panel
1295 * @param adjustPanelHeight
1296 * - when false, just adjust existing height according to other
1298 * @param annotationHeight
1299 * @return height to use for the ScrollerPreferredVisibleSize
1301 public int adjustForAlignFrame(boolean adjustPanelHeight,
1302 int annotationHeight)
1305 * Estimate available height in the AlignFrame for alignment +
1306 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1307 * hscroll, status bar, insets.
1309 int stuff = (ap.getViewName() != null ? 30 : 0)
1310 + (Platform.isAMac() ? 120 : 140);
1311 int availableHeight = ap.alignFrame.getHeight() - stuff;
1312 int rowHeight = av.getCharHeight();
1314 if (adjustPanelHeight)
1316 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1319 * If not enough vertical space, maximize annotation height while keeping
1320 * at least two rows of alignment visible
1322 if (annotationHeight + alignmentHeight > availableHeight)
1324 annotationHeight = Math.min(annotationHeight,
1325 availableHeight - 2 * rowHeight);
1330 // maintain same window layout whilst updating sliders
1331 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1332 availableHeight - 2 * rowHeight);
1334 return annotationHeight;