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.gui.JalviewColourChooser.ColourChooserListener;
30 import jalview.renderer.AnnotationRenderer;
31 import jalview.renderer.AwtRenderPanelI;
32 import jalview.schemes.ResidueProperties;
33 import jalview.util.Comparison;
34 import jalview.util.MessageManager;
35 import jalview.util.Platform;
36 import jalview.viewmodel.ViewportListenerI;
37 import jalview.viewmodel.ViewportRanges;
39 import java.awt.AlphaComposite;
40 import java.awt.Color;
41 import java.awt.Dimension;
42 import java.awt.FontMetrics;
43 import java.awt.Graphics;
44 import java.awt.Graphics2D;
45 import java.awt.Image;
46 import java.awt.Rectangle;
47 import java.awt.RenderingHints;
48 import java.awt.event.ActionEvent;
49 import java.awt.event.ActionListener;
50 import java.awt.event.AdjustmentEvent;
51 import java.awt.event.AdjustmentListener;
52 import java.awt.event.MouseEvent;
53 import java.awt.event.MouseListener;
54 import java.awt.event.MouseMotionListener;
55 import java.awt.event.MouseWheelEvent;
56 import java.awt.event.MouseWheelListener;
57 import java.awt.image.BufferedImage;
58 import java.beans.PropertyChangeEvent;
59 import java.util.ArrayList;
60 import java.util.Collections;
61 import java.util.List;
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;
117 // private Graphics2D gg;
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 // setBackground(Color.white); // BH 2019
155 ToolTipManager.sharedInstance().registerComponent(this);
156 ToolTipManager.sharedInstance().setInitialDelay(0);
157 ToolTipManager.sharedInstance().setDismissDelay(10000);
160 this.setLayout(null);
161 addMouseListener(this);
162 addMouseMotionListener(this);
163 ap.annotationScroller.getVerticalScrollBar()
164 .addAdjustmentListener(this);
165 // save any wheel listeners on the scroller, so we can propagate scroll
167 _mwl = ap.annotationScroller.getMouseWheelListeners();
168 // and then set our own listener to consume all mousewheel events
169 ap.annotationScroller.addMouseWheelListener(this);
170 renderer = new AnnotationRenderer();
172 av.getRanges().addPropertyChangeListener(this);
175 public AnnotationPanel(AlignViewport av)
178 renderer = new AnnotationRenderer();
182 public void mouseWheelMoved(MouseWheelEvent e)
187 double wheelRotation = e.getPreciseWheelRotation();
188 if (wheelRotation > 0)
190 av.getRanges().scrollRight(true);
192 else if (wheelRotation < 0)
194 av.getRanges().scrollRight(false);
199 // TODO: find the correct way to let the event bubble up to
200 // ap.annotationScroller
201 for (MouseWheelListener mwl : _mwl)
205 mwl.mouseWheelMoved(e);
216 public Dimension getPreferredScrollableViewportSize()
218 Dimension ps = getPreferredSize();
219 return new Dimension(ps.width, adjustForAlignFrame(false, ps.height));
223 public int getScrollableBlockIncrement(Rectangle visibleRect,
224 int orientation, int direction)
230 public boolean getScrollableTracksViewportHeight()
236 public boolean getScrollableTracksViewportWidth()
242 public int getScrollableUnitIncrement(Rectangle visibleRect,
243 int orientation, int direction)
252 * java.awt.event.AdjustmentListener#adjustmentValueChanged(java.awt.event
256 public void adjustmentValueChanged(AdjustmentEvent evt)
258 // update annotation label display
259 ap.getAlabels().setScrollOffset(-evt.getValue());
263 * Calculates the height of the annotation displayed in the annotation panel.
264 * Callers should normally call the ap.adjustAnnotationHeight method to ensure
265 * all annotation associated components are updated correctly.
268 public int adjustPanelHeight()
270 int height = av.calcPanelHeight();
271 this.setPreferredSize(new Dimension(1, height));
274 // revalidate only when the alignment panel is fully constructed
288 public void actionPerformed(ActionEvent evt)
290 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
295 Annotation[] anot = aa[activeRow].annotations;
297 if (anot.length < av.getColumnSelection().getMax())
299 Annotation[] temp = new Annotation[av.getColumnSelection().getMax()
301 System.arraycopy(anot, 0, temp, 0, anot.length);
303 aa[activeRow].annotations = anot;
306 String action = evt.getActionCommand();
307 if (action.equals(REMOVE))
309 for (int index : av.getColumnSelection().getSelected())
311 if (av.getAlignment().getHiddenColumns().isVisible(index))
317 else if (action.equals(LABEL))
319 String exMesg = collectAnnotVals(anot, LABEL);
320 String label = JvOptionPane.showInputDialog(
321 MessageManager.getString("label.enter_label"), exMesg);
328 if ((label.length() > 0) && !aa[activeRow].hasText)
330 aa[activeRow].hasText = true;
333 for (int index : av.getColumnSelection().getSelected())
335 if (!av.getAlignment().getHiddenColumns().isVisible(index))
340 if (anot[index] == null)
342 anot[index] = new Annotation(label, "", ' ', 0);
346 anot[index].displayCharacter = label;
350 else if (action.equals(COLOUR))
352 final Annotation[] fAnot = anot;
353 String title = MessageManager
354 .getString("label.select_foreground_colour");
355 ColourChooserListener listener = new ColourChooserListener()
358 public void colourSelected(Color c)
360 HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
361 for (int index : av.getColumnSelection().getSelected())
363 if (hiddenColumns.isVisible(index))
365 if (fAnot[index] == null)
367 fAnot[index] = new Annotation("", "", ' ', 0);
369 fAnot[index].colour = c;
373 JalviewColourChooser.showColourChooser(this,
374 title, Color.black, listener);
377 // HELIX, SHEET or STEM
380 String symbol = "\u03B1"; // alpha
382 if (action.equals(HELIX))
386 else if (action.equals(SHEET))
389 symbol = "\u03B2"; // beta
392 // Added by LML to color stems
393 else if (action.equals(STEM))
396 int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
397 symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
400 if (!aa[activeRow].hasIcons)
402 aa[activeRow].hasIcons = true;
405 String label = JvOptionPane.showInputDialog(MessageManager
406 .getString("label.enter_label_for_the_structure"), symbol);
413 if ((label.length() > 0) && !aa[activeRow].hasText)
415 aa[activeRow].hasText = true;
416 if (action.equals(STEM))
418 aa[activeRow].showAllColLabels = true;
421 for (int index : av.getColumnSelection().getSelected())
423 if (!av.getAlignment().getHiddenColumns().isVisible(index))
428 if (anot[index] == null)
430 anot[index] = new Annotation(label, "", type, 0);
433 anot[index].secondaryStructure = type != 'S' ? type
434 : label.length() == 0 ? ' ' : label.charAt(0);
435 anot[index].displayCharacter = label;
440 av.getAlignment().validateAnnotation(aa[activeRow]);
441 ap.alignmentChanged();
442 ap.alignFrame.setMenusForViewport();
450 * Returns any existing annotation concatenated as a string. For each
451 * annotation, takes the description, if any, else the secondary structure
452 * character (if type is HELIX, SHEET or STEM), else the display character (if
459 private String collectAnnotVals(Annotation[] anots, String type)
461 // TODO is this method wanted? why? 'last' is not used
463 StringBuilder collatedInput = new StringBuilder(64);
465 ColumnSelection viscols = av.getColumnSelection();
466 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
469 * the selection list (read-only view) is in selection order, not
470 * column order; make a copy so we can sort it
472 List<Integer> selected = new ArrayList<>(viscols.getSelected());
473 Collections.sort(selected);
474 for (int index : selected)
476 // always check for current display state - just in case
477 if (!hidden.isVisible(index))
481 String tlabel = null;
482 if (anots[index] != null)
483 { // LML added stem code
484 if (type.equals(HELIX) || type.equals(SHEET) || type.equals(STEM)
485 || type.equals(LABEL))
487 tlabel = anots[index].description;
488 if (tlabel == null || tlabel.length() < 1)
490 if (type.equals(HELIX) || type.equals(SHEET)
491 || type.equals(STEM))
493 tlabel = "" + anots[index].secondaryStructure;
497 tlabel = "" + anots[index].displayCharacter;
501 if (tlabel != null && !tlabel.equals(last))
503 if (last.length() > 0)
505 collatedInput.append(" ");
507 collatedInput.append(tlabel);
511 return collatedInput.toString();
515 * Action on right mouse pressed on Mac is to show a pop-up menu for the
516 * annotation. Action on left mouse pressed is to find which annotation is
517 * pressed and mark the start of a column selection or graph resize operation.
522 public void mousePressed(MouseEvent evt)
525 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
530 mouseDragLastX = evt.getX();
531 mouseDragLastY = evt.getY();
534 * add visible annotation heights until we reach the y
535 * position, to find which annotation it is in
540 final int y = evt.getY();
541 for (int i = 0; i < aa.length; i++)
545 height += aa[i].height;
554 else if (aa[i].graph > 0)
557 * we have clicked on a resizable graph annotation
566 * isPopupTrigger fires in mousePressed on Mac,
567 * not until mouseRelease on Windows
569 if (evt.isPopupTrigger() && activeRow != -1)
571 showPopupMenu(y, evt.getX());
575 ap.getScalePanel().mousePressed(evt);
579 * Construct and display a context menu at the right-click position
584 void showPopupMenu(final int y, int x)
586 if (av.getColumnSelection() == null
587 || av.getColumnSelection().isEmpty())
592 JPopupMenu pop = new JPopupMenu(
593 MessageManager.getString("label.structure_type"));
596 * Just display the needed structure options
598 if (av.getAlignment().isNucleotide())
600 item = new JMenuItem(STEM);
601 item.addActionListener(this);
606 item = new JMenuItem(HELIX);
607 item.addActionListener(this);
609 item = new JMenuItem(SHEET);
610 item.addActionListener(this);
613 item = new JMenuItem(LABEL);
614 item.addActionListener(this);
616 item = new JMenuItem(COLOUR);
617 item.addActionListener(this);
619 item = new JMenuItem(REMOVE);
620 item.addActionListener(this);
622 pop.show(this, x, y);
626 * Action on mouse up is to clear mouse drag data and call mouseReleased on
627 * ScalePanel, to deal with defining the selection group (if any) defined by
633 public void mouseReleased(MouseEvent evt)
638 mouseDragging = false;
639 dragMode = DragMode.Undefined;
640 ap.getScalePanel().mouseReleased(evt);
643 * isPopupTrigger is set in mouseReleased on Windows
644 * (in mousePressed on Mac)
646 if (evt.isPopupTrigger() && activeRow != -1)
648 showPopupMenu(evt.getY(), evt.getX());
660 public void mouseEntered(MouseEvent evt)
662 this.mouseDragging = false;
663 ap.getScalePanel().mouseEntered(evt);
667 * On leaving the panel, calls ScalePanel.mouseExited to deal with scrolling
668 * with column selection on a mouse drag
673 public void mouseExited(MouseEvent evt)
675 ap.getScalePanel().mouseExited(evt);
685 public void mouseDragged(MouseEvent evt)
688 * todo: if dragMode is Undefined:
689 * - set to Select if dx > dy
690 * - set to Resize if dy > dx
691 * - do nothing if dx == dy
693 final int x = evt.getX();
694 final int y = evt.getY();
695 if (dragMode == DragMode.Undefined)
697 int dx = Math.abs(x - mouseDragLastX);
698 int dy = Math.abs(y - mouseDragLastY);
699 if (graphStretch == -1 || dx > dy)
702 * mostly horizontal drag, or not a graph annotation
704 dragMode = DragMode.Select;
709 * mostly vertical drag
711 dragMode = DragMode.Resize;
715 if (dragMode == DragMode.Undefined)
718 * drag is diagonal - defer deciding whether to
719 * treat as up/down or left/right
726 if (dragMode == DragMode.Resize)
729 * resize graph annotation if mouse was dragged up or down
731 int deltaY = mouseDragLastY - evt.getY();
734 AlignmentAnnotation graphAnnotation = av.getAlignment()
735 .getAlignmentAnnotation()[graphStretch];
736 int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
737 graphAnnotation.graphHeight = newHeight;
739 ap.paintAlignment(false, false);
745 * for mouse drag left or right, delegate to
746 * ScalePanel to adjust the column selection
748 ap.getScalePanel().mouseDragged(evt);
758 * Constructs the tooltip, and constructs and displays a status message, for
759 * the current mouse position
764 public void mouseMoved(MouseEvent evt)
766 int yPos = evt.getY();
767 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
769 int row = getRowIndex(yPos, aa);
773 this.setToolTipText(null);
777 int column = (evt.getX() / av.getCharWidth())
778 + av.getRanges().getStartRes();
779 column = Math.min(column, av.getRanges().getEndRes());
781 if (av.hasHiddenColumns())
783 column = av.getAlignment().getHiddenColumns()
784 .visibleToAbsoluteColumn(column);
787 AlignmentAnnotation ann = aa[row];
788 if (row > -1 && ann.annotations != null
789 && column < ann.annotations.length)
791 setToolTipText(buildToolTip(ann, column, aa));
792 String msg = getStatusMessage(av.getAlignment(), column, ann);
793 ap.alignFrame.setStatus(msg);
797 this.setToolTipText(null);
798 ap.alignFrame.setStatus(" ");
803 * Answers the index in the annotations array of the visible annotation at the
804 * given y position. This is done by adding the heights of visible annotations
805 * until the y position has been exceeded. Answers -1 if no annotations are
806 * visible, or the y position is below all annotations.
812 static int getRowIndex(int yPos, AlignmentAnnotation[] aa)
821 for (int i = 0; i < aa.length; i++)
825 height += aa[i].height;
838 * Answers a tooltip for the annotation at the current mouse position
844 static String buildToolTip(AlignmentAnnotation ann, int column,
845 AlignmentAnnotation[] anns)
847 String tooltip = null;
848 if (ann.graphGroup > -1)
850 StringBuilder tip = new StringBuilder(32);
851 tip.append("<html>");
852 for (int i = 0; i < anns.length; i++)
854 if (anns[i].graphGroup == ann.graphGroup
855 && anns[i].annotations[column] != null)
857 tip.append(anns[i].label);
858 String description = anns[i].annotations[column].description;
859 if (description != null && description.length() > 0)
861 tip.append(" ").append(description);
866 if (tip.length() != 6)
868 tip.setLength(tip.length() - 4);
869 tooltip = tip.toString() + "</html>";
872 else if (column < ann.annotations.length
873 && ann.annotations[column] != null)
875 String description = ann.annotations[column].description;
876 if (description != null && description.length() > 0)
878 tooltip = JvSwingUtils.wrapTooltip(true, description);
882 tooltip = null; // no tooltip if null or empty description
887 // clear the tooltip.
894 * Constructs and returns the status bar message
900 static String getStatusMessage(AlignmentI al, int column,
901 AlignmentAnnotation ann)
904 * show alignment column and annotation description if any
906 StringBuilder text = new StringBuilder(32);
907 text.append(MessageManager.getString("label.column")).append(" ")
910 if (column < ann.annotations.length && ann.annotations[column] != null)
912 String description = ann.annotations[column].description;
913 if (description != null && description.trim().length() > 0)
915 text.append(" ").append(description);
920 * if the annotation is sequence-specific, show the sequence number
921 * in the alignment, and (if not a gap) the residue and position
923 SequenceI seqref = ann.sequenceRef;
926 int seqIndex = al.findIndex(seqref);
929 text.append(", ").append(MessageManager.getString("label.sequence"))
930 .append(" ").append(seqIndex + 1);
931 char residue = seqref.getCharAt(column);
932 if (!Comparison.isGap(residue))
936 if (al.isNucleotide())
938 name = ResidueProperties.nucleotideName
939 .get(String.valueOf(residue));
940 text.append(" Nucleotide: ")
941 .append(name != null ? name : residue);
945 name = 'X' == residue ? "X"
946 : ('*' == residue ? "STOP"
947 : ResidueProperties.aa2Triplet
948 .get(String.valueOf(residue)));
949 text.append(" Residue: ").append(name != null ? name : residue);
951 int residuePos = seqref.findPosition(column);
952 text.append(" (").append(residuePos).append(")");
957 return text.toString();
967 public void mouseClicked(MouseEvent evt)
969 // if (activeRow != -1)
971 // AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
972 // AlignmentAnnotation anot = aa[activeRow];
976 // TODO mouseClicked-content and drawCursor are quite experimental!
977 public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
980 int pady = av.getCharHeight() / 5;
982 graphics.setColor(Color.black);
983 graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
985 if (av.validCharWidth)
987 graphics.setColor(Color.white);
989 char s = seq.getCharAt(res);
991 charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
992 graphics.drawString(String.valueOf(s), charOffset + x1,
993 (y1 + av.getCharHeight()) - pady);
998 private volatile boolean imageFresh = false;
999 private Rectangle visibleRect = new Rectangle(), clipBounds = new Rectangle();
1008 public void paintComponent(Graphics g)
1011 // BH: note that this method is generally recommended to
1012 // call super.paintComponent(g). Otherwise, the children of this
1013 // component will not be rendered. That is not needed here
1014 // because AnnotationPanel does not have any children. It is
1015 // just a JPanel contained in a JViewPort.
1017 computeVisibleRect(visibleRect);
1019 g.setColor(Color.white);
1020 g.fillRect(0, 0, visibleRect.width, visibleRect.height);
1024 // BH 2018 optimizing generation of new Rectangle().
1025 if (fastPaint || (visibleRect.width != (clipBounds = g.getClipBounds(clipBounds)).width)
1026 || (visibleRect.height != clipBounds.height))
1030 g.drawImage(image, 0, 0, this);
1035 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
1036 + 1) * av.getCharWidth();
1042 if (image == null || imgWidth != image.getWidth(this)
1043 || image.getHeight(this) != getHeight())
1047 image = new BufferedImage(imgWidth,
1048 ap.getAnnotationPanel().getHeight(),
1049 BufferedImage.TYPE_INT_RGB);
1050 } catch (OutOfMemoryError oom)
1055 } catch (Exception x)
1060 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1064 gg = (Graphics2D) image.getGraphics();
1068 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1069 RenderingHints.VALUE_ANTIALIAS_ON);
1072 gg.setFont(av.getFont());
1073 fm = gg.getFontMetrics();
1074 gg.setColor(Color.white);
1075 gg.fillRect(0, 0, imgWidth, image.getHeight());
1078 gg = (Graphics2D) image.getGraphics();
1082 drawComponent(gg, av.getRanges().getStartRes(),
1083 av.getRanges().getEndRes() + 1);
1086 g.drawImage(image, 0, 0, this);
1090 * set true to enable redraw timing debug output on stderr
1092 private final boolean debugRedraw = false;
1095 * non-Thread safe repaint
1098 * repaint with horizontal shift in alignment
1100 public void fastPaint(int horizontal)
1102 if ((horizontal == 0) || image == null
1103 || av.getAlignment().getAlignmentAnnotation() == null
1104 || av.getAlignment().getAlignmentAnnotation().length < 1
1105 || av.isCalcInProgress())
1111 int sr = av.getRanges().getStartRes();
1112 int er = av.getRanges().getEndRes() + 1;
1115 Graphics2D gg = (Graphics2D) image.getGraphics();
1117 gg.copyArea(0, 0, imgWidth, getHeight(),
1118 -horizontal * av.getCharWidth(), 0);
1120 if (horizontal > 0) // scrollbar pulled right, image to the left
1122 transX = (er - sr - horizontal) * av.getCharWidth();
1123 sr = er - horizontal;
1125 else if (horizontal < 0)
1127 er = sr - horizontal;
1130 gg.translate(transX, 0);
1132 drawComponent(gg, sr, er);
1134 gg.translate(-transX, 0);
1140 // Call repaint on alignment panel so that repaints from other alignment
1141 // panel components can be aggregated. Otherwise performance of the overview
1142 // window and others may be adversely affected.
1143 av.getAlignPanel().repaint();
1146 private volatile boolean lastImageGood = false;
1158 public void drawComponent(Graphics g, int startRes, int endRes)
1160 BufferedImage oldFaded = fadedImage;
1161 if (av.isCalcInProgress())
1165 lastImageGood = false;
1168 // We'll keep a record of the old image,
1169 // and draw a faded image until the calculation
1172 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1173 || fadedImage.getHeight() != image.getHeight()))
1175 // System.err.println("redraw faded image ("+(fadedImage==null ?
1176 // "null image" : "") + " lastGood="+lastImageGood+")");
1177 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1178 BufferedImage.TYPE_INT_RGB);
1180 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1182 fadedG.setColor(Color.white);
1183 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1185 fadedG.setComposite(
1186 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1187 fadedG.drawImage(image, 0, 0, this);
1190 // make sure we don't overwrite the last good faded image until all
1191 // calculations have finished
1192 lastImageGood = false;
1197 if (fadedImage != null)
1199 oldFaded = fadedImage;
1204 g.setColor(Color.white);
1205 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1207 g.setFont(av.getFont());
1210 fm = g.getFontMetrics();
1213 if ((av.getAlignment().getAlignmentAnnotation() == null)
1214 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1216 g.setColor(Color.white);
1217 g.fillRect(0, 0, getWidth(), getHeight());
1218 g.setColor(Color.black);
1219 if (av.validCharWidth)
1221 g.drawString(MessageManager
1222 .getString("label.alignment_has_no_annotations"), 20, 15);
1227 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1229 if (!lastImageGood && fadedImage == null)
1231 fadedImage = oldFaded;
1236 public FontMetrics getFontMetrics()
1242 public Image getFadedImage()
1248 public int getFadedImageWidth()
1253 private int[] bounds = new int[2];
1256 public int[] getVisibleVRange()
1258 if (ap != null && ap.getAlabels() != null)
1260 int sOffset = -ap.getAlabels().getScrollOffset();
1261 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1262 bounds[0] = sOffset;
1263 bounds[1] = visHeight;
1273 * Try to ensure any references held are nulled
1275 public void dispose()
1285 * I created the renderer so I will dispose of it
1287 if (renderer != null)
1294 public void propertyChange(PropertyChangeEvent evt)
1296 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1297 // Both scrolling and resizing change viewport ranges: scrolling changes
1298 // both start and end points, but resize only changes end values.
1299 // Here we only want to fastpaint on a scroll, with resize using a normal
1300 // paint, so scroll events are identified as changes to the horizontal or
1301 // vertical start value.
1302 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1304 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1306 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1308 fastPaint(((int[]) evt.getNewValue())[0]
1309 - ((int[]) evt.getOldValue())[0]);
1311 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
1318 * computes the visible height of the annotation panel
1320 * @param adjustPanelHeight
1321 * - when false, just adjust existing height according to other
1323 * @param annotationHeight
1324 * @return height to use for the ScrollerPreferredVisibleSize
1326 public int adjustForAlignFrame(boolean adjustPanelHeight,
1327 int annotationHeight)
1330 * Estimate available height in the AlignFrame for alignment +
1331 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1332 * hscroll, status bar, insets.
1334 int stuff = (ap.getViewName() != null ? 30 : 0)
1335 + (Platform.isAMacAndNotJS() ? 120 : 140);
1336 int availableHeight = ap.alignFrame.getHeight() - stuff;
1337 int rowHeight = av.getCharHeight();
1339 if (adjustPanelHeight)
1341 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1344 * If not enough vertical space, maximize annotation height while keeping
1345 * at least two rows of alignment visible
1347 if (annotationHeight + alignmentHeight > availableHeight)
1349 annotationHeight = Math.min(annotationHeight,
1350 availableHeight - 2 * rowHeight);
1355 // maintain same window layout whilst updating sliders
1356 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1357 availableHeight - 2 * rowHeight);
1359 return annotationHeight;