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 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 setToolTipText(buildToolTip(ann, column, aa));
790 String msg = getStatusMessage(av.getAlignment(), column, ann);
791 ap.alignFrame.setStatus(msg);
795 this.setToolTipText(null);
796 ap.alignFrame.setStatus(" ");
801 * Answers the index in the annotations array of the visible annotation at the
802 * given y position. This is done by adding the heights of visible annotations
803 * until the y position has been exceeded. Answers -1 if no annotations are
804 * visible, or the y position is below all annotations.
810 static int getRowIndex(int yPos, AlignmentAnnotation[] aa)
819 for (int i = 0; i < aa.length; i++)
823 height += aa[i].height;
836 * Answers a tooltip for the annotation at the current mouse position
842 static String buildToolTip(AlignmentAnnotation ann, int column,
843 AlignmentAnnotation[] anns)
845 String tooltip = null;
846 if (ann.graphGroup > -1)
848 StringBuilder tip = new StringBuilder(32);
849 tip.append("<html>");
850 for (int i = 0; i < anns.length; i++)
852 if (anns[i].graphGroup == ann.graphGroup
853 && anns[i].annotations[column] != null)
855 tip.append(anns[i].label);
856 String description = anns[i].annotations[column].description;
857 if (description != null && description.length() > 0)
859 tip.append(" ").append(description);
864 if (tip.length() != 6)
866 tip.setLength(tip.length() - 4);
867 tooltip = tip.toString() + "</html>";
870 else if (column < ann.annotations.length
871 && ann.annotations[column] != null)
873 String description = ann.annotations[column].description;
874 if (description != null && description.length() > 0)
876 tooltip = JvSwingUtils.wrapTooltip(true, description);
880 tooltip = null; // no tooltip if null or empty description
885 // clear the tooltip.
892 * Constructs and returns the status bar message
898 static String getStatusMessage(AlignmentI al, int column,
899 AlignmentAnnotation ann)
902 * show alignment column and annotation description if any
904 StringBuilder text = new StringBuilder(32);
905 text.append(MessageManager.getString("label.column")).append(" ")
908 if (column < ann.annotations.length && ann.annotations[column] != null)
910 String description = ann.annotations[column].description;
911 if (description != null && description.trim().length() > 0)
913 text.append(" ").append(description);
918 * if the annotation is sequence-specific, show the sequence number
919 * in the alignment, and (if not a gap) the residue and position
921 SequenceI seqref = ann.sequenceRef;
924 int seqIndex = al.findIndex(seqref);
927 text.append(", ").append(MessageManager.getString("label.sequence"))
928 .append(" ").append(seqIndex + 1);
929 char residue = seqref.getCharAt(column);
930 if (!Comparison.isGap(residue))
934 if (al.isNucleotide())
936 name = ResidueProperties.nucleotideName
937 .get(String.valueOf(residue));
938 text.append(" Nucleotide: ")
939 .append(name != null ? name : residue);
943 name = 'X' == residue ? "X"
944 : ('*' == residue ? "STOP"
945 : ResidueProperties.aa2Triplet
946 .get(String.valueOf(residue)));
947 text.append(" Residue: ").append(name != null ? name : residue);
949 int residuePos = seqref.findPosition(column);
950 text.append(" (").append(residuePos).append(")");
955 return text.toString();
965 public void mouseClicked(MouseEvent evt)
967 // if (activeRow != -1)
969 // AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
970 // AlignmentAnnotation anot = aa[activeRow];
974 // TODO mouseClicked-content and drawCursor are quite experimental!
975 public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
978 int pady = av.getCharHeight() / 5;
980 graphics.setColor(Color.black);
981 graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
983 if (av.validCharWidth)
985 graphics.setColor(Color.white);
987 char s = seq.getCharAt(res);
989 charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
990 graphics.drawString(String.valueOf(s), charOffset + x1,
991 (y1 + av.getCharHeight()) - pady);
996 private volatile boolean imageFresh = false;
997 private Rectangle visibleRect = new Rectangle(), clipBounds = new Rectangle();
1006 public void paintComponent(Graphics g)
1009 // BH: note that this method is generally recommended to
1010 // call super.paintComponent(g). Otherwise, the children of this
1011 // component will not be rendered. That is not needed here
1012 // because AnnotationPanel does not have any children. It is
1013 // just a JPanel contained in a JViewPort.
1015 computeVisibleRect(visibleRect);
1017 g.setColor(Color.white);
1018 g.fillRect(0, 0, visibleRect.width, visibleRect.height);
1022 // BH 2018 optimizing generation of new Rectangle().
1023 if (fastPaint || (visibleRect.width != (clipBounds = g.getClipBounds(clipBounds)).width)
1024 || (visibleRect.height != clipBounds.height))
1028 g.drawImage(image, 0, 0, this);
1033 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
1034 + 1) * av.getCharWidth();
1040 if (image == null || imgWidth != image.getWidth(this)
1041 || image.getHeight(this) != getHeight())
1045 image = new BufferedImage(imgWidth,
1046 ap.getAnnotationPanel().getHeight(),
1047 BufferedImage.TYPE_INT_RGB);
1048 } catch (OutOfMemoryError oom)
1053 } catch (Exception x)
1058 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1062 gg = (Graphics2D) image.getGraphics();
1066 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1067 RenderingHints.VALUE_ANTIALIAS_ON);
1070 gg.setFont(av.getFont());
1071 fm = gg.getFontMetrics();
1072 gg.setColor(Color.white);
1073 gg.fillRect(0, 0, imgWidth, image.getHeight());
1076 gg = (Graphics2D) image.getGraphics();
1080 drawComponent(gg, av.getRanges().getStartRes(),
1081 av.getRanges().getEndRes() + 1);
1084 g.drawImage(image, 0, 0, this);
1088 * set true to enable redraw timing debug output on stderr
1090 private final boolean debugRedraw = false;
1093 * non-Thread safe repaint
1096 * repaint with horizontal shift in alignment
1098 public void fastPaint(int horizontal)
1100 if ((horizontal == 0) || image == null
1101 || av.getAlignment().getAlignmentAnnotation() == null
1102 || av.getAlignment().getAlignmentAnnotation().length < 1
1103 || av.isCalcInProgress())
1109 int sr = av.getRanges().getStartRes();
1110 int er = av.getRanges().getEndRes() + 1;
1113 Graphics2D gg = (Graphics2D) image.getGraphics();
1115 gg.copyArea(0, 0, imgWidth, getHeight(),
1116 -horizontal * av.getCharWidth(), 0);
1118 if (horizontal > 0) // scrollbar pulled right, image to the left
1120 transX = (er - sr - horizontal) * av.getCharWidth();
1121 sr = er - horizontal;
1123 else if (horizontal < 0)
1125 er = sr - horizontal;
1128 gg.translate(transX, 0);
1130 drawComponent(gg, sr, er);
1132 gg.translate(-transX, 0);
1139 // Call repaint on alignment panel so that repaints from other alignment
1140 // panel components can be aggregated. Otherwise performance of the overview
1141 // window and others may be adversely affected.
1142 av.getAlignPanel().repaint();
1145 private volatile boolean lastImageGood = false;
1157 public void drawComponent(Graphics g, int startRes, int endRes)
1159 BufferedImage oldFaded = fadedImage;
1160 if (av.isCalcInProgress())
1164 lastImageGood = false;
1167 // We'll keep a record of the old image,
1168 // and draw a faded image until the calculation
1171 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1172 || fadedImage.getHeight() != image.getHeight()))
1174 // System.err.println("redraw faded image ("+(fadedImage==null ?
1175 // "null image" : "") + " lastGood="+lastImageGood+")");
1176 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1177 BufferedImage.TYPE_INT_RGB);
1179 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1181 fadedG.setColor(Color.white);
1182 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1184 fadedG.setComposite(
1185 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1186 fadedG.drawImage(image, 0, 0, this);
1189 // make sure we don't overwrite the last good faded image until all
1190 // calculations have finished
1191 lastImageGood = false;
1196 if (fadedImage != null)
1198 oldFaded = fadedImage;
1203 g.setColor(Color.white);
1204 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1206 g.setFont(av.getFont());
1209 fm = g.getFontMetrics();
1212 if ((av.getAlignment().getAlignmentAnnotation() == null)
1213 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1215 g.setColor(Color.white);
1216 g.fillRect(0, 0, getWidth(), getHeight());
1217 g.setColor(Color.black);
1218 if (av.validCharWidth)
1220 g.drawString(MessageManager
1221 .getString("label.alignment_has_no_annotations"), 20, 15);
1226 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1228 if (!lastImageGood && fadedImage == null)
1230 fadedImage = oldFaded;
1235 public FontMetrics getFontMetrics()
1241 public Image getFadedImage()
1247 public int getFadedImageWidth()
1252 private int[] bounds = new int[2];
1255 public int[] getVisibleVRange()
1257 if (ap != null && ap.getAlabels() != null)
1259 int sOffset = -ap.getAlabels().getScrollOffset();
1260 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1261 bounds[0] = sOffset;
1262 bounds[1] = visHeight;
1272 * Try to ensure any references held are nulled
1274 public void dispose()
1284 * I created the renderer so I will dispose of it
1286 if (renderer != null)
1293 public void propertyChange(PropertyChangeEvent evt)
1295 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1296 // Both scrolling and resizing change viewport ranges: scrolling changes
1297 // both start and end points, but resize only changes end values.
1298 // Here we only want to fastpaint on a scroll, with resize using a normal
1299 // paint, so scroll events are identified as changes to the horizontal or
1300 // vertical start value.
1301 switch (evt.getPropertyName())
1303 case ViewportRanges.STARTRES:
1304 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1306 case ViewportRanges.STARTRESANDSEQ:
1307 fastPaint(((int[]) evt.getNewValue())[0]
1308 - ((int[]) evt.getOldValue())[0]);
1310 case ViewportRanges.MOVE_VIEWPORT:
1313 case ViewportRanges.STARTSEQ:
1314 case ViewportRanges.ENDRES:
1315 case ViewportRanges.ENDSEQ:
1322 * computes the visible height of the annotation panel
1324 * @param adjustPanelHeight
1325 * - when false, just adjust existing height according to other
1327 * @param annotationHeight
1328 * @return height to use for the ScrollerPreferredVisibleSize
1330 public int adjustForAlignFrame(boolean adjustPanelHeight,
1331 int annotationHeight)
1334 * Estimate available height in the AlignFrame for alignment +
1335 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1336 * hscroll, status bar, insets.
1338 int stuff = (ap.getViewName() != null ? 30 : 0)
1339 + (Platform.isAMacAndNotJS() ? 120 : 140);
1340 int availableHeight = ap.alignFrame.getHeight() - stuff;
1341 int rowHeight = av.getCharHeight();
1343 if (adjustPanelHeight)
1345 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1348 * If not enough vertical space, maximize annotation height while keeping
1349 * at least two rows of alignment visible
1351 if (annotationHeight + alignmentHeight > availableHeight)
1353 annotationHeight = Math.min(annotationHeight,
1354 availableHeight - 2 * rowHeight);
1359 // maintain same window layout whilst updating sliders
1360 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1361 availableHeight - 2 * rowHeight);
1363 return annotationHeight;