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 java.awt.AlphaComposite;
24 import java.awt.Color;
25 import java.awt.Dimension;
26 import java.awt.FontMetrics;
27 import java.awt.Graphics;
28 import java.awt.Graphics2D;
29 import java.awt.Image;
30 import java.awt.Rectangle;
31 import java.awt.RenderingHints;
32 import java.awt.event.ActionEvent;
33 import java.awt.event.ActionListener;
34 import java.awt.event.AdjustmentEvent;
35 import java.awt.event.AdjustmentListener;
36 import java.awt.event.MouseEvent;
37 import java.awt.event.MouseListener;
38 import java.awt.event.MouseMotionListener;
39 import java.awt.event.MouseWheelEvent;
40 import java.awt.event.MouseWheelListener;
41 import java.awt.image.BufferedImage;
42 import java.beans.PropertyChangeEvent;
43 import java.util.ArrayList;
44 import java.util.Collections;
45 import java.util.List;
47 import javax.swing.JMenuItem;
48 import javax.swing.JPanel;
49 import javax.swing.JPopupMenu;
50 import javax.swing.Scrollable;
51 import javax.swing.ToolTipManager;
53 import jalview.datamodel.AlignmentAnnotation;
54 import jalview.datamodel.AlignmentI;
55 import jalview.datamodel.Annotation;
56 import jalview.datamodel.ColumnSelection;
57 import jalview.datamodel.HiddenColumns;
58 import jalview.datamodel.SequenceI;
59 import jalview.gui.JalviewColourChooser.ColourChooserListener;
60 import jalview.renderer.AnnotationRenderer;
61 import jalview.renderer.AwtRenderPanelI;
62 import jalview.schemes.ResidueProperties;
63 import jalview.util.Comparison;
64 import jalview.util.MessageManager;
65 import jalview.util.Platform;
66 import jalview.viewmodel.ViewportListenerI;
67 import jalview.viewmodel.ViewportRanges;
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 setName("AnnotationPanel");
154 ToolTipManager.sharedInstance().registerComponent(this);
155 ToolTipManager.sharedInstance().setInitialDelay(0);
156 ToolTipManager.sharedInstance().setDismissDelay(10000);
159 this.setLayout(null);
160 addMouseListener(this);
161 addMouseMotionListener(this);
162 ap.annotationScroller.getVerticalScrollBar()
163 .addAdjustmentListener(this);
164 // save any wheel listeners on the scroller, so we can propagate scroll
166 _mwl = ap.annotationScroller.getMouseWheelListeners();
167 // and then set our own listener to consume all mousewheel events
168 ap.annotationScroller.addMouseWheelListener(this);
169 renderer = new AnnotationRenderer();
171 av.getRanges().addPropertyChangeListener(this);
174 public AnnotationPanel(AlignViewport av)
177 renderer = new AnnotationRenderer();
181 public void mouseWheelMoved(MouseWheelEvent e)
186 double wheelRotation = e.getPreciseWheelRotation();
187 if (wheelRotation > 0)
189 av.getRanges().scrollRight(true);
191 else if (wheelRotation < 0)
193 av.getRanges().scrollRight(false);
198 // TODO: find the correct way to let the event bubble up to
199 // ap.annotationScroller
200 for (MouseWheelListener mwl : _mwl)
204 mwl.mouseWheelMoved(e);
215 public Dimension getPreferredScrollableViewportSize()
217 Dimension ps = getPreferredSize();
218 return new Dimension(ps.width, adjustForAlignFrame(false, ps.height));
222 public int getScrollableBlockIncrement(Rectangle visibleRect,
223 int orientation, int direction)
229 public boolean getScrollableTracksViewportHeight()
235 public boolean getScrollableTracksViewportWidth()
241 public int getScrollableUnitIncrement(Rectangle visibleRect,
242 int orientation, int direction)
251 * java.awt.event.AdjustmentListener#adjustmentValueChanged(java.awt.event
255 public void adjustmentValueChanged(AdjustmentEvent evt)
257 // update annotation label display
258 ap.getAlabels().setScrollOffset(-evt.getValue());
262 * Calculates the height of the annotation displayed in the annotation panel.
263 * Callers should normally call the ap.adjustAnnotationHeight method to ensure
264 * all annotation associated components are updated correctly.
267 public int adjustPanelHeight()
269 int height = av.calcPanelHeight();
270 this.setPreferredSize(new Dimension(1, height));
273 // revalidate only when the alignment panel is fully constructed
287 public void actionPerformed(ActionEvent evt)
289 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
294 Annotation[] anot = aa[activeRow].annotations;
296 if (anot.length < av.getColumnSelection().getMax())
298 Annotation[] temp = new Annotation[av.getColumnSelection().getMax()
300 System.arraycopy(anot, 0, temp, 0, anot.length);
302 aa[activeRow].annotations = anot;
305 String action = evt.getActionCommand();
306 if (action.equals(REMOVE))
308 for (int index : av.getColumnSelection().getSelected())
310 if (av.getAlignment().getHiddenColumns().isVisible(index))
316 else if (action.equals(LABEL))
318 String exMesg = collectAnnotVals(anot, LABEL);
319 String label = JvOptionPane.showInputDialog(
320 MessageManager.getString("label.enter_label"), exMesg);
327 if ((label.length() > 0) && !aa[activeRow].hasText)
329 aa[activeRow].hasText = true;
332 for (int index : av.getColumnSelection().getSelected())
334 if (!av.getAlignment().getHiddenColumns().isVisible(index))
339 if (anot[index] == null)
341 anot[index] = new Annotation(label, "", ' ', 0);
345 anot[index].displayCharacter = label;
349 else if (action.equals(COLOUR))
351 final Annotation[] fAnot = anot;
352 String title = MessageManager
353 .getString("label.select_foreground_colour");
354 ColourChooserListener listener = new ColourChooserListener()
357 public void colourSelected(Color c)
359 HiddenColumns hiddenColumns = av.getAlignment()
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;
374 JalviewColourChooser.showColourChooser(this, title, Color.black,
378 // HELIX, SHEET or STEM
381 String symbol = "\u03B1"; // alpha
383 if (action.equals(HELIX))
387 else if (action.equals(SHEET))
390 symbol = "\u03B2"; // beta
393 // Added by LML to color stems
394 else if (action.equals(STEM))
397 int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
398 symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
401 if (!aa[activeRow].hasIcons)
403 aa[activeRow].hasIcons = true;
406 String label = JvOptionPane.showInputDialog(MessageManager
407 .getString("label.enter_label_for_the_structure"), symbol);
414 if ((label.length() > 0) && !aa[activeRow].hasText)
416 aa[activeRow].hasText = true;
417 if (action.equals(STEM))
419 aa[activeRow].showAllColLabels = true;
422 for (int index : av.getColumnSelection().getSelected())
424 if (!av.getAlignment().getHiddenColumns().isVisible(index))
429 if (anot[index] == null)
431 anot[index] = new Annotation(label, "", type, 0);
434 anot[index].secondaryStructure = type != 'S' ? type
435 : label.length() == 0 ? ' ' : label.charAt(0);
436 anot[index].displayCharacter = label;
441 av.getAlignment().validateAnnotation(aa[activeRow]);
442 ap.alignmentChanged();
443 ap.alignFrame.setMenusForViewport();
451 * Returns any existing annotation concatenated as a string. For each
452 * annotation, takes the description, if any, else the secondary structure
453 * character (if type is HELIX, SHEET or STEM), else the display character (if
460 private String collectAnnotVals(Annotation[] anots, String type)
462 // TODO is this method wanted? why? 'last' is not used
464 StringBuilder collatedInput = new StringBuilder(64);
466 ColumnSelection viscols = av.getColumnSelection();
467 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
470 * the selection list (read-only view) is in selection order, not
471 * column order; make a copy so we can sort it
473 List<Integer> selected = new ArrayList<>(viscols.getSelected());
474 Collections.sort(selected);
475 for (int index : selected)
477 // always check for current display state - just in case
478 if (!hidden.isVisible(index))
482 String tlabel = null;
483 if (anots[index] != null)
484 { // LML added stem code
485 if (type.equals(HELIX) || type.equals(SHEET) || type.equals(STEM)
486 || type.equals(LABEL))
488 tlabel = anots[index].description;
489 if (tlabel == null || tlabel.length() < 1)
491 if (type.equals(HELIX) || type.equals(SHEET)
492 || type.equals(STEM))
494 tlabel = "" + anots[index].secondaryStructure;
498 tlabel = "" + anots[index].displayCharacter;
502 if (tlabel != null && !tlabel.equals(last))
504 if (last.length() > 0)
506 collatedInput.append(" ");
508 collatedInput.append(tlabel);
512 return collatedInput.toString();
516 * Action on right mouse pressed on Mac is to show a pop-up menu for the
517 * annotation. Action on left mouse pressed is to find which annotation is
518 * pressed and mark the start of a column selection or graph resize operation.
523 public void mousePressed(MouseEvent evt)
526 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
531 mouseDragLastX = evt.getX();
532 mouseDragLastY = evt.getY();
535 * add visible annotation heights until we reach the y
536 * position, to find which annotation it is in
541 final int y = evt.getY();
542 for (int i = 0; i < aa.length; i++)
546 height += aa[i].height;
555 else if (aa[i].graph > 0)
558 * we have clicked on a resizable graph annotation
567 * isPopupTrigger fires in mousePressed on Mac,
568 * not until mouseRelease on Windows
570 if (evt.isPopupTrigger() && activeRow != -1)
572 showPopupMenu(y, evt.getX());
576 ap.getScalePanel().mousePressed(evt);
580 * Construct and display a context menu at the right-click position
585 void showPopupMenu(final int y, int x)
587 if (av.getColumnSelection() == null
588 || av.getColumnSelection().isEmpty())
593 JPopupMenu pop = new JPopupMenu(
594 MessageManager.getString("label.structure_type"));
597 * Just display the needed structure options
599 if (av.getAlignment().isNucleotide())
601 item = new JMenuItem(STEM);
602 item.addActionListener(this);
607 item = new JMenuItem(HELIX);
608 item.addActionListener(this);
610 item = new JMenuItem(SHEET);
611 item.addActionListener(this);
614 item = new JMenuItem(LABEL);
615 item.addActionListener(this);
617 item = new JMenuItem(COLOUR);
618 item.addActionListener(this);
620 item = new JMenuItem(REMOVE);
621 item.addActionListener(this);
623 pop.show(this, x, y);
627 * Action on mouse up is to clear mouse drag data and call mouseReleased on
628 * ScalePanel, to deal with defining the selection group (if any) defined by
634 public void mouseReleased(MouseEvent evt)
639 mouseDragging = false;
640 if (dragMode == DragMode.Resize)
642 ap.adjustAnnotationHeight();
644 dragMode = DragMode.Undefined;
645 ap.getScalePanel().mouseReleased(evt);
648 * isPopupTrigger is set in mouseReleased on Windows
649 * (in mousePressed on Mac)
651 if (evt.isPopupTrigger() && activeRow != -1)
653 showPopupMenu(evt.getY(), evt.getX());
665 public void mouseEntered(MouseEvent evt)
667 this.mouseDragging = false;
668 ap.getScalePanel().mouseEntered(evt);
672 * On leaving the panel, calls ScalePanel.mouseExited to deal with scrolling
673 * with column selection on a mouse drag
678 public void mouseExited(MouseEvent evt)
680 ap.getScalePanel().mouseExited(evt);
684 * Action on starting or continuing a mouse drag. There are two possible
687 * <li>drag up or down on a graphed annotation increases or decreases the
688 * height of the graph</li>
689 * <li>dragging left or right selects the columns dragged across</li>
691 * A drag on a graph annotation is treated as column selection if it starts
692 * with more horizontal than vertical movement, and as resize if it starts
693 * with more vertical than horizontal movement. Once started, the drag does
699 public void mouseDragged(MouseEvent evt)
702 * if dragMode is Undefined:
703 * - set to Select if dx > dy
704 * - set to Resize if dy > dx
705 * - do nothing if dx == dy
707 final int x = evt.getX();
708 final int y = evt.getY();
709 if (dragMode == DragMode.Undefined)
711 int dx = Math.abs(x - mouseDragLastX);
712 int dy = Math.abs(y - mouseDragLastY);
713 if (graphStretch == -1 || dx > dy)
716 * mostly horizontal drag, or not a graph annotation
718 dragMode = DragMode.Select;
723 * mostly vertical drag
725 dragMode = DragMode.Resize;
729 if (dragMode == DragMode.Undefined)
732 * drag is diagonal - defer deciding whether to
733 * treat as up/down or left/right
740 if (dragMode == DragMode.Resize)
743 * resize graph annotation if mouse was dragged up or down
745 int deltaY = mouseDragLastY - evt.getY();
748 AlignmentAnnotation graphAnnotation = av.getAlignment()
749 .getAlignmentAnnotation()[graphStretch];
750 int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
751 graphAnnotation.graphHeight = newHeight;
754 ap.paintAlignment(false, false);
760 * for mouse drag left or right, delegate to
761 * ScalePanel to adjust the column selection
763 ap.getScalePanel().mouseDragged(evt);
773 * Constructs the tooltip, and constructs and displays a status message, for
774 * the current mouse position
779 public void mouseMoved(MouseEvent evt)
781 int yPos = evt.getY();
782 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
784 int row = getRowIndex(yPos, aa);
788 this.setToolTipText(null);
792 int column = (evt.getX() / av.getCharWidth())
793 + av.getRanges().getStartRes();
794 column = Math.min(column, av.getRanges().getEndRes());
796 if (av.hasHiddenColumns())
798 column = av.getAlignment().getHiddenColumns()
799 .visibleToAbsoluteColumn(column);
802 AlignmentAnnotation ann = aa[row];
803 if (row > -1 && ann.annotations != null
804 && column < ann.annotations.length)
806 String toolTip = buildToolTip(ann, column, aa);
807 setToolTipText(toolTip == null ? null
808 : JvSwingUtils.wrapTooltip(true, toolTip));
809 String msg = getStatusMessage(av.getAlignment(), column, ann);
810 ap.alignFrame.setStatus(msg);
814 this.setToolTipText(null);
815 ap.alignFrame.setStatus(" ");
820 * Answers the index in the annotations array of the visible annotation at the
821 * given y position. This is done by adding the heights of visible annotations
822 * until the y position has been exceeded. Answers -1 if no annotations are
823 * visible, or the y position is below all annotations.
829 static int getRowIndex(int yPos, AlignmentAnnotation[] aa)
838 for (int i = 0; i < aa.length; i++)
842 height += aa[i].height;
855 * Answers a tooltip for the annotation at the current mouse position, not
856 * wrapped in <html> tags (apply if wanted). Answers null if there is no
863 static String buildToolTip(AlignmentAnnotation ann, int column,
864 AlignmentAnnotation[] anns)
866 String tooltip = null;
867 if (ann.graphGroup > -1)
869 StringBuilder tip = new StringBuilder(32);
870 boolean first = true;
871 for (int i = 0; i < anns.length; i++)
873 if (anns[i].graphGroup == ann.graphGroup
874 && anns[i].annotations[column] != null)
881 tip.append(anns[i].label);
882 String description = anns[i].annotations[column].description;
883 if (description != null && description.length() > 0)
885 tip.append(" ").append(description);
889 tooltip = first ? null : tip.toString();
891 else if (column < ann.annotations.length
892 && ann.annotations[column] != null)
894 tooltip = ann.annotations[column].description;
895 if ("".equals(tooltip))
905 * Constructs and returns the status bar message
911 static String getStatusMessage(AlignmentI al, int column,
912 AlignmentAnnotation ann)
915 * show alignment column and annotation description if any
917 StringBuilder text = new StringBuilder(32);
918 text.append(MessageManager.getString("label.column")).append(" ")
921 if (column < ann.annotations.length && ann.annotations[column] != null)
923 String description = ann.annotations[column].description;
924 if (description != null && description.trim().length() > 0)
926 text.append(" ").append(description);
931 * if the annotation is sequence-specific, show the sequence number
932 * in the alignment, and (if not a gap) the residue and position
934 SequenceI seqref = ann.sequenceRef;
937 int seqIndex = al.findIndex(seqref);
940 text.append(", ").append(MessageManager.getString("label.sequence"))
941 .append(" ").append(seqIndex + 1);
942 char residue = seqref.getCharAt(column);
943 if (!Comparison.isGap(residue))
947 if (al.isNucleotide())
949 name = ResidueProperties.nucleotideName
950 .get(String.valueOf(residue));
951 text.append(" Nucleotide: ")
952 .append(name != null ? name : residue);
956 name = 'X' == residue ? "X"
957 : ('*' == residue ? "STOP"
958 : ResidueProperties.aa2Triplet
959 .get(String.valueOf(residue)));
960 text.append(" Residue: ").append(name != null ? name : residue);
962 int residuePos = seqref.findPosition(column);
963 text.append(" (").append(residuePos).append(")");
968 return text.toString();
978 public void mouseClicked(MouseEvent evt)
980 // if (activeRow != -1)
982 // AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
983 // AlignmentAnnotation anot = aa[activeRow];
987 // TODO mouseClicked-content and drawCursor are quite experimental!
988 public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
991 int pady = av.getCharHeight() / 5;
993 graphics.setColor(Color.black);
994 graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
996 if (av.validCharWidth)
998 graphics.setColor(Color.white);
1000 char s = seq.getCharAt(res);
1002 charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
1003 graphics.drawString(String.valueOf(s), charOffset + x1,
1004 (y1 + av.getCharHeight()) - pady);
1009 private volatile boolean imageFresh = false;
1010 private Rectangle visibleRect = new Rectangle(),
1011 clipBounds = new Rectangle();
1020 public void paintComponent(Graphics g)
1022 // BH: note that this method is generally recommended to
1023 // call super.paintComponent(g). Otherwise, the children of this
1024 // component will not be rendered. That is not needed here
1025 // because AnnotationPanel does not have any children. It is
1026 // just a JPanel contained in a JViewPort.
1028 computeVisibleRect(visibleRect);
1029 g.setColor(Color.white);
1030 g.fillRect(0, 0, visibleRect.width, visibleRect.height);
1032 ViewportRanges ranges = av.getRanges();
1034 if (allowFastPaint && image != null)
1036 // BH 2018 optimizing generation of new Rectangle().
1038 || (visibleRect.width != (clipBounds = g
1039 .getClipBounds(clipBounds)).width)
1040 || (visibleRect.height != clipBounds.height))
1044 g.drawImage(image, 0, 0, this);
1049 imgWidth = (ranges.getEndRes() - ranges.getStartRes() + 1)
1050 * av.getCharWidth();
1057 if (image == null || imgWidth != image.getWidth(this)
1058 || image.getHeight(this) != getHeight())
1062 image = new BufferedImage(imgWidth,
1063 ap.getAnnotationPanel().getHeight(),
1064 BufferedImage.TYPE_INT_RGB);
1065 } catch (OutOfMemoryError oom)
1070 } catch (Exception x)
1075 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1079 gg = (Graphics2D) image.getGraphics();
1083 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1084 RenderingHints.VALUE_ANTIALIAS_ON);
1087 gg.setFont(av.getFont());
1088 fm = gg.getFontMetrics();
1089 gg.setColor(Color.white);
1090 gg.fillRect(0, 0, imgWidth, image.getHeight());
1095 gg = (Graphics2D) image.getGraphics();
1099 drawComponent(gg, av.getRanges().getStartRes(),
1100 av.getRanges().getEndRes() + 1);
1103 g.drawImage(image, 0, 0, this);
1107 * non-Thread safe repaint
1110 * repaint with horizontal shift in alignment
1112 public void fastPaint(int horizontal)
1114 if ((horizontal == 0) || image == null
1115 || av.getAlignment().getAlignmentAnnotation() == null
1116 || av.getAlignment().getAlignmentAnnotation().length < 1
1117 || av.isCalcInProgress())
1123 int sr = av.getRanges().getStartRes();
1124 int er = av.getRanges().getEndRes() + 1;
1132 Graphics2D gg = (Graphics2D) image.getGraphics();
1134 if (imgWidth>Math.abs(horizontal*av.getCharWidth())) {
1135 //scroll is less than imgWidth away so can re-use buffered graphics
1136 gg.copyArea(0, 0, imgWidth, getHeight(),
1137 -horizontal * av.getCharWidth(), 0);
1139 if (horizontal > 0) // scrollbar pulled right, image to the left
1141 transX = (er - sr - horizontal) * av.getCharWidth();
1142 sr = er - horizontal;
1144 else if (horizontal < 0)
1146 er = sr - horizontal;
1150 gg.translate(transX, 0);
1152 drawComponent(gg, sr, er);
1154 gg.translate(-transX, 0);
1159 // Call repaint on alignment panel so that repaints from other alignment
1160 // panel components can be aggregated. Otherwise performance of the overview
1161 // window and others may be adversely affected.
1162 av.getAlignPanel().repaint();
1165 private volatile boolean lastImageGood = false;
1177 public void drawComponent(Graphics g, int startRes, int endRes)
1179 BufferedImage oldFaded = fadedImage;
1180 if (av.isCalcInProgress())
1184 lastImageGood = false;
1187 // We'll keep a record of the old image,
1188 // and draw a faded image until the calculation
1191 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1192 || fadedImage.getHeight() != image.getHeight()))
1194 // System.err.println("redraw faded image ("+(fadedImage==null ?
1195 // "null image" : "") + " lastGood="+lastImageGood+")");
1196 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1197 BufferedImage.TYPE_INT_RGB);
1199 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1201 fadedG.setColor(Color.white);
1202 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1204 fadedG.setComposite(
1205 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1206 fadedG.drawImage(image, 0, 0, this);
1209 // make sure we don't overwrite the last good faded image until all
1210 // calculations have finished
1211 lastImageGood = false;
1216 if (fadedImage != null)
1218 oldFaded = fadedImage;
1223 g.setColor(Color.white);
1224 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1226 g.setFont(av.getFont());
1229 fm = g.getFontMetrics();
1232 if ((av.getAlignment().getAlignmentAnnotation() == null)
1233 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1235 g.setColor(Color.white);
1236 g.fillRect(0, 0, getWidth(), getHeight());
1237 g.setColor(Color.black);
1238 if (av.validCharWidth)
1240 g.drawString(MessageManager
1241 .getString("label.alignment_has_no_annotations"), 20, 15);
1246 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1248 if (!lastImageGood && fadedImage == null)
1250 fadedImage = oldFaded;
1255 public FontMetrics getFontMetrics()
1261 public Image getFadedImage()
1267 public int getFadedImageWidth()
1272 private int[] bounds = new int[2];
1274 private boolean allowFastPaint;
1276 public int[] getVisibleVRange()
1278 if (ap != null && ap.getAlabels() != null)
1280 int sOffset = -ap.getAlabels().getScrollOffset();
1281 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1282 bounds[0] = sOffset;
1283 bounds[1] = visHeight;
1293 * Try to ensure any references held are nulled
1295 public void dispose()
1305 * I created the renderer so I will dispose of it
1307 if (renderer != null)
1314 public void propertyChange(PropertyChangeEvent evt)
1316 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1317 // Both scrolling and resizing change viewport ranges: scrolling changes
1318 // both start and end points, but resize only changes end values.
1319 // Here we only want to fastpaint on a scroll, with resize using a normal
1320 // paint, so scroll events are identified as changes to the horizontal or
1321 // vertical start value.
1322 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1324 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1326 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1328 fastPaint(((int[]) evt.getNewValue())[0]
1329 - ((int[]) evt.getOldValue())[0]);
1331 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
1338 * computes the visible height of the annotation panel
1340 * @param adjustPanelHeight
1341 * - when false, just adjust existing height according to other
1343 * @param annotationHeight
1344 * @return height to use for the ScrollerPreferredVisibleSize
1346 public int adjustForAlignFrame(boolean adjustPanelHeight,
1347 int annotationHeight)
1350 * Estimate available height in the AlignFrame for alignment +
1351 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1352 * hscroll, status bar, insets.
1354 int stuff = (ap.getViewName() != null ? 30 : 0)
1355 + (Platform.isAMacAndNotJS() ? 120 : 140);
1356 int availableHeight = ap.alignFrame.getHeight() - stuff;
1357 int rowHeight = av.getCharHeight();
1359 if (adjustPanelHeight)
1361 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1364 * If not enough vertical space, maximize annotation height while keeping
1365 * at least two rows of alignment visible
1367 if (annotationHeight + alignmentHeight > availableHeight)
1369 annotationHeight = Math.min(annotationHeight,
1370 availableHeight - 2 * rowHeight);
1375 // maintain same window layout whilst updating sliders
1376 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1377 availableHeight - 2 * rowHeight);
1379 return annotationHeight;
1383 * Clears the flag that allows a 'fast paint' on the next repaint, so
1384 * requiring a full repaint
1386 public void setNoFastPaint()
1388 allowFastPaint = false;