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()
360 for (int index : av.getColumnSelection().getSelected())
362 if (hiddenColumns.isVisible(index))
364 if (fAnot[index] == null)
366 fAnot[index] = new Annotation("", "", ' ', 0);
368 fAnot[index].colour = c;
373 JalviewColourChooser.showColourChooser(this, title, Color.black,
377 // HELIX, SHEET or STEM
380 String symbol = "\u03B1"; // alpha
382 if (action.equals(HELIX))
386 else if (action.equals(SHEET))
389 symbol = "\u03B2"; // beta
392 // Added by LML to color stems
393 else if (action.equals(STEM))
396 int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
397 symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
400 if (!aa[activeRow].hasIcons)
402 aa[activeRow].hasIcons = true;
405 String label = JvOptionPane.showInputDialog(MessageManager
406 .getString("label.enter_label_for_the_structure"), symbol);
413 if ((label.length() > 0) && !aa[activeRow].hasText)
415 aa[activeRow].hasText = true;
416 if (action.equals(STEM))
418 aa[activeRow].showAllColLabels = true;
421 for (int index : av.getColumnSelection().getSelected())
423 if (!av.getAlignment().getHiddenColumns().isVisible(index))
428 if (anot[index] == null)
430 anot[index] = new Annotation(label, "", type, 0);
433 anot[index].secondaryStructure = type != 'S' ? type
434 : label.length() == 0 ? ' ' : label.charAt(0);
435 anot[index].displayCharacter = label;
440 av.getAlignment().validateAnnotation(aa[activeRow]);
441 ap.alignmentChanged();
442 ap.alignFrame.setMenusForViewport();
450 * Returns any existing annotation concatenated as a string. For each
451 * annotation, takes the description, if any, else the secondary structure
452 * character (if type is HELIX, SHEET or STEM), else the display character (if
459 private String collectAnnotVals(Annotation[] anots, String type)
461 // TODO is this method wanted? why? 'last' is not used
463 StringBuilder collatedInput = new StringBuilder(64);
465 ColumnSelection viscols = av.getColumnSelection();
466 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
469 * the selection list (read-only view) is in selection order, not
470 * column order; make a copy so we can sort it
472 List<Integer> selected = new ArrayList<>(viscols.getSelected());
473 Collections.sort(selected);
474 for (int index : selected)
476 // always check for current display state - just in case
477 if (!hidden.isVisible(index))
481 String tlabel = null;
482 if (anots[index] != null)
483 { // LML added stem code
484 if (type.equals(HELIX) || type.equals(SHEET) || type.equals(STEM)
485 || type.equals(LABEL))
487 tlabel = anots[index].description;
488 if (tlabel == null || tlabel.length() < 1)
490 if (type.equals(HELIX) || type.equals(SHEET)
491 || type.equals(STEM))
493 tlabel = "" + anots[index].secondaryStructure;
497 tlabel = "" + anots[index].displayCharacter;
501 if (tlabel != null && !tlabel.equals(last))
503 if (last.length() > 0)
505 collatedInput.append(" ");
507 collatedInput.append(tlabel);
511 return collatedInput.toString();
515 * Action on right mouse pressed on Mac is to show a pop-up menu for the
516 * annotation. Action on left mouse pressed is to find which annotation is
517 * pressed and mark the start of a column selection or graph resize operation.
522 public void mousePressed(MouseEvent evt)
525 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
530 mouseDragLastX = evt.getX();
531 mouseDragLastY = evt.getY();
534 * add visible annotation heights until we reach the y
535 * position, to find which annotation it is in
540 final int y = evt.getY();
541 for (int i = 0; i < aa.length; i++)
545 height += aa[i].height;
554 else if (aa[i].graph > 0)
557 * we have clicked on a resizable graph annotation
566 * isPopupTrigger fires in mousePressed on Mac,
567 * not until mouseRelease on Windows
569 if (evt.isPopupTrigger() && activeRow != -1)
571 showPopupMenu(y, evt.getX());
575 ap.getScalePanel().mousePressed(evt);
579 * Construct and display a context menu at the right-click position
584 void showPopupMenu(final int y, int x)
586 if (av.getColumnSelection() == null
587 || av.getColumnSelection().isEmpty())
592 JPopupMenu pop = new JPopupMenu(
593 MessageManager.getString("label.structure_type"));
596 * Just display the needed structure options
598 if (av.getAlignment().isNucleotide())
600 item = new JMenuItem(STEM);
601 item.addActionListener(this);
606 item = new JMenuItem(HELIX);
607 item.addActionListener(this);
609 item = new JMenuItem(SHEET);
610 item.addActionListener(this);
613 item = new JMenuItem(LABEL);
614 item.addActionListener(this);
616 item = new JMenuItem(COLOUR);
617 item.addActionListener(this);
619 item = new JMenuItem(REMOVE);
620 item.addActionListener(this);
622 pop.show(this, x, y);
626 * Action on mouse up is to clear mouse drag data and call mouseReleased on
627 * ScalePanel, to deal with defining the selection group (if any) defined by
633 public void mouseReleased(MouseEvent evt)
638 mouseDragging = false;
639 if (dragMode == DragMode.Resize)
641 ap.adjustAnnotationHeight();
643 dragMode = DragMode.Undefined;
644 ap.getScalePanel().mouseReleased(evt);
647 * isPopupTrigger is set in mouseReleased on Windows
648 * (in mousePressed on Mac)
650 if (evt.isPopupTrigger() && activeRow != -1)
652 showPopupMenu(evt.getY(), evt.getX());
664 public void mouseEntered(MouseEvent evt)
666 this.mouseDragging = false;
667 ap.getScalePanel().mouseEntered(evt);
671 * On leaving the panel, calls ScalePanel.mouseExited to deal with scrolling
672 * with column selection on a mouse drag
677 public void mouseExited(MouseEvent evt)
679 ap.getScalePanel().mouseExited(evt);
683 * Action on starting or continuing a mouse drag. There are two possible
686 * <li>drag up or down on a graphed annotation increases or decreases the
687 * height of the graph</li>
688 * <li>dragging left or right selects the columns dragged across</li>
690 * A drag on a graph annotation is treated as column selection if it starts
691 * with more horizontal than vertical movement, and as resize if it starts
692 * with more vertical than horizontal movement. Once started, the drag does
698 public void mouseDragged(MouseEvent evt)
701 * if dragMode is Undefined:
702 * - set to Select if dx > dy
703 * - set to Resize if dy > dx
704 * - do nothing if dx == dy
706 final int x = evt.getX();
707 final int y = evt.getY();
708 if (dragMode == DragMode.Undefined)
710 int dx = Math.abs(x - mouseDragLastX);
711 int dy = Math.abs(y - mouseDragLastY);
712 if (graphStretch == -1 || dx > dy)
715 * mostly horizontal drag, or not a graph annotation
717 dragMode = DragMode.Select;
722 * mostly vertical drag
724 dragMode = DragMode.Resize;
728 if (dragMode == DragMode.Undefined)
731 * drag is diagonal - defer deciding whether to
732 * treat as up/down or left/right
739 if (dragMode == DragMode.Resize)
742 * resize graph annotation if mouse was dragged up or down
744 int deltaY = mouseDragLastY - evt.getY();
747 AlignmentAnnotation graphAnnotation = av.getAlignment()
748 .getAlignmentAnnotation()[graphStretch];
749 int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
750 graphAnnotation.graphHeight = newHeight;
752 ap.paintAlignment(false, false);
758 * for mouse drag left or right, delegate to
759 * ScalePanel to adjust the column selection
761 ap.getScalePanel().mouseDragged(evt);
771 * Constructs the tooltip, and constructs and displays a status message, for
772 * the current mouse position
777 public void mouseMoved(MouseEvent evt)
779 int yPos = evt.getY();
780 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
782 int row = getRowIndex(yPos, aa);
786 this.setToolTipText(null);
790 int column = (evt.getX() / av.getCharWidth())
791 + av.getRanges().getStartRes();
792 column = Math.min(column, av.getRanges().getEndRes());
794 if (av.hasHiddenColumns())
796 column = av.getAlignment().getHiddenColumns()
797 .visibleToAbsoluteColumn(column);
800 AlignmentAnnotation ann = aa[row];
801 if (row > -1 && ann.annotations != null
802 && column < ann.annotations.length)
804 String toolTip = buildToolTip(ann, column, aa);
805 setToolTipText(toolTip == null ? null
806 : JvSwingUtils.wrapTooltip(true, toolTip));
807 String msg = getStatusMessage(av.getAlignment(), column, ann);
808 ap.alignFrame.setStatus(msg);
812 this.setToolTipText(null);
813 ap.alignFrame.setStatus(" ");
818 * Answers the index in the annotations array of the visible annotation at the
819 * given y position. This is done by adding the heights of visible annotations
820 * until the y position has been exceeded. Answers -1 if no annotations are
821 * visible, or the y position is below all annotations.
827 static int getRowIndex(int yPos, AlignmentAnnotation[] aa)
836 for (int i = 0; i < aa.length; i++)
840 height += aa[i].height;
853 * Answers a tooltip for the annotation at the current mouse position, not
854 * wrapped in <html> tags (apply if wanted). Answers null if there is no
861 static String buildToolTip(AlignmentAnnotation ann, int column,
862 AlignmentAnnotation[] anns)
864 String tooltip = null;
865 if (ann.graphGroup > -1)
867 StringBuilder tip = new StringBuilder(32);
868 boolean first = true;
869 for (int i = 0; i < anns.length; i++)
871 if (anns[i].graphGroup == ann.graphGroup
872 && anns[i].annotations[column] != null)
879 tip.append(anns[i].label);
880 String description = anns[i].annotations[column].description;
881 if (description != null && description.length() > 0)
883 tip.append(" ").append(description);
887 tooltip = first ? null : tip.toString();
889 else if (column < ann.annotations.length
890 && ann.annotations[column] != null)
892 tooltip = ann.annotations[column].description;
899 * Constructs and returns the status bar message
905 static String getStatusMessage(AlignmentI al, int column,
906 AlignmentAnnotation ann)
909 * show alignment column and annotation description if any
911 StringBuilder text = new StringBuilder(32);
912 text.append(MessageManager.getString("label.column")).append(" ")
915 if (column < ann.annotations.length && ann.annotations[column] != null)
917 String description = ann.annotations[column].description;
918 if (description != null && description.trim().length() > 0)
920 text.append(" ").append(description);
925 * if the annotation is sequence-specific, show the sequence number
926 * in the alignment, and (if not a gap) the residue and position
928 SequenceI seqref = ann.sequenceRef;
931 int seqIndex = al.findIndex(seqref);
934 text.append(", ").append(MessageManager.getString("label.sequence"))
935 .append(" ").append(seqIndex + 1);
936 char residue = seqref.getCharAt(column);
937 if (!Comparison.isGap(residue))
941 if (al.isNucleotide())
943 name = ResidueProperties.nucleotideName
944 .get(String.valueOf(residue));
945 text.append(" Nucleotide: ")
946 .append(name != null ? name : residue);
950 name = 'X' == residue ? "X"
951 : ('*' == residue ? "STOP"
952 : ResidueProperties.aa2Triplet
953 .get(String.valueOf(residue)));
954 text.append(" Residue: ").append(name != null ? name : residue);
956 int residuePos = seqref.findPosition(column);
957 text.append(" (").append(residuePos).append(")");
962 return text.toString();
972 public void mouseClicked(MouseEvent evt)
974 // if (activeRow != -1)
976 // AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
977 // AlignmentAnnotation anot = aa[activeRow];
981 // TODO mouseClicked-content and drawCursor are quite experimental!
982 public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
985 int pady = av.getCharHeight() / 5;
987 graphics.setColor(Color.black);
988 graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
990 if (av.validCharWidth)
992 graphics.setColor(Color.white);
994 char s = seq.getCharAt(res);
996 charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
997 graphics.drawString(String.valueOf(s), charOffset + x1,
998 (y1 + av.getCharHeight()) - pady);
1003 private volatile boolean imageFresh = false;
1005 private Rectangle visibleRect = new Rectangle(),
1006 clipBounds = new Rectangle();
1015 public void paintComponent(Graphics g)
1018 // BH: note that this method is generally recommended to
1019 // call super.paintComponent(g). Otherwise, the children of this
1020 // component will not be rendered. That is not needed here
1021 // because AnnotationPanel does not have any children. It is
1022 // just a JPanel contained in a JViewPort.
1024 computeVisibleRect(visibleRect);
1026 g.setColor(Color.white);
1027 g.fillRect(0, 0, visibleRect.width, visibleRect.height);
1031 // BH 2018 optimizing generation of new Rectangle().
1033 || (visibleRect.width != (clipBounds = g
1034 .getClipBounds(clipBounds)).width)
1035 || (visibleRect.height != clipBounds.height))
1038 g.drawImage(image, 0, 0, this);
1043 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
1044 + 1) * av.getCharWidth();
1050 if (image == null || imgWidth != image.getWidth(this)
1051 || image.getHeight(this) != getHeight())
1055 image = new BufferedImage(imgWidth,
1056 ap.getAnnotationPanel().getHeight(),
1057 BufferedImage.TYPE_INT_RGB);
1058 } catch (OutOfMemoryError oom)
1063 } catch (Exception x)
1068 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1072 gg = (Graphics2D) image.getGraphics();
1076 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1077 RenderingHints.VALUE_ANTIALIAS_ON);
1080 gg.setFont(av.getFont());
1081 fm = gg.getFontMetrics();
1082 gg.setColor(Color.white);
1083 gg.fillRect(0, 0, imgWidth, image.getHeight());
1088 gg = (Graphics2D) image.getGraphics();
1092 drawComponent(gg, av.getRanges().getStartRes(),
1093 av.getRanges().getEndRes() + 1);
1096 g.drawImage(image, 0, 0, this);
1100 * set true to enable redraw timing debug output on stderr
1102 private final boolean debugRedraw = false;
1105 * non-Thread safe repaint
1108 * repaint with horizontal shift in alignment
1110 public void fastPaint(int horizontal)
1112 if ((horizontal == 0) || image == null
1113 || av.getAlignment().getAlignmentAnnotation() == null
1114 || av.getAlignment().getAlignmentAnnotation().length < 1
1115 || av.isCalcInProgress())
1121 int sr = av.getRanges().getStartRes();
1122 int er = av.getRanges().getEndRes() + 1;
1125 Graphics2D gg = (Graphics2D) image.getGraphics();
1127 if (imgWidth>Math.abs(horizontal*av.getCharWidth())) {
1128 //scroll is less than imgWidth away so can re-use buffered graphics
1129 gg.copyArea(0, 0, imgWidth, getHeight(),
1130 -horizontal * av.getCharWidth(), 0);
1132 if (horizontal > 0) // scrollbar pulled right, image to the left
1134 transX = (er - sr - horizontal) * av.getCharWidth();
1135 sr = er - horizontal;
1137 else if (horizontal < 0)
1139 er = sr - horizontal;
1142 gg.translate(transX, 0);
1144 drawComponent(gg, sr, er);
1146 gg.translate(-transX, 0);
1152 // Call repaint on alignment panel so that repaints from other alignment
1153 // panel components can be aggregated. Otherwise performance of the overview
1154 // window and others may be adversely affected.
1155 av.getAlignPanel().repaint();
1158 private volatile boolean lastImageGood = false;
1170 public void drawComponent(Graphics g, int startRes, int endRes)
1172 BufferedImage oldFaded = fadedImage;
1173 if (av.isCalcInProgress())
1177 lastImageGood = false;
1180 // We'll keep a record of the old image,
1181 // and draw a faded image until the calculation
1184 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1185 || fadedImage.getHeight() != image.getHeight()))
1187 // System.err.println("redraw faded image ("+(fadedImage==null ?
1188 // "null image" : "") + " lastGood="+lastImageGood+")");
1189 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1190 BufferedImage.TYPE_INT_RGB);
1192 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1194 fadedG.setColor(Color.white);
1195 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1197 fadedG.setComposite(
1198 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1199 fadedG.drawImage(image, 0, 0, this);
1202 // make sure we don't overwrite the last good faded image until all
1203 // calculations have finished
1204 lastImageGood = false;
1209 if (fadedImage != null)
1211 oldFaded = fadedImage;
1216 g.setColor(Color.white);
1217 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1219 g.setFont(av.getFont());
1222 fm = g.getFontMetrics();
1225 if ((av.getAlignment().getAlignmentAnnotation() == null)
1226 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1228 g.setColor(Color.white);
1229 g.fillRect(0, 0, getWidth(), getHeight());
1230 g.setColor(Color.black);
1231 if (av.validCharWidth)
1233 g.drawString(MessageManager
1234 .getString("label.alignment_has_no_annotations"), 20, 15);
1239 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1241 if (!lastImageGood && fadedImage == null)
1243 fadedImage = oldFaded;
1248 public FontMetrics getFontMetrics()
1254 public Image getFadedImage()
1260 public int getFadedImageWidth()
1265 private int[] bounds = new int[2];
1268 public int[] getVisibleVRange()
1270 if (ap != null && ap.getAlabels() != null)
1272 int sOffset = -ap.getAlabels().getScrollOffset();
1273 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1274 bounds[0] = sOffset;
1275 bounds[1] = visHeight;
1285 * Try to ensure any references held are nulled
1287 public void dispose()
1297 * I created the renderer so I will dispose of it
1299 if (renderer != null)
1306 public void propertyChange(PropertyChangeEvent evt)
1308 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1309 // Both scrolling and resizing change viewport ranges: scrolling changes
1310 // both start and end points, but resize only changes end values.
1311 // Here we only want to fastpaint on a scroll, with resize using a normal
1312 // paint, so scroll events are identified as changes to the horizontal or
1313 // vertical start value.
1314 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1316 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1318 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1320 fastPaint(((int[]) evt.getNewValue())[0]
1321 - ((int[]) evt.getOldValue())[0]);
1323 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
1330 * computes the visible height of the annotation panel
1332 * @param adjustPanelHeight
1333 * - when false, just adjust existing height according to other
1335 * @param annotationHeight
1336 * @return height to use for the ScrollerPreferredVisibleSize
1338 public int adjustForAlignFrame(boolean adjustPanelHeight,
1339 int annotationHeight)
1342 * Estimate available height in the AlignFrame for alignment +
1343 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1344 * hscroll, status bar, insets.
1346 int stuff = (ap.getViewName() != null ? 30 : 0)
1347 + (Platform.isAMacAndNotJS() ? 120 : 140);
1348 int availableHeight = ap.alignFrame.getHeight() - stuff;
1349 int rowHeight = av.getCharHeight();
1351 if (adjustPanelHeight)
1353 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1356 * If not enough vertical space, maximize annotation height while keeping
1357 * at least two rows of alignment visible
1359 if (annotationHeight + alignmentHeight > availableHeight)
1361 annotationHeight = Math.min(annotationHeight,
1362 availableHeight - 2 * rowHeight);
1367 // maintain same window layout whilst updating sliders
1368 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1369 availableHeight - 2 * rowHeight);
1371 return annotationHeight;