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.AlignmentI;
25 import jalview.datamodel.Annotation;
26 import jalview.datamodel.ColumnSelection;
27 import jalview.datamodel.HiddenColumns;
28 import jalview.datamodel.SequenceI;
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.JColorChooser;
63 import javax.swing.JMenuItem;
64 import javax.swing.JPanel;
65 import javax.swing.JPopupMenu;
66 import javax.swing.Scrollable;
67 import javax.swing.ToolTipManager;
70 * AnnotationPanel displays visible portion of annotation rows below unwrapped
76 public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
77 MouseListener, MouseWheelListener, MouseMotionListener,
78 ActionListener, AdjustmentListener, Scrollable, ViewportListenerI
82 Select, Resize, Undefined
85 String HELIX = MessageManager.getString("label.helix");
87 String SHEET = MessageManager.getString("label.sheet");
90 * For RNA secondary structure "stems" aka helices
92 String STEM = MessageManager.getString("label.rna_helix");
94 String LABEL = MessageManager.getString("label.label");
96 String REMOVE = MessageManager.getString("label.remove_annotation");
98 String COLOUR = MessageManager.getString("action.colour");
100 public final Color HELIX_COLOUR = Color.red.darker();
102 public final Color SHEET_COLOUR = Color.green.darker().darker();
104 public final Color STEM_COLOUR = Color.blue.darker();
107 public AlignViewport av;
111 public int activeRow = -1;
113 public BufferedImage image;
115 public volatile BufferedImage fadedImage;
119 public FontMetrics fm;
121 public int imgWidth = 0;
123 boolean fastPaint = false;
125 // Used For mouse Dragging and resizing graphs
126 int graphStretch = -1;
128 int mouseDragLastX = -1;
130 int mouseDragLastY = -1;
132 DragMode dragMode = DragMode.Undefined;
134 boolean mouseDragging = false;
136 // for editing cursor
141 public final AnnotationRenderer renderer;
143 private MouseWheelListener[] _mwl;
146 * Creates a new AnnotationPanel object.
151 public AnnotationPanel(AlignmentPanel ap)
153 ToolTipManager.sharedInstance().registerComponent(this);
154 ToolTipManager.sharedInstance().setInitialDelay(0);
155 ToolTipManager.sharedInstance().setDismissDelay(10000);
158 this.setLayout(null);
159 addMouseListener(this);
160 addMouseMotionListener(this);
161 ap.annotationScroller.getVerticalScrollBar()
162 .addAdjustmentListener(this);
163 // save any wheel listeners on the scroller, so we can propagate scroll
165 _mwl = ap.annotationScroller.getMouseWheelListeners();
166 // and then set our own listener to consume all mousewheel events
167 ap.annotationScroller.addMouseWheelListener(this);
168 renderer = new AnnotationRenderer();
170 av.getRanges().addPropertyChangeListener(this);
173 public AnnotationPanel(AlignViewport av)
176 renderer = new AnnotationRenderer();
180 public void mouseWheelMoved(MouseWheelEvent e)
185 double wheelRotation = e.getPreciseWheelRotation();
186 if (wheelRotation > 0)
188 av.getRanges().scrollRight(true);
190 else if (wheelRotation < 0)
192 av.getRanges().scrollRight(false);
197 // TODO: find the correct way to let the event bubble up to
198 // ap.annotationScroller
199 for (MouseWheelListener mwl : _mwl)
203 mwl.mouseWheelMoved(e);
214 public Dimension getPreferredScrollableViewportSize()
216 Dimension ps = getPreferredSize();
217 return new Dimension(ps.width, adjustForAlignFrame(false, ps.height));
221 public int getScrollableBlockIncrement(Rectangle visibleRect,
222 int orientation, int direction)
228 public boolean getScrollableTracksViewportHeight()
234 public boolean getScrollableTracksViewportWidth()
240 public int getScrollableUnitIncrement(Rectangle visibleRect,
241 int orientation, int direction)
250 * java.awt.event.AdjustmentListener#adjustmentValueChanged(java.awt.event
254 public void adjustmentValueChanged(AdjustmentEvent evt)
256 // update annotation label display
257 ap.getAlabels().setScrollOffset(-evt.getValue());
261 * Calculates the height of the annotation displayed in the annotation panel.
262 * Callers should normally call the ap.adjustAnnotationHeight method to ensure
263 * all annotation associated components are updated correctly.
266 public int adjustPanelHeight()
268 int height = av.calcPanelHeight();
269 this.setPreferredSize(new Dimension(1, height));
272 // revalidate only when the alignment panel is fully constructed
286 public void actionPerformed(ActionEvent evt)
288 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
293 Annotation[] anot = aa[activeRow].annotations;
295 if (anot.length < av.getColumnSelection().getMax())
297 Annotation[] temp = new Annotation[av.getColumnSelection().getMax()
299 System.arraycopy(anot, 0, temp, 0, anot.length);
301 aa[activeRow].annotations = anot;
304 String action = evt.getActionCommand();
305 if (action.equals(REMOVE))
307 for (int index : av.getColumnSelection().getSelected())
309 if (av.getAlignment().getHiddenColumns().isVisible(index))
315 else if (action.equals(LABEL))
317 String exMesg = collectAnnotVals(anot, LABEL);
318 String label = JvOptionPane.showInputDialog(this,
319 MessageManager.getString("label.enter_label"), exMesg);
326 if ((label.length() > 0) && !aa[activeRow].hasText)
328 aa[activeRow].hasText = true;
331 for (int index : av.getColumnSelection().getSelected())
333 if (!av.getAlignment().getHiddenColumns().isVisible(index))
338 if (anot[index] == null)
340 anot[index] = new Annotation(label, "", ' ', 0);
344 anot[index].displayCharacter = label;
348 else if (action.equals(COLOUR))
350 Color col = JColorChooser.showDialog(this,
351 MessageManager.getString("label.select_foreground_colour"),
354 for (int index : av.getColumnSelection().getSelected())
356 if (!av.getAlignment().getHiddenColumns().isVisible(index))
361 if (anot[index] == null)
363 anot[index] = new Annotation("", "", ' ', 0);
366 anot[index].colour = col;
370 // HELIX, SHEET or STEM
373 String symbol = "\u03B1"; // alpha
375 if (action.equals(HELIX))
379 else if (action.equals(SHEET))
382 symbol = "\u03B2"; // beta
385 // Added by LML to color stems
386 else if (action.equals(STEM))
389 int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
390 symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
393 if (!aa[activeRow].hasIcons)
395 aa[activeRow].hasIcons = true;
398 String label = JvOptionPane.showInputDialog(MessageManager
399 .getString("label.enter_label_for_the_structure"), symbol);
406 if ((label.length() > 0) && !aa[activeRow].hasText)
408 aa[activeRow].hasText = true;
409 if (action.equals(STEM))
411 aa[activeRow].showAllColLabels = true;
414 for (int index : av.getColumnSelection().getSelected())
416 if (!av.getAlignment().getHiddenColumns().isVisible(index))
421 if (anot[index] == null)
423 anot[index] = new Annotation(label, "", type, 0);
426 anot[index].secondaryStructure = type != 'S' ? type
427 : label.length() == 0 ? ' ' : label.charAt(0);
428 anot[index].displayCharacter = label;
433 av.getAlignment().validateAnnotation(aa[activeRow]);
434 ap.alignmentChanged();
435 ap.alignFrame.setMenusForViewport();
443 * Returns any existing annotation concatenated as a string. For each
444 * annotation, takes the description, if any, else the secondary structure
445 * character (if type is HELIX, SHEET or STEM), else the display character (if
452 private String collectAnnotVals(Annotation[] anots, String type)
454 // TODO is this method wanted? why? 'last' is not used
456 StringBuilder collatedInput = new StringBuilder(64);
458 ColumnSelection viscols = av.getColumnSelection();
459 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
462 * the selection list (read-only view) is in selection order, not
463 * column order; make a copy so we can sort it
465 List<Integer> selected = new ArrayList<>(viscols.getSelected());
466 Collections.sort(selected);
467 for (int index : selected)
469 // always check for current display state - just in case
470 if (!hidden.isVisible(index))
474 String tlabel = null;
475 if (anots[index] != null)
476 { // LML added stem code
477 if (type.equals(HELIX) || type.equals(SHEET) || type.equals(STEM)
478 || type.equals(LABEL))
480 tlabel = anots[index].description;
481 if (tlabel == null || tlabel.length() < 1)
483 if (type.equals(HELIX) || type.equals(SHEET)
484 || type.equals(STEM))
486 tlabel = "" + anots[index].secondaryStructure;
490 tlabel = "" + anots[index].displayCharacter;
494 if (tlabel != null && !tlabel.equals(last))
496 if (last.length() > 0)
498 collatedInput.append(" ");
500 collatedInput.append(tlabel);
504 return collatedInput.toString();
508 * Action on right mouse pressed on Mac is to show a pop-up menu for the
509 * annotation. Action on left mouse pressed is to find which annotation is
510 * pressed and mark the start of a column selection or graph resize operation.
515 public void mousePressed(MouseEvent evt)
518 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
523 mouseDragLastX = evt.getX();
524 mouseDragLastY = evt.getY();
527 * add visible annotation heights until we reach the y
528 * position, to find which annotation it is in
533 final int y = evt.getY();
534 for (int i = 0; i < aa.length; i++)
538 height += aa[i].height;
547 else if (aa[i].graph > 0)
550 * we have clicked on a resizable graph annotation
559 * isPopupTrigger fires in mousePressed on Mac,
560 * not until mouseRelease on Windows
562 if (evt.isPopupTrigger() && activeRow != -1)
564 showPopupMenu(y, evt.getX());
568 ap.getScalePanel().mousePressed(evt);
572 * Construct and display a context menu at the right-click position
577 void showPopupMenu(final int y, int x)
579 if (av.getColumnSelection() == null
580 || av.getColumnSelection().isEmpty())
585 JPopupMenu pop = new JPopupMenu(
586 MessageManager.getString("label.structure_type"));
589 * Just display the needed structure options
591 if (av.getAlignment().isNucleotide())
593 item = new JMenuItem(STEM);
594 item.addActionListener(this);
599 item = new JMenuItem(HELIX);
600 item.addActionListener(this);
602 item = new JMenuItem(SHEET);
603 item.addActionListener(this);
606 item = new JMenuItem(LABEL);
607 item.addActionListener(this);
609 item = new JMenuItem(COLOUR);
610 item.addActionListener(this);
612 item = new JMenuItem(REMOVE);
613 item.addActionListener(this);
615 pop.show(this, x, y);
619 * Action on mouse up is to clear mouse drag data and call mouseReleased on
620 * ScalePanel, to deal with defining the selection group (if any) defined by
626 public void mouseReleased(MouseEvent evt)
631 mouseDragging = false;
632 dragMode = DragMode.Undefined;
633 ap.getScalePanel().mouseReleased(evt);
636 * isPopupTrigger is set in mouseReleased on Windows
637 * (in mousePressed on Mac)
639 if (evt.isPopupTrigger() && activeRow != -1)
641 showPopupMenu(evt.getY(), evt.getX());
653 public void mouseEntered(MouseEvent evt)
655 ap.getScalePanel().mouseEntered(evt);
665 public void mouseExited(MouseEvent evt)
667 ap.getScalePanel().mouseExited(evt);
677 public void mouseDragged(MouseEvent evt)
680 * todo: if dragMode is Undefined:
681 * - set to Select if dx > dy
682 * - set to Resize if dy > dx
683 * - do nothing if dx == dy
685 final int x = evt.getX();
686 final int y = evt.getY();
687 if (dragMode == DragMode.Undefined)
689 int dx = Math.abs(x - mouseDragLastX);
690 int dy = Math.abs(y - mouseDragLastY);
691 if (graphStretch == -1 || dx > dy)
694 * mostly horizontal drag, or not a graph annotation
696 dragMode = DragMode.Select;
701 * mostly vertical drag
703 dragMode = DragMode.Resize;
707 if (dragMode == DragMode.Undefined)
710 * drag is diagonal - defer deciding whether to
711 * treat as up/down or left/right
718 if (dragMode == DragMode.Resize)
721 * resize graph annotation if mouse was dragged up or down
723 int deltaY = mouseDragLastY - evt.getY();
726 AlignmentAnnotation graphAnnotation = av.getAlignment()
727 .getAlignmentAnnotation()[graphStretch];
728 int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
729 graphAnnotation.graphHeight = newHeight;
731 ap.paintAlignment(false, false);
737 * for mouse drag left or right, delegate to
738 * ScalePanel to adjust the column selection
740 ap.getScalePanel().mouseDragged(evt);
750 * Constructs the tooltip, and constructs and displays a status message, for
751 * the current mouse position
756 public void mouseMoved(MouseEvent evt)
758 int yPos = evt.getY();
759 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
761 int row = getRowIndex(yPos, aa);
765 this.setToolTipText(null);
769 int column = (evt.getX() / av.getCharWidth())
770 + av.getRanges().getStartRes();
771 column = Math.min(column, av.getRanges().getEndRes());
773 if (av.hasHiddenColumns())
775 column = av.getAlignment().getHiddenColumns()
776 .visibleToAbsoluteColumn(column);
779 AlignmentAnnotation ann = aa[row];
780 if (row > -1 && ann.annotations != null
781 && column < ann.annotations.length)
783 setToolTipText(buildToolTip(ann, column, aa));
784 String msg = getStatusMessage(av.getAlignment(), column, ann);
785 ap.alignFrame.setStatus(msg);
789 this.setToolTipText(null);
790 ap.alignFrame.setStatus(" ");
795 * Answers the index in the annotations array of the visible annotation at the
796 * given y position. This is done by adding the heights of visible annotations
797 * until the y position has been exceeded. Answers -1 if no annotations are
798 * visible, or the y position is below all annotations.
804 static int getRowIndex(int yPos, AlignmentAnnotation[] aa)
813 for (int i = 0; i < aa.length; i++)
817 height += aa[i].height;
830 * Answers a tooltip for the annotation at the current mouse position
836 static String buildToolTip(AlignmentAnnotation ann, int column,
837 AlignmentAnnotation[] anns)
839 String tooltip = null;
840 if (ann.graphGroup > -1)
842 StringBuilder tip = new StringBuilder(32);
843 tip.append("<html>");
844 for (int i = 0; i < anns.length; i++)
846 if (anns[i].graphGroup == ann.graphGroup
847 && anns[i].annotations[column] != null)
849 tip.append(anns[i].label);
850 String description = anns[i].annotations[column].description;
851 if (description != null && description.length() > 0)
853 tip.append(" ").append(description);
858 if (tip.length() != 6)
860 tip.setLength(tip.length() - 4);
861 tooltip = tip.toString() + "</html>";
864 else if (column < ann.annotations.length
865 && ann.annotations[column] != null)
867 String description = ann.annotations[column].description;
868 if (description != null && description.length() > 0)
870 tooltip = JvSwingUtils.wrapTooltip(true, description);
874 tooltip = null; // no tooltip if null or empty description
879 // clear the tooltip.
886 * Constructs and returns the status bar message
892 static String getStatusMessage(AlignmentI al, int column,
893 AlignmentAnnotation ann)
896 * show alignment column and annotation description if any
898 StringBuilder text = new StringBuilder(32);
899 text.append(MessageManager.getString("label.column")).append(" ")
902 if (column < ann.annotations.length && ann.annotations[column] != null)
904 String description = ann.annotations[column].description;
905 if (description != null && description.trim().length() > 0)
907 text.append(" ").append(description);
912 * if the annotation is sequence-specific, show the sequence number
913 * in the alignment, and (if not a gap) the residue and position
915 SequenceI seqref = ann.sequenceRef;
918 int seqIndex = al.findIndex(seqref);
921 text.append(", ").append(MessageManager.getString("label.sequence"))
922 .append(" ").append(seqIndex + 1);
923 char residue = seqref.getCharAt(column);
924 if (!Comparison.isGap(residue))
928 if (al.isNucleotide())
930 name = ResidueProperties.nucleotideName
931 .get(String.valueOf(residue));
932 text.append(" Nucleotide: ")
933 .append(name != null ? name : residue);
937 name = 'X' == residue ? "X"
938 : ('*' == residue ? "STOP"
939 : ResidueProperties.aa2Triplet
940 .get(String.valueOf(residue)));
941 text.append(" Residue: ").append(name != null ? name : residue);
943 int residuePos = seqref.findPosition(column);
944 text.append(" (").append(residuePos).append(")");
949 return text.toString();
959 public void mouseClicked(MouseEvent evt)
961 // if (activeRow != -1)
963 // AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
964 // AlignmentAnnotation anot = aa[activeRow];
968 // TODO mouseClicked-content and drawCursor are quite experimental!
969 public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
972 int pady = av.getCharHeight() / 5;
974 graphics.setColor(Color.black);
975 graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
977 if (av.validCharWidth)
979 graphics.setColor(Color.white);
981 char s = seq.getCharAt(res);
983 charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
984 graphics.drawString(String.valueOf(s), charOffset + x1,
985 (y1 + av.getCharHeight()) - pady);
990 private volatile boolean imageFresh = false;
999 public void paintComponent(Graphics g)
1001 super.paintComponent(g);
1003 g.setColor(Color.white);
1004 g.fillRect(0, 0, getWidth(), getHeight());
1008 if (fastPaint || (getVisibleRect().width != g.getClipBounds().width)
1009 || (getVisibleRect().height != g.getClipBounds().height))
1011 g.drawImage(image, 0, 0, this);
1016 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
1017 + 1) * av.getCharWidth();
1022 if (image == null || imgWidth != image.getWidth(this)
1023 || image.getHeight(this) != getHeight())
1027 image = new BufferedImage(imgWidth,
1028 ap.getAnnotationPanel().getHeight(),
1029 BufferedImage.TYPE_INT_RGB);
1030 } catch (OutOfMemoryError oom)
1035 } catch (Exception x)
1040 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1044 gg = (Graphics2D) image.getGraphics();
1048 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1049 RenderingHints.VALUE_ANTIALIAS_ON);
1052 gg.setFont(av.getFont());
1053 fm = gg.getFontMetrics();
1054 gg.setColor(Color.white);
1055 gg.fillRect(0, 0, imgWidth, image.getHeight());
1059 drawComponent(gg, av.getRanges().getStartRes(),
1060 av.getRanges().getEndRes() + 1);
1062 g.drawImage(image, 0, 0, this);
1066 * set true to enable redraw timing debug output on stderr
1068 private final boolean debugRedraw = false;
1071 * non-Thread safe repaint
1074 * repaint with horizontal shift in alignment
1076 public void fastPaint(int horizontal)
1078 if ((horizontal == 0) || gg == null
1079 || av.getAlignment().getAlignmentAnnotation() == null
1080 || av.getAlignment().getAlignmentAnnotation().length < 1
1081 || av.isCalcInProgress())
1087 int sr = av.getRanges().getStartRes();
1088 int er = av.getRanges().getEndRes() + 1;
1091 gg.copyArea(0, 0, imgWidth, getHeight(),
1092 -horizontal * av.getCharWidth(), 0);
1094 if (horizontal > 0) // scrollbar pulled right, image to the left
1096 transX = (er - sr - horizontal) * av.getCharWidth();
1097 sr = er - horizontal;
1099 else if (horizontal < 0)
1101 er = sr - horizontal;
1104 gg.translate(transX, 0);
1106 drawComponent(gg, sr, er);
1108 gg.translate(-transX, 0);
1112 // Call repaint on alignment panel so that repaints from other alignment
1113 // panel components can be aggregated. Otherwise performance of the overview
1114 // window and others may be adversely affected.
1115 av.getAlignPanel().repaint();
1118 private volatile boolean lastImageGood = false;
1130 public void drawComponent(Graphics g, int startRes, int endRes)
1132 BufferedImage oldFaded = fadedImage;
1133 if (av.isCalcInProgress())
1137 lastImageGood = false;
1140 // We'll keep a record of the old image,
1141 // and draw a faded image until the calculation
1144 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1145 || fadedImage.getHeight() != image.getHeight()))
1147 // System.err.println("redraw faded image ("+(fadedImage==null ?
1148 // "null image" : "") + " lastGood="+lastImageGood+")");
1149 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1150 BufferedImage.TYPE_INT_RGB);
1152 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1154 fadedG.setColor(Color.white);
1155 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1157 fadedG.setComposite(
1158 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1159 fadedG.drawImage(image, 0, 0, this);
1162 // make sure we don't overwrite the last good faded image until all
1163 // calculations have finished
1164 lastImageGood = false;
1169 if (fadedImage != null)
1171 oldFaded = fadedImage;
1176 g.setColor(Color.white);
1177 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1179 g.setFont(av.getFont());
1182 fm = g.getFontMetrics();
1185 if ((av.getAlignment().getAlignmentAnnotation() == null)
1186 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1188 g.setColor(Color.white);
1189 g.fillRect(0, 0, getWidth(), getHeight());
1190 g.setColor(Color.black);
1191 if (av.validCharWidth)
1193 g.drawString(MessageManager
1194 .getString("label.alignment_has_no_annotations"), 20, 15);
1199 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1201 if (!lastImageGood && fadedImage == null)
1203 fadedImage = oldFaded;
1208 public FontMetrics getFontMetrics()
1214 public Image getFadedImage()
1220 public int getFadedImageWidth()
1225 private int[] bounds = new int[2];
1228 public int[] getVisibleVRange()
1230 if (ap != null && ap.getAlabels() != null)
1232 int sOffset = -ap.getAlabels().getScrollOffset();
1233 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1234 bounds[0] = sOffset;
1235 bounds[1] = visHeight;
1245 * Try to ensure any references held are nulled
1247 public void dispose()
1257 * I created the renderer so I will dispose of it
1259 if (renderer != null)
1266 public void propertyChange(PropertyChangeEvent evt)
1268 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1269 // Both scrolling and resizing change viewport ranges: scrolling changes
1270 // both start and end points, but resize only changes end values.
1271 // Here we only want to fastpaint on a scroll, with resize using a normal
1272 // paint, so scroll events are identified as changes to the horizontal or
1273 // vertical start value.
1274 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1276 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1278 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1280 fastPaint(((int[]) evt.getNewValue())[0]
1281 - ((int[]) evt.getOldValue())[0]);
1283 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
1290 * computes the visible height of the annotation panel
1292 * @param adjustPanelHeight
1293 * - when false, just adjust existing height according to other
1295 * @param annotationHeight
1296 * @return height to use for the ScrollerPreferredVisibleSize
1298 public int adjustForAlignFrame(boolean adjustPanelHeight,
1299 int annotationHeight)
1302 * Estimate available height in the AlignFrame for alignment +
1303 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1304 * hscroll, status bar, insets.
1306 int stuff = (ap.getViewName() != null ? 30 : 0)
1307 + (Platform.isAMac() ? 120 : 140);
1308 int availableHeight = ap.alignFrame.getHeight() - stuff;
1309 int rowHeight = av.getCharHeight();
1311 if (adjustPanelHeight)
1313 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1316 * If not enough vertical space, maximize annotation height while keeping
1317 * at least two rows of alignment visible
1319 if (annotationHeight + alignmentHeight > availableHeight)
1321 annotationHeight = Math.min(annotationHeight,
1322 availableHeight - 2 * rowHeight);
1327 // maintain same window layout whilst updating sliders
1328 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1329 availableHeight - 2 * rowHeight);
1331 return annotationHeight;