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 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(
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 final Annotation[] fAnot = anot;
351 String title = MessageManager
352 .getString("label.select_foreground_colour");
353 ColourChooserListener listener = new ColourChooserListener()
356 public void colourSelected(Color c)
358 HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
359 for (int index : av.getColumnSelection().getSelected())
361 if (hiddenColumns.isVisible(index))
363 if (fAnot[index] == null)
365 fAnot[index] = new Annotation("", "", ' ', 0);
367 fAnot[index].colour = c;
371 JalviewColourChooser.showColourChooser(this,
372 title, Color.black, listener);
375 // HELIX, SHEET or STEM
378 String symbol = "\u03B1"; // alpha
380 if (action.equals(HELIX))
384 else if (action.equals(SHEET))
387 symbol = "\u03B2"; // beta
390 // Added by LML to color stems
391 else if (action.equals(STEM))
394 int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
395 symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
398 if (!aa[activeRow].hasIcons)
400 aa[activeRow].hasIcons = true;
403 String label = JvOptionPane.showInputDialog(MessageManager
404 .getString("label.enter_label_for_the_structure"), symbol);
411 if ((label.length() > 0) && !aa[activeRow].hasText)
413 aa[activeRow].hasText = true;
414 if (action.equals(STEM))
416 aa[activeRow].showAllColLabels = true;
419 for (int index : av.getColumnSelection().getSelected())
421 if (!av.getAlignment().getHiddenColumns().isVisible(index))
426 if (anot[index] == null)
428 anot[index] = new Annotation(label, "", type, 0);
431 anot[index].secondaryStructure = type != 'S' ? type
432 : label.length() == 0 ? ' ' : label.charAt(0);
433 anot[index].displayCharacter = label;
438 av.getAlignment().validateAnnotation(aa[activeRow]);
439 ap.alignmentChanged();
440 ap.alignFrame.setMenusForViewport();
448 * Returns any existing annotation concatenated as a string. For each
449 * annotation, takes the description, if any, else the secondary structure
450 * character (if type is HELIX, SHEET or STEM), else the display character (if
457 private String collectAnnotVals(Annotation[] anots, String type)
459 // TODO is this method wanted? why? 'last' is not used
461 StringBuilder collatedInput = new StringBuilder(64);
463 ColumnSelection viscols = av.getColumnSelection();
464 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
467 * the selection list (read-only view) is in selection order, not
468 * column order; make a copy so we can sort it
470 List<Integer> selected = new ArrayList<>(viscols.getSelected());
471 Collections.sort(selected);
472 for (int index : selected)
474 // always check for current display state - just in case
475 if (!hidden.isVisible(index))
479 String tlabel = null;
480 if (anots[index] != null)
481 { // LML added stem code
482 if (type.equals(HELIX) || type.equals(SHEET) || type.equals(STEM)
483 || type.equals(LABEL))
485 tlabel = anots[index].description;
486 if (tlabel == null || tlabel.length() < 1)
488 if (type.equals(HELIX) || type.equals(SHEET)
489 || type.equals(STEM))
491 tlabel = "" + anots[index].secondaryStructure;
495 tlabel = "" + anots[index].displayCharacter;
499 if (tlabel != null && !tlabel.equals(last))
501 if (last.length() > 0)
503 collatedInput.append(" ");
505 collatedInput.append(tlabel);
509 return collatedInput.toString();
513 * Action on right mouse pressed on Mac is to show a pop-up menu for the
514 * annotation. Action on left mouse pressed is to find which annotation is
515 * pressed and mark the start of a column selection or graph resize operation.
520 public void mousePressed(MouseEvent evt)
523 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
528 mouseDragLastX = evt.getX();
529 mouseDragLastY = evt.getY();
532 * add visible annotation heights until we reach the y
533 * position, to find which annotation it is in
538 final int y = evt.getY();
539 for (int i = 0; i < aa.length; i++)
543 height += aa[i].height;
552 else if (aa[i].graph > 0)
555 * we have clicked on a resizable graph annotation
564 * isPopupTrigger fires in mousePressed on Mac,
565 * not until mouseRelease on Windows
567 if (evt.isPopupTrigger() && activeRow != -1)
569 showPopupMenu(y, evt.getX());
573 ap.getScalePanel().mousePressed(evt);
577 * Construct and display a context menu at the right-click position
582 void showPopupMenu(final int y, int x)
584 if (av.getColumnSelection() == null
585 || av.getColumnSelection().isEmpty())
590 JPopupMenu pop = new JPopupMenu(
591 MessageManager.getString("label.structure_type"));
594 * Just display the needed structure options
596 if (av.getAlignment().isNucleotide())
598 item = new JMenuItem(STEM);
599 item.addActionListener(this);
604 item = new JMenuItem(HELIX);
605 item.addActionListener(this);
607 item = new JMenuItem(SHEET);
608 item.addActionListener(this);
611 item = new JMenuItem(LABEL);
612 item.addActionListener(this);
614 item = new JMenuItem(COLOUR);
615 item.addActionListener(this);
617 item = new JMenuItem(REMOVE);
618 item.addActionListener(this);
620 pop.show(this, x, y);
624 * Action on mouse up is to clear mouse drag data and call mouseReleased on
625 * ScalePanel, to deal with defining the selection group (if any) defined by
631 public void mouseReleased(MouseEvent evt)
636 mouseDragging = false;
637 dragMode = DragMode.Undefined;
638 ap.getScalePanel().mouseReleased(evt);
641 * isPopupTrigger is set in mouseReleased on Windows
642 * (in mousePressed on Mac)
644 if (evt.isPopupTrigger() && activeRow != -1)
646 showPopupMenu(evt.getY(), evt.getX());
658 public void mouseEntered(MouseEvent evt)
660 this.mouseDragging = false;
661 ap.getScalePanel().mouseEntered(evt);
665 * On leaving the panel, calls ScalePanel.mouseExited to deal with scrolling
666 * with column selection on a mouse drag
671 public void mouseExited(MouseEvent evt)
673 ap.getScalePanel().mouseExited(evt);
683 public void mouseDragged(MouseEvent evt)
686 * todo: if dragMode is Undefined:
687 * - set to Select if dx > dy
688 * - set to Resize if dy > dx
689 * - do nothing if dx == dy
691 final int x = evt.getX();
692 final int y = evt.getY();
693 if (dragMode == DragMode.Undefined)
695 int dx = Math.abs(x - mouseDragLastX);
696 int dy = Math.abs(y - mouseDragLastY);
697 if (graphStretch == -1 || dx > dy)
700 * mostly horizontal drag, or not a graph annotation
702 dragMode = DragMode.Select;
707 * mostly vertical drag
709 dragMode = DragMode.Resize;
713 if (dragMode == DragMode.Undefined)
716 * drag is diagonal - defer deciding whether to
717 * treat as up/down or left/right
724 if (dragMode == DragMode.Resize)
727 * resize graph annotation if mouse was dragged up or down
729 int deltaY = mouseDragLastY - evt.getY();
732 AlignmentAnnotation graphAnnotation = av.getAlignment()
733 .getAlignmentAnnotation()[graphStretch];
734 int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
735 graphAnnotation.graphHeight = newHeight;
737 ap.paintAlignment(false, false);
743 * for mouse drag left or right, delegate to
744 * ScalePanel to adjust the column selection
746 ap.getScalePanel().mouseDragged(evt);
756 * Constructs the tooltip, and constructs and displays a status message, for
757 * the current mouse position
762 public void mouseMoved(MouseEvent evt)
764 int yPos = evt.getY();
765 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
767 int row = getRowIndex(yPos, aa);
771 this.setToolTipText(null);
775 int column = (evt.getX() / av.getCharWidth())
776 + av.getRanges().getStartRes();
777 column = Math.min(column, av.getRanges().getEndRes());
779 if (av.hasHiddenColumns())
781 column = av.getAlignment().getHiddenColumns()
782 .visibleToAbsoluteColumn(column);
785 AlignmentAnnotation ann = aa[row];
786 if (row > -1 && ann.annotations != null
787 && column < ann.annotations.length)
789 String toolTip = buildToolTip(ann, column, aa);
790 setToolTipText(toolTip == null ? null
791 : JvSwingUtils.wrapTooltip(true, toolTip));
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, not
839 * wrapped in <html> tags (apply if wanted). Answers null if there is no
846 static String buildToolTip(AlignmentAnnotation ann, int column,
847 AlignmentAnnotation[] anns)
849 String tooltip = null;
850 if (ann.graphGroup > -1)
852 StringBuilder tip = new StringBuilder(32);
853 boolean first = true;
854 for (int i = 0; i < anns.length; i++)
856 if (anns[i].graphGroup == ann.graphGroup
857 && anns[i].annotations[column] != null)
864 tip.append(anns[i].label);
865 String description = anns[i].annotations[column].description;
866 if (description != null && description.length() > 0)
868 tip.append(" ").append(description);
872 tooltip = first ? null : tip.toString();
874 else if (column < ann.annotations.length
875 && ann.annotations[column] != null)
877 tooltip = ann.annotations[column].description;
884 * Constructs and returns the status bar message
890 static String getStatusMessage(AlignmentI al, int column,
891 AlignmentAnnotation ann)
894 * show alignment column and annotation description if any
896 StringBuilder text = new StringBuilder(32);
897 text.append(MessageManager.getString("label.column")).append(" ")
900 if (column < ann.annotations.length && ann.annotations[column] != null)
902 String description = ann.annotations[column].description;
903 if (description != null && description.trim().length() > 0)
905 text.append(" ").append(description);
910 * if the annotation is sequence-specific, show the sequence number
911 * in the alignment, and (if not a gap) the residue and position
913 SequenceI seqref = ann.sequenceRef;
916 int seqIndex = al.findIndex(seqref);
919 text.append(", ").append(MessageManager.getString("label.sequence"))
920 .append(" ").append(seqIndex + 1);
921 char residue = seqref.getCharAt(column);
922 if (!Comparison.isGap(residue))
926 if (al.isNucleotide())
928 name = ResidueProperties.nucleotideName
929 .get(String.valueOf(residue));
930 text.append(" Nucleotide: ")
931 .append(name != null ? name : residue);
935 name = 'X' == residue ? "X"
936 : ('*' == residue ? "STOP"
937 : ResidueProperties.aa2Triplet
938 .get(String.valueOf(residue)));
939 text.append(" Residue: ").append(name != null ? name : residue);
941 int residuePos = seqref.findPosition(column);
942 text.append(" (").append(residuePos).append(")");
947 return text.toString();
957 public void mouseClicked(MouseEvent evt)
959 // if (activeRow != -1)
961 // AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
962 // AlignmentAnnotation anot = aa[activeRow];
966 // TODO mouseClicked-content and drawCursor are quite experimental!
967 public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
970 int pady = av.getCharHeight() / 5;
972 graphics.setColor(Color.black);
973 graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
975 if (av.validCharWidth)
977 graphics.setColor(Color.white);
979 char s = seq.getCharAt(res);
981 charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
982 graphics.drawString(String.valueOf(s), charOffset + x1,
983 (y1 + av.getCharHeight()) - pady);
988 private volatile boolean imageFresh = false;
989 private Rectangle visibleRect = new Rectangle(), clipBounds = new Rectangle();
998 public void paintComponent(Graphics g)
1001 // BH: note that this method is generally recommended to
1002 // call super.paintComponent(g). Otherwise, the children of this
1003 // component will not be rendered. That is not needed here
1004 // because AnnotationPanel does not have any children. It is
1005 // just a JPanel contained in a JViewPort.
1007 computeVisibleRect(visibleRect);
1009 g.setColor(Color.white);
1010 g.fillRect(0, 0, visibleRect.width, visibleRect.height);
1014 // BH 2018 optimizing generation of new Rectangle().
1015 if (fastPaint || (visibleRect.width != (clipBounds = g.getClipBounds(clipBounds)).width)
1016 || (visibleRect.height != clipBounds.height))
1020 g.drawImage(image, 0, 0, this);
1025 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
1026 + 1) * av.getCharWidth();
1032 if (image == null || imgWidth != image.getWidth(this)
1033 || image.getHeight(this) != getHeight())
1037 image = new BufferedImage(imgWidth,
1038 ap.getAnnotationPanel().getHeight(),
1039 BufferedImage.TYPE_INT_RGB);
1040 } catch (OutOfMemoryError oom)
1045 } catch (Exception x)
1050 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1054 gg = (Graphics2D) image.getGraphics();
1058 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1059 RenderingHints.VALUE_ANTIALIAS_ON);
1062 gg.setFont(av.getFont());
1063 fm = gg.getFontMetrics();
1064 gg.setColor(Color.white);
1065 gg.fillRect(0, 0, imgWidth, image.getHeight());
1068 gg = (Graphics2D) image.getGraphics();
1072 drawComponent(gg, av.getRanges().getStartRes(),
1073 av.getRanges().getEndRes() + 1);
1076 g.drawImage(image, 0, 0, this);
1080 * set true to enable redraw timing debug output on stderr
1082 private final boolean debugRedraw = false;
1085 * non-Thread safe repaint
1088 * repaint with horizontal shift in alignment
1090 public void fastPaint(int horizontal)
1092 if ((horizontal == 0) || image == null
1093 || av.getAlignment().getAlignmentAnnotation() == null
1094 || av.getAlignment().getAlignmentAnnotation().length < 1
1095 || av.isCalcInProgress())
1101 int sr = av.getRanges().getStartRes();
1102 int er = av.getRanges().getEndRes() + 1;
1105 Graphics2D gg = (Graphics2D) image.getGraphics();
1107 gg.copyArea(0, 0, imgWidth, getHeight(),
1108 -horizontal * av.getCharWidth(), 0);
1110 if (horizontal > 0) // scrollbar pulled right, image to the left
1112 transX = (er - sr - horizontal) * av.getCharWidth();
1113 sr = er - horizontal;
1115 else if (horizontal < 0)
1117 er = sr - horizontal;
1120 gg.translate(transX, 0);
1122 drawComponent(gg, sr, er);
1124 gg.translate(-transX, 0);
1130 // Call repaint on alignment panel so that repaints from other alignment
1131 // panel components can be aggregated. Otherwise performance of the overview
1132 // window and others may be adversely affected.
1133 av.getAlignPanel().repaint();
1136 private volatile boolean lastImageGood = false;
1148 public void drawComponent(Graphics g, int startRes, int endRes)
1150 BufferedImage oldFaded = fadedImage;
1151 if (av.isCalcInProgress())
1155 lastImageGood = false;
1158 // We'll keep a record of the old image,
1159 // and draw a faded image until the calculation
1162 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1163 || fadedImage.getHeight() != image.getHeight()))
1165 // System.err.println("redraw faded image ("+(fadedImage==null ?
1166 // "null image" : "") + " lastGood="+lastImageGood+")");
1167 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1168 BufferedImage.TYPE_INT_RGB);
1170 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1172 fadedG.setColor(Color.white);
1173 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1175 fadedG.setComposite(
1176 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1177 fadedG.drawImage(image, 0, 0, this);
1180 // make sure we don't overwrite the last good faded image until all
1181 // calculations have finished
1182 lastImageGood = false;
1187 if (fadedImage != null)
1189 oldFaded = fadedImage;
1194 g.setColor(Color.white);
1195 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1197 g.setFont(av.getFont());
1200 fm = g.getFontMetrics();
1203 if ((av.getAlignment().getAlignmentAnnotation() == null)
1204 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1206 g.setColor(Color.white);
1207 g.fillRect(0, 0, getWidth(), getHeight());
1208 g.setColor(Color.black);
1209 if (av.validCharWidth)
1211 g.drawString(MessageManager
1212 .getString("label.alignment_has_no_annotations"), 20, 15);
1217 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1219 if (!lastImageGood && fadedImage == null)
1221 fadedImage = oldFaded;
1226 public FontMetrics getFontMetrics()
1232 public Image getFadedImage()
1238 public int getFadedImageWidth()
1243 private int[] bounds = new int[2];
1246 public int[] getVisibleVRange()
1248 if (ap != null && ap.getAlabels() != null)
1250 int sOffset = -ap.getAlabels().getScrollOffset();
1251 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1252 bounds[0] = sOffset;
1253 bounds[1] = visHeight;
1263 * Try to ensure any references held are nulled
1265 public void dispose()
1275 * I created the renderer so I will dispose of it
1277 if (renderer != null)
1284 public void propertyChange(PropertyChangeEvent evt)
1286 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1287 // Both scrolling and resizing change viewport ranges: scrolling changes
1288 // both start and end points, but resize only changes end values.
1289 // Here we only want to fastpaint on a scroll, with resize using a normal
1290 // paint, so scroll events are identified as changes to the horizontal or
1291 // vertical start value.
1292 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1294 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1296 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1298 fastPaint(((int[]) evt.getNewValue())[0]
1299 - ((int[]) evt.getOldValue())[0]);
1301 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
1308 * computes the visible height of the annotation panel
1310 * @param adjustPanelHeight
1311 * - when false, just adjust existing height according to other
1313 * @param annotationHeight
1314 * @return height to use for the ScrollerPreferredVisibleSize
1316 public int adjustForAlignFrame(boolean adjustPanelHeight,
1317 int annotationHeight)
1320 * Estimate available height in the AlignFrame for alignment +
1321 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1322 * hscroll, status bar, insets.
1324 int stuff = (ap.getViewName() != null ? 30 : 0)
1325 + (Platform.isAMacAndNotJS() ? 120 : 140);
1326 int availableHeight = ap.alignFrame.getHeight() - stuff;
1327 int rowHeight = av.getCharHeight();
1329 if (adjustPanelHeight)
1331 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1334 * If not enough vertical space, maximize annotation height while keeping
1335 * at least two rows of alignment visible
1337 if (annotationHeight + alignmentHeight > availableHeight)
1339 annotationHeight = Math.min(annotationHeight,
1340 availableHeight - 2 * rowHeight);
1345 // maintain same window layout whilst updating sliders
1346 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1347 availableHeight - 2 * rowHeight);
1349 return annotationHeight;