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 if (dragMode == DragMode.Resize)
639 ap.adjustAnnotationHeight();
641 dragMode = DragMode.Undefined;
642 ap.getScalePanel().mouseReleased(evt);
645 * isPopupTrigger is set in mouseReleased on Windows
646 * (in mousePressed on Mac)
648 if (evt.isPopupTrigger() && activeRow != -1)
650 showPopupMenu(evt.getY(), evt.getX());
662 public void mouseEntered(MouseEvent evt)
664 this.mouseDragging = false;
665 ap.getScalePanel().mouseEntered(evt);
669 * On leaving the panel, calls ScalePanel.mouseExited to deal with scrolling
670 * with column selection on a mouse drag
675 public void mouseExited(MouseEvent evt)
677 ap.getScalePanel().mouseExited(evt);
681 * Action on starting or continuing a mouse drag. There are two possible
684 * <li>drag up or down on a graphed annotation increases or decreases the
685 * height of the graph</li>
686 * <li>dragging left or right selects the columns dragged across</li>
688 * A drag on a graph annotation is treated as column selection if it starts
689 * with more horizontal than vertical movement, and as resize if it starts
690 * with more vertical than horizontal movement. Once started, the drag does
696 public void mouseDragged(MouseEvent evt)
699 * if dragMode is Undefined:
700 * - set to Select if dx > dy
701 * - set to Resize if dy > dx
702 * - do nothing if dx == dy
704 final int x = evt.getX();
705 final int y = evt.getY();
706 if (dragMode == DragMode.Undefined)
708 int dx = Math.abs(x - mouseDragLastX);
709 int dy = Math.abs(y - mouseDragLastY);
710 if (graphStretch == -1 || dx > dy)
713 * mostly horizontal drag, or not a graph annotation
715 dragMode = DragMode.Select;
720 * mostly vertical drag
722 dragMode = DragMode.Resize;
726 if (dragMode == DragMode.Undefined)
729 * drag is diagonal - defer deciding whether to
730 * treat as up/down or left/right
737 if (dragMode == DragMode.Resize)
740 * resize graph annotation if mouse was dragged up or down
742 int deltaY = mouseDragLastY - evt.getY();
745 AlignmentAnnotation graphAnnotation = av.getAlignment()
746 .getAlignmentAnnotation()[graphStretch];
747 int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
748 graphAnnotation.graphHeight = newHeight;
750 ap.paintAlignment(false, false);
756 * for mouse drag left or right, delegate to
757 * ScalePanel to adjust the column selection
759 ap.getScalePanel().mouseDragged(evt);
769 * Constructs the tooltip, and constructs and displays a status message, for
770 * the current mouse position
775 public void mouseMoved(MouseEvent evt)
777 int yPos = evt.getY();
778 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
780 int row = getRowIndex(yPos, aa);
784 this.setToolTipText(null);
788 int column = (evt.getX() / av.getCharWidth())
789 + av.getRanges().getStartRes();
790 column = Math.min(column, av.getRanges().getEndRes());
792 if (av.hasHiddenColumns())
794 column = av.getAlignment().getHiddenColumns()
795 .visibleToAbsoluteColumn(column);
798 AlignmentAnnotation ann = aa[row];
799 if (row > -1 && ann.annotations != null
800 && column < ann.annotations.length)
802 String toolTip = buildToolTip(ann, column, aa);
803 setToolTipText(toolTip == null ? null
804 : JvSwingUtils.wrapTooltip(true, toolTip));
805 String msg = getStatusMessage(av.getAlignment(), column, ann);
806 ap.alignFrame.setStatus(msg);
810 this.setToolTipText(null);
811 ap.alignFrame.setStatus(" ");
816 * Answers the index in the annotations array of the visible annotation at the
817 * given y position. This is done by adding the heights of visible annotations
818 * until the y position has been exceeded. Answers -1 if no annotations are
819 * visible, or the y position is below all annotations.
825 static int getRowIndex(int yPos, AlignmentAnnotation[] aa)
834 for (int i = 0; i < aa.length; i++)
838 height += aa[i].height;
851 * Answers a tooltip for the annotation at the current mouse position, not
852 * wrapped in <html> tags (apply if wanted). Answers null if there is no
859 static String buildToolTip(AlignmentAnnotation ann, int column,
860 AlignmentAnnotation[] anns)
862 String tooltip = null;
863 if (ann.graphGroup > -1)
865 StringBuilder tip = new StringBuilder(32);
866 boolean first = true;
867 for (int i = 0; i < anns.length; i++)
869 if (anns[i].graphGroup == ann.graphGroup
870 && anns[i].annotations[column] != null)
877 tip.append(anns[i].label);
878 String description = anns[i].annotations[column].description;
879 if (description != null && description.length() > 0)
881 tip.append(" ").append(description);
885 tooltip = first ? null : tip.toString();
887 else if (column < ann.annotations.length
888 && ann.annotations[column] != null)
890 tooltip = ann.annotations[column].description;
897 * Constructs and returns the status bar message
903 static String getStatusMessage(AlignmentI al, int column,
904 AlignmentAnnotation ann)
907 * show alignment column and annotation description if any
909 StringBuilder text = new StringBuilder(32);
910 text.append(MessageManager.getString("label.column")).append(" ")
913 if (column < ann.annotations.length && ann.annotations[column] != null)
915 String description = ann.annotations[column].description;
916 if (description != null && description.trim().length() > 0)
918 text.append(" ").append(description);
923 * if the annotation is sequence-specific, show the sequence number
924 * in the alignment, and (if not a gap) the residue and position
926 SequenceI seqref = ann.sequenceRef;
929 int seqIndex = al.findIndex(seqref);
932 text.append(", ").append(MessageManager.getString("label.sequence"))
933 .append(" ").append(seqIndex + 1);
934 char residue = seqref.getCharAt(column);
935 if (!Comparison.isGap(residue))
939 if (al.isNucleotide())
941 name = ResidueProperties.nucleotideName
942 .get(String.valueOf(residue));
943 text.append(" Nucleotide: ")
944 .append(name != null ? name : residue);
948 name = 'X' == residue ? "X"
949 : ('*' == residue ? "STOP"
950 : ResidueProperties.aa2Triplet
951 .get(String.valueOf(residue)));
952 text.append(" Residue: ").append(name != null ? name : residue);
954 int residuePos = seqref.findPosition(column);
955 text.append(" (").append(residuePos).append(")");
960 return text.toString();
970 public void mouseClicked(MouseEvent evt)
972 // if (activeRow != -1)
974 // AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
975 // AlignmentAnnotation anot = aa[activeRow];
979 // TODO mouseClicked-content and drawCursor are quite experimental!
980 public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
983 int pady = av.getCharHeight() / 5;
985 graphics.setColor(Color.black);
986 graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
988 if (av.validCharWidth)
990 graphics.setColor(Color.white);
992 char s = seq.getCharAt(res);
994 charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
995 graphics.drawString(String.valueOf(s), charOffset + x1,
996 (y1 + av.getCharHeight()) - pady);
1001 private volatile boolean imageFresh = false;
1002 private Rectangle visibleRect = new Rectangle(), clipBounds = new Rectangle();
1011 public void paintComponent(Graphics g)
1014 // BH: note that this method is generally recommended to
1015 // call super.paintComponent(g). Otherwise, the children of this
1016 // component will not be rendered. That is not needed here
1017 // because AnnotationPanel does not have any children. It is
1018 // just a JPanel contained in a JViewPort.
1020 computeVisibleRect(visibleRect);
1022 g.setColor(Color.white);
1023 g.fillRect(0, 0, visibleRect.width, visibleRect.height);
1027 // BH 2018 optimizing generation of new Rectangle().
1028 if (fastPaint || (visibleRect.width != (clipBounds = g.getClipBounds(clipBounds)).width)
1029 || (visibleRect.height != clipBounds.height))
1033 g.drawImage(image, 0, 0, this);
1038 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
1039 + 1) * av.getCharWidth();
1045 if (image == null || imgWidth != image.getWidth(this)
1046 || image.getHeight(this) != getHeight())
1050 image = new BufferedImage(imgWidth,
1051 ap.getAnnotationPanel().getHeight(),
1052 BufferedImage.TYPE_INT_RGB);
1053 } catch (OutOfMemoryError oom)
1058 } catch (Exception x)
1063 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1067 gg = (Graphics2D) image.getGraphics();
1071 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1072 RenderingHints.VALUE_ANTIALIAS_ON);
1075 gg.setFont(av.getFont());
1076 fm = gg.getFontMetrics();
1077 gg.setColor(Color.white);
1078 gg.fillRect(0, 0, imgWidth, image.getHeight());
1081 gg = (Graphics2D) image.getGraphics();
1085 drawComponent(gg, av.getRanges().getStartRes(),
1086 av.getRanges().getEndRes() + 1);
1089 g.drawImage(image, 0, 0, this);
1093 * set true to enable redraw timing debug output on stderr
1095 private final boolean debugRedraw = false;
1098 * non-Thread safe repaint
1101 * repaint with horizontal shift in alignment
1103 public void fastPaint(int horizontal)
1105 if ((horizontal == 0) || image == null
1106 || av.getAlignment().getAlignmentAnnotation() == null
1107 || av.getAlignment().getAlignmentAnnotation().length < 1
1108 || av.isCalcInProgress())
1114 int sr = av.getRanges().getStartRes();
1115 int er = av.getRanges().getEndRes() + 1;
1118 Graphics2D gg = (Graphics2D) image.getGraphics();
1120 gg.copyArea(0, 0, imgWidth, getHeight(),
1121 -horizontal * av.getCharWidth(), 0);
1123 if (horizontal > 0) // scrollbar pulled right, image to the left
1125 transX = (er - sr - horizontal) * av.getCharWidth();
1126 sr = er - horizontal;
1128 else if (horizontal < 0)
1130 er = sr - horizontal;
1133 gg.translate(transX, 0);
1135 drawComponent(gg, sr, er);
1137 gg.translate(-transX, 0);
1143 // Call repaint on alignment panel so that repaints from other alignment
1144 // panel components can be aggregated. Otherwise performance of the overview
1145 // window and others may be adversely affected.
1146 av.getAlignPanel().repaint();
1149 private volatile boolean lastImageGood = false;
1161 public void drawComponent(Graphics g, int startRes, int endRes)
1163 BufferedImage oldFaded = fadedImage;
1164 if (av.isCalcInProgress())
1168 lastImageGood = false;
1171 // We'll keep a record of the old image,
1172 // and draw a faded image until the calculation
1175 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1176 || fadedImage.getHeight() != image.getHeight()))
1178 // System.err.println("redraw faded image ("+(fadedImage==null ?
1179 // "null image" : "") + " lastGood="+lastImageGood+")");
1180 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1181 BufferedImage.TYPE_INT_RGB);
1183 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1185 fadedG.setColor(Color.white);
1186 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1188 fadedG.setComposite(
1189 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1190 fadedG.drawImage(image, 0, 0, this);
1193 // make sure we don't overwrite the last good faded image until all
1194 // calculations have finished
1195 lastImageGood = false;
1200 if (fadedImage != null)
1202 oldFaded = fadedImage;
1207 g.setColor(Color.white);
1208 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1210 g.setFont(av.getFont());
1213 fm = g.getFontMetrics();
1216 if ((av.getAlignment().getAlignmentAnnotation() == null)
1217 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1219 g.setColor(Color.white);
1220 g.fillRect(0, 0, getWidth(), getHeight());
1221 g.setColor(Color.black);
1222 if (av.validCharWidth)
1224 g.drawString(MessageManager
1225 .getString("label.alignment_has_no_annotations"), 20, 15);
1230 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1232 if (!lastImageGood && fadedImage == null)
1234 fadedImage = oldFaded;
1239 public FontMetrics getFontMetrics()
1245 public Image getFadedImage()
1251 public int getFadedImageWidth()
1256 private int[] bounds = new int[2];
1259 public int[] getVisibleVRange()
1261 if (ap != null && ap.getAlabels() != null)
1263 int sOffset = -ap.getAlabels().getScrollOffset();
1264 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1265 bounds[0] = sOffset;
1266 bounds[1] = visHeight;
1276 * Try to ensure any references held are nulled
1278 public void dispose()
1288 * I created the renderer so I will dispose of it
1290 if (renderer != null)
1297 public void propertyChange(PropertyChangeEvent evt)
1299 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1300 // Both scrolling and resizing change viewport ranges: scrolling changes
1301 // both start and end points, but resize only changes end values.
1302 // Here we only want to fastpaint on a scroll, with resize using a normal
1303 // paint, so scroll events are identified as changes to the horizontal or
1304 // vertical start value.
1305 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1307 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1309 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1311 fastPaint(((int[]) evt.getNewValue())[0]
1312 - ((int[]) evt.getOldValue())[0]);
1314 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
1321 * computes the visible height of the annotation panel
1323 * @param adjustPanelHeight
1324 * - when false, just adjust existing height according to other
1326 * @param annotationHeight
1327 * @return height to use for the ScrollerPreferredVisibleSize
1329 public int adjustForAlignFrame(boolean adjustPanelHeight,
1330 int annotationHeight)
1333 * Estimate available height in the AlignFrame for alignment +
1334 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1335 * hscroll, status bar, insets.
1337 int stuff = (ap.getViewName() != null ? 30 : 0)
1338 + (Platform.isAMacAndNotJS() ? 120 : 140);
1339 int availableHeight = ap.alignFrame.getHeight() - stuff;
1340 int rowHeight = av.getCharHeight();
1342 if (adjustPanelHeight)
1344 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1347 * If not enough vertical space, maximize annotation height while keeping
1348 * at least two rows of alignment visible
1350 if (annotationHeight + alignmentHeight > availableHeight)
1352 annotationHeight = Math.min(annotationHeight,
1353 availableHeight - 2 * rowHeight);
1358 // maintain same window layout whilst updating sliders
1359 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1360 availableHeight - 2 * rowHeight);
1362 return annotationHeight;