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;
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;
982 public void paintComponent(Graphics g)
984 super.paintComponent(g);
986 g.setColor(Color.white);
987 g.fillRect(0, 0, getWidth(), getHeight());
991 if (fastPaint || (getVisibleRect().width != g.getClipBounds().width)
992 || (getVisibleRect().height != g.getClipBounds().height))
994 g.drawImage(image, 0, 0, this);
999 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
1000 + 1) * av.getCharWidth();
1005 if (image == null || imgWidth != image.getWidth(this)
1006 || image.getHeight(this) != getHeight())
1010 image = new BufferedImage(imgWidth,
1011 ap.getAnnotationPanel().getHeight(),
1012 BufferedImage.TYPE_INT_RGB);
1013 } catch (OutOfMemoryError oom)
1018 } catch (Exception x)
1023 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1027 gg = (Graphics2D) image.getGraphics();
1031 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1032 RenderingHints.VALUE_ANTIALIAS_ON);
1035 gg.setFont(av.getFont());
1036 fm = gg.getFontMetrics();
1037 gg.setColor(Color.white);
1038 gg.fillRect(0, 0, imgWidth, image.getHeight());
1042 drawComponent(gg, av.getRanges().getStartRes(),
1043 av.getRanges().getEndRes() + 1);
1045 g.drawImage(image, 0, 0, this);
1049 * set true to enable redraw timing debug output on stderr
1051 private final boolean debugRedraw = false;
1054 * non-Thread safe repaint
1057 * repaint with horizontal shift in alignment
1059 public void fastPaint(int horizontal)
1061 if ((horizontal == 0) || gg == null
1062 || av.getAlignment().getAlignmentAnnotation() == null
1063 || av.getAlignment().getAlignmentAnnotation().length < 1
1064 || av.isCalcInProgress())
1070 int sr = av.getRanges().getStartRes();
1071 int er = av.getRanges().getEndRes() + 1;
1074 gg.copyArea(0, 0, imgWidth, getHeight(),
1075 -horizontal * av.getCharWidth(), 0);
1077 if (horizontal > 0) // scrollbar pulled right, image to the left
1079 transX = (er - sr - horizontal) * av.getCharWidth();
1080 sr = er - horizontal;
1082 else if (horizontal < 0)
1084 er = sr - horizontal;
1087 gg.translate(transX, 0);
1089 drawComponent(gg, sr, er);
1091 gg.translate(-transX, 0);
1095 // Call repaint on alignment panel so that repaints from other alignment
1096 // panel components can be aggregated. Otherwise performance of the overview
1097 // window and others may be adversely affected.
1098 av.getAlignPanel().repaint();
1101 private volatile boolean lastImageGood = false;
1113 public void drawComponent(Graphics g, int startRes, int endRes)
1115 BufferedImage oldFaded = fadedImage;
1116 if (av.isCalcInProgress())
1120 lastImageGood = false;
1123 // We'll keep a record of the old image,
1124 // and draw a faded image until the calculation
1127 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1128 || fadedImage.getHeight() != image.getHeight()))
1130 // System.err.println("redraw faded image ("+(fadedImage==null ?
1131 // "null image" : "") + " lastGood="+lastImageGood+")");
1132 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1133 BufferedImage.TYPE_INT_RGB);
1135 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1137 fadedG.setColor(Color.white);
1138 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1140 fadedG.setComposite(
1141 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1142 fadedG.drawImage(image, 0, 0, this);
1145 // make sure we don't overwrite the last good faded image until all
1146 // calculations have finished
1147 lastImageGood = false;
1152 if (fadedImage != null)
1154 oldFaded = fadedImage;
1159 g.setColor(Color.white);
1160 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1162 g.setFont(av.getFont());
1165 fm = g.getFontMetrics();
1168 if ((av.getAlignment().getAlignmentAnnotation() == null)
1169 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1171 g.setColor(Color.white);
1172 g.fillRect(0, 0, getWidth(), getHeight());
1173 g.setColor(Color.black);
1174 if (av.validCharWidth)
1176 g.drawString(MessageManager
1177 .getString("label.alignment_has_no_annotations"), 20, 15);
1182 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1184 if (!lastImageGood && fadedImage == null)
1186 fadedImage = oldFaded;
1191 public FontMetrics getFontMetrics()
1197 public Image getFadedImage()
1203 public int getFadedImageWidth()
1208 private int[] bounds = new int[2];
1211 public int[] getVisibleVRange()
1213 if (ap != null && ap.getAlabels() != null)
1215 int sOffset = -ap.getAlabels().getScrollOffset();
1216 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1217 bounds[0] = sOffset;
1218 bounds[1] = visHeight;
1228 * Try to ensure any references held are nulled
1230 public void dispose()
1240 * I created the renderer so I will dispose of it
1242 if (renderer != null)
1249 public void propertyChange(PropertyChangeEvent evt)
1251 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1252 // Both scrolling and resizing change viewport ranges: scrolling changes
1253 // both start and end points, but resize only changes end values.
1254 // Here we only want to fastpaint on a scroll, with resize using a normal
1255 // paint, so scroll events are identified as changes to the horizontal or
1256 // vertical start value.
1257 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1259 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1261 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1263 fastPaint(((int[]) evt.getNewValue())[0]
1264 - ((int[]) evt.getOldValue())[0]);
1266 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
1273 * computes the visible height of the annotation panel
1275 * @param adjustPanelHeight
1276 * - when false, just adjust existing height according to other
1278 * @param annotationHeight
1279 * @return height to use for the ScrollerPreferredVisibleSize
1281 public int adjustForAlignFrame(boolean adjustPanelHeight,
1282 int annotationHeight)
1285 * Estimate available height in the AlignFrame for alignment +
1286 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1287 * hscroll, status bar, insets.
1289 int stuff = (ap.getViewName() != null ? 30 : 0)
1290 + (Platform.isAMac() ? 120 : 140);
1291 int availableHeight = ap.alignFrame.getHeight() - stuff;
1292 int rowHeight = av.getCharHeight();
1294 if (adjustPanelHeight)
1296 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1299 * If not enough vertical space, maximize annotation height while keeping
1300 * at least two rows of alignment visible
1302 if (annotationHeight + alignmentHeight > availableHeight)
1304 annotationHeight = Math.min(annotationHeight,
1305 availableHeight - 2 * rowHeight);
1310 // maintain same window layout whilst updating sliders
1311 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1312 availableHeight - 2 * rowHeight);
1314 return annotationHeight;