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.Annotation;
25 import jalview.datamodel.ColumnSelection;
26 import jalview.datamodel.HiddenColumns;
27 import jalview.datamodel.SequenceI;
28 import jalview.gui.JalviewColourChooser.ColourChooserListener;
29 import jalview.renderer.AnnotationRenderer;
30 import jalview.renderer.AwtRenderPanelI;
31 import jalview.schemes.ResidueProperties;
32 import jalview.util.Comparison;
33 import jalview.util.MessageManager;
34 import jalview.util.Platform;
35 import jalview.viewmodel.ViewportListenerI;
36 import jalview.viewmodel.ViewportRanges;
38 import java.awt.AlphaComposite;
39 import java.awt.Color;
40 import java.awt.Dimension;
41 import java.awt.FontMetrics;
42 import java.awt.Graphics;
43 import java.awt.Graphics2D;
44 import java.awt.Image;
45 import java.awt.Rectangle;
46 import java.awt.RenderingHints;
47 import java.awt.event.ActionEvent;
48 import java.awt.event.ActionListener;
49 import java.awt.event.AdjustmentEvent;
50 import java.awt.event.AdjustmentListener;
51 import java.awt.event.MouseEvent;
52 import java.awt.event.MouseListener;
53 import java.awt.event.MouseMotionListener;
54 import java.awt.event.MouseWheelEvent;
55 import java.awt.event.MouseWheelListener;
56 import java.awt.image.BufferedImage;
57 import java.beans.PropertyChangeEvent;
58 import java.util.ArrayList;
59 import java.util.Collections;
60 import java.util.List;
62 import javax.swing.JMenuItem;
63 import javax.swing.JPanel;
64 import javax.swing.JPopupMenu;
65 import javax.swing.Scrollable;
66 import javax.swing.ToolTipManager;
69 * AnnotationPanel displays visible portion of annotation rows below unwrapped
75 public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
76 MouseListener, MouseWheelListener, MouseMotionListener,
77 ActionListener, AdjustmentListener, Scrollable, ViewportListenerI
81 Select, Resize, Undefined
84 String HELIX = MessageManager.getString("label.helix");
86 String SHEET = MessageManager.getString("label.sheet");
89 * For RNA secondary structure "stems" aka helices
91 String STEM = MessageManager.getString("label.rna_helix");
93 String LABEL = MessageManager.getString("label.label");
95 String REMOVE = MessageManager.getString("label.remove_annotation");
97 String COLOUR = MessageManager.getString("action.colour");
99 public final Color HELIX_COLOUR = Color.red.darker();
101 public final Color SHEET_COLOUR = Color.green.darker().darker();
103 public final Color STEM_COLOUR = Color.blue.darker();
106 public AlignViewport av;
110 public int activeRow = -1;
112 public BufferedImage image;
114 public volatile BufferedImage fadedImage;
116 // private Graphics2D gg;
118 public FontMetrics fm;
120 public int imgWidth = 0;
122 boolean fastPaint = false;
124 // Used For mouse Dragging and resizing graphs
125 int graphStretch = -1;
127 int mouseDragLastX = -1;
129 int mouseDragLastY = -1;
131 DragMode dragMode = DragMode.Undefined;
133 boolean mouseDragging = false;
135 // for editing cursor
140 public final AnnotationRenderer renderer;
142 private MouseWheelListener[] _mwl;
145 * Creates a new AnnotationPanel object.
150 public AnnotationPanel(AlignmentPanel ap)
152 // setBackground(Color.white); // BH 2019
154 ToolTipManager.sharedInstance().registerComponent(this);
155 ToolTipManager.sharedInstance().setInitialDelay(0);
156 ToolTipManager.sharedInstance().setDismissDelay(10000);
159 this.setLayout(null);
160 addMouseListener(this);
161 addMouseMotionListener(this);
162 ap.annotationScroller.getVerticalScrollBar()
163 .addAdjustmentListener(this);
164 // save any wheel listeners on the scroller, so we can propagate scroll
166 _mwl = ap.annotationScroller.getMouseWheelListeners();
167 // and then set our own listener to consume all mousewheel events
168 ap.annotationScroller.addMouseWheelListener(this);
169 renderer = new AnnotationRenderer();
171 av.getRanges().addPropertyChangeListener(this);
174 public AnnotationPanel(AlignViewport av)
177 renderer = new AnnotationRenderer();
181 public void mouseWheelMoved(MouseWheelEvent e)
186 double wheelRotation = e.getPreciseWheelRotation();
187 if (wheelRotation > 0)
189 av.getRanges().scrollRight(true);
191 else if (wheelRotation < 0)
193 av.getRanges().scrollRight(false);
198 // TODO: find the correct way to let the event bubble up to
199 // ap.annotationScroller
200 for (MouseWheelListener mwl : _mwl)
204 mwl.mouseWheelMoved(e);
215 public Dimension getPreferredScrollableViewportSize()
217 Dimension ps = getPreferredSize();
218 return new Dimension(ps.width, adjustForAlignFrame(false, ps.height));
222 public int getScrollableBlockIncrement(Rectangle visibleRect,
223 int orientation, int direction)
229 public boolean getScrollableTracksViewportHeight()
235 public boolean getScrollableTracksViewportWidth()
241 public int getScrollableUnitIncrement(Rectangle visibleRect,
242 int orientation, int direction)
251 * java.awt.event.AdjustmentListener#adjustmentValueChanged(java.awt.event
255 public void adjustmentValueChanged(AdjustmentEvent evt)
257 // update annotation label display
258 ap.getAlabels().setScrollOffset(-evt.getValue());
262 * Calculates the height of the annotation displayed in the annotation panel.
263 * Callers should normally call the ap.adjustAnnotationHeight method to ensure
264 * all annotation associated components are updated correctly.
267 public int adjustPanelHeight()
269 int height = av.calcPanelHeight();
270 this.setPreferredSize(new Dimension(1, height));
273 // revalidate only when the alignment panel is fully constructed
287 public void actionPerformed(ActionEvent evt)
289 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
294 Annotation[] anot = aa[activeRow].annotations;
296 if (anot.length < av.getColumnSelection().getMax())
298 Annotation[] temp = new Annotation[av.getColumnSelection().getMax()
300 System.arraycopy(anot, 0, temp, 0, anot.length);
302 aa[activeRow].annotations = anot;
305 String action = evt.getActionCommand();
306 if (action.equals(REMOVE))
308 for (int index : av.getColumnSelection().getSelected())
310 if (av.getAlignment().getHiddenColumns().isVisible(index))
316 else if (action.equals(LABEL))
318 String exMesg = collectAnnotVals(anot, LABEL);
319 String label = JvOptionPane.showInputDialog(
320 MessageManager.getString("label.enter_label"), exMesg);
327 if ((label.length() > 0) && !aa[activeRow].hasText)
329 aa[activeRow].hasText = true;
332 for (int index : av.getColumnSelection().getSelected())
334 if (!av.getAlignment().getHiddenColumns().isVisible(index))
339 if (anot[index] == null)
341 anot[index] = new Annotation(label, "", ' ', 0);
345 anot[index].displayCharacter = label;
349 else if (action.equals(COLOUR))
351 final Annotation[] fAnot = anot;
352 String title = MessageManager
353 .getString("label.select_foreground_colour");
354 ColourChooserListener listener = new ColourChooserListener()
357 public void colourSelected(Color c)
359 HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
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;
372 JalviewColourChooser.showColourChooser(this,
373 title, Color.black, listener);
376 // HELIX, SHEET or STEM
379 String symbol = "\u03B1"; // alpha
381 if (action.equals(HELIX))
385 else if (action.equals(SHEET))
388 symbol = "\u03B2"; // beta
391 // Added by LML to color stems
392 else if (action.equals(STEM))
395 int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
396 symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
399 if (!aa[activeRow].hasIcons)
401 aa[activeRow].hasIcons = true;
404 String label = JvOptionPane.showInputDialog(MessageManager
405 .getString("label.enter_label_for_the_structure"), symbol);
412 if ((label.length() > 0) && !aa[activeRow].hasText)
414 aa[activeRow].hasText = true;
415 if (action.equals(STEM))
417 aa[activeRow].showAllColLabels = true;
420 for (int index : av.getColumnSelection().getSelected())
422 if (!av.getAlignment().getHiddenColumns().isVisible(index))
427 if (anot[index] == null)
429 anot[index] = new Annotation(label, "", type, 0);
432 anot[index].secondaryStructure = type != 'S' ? type
433 : label.length() == 0 ? ' ' : label.charAt(0);
434 anot[index].displayCharacter = label;
439 av.getAlignment().validateAnnotation(aa[activeRow]);
440 ap.alignmentChanged();
441 ap.alignFrame.setMenusForViewport();
449 * Returns any existing annotation concatenated as a string. For each
450 * annotation, takes the description, if any, else the secondary structure
451 * character (if type is HELIX, SHEET or STEM), else the display character (if
458 private String collectAnnotVals(Annotation[] anots, String type)
460 // TODO is this method wanted? why? 'last' is not used
462 StringBuilder collatedInput = new StringBuilder(64);
464 ColumnSelection viscols = av.getColumnSelection();
465 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
468 * the selection list (read-only view) is in selection order, not
469 * column order; make a copy so we can sort it
471 List<Integer> selected = new ArrayList<>(viscols.getSelected());
472 Collections.sort(selected);
473 for (int index : selected)
475 // always check for current display state - just in case
476 if (!hidden.isVisible(index))
480 String tlabel = null;
481 if (anots[index] != null)
482 { // LML added stem code
483 if (type.equals(HELIX) || type.equals(SHEET) || type.equals(STEM)
484 || type.equals(LABEL))
486 tlabel = anots[index].description;
487 if (tlabel == null || tlabel.length() < 1)
489 if (type.equals(HELIX) || type.equals(SHEET)
490 || type.equals(STEM))
492 tlabel = "" + anots[index].secondaryStructure;
496 tlabel = "" + anots[index].displayCharacter;
500 if (tlabel != null && !tlabel.equals(last))
502 if (last.length() > 0)
504 collatedInput.append(" ");
506 collatedInput.append(tlabel);
510 return collatedInput.toString();
514 * Action on right mouse pressed on Mac is to show a pop-up menu for the
515 * annotation. Action on left mouse pressed is to find which annotation is
516 * pressed and mark the start of a column selection or graph resize operation.
521 public void mousePressed(MouseEvent evt)
524 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
529 mouseDragLastX = evt.getX();
530 mouseDragLastY = evt.getY();
533 * add visible annotation heights until we reach the y
534 * position, to find which annotation it is in
539 final int y = evt.getY();
540 for (int i = 0; i < aa.length; i++)
544 height += aa[i].height;
553 else if (aa[i].graph > 0)
556 * we have clicked on a resizable graph annotation
565 * isPopupTrigger fires in mousePressed on Mac,
566 * not until mouseRelease on Windows
568 if (evt.isPopupTrigger() && activeRow != -1)
570 showPopupMenu(y, evt.getX());
574 ap.getScalePanel().mousePressed(evt);
578 * Construct and display a context menu at the right-click position
583 void showPopupMenu(final int y, int x)
585 if (av.getColumnSelection() == null
586 || av.getColumnSelection().isEmpty())
591 JPopupMenu pop = new JPopupMenu(
592 MessageManager.getString("label.structure_type"));
595 * Just display the needed structure options
597 if (av.getAlignment().isNucleotide())
599 item = new JMenuItem(STEM);
600 item.addActionListener(this);
605 item = new JMenuItem(HELIX);
606 item.addActionListener(this);
608 item = new JMenuItem(SHEET);
609 item.addActionListener(this);
612 item = new JMenuItem(LABEL);
613 item.addActionListener(this);
615 item = new JMenuItem(COLOUR);
616 item.addActionListener(this);
618 item = new JMenuItem(REMOVE);
619 item.addActionListener(this);
621 pop.show(this, x, y);
625 * Action on mouse up is to clear mouse drag data and call mouseReleased on
626 * ScalePanel, to deal with defining the selection group (if any) defined by
632 public void mouseReleased(MouseEvent evt)
637 mouseDragging = false;
638 dragMode = DragMode.Undefined;
639 ap.getScalePanel().mouseReleased(evt);
642 * isPopupTrigger is set in mouseReleased on Windows
643 * (in mousePressed on Mac)
645 if (evt.isPopupTrigger() && activeRow != -1)
647 showPopupMenu(evt.getY(), evt.getX());
659 public void mouseEntered(MouseEvent evt)
661 this.mouseDragging = false;
662 ap.getScalePanel().mouseEntered(evt);
666 * On leaving the panel, calls ScalePanel.mouseExited to deal with scrolling
667 * with column selection on a mouse drag
672 public void mouseExited(MouseEvent evt)
674 ap.getScalePanel().mouseExited(evt);
684 public void mouseDragged(MouseEvent evt)
687 * todo: if dragMode is Undefined:
688 * - set to Select if dx > dy
689 * - set to Resize if dy > dx
690 * - do nothing if dx == dy
692 final int x = evt.getX();
693 final int y = evt.getY();
694 if (dragMode == DragMode.Undefined)
696 int dx = Math.abs(x - mouseDragLastX);
697 int dy = Math.abs(y - mouseDragLastY);
698 if (graphStretch == -1 || dx > dy)
701 * mostly horizontal drag, or not a graph annotation
703 dragMode = DragMode.Select;
708 * mostly vertical drag
710 dragMode = DragMode.Resize;
714 if (dragMode == DragMode.Undefined)
717 * drag is diagonal - defer deciding whether to
718 * treat as up/down or left/right
725 if (dragMode == DragMode.Resize)
728 * resize graph annotation if mouse was dragged up or down
730 int deltaY = mouseDragLastY - evt.getY();
733 AlignmentAnnotation graphAnnotation = av.getAlignment()
734 .getAlignmentAnnotation()[graphStretch];
735 int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
736 graphAnnotation.graphHeight = newHeight;
738 ap.paintAlignment(false, false);
744 * for mouse drag left or right, delegate to
745 * ScalePanel to adjust the column selection
747 ap.getScalePanel().mouseDragged(evt);
757 * Constructs the tooltip, and constructs and displays a status message, for
758 * the current mouse position
763 public void mouseMoved(MouseEvent evt)
765 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
769 this.setToolTipText(null);
776 for (int i = 0; i < aa.length; i++)
780 height += aa[i].height;
783 if (evt.getY() < height)
792 this.setToolTipText(null);
796 int column = (evt.getX() / av.getCharWidth())
797 + av.getRanges().getStartRes();
799 if (av.hasHiddenColumns())
801 column = av.getAlignment().getHiddenColumns()
802 .visibleToAbsoluteColumn(column);
805 AlignmentAnnotation ann = aa[row];
806 if (row > -1 && ann.annotations != null
807 && column < ann.annotations.length)
809 buildToolTip(ann, column, aa);
810 setStatusMessage(column, ann);
814 this.setToolTipText(null);
815 ap.alignFrame.setStatus(" ");
820 * Builds a tooltip for the annotation at the current mouse position.
826 void buildToolTip(AlignmentAnnotation ann, int column,
827 AlignmentAnnotation[] anns)
829 if (ann.graphGroup > -1)
831 StringBuilder tip = new StringBuilder(32);
832 tip.append("<html>");
833 for (int i = 0; i < anns.length; i++)
835 if (anns[i].graphGroup == ann.graphGroup
836 && anns[i].annotations[column] != null)
838 tip.append(anns[i].label);
839 String description = anns[i].annotations[column].description;
840 if (description != null && description.length() > 0)
842 tip.append(" ").append(description);
847 if (tip.length() != 6)
849 tip.setLength(tip.length() - 4);
850 this.setToolTipText(tip.toString() + "</html>");
853 else if (ann.annotations[column] != null)
855 String description = ann.annotations[column].description;
856 if (description != null && description.length() > 0)
858 this.setToolTipText(JvSwingUtils.wrapTooltip(true, description));
862 this.setToolTipText(null); // no tooltip if null or empty description
867 // clear the tooltip.
868 this.setToolTipText(null);
873 * Constructs and displays the status bar message
878 void setStatusMessage(int column, AlignmentAnnotation ann)
881 * show alignment column and annotation description if any
883 StringBuilder text = new StringBuilder(32);
884 text.append(MessageManager.getString("label.column")).append(" ")
887 if (ann.annotations[column] != null)
889 String description = ann.annotations[column].description;
890 if (description != null && description.trim().length() > 0)
892 text.append(" ").append(description);
897 * if the annotation is sequence-specific, show the sequence number
898 * in the alignment, and (if not a gap) the residue and position
900 SequenceI seqref = ann.sequenceRef;
903 int seqIndex = av.getAlignment().findIndex(seqref);
906 text.append(", ").append(MessageManager.getString("label.sequence"))
907 .append(" ").append(seqIndex + 1);
908 char residue = seqref.getCharAt(column);
909 if (!Comparison.isGap(residue))
913 if (av.getAlignment().isNucleotide())
915 name = ResidueProperties.nucleotideName
916 .get(String.valueOf(residue));
917 text.append(" Nucleotide: ")
918 .append(name != null ? name : residue);
922 name = 'X' == residue ? "X"
923 : ('*' == residue ? "STOP"
924 : ResidueProperties.aa2Triplet
925 .get(String.valueOf(residue)));
926 text.append(" Residue: ").append(name != null ? name : residue);
928 int residuePos = seqref.findPosition(column);
929 text.append(" (").append(residuePos).append(")");
934 ap.alignFrame.setStatus(text.toString());
944 public void mouseClicked(MouseEvent evt)
946 // if (activeRow != -1)
948 // AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
949 // AlignmentAnnotation anot = aa[activeRow];
953 // TODO mouseClicked-content and drawCursor are quite experimental!
954 public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
957 int pady = av.getCharHeight() / 5;
959 graphics.setColor(Color.black);
960 graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
962 if (av.validCharWidth)
964 graphics.setColor(Color.white);
966 char s = seq.getCharAt(res);
968 charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
969 graphics.drawString(String.valueOf(s), charOffset + x1,
970 (y1 + av.getCharHeight()) - pady);
975 private volatile boolean imageFresh = false;
976 private Rectangle visibleRect = new Rectangle(), clipBounds = new Rectangle();
985 public void paintComponent(Graphics g)
988 // BH: note that this method is generally recommended to
989 // call super.paintComponent(g). Otherwise, the children of this
990 // component will not be rendered. That is not needed here
991 // because AnnotationPanel does not have any children. It is
992 // just a JPanel contained in a JViewPort.
994 computeVisibleRect(visibleRect);
996 g.setColor(Color.white);
997 g.fillRect(0, 0, visibleRect.width, visibleRect.height);
1001 // BH 2018 optimizing generation of new Rectangle().
1002 if (fastPaint || (visibleRect.width != (clipBounds = g.getClipBounds(clipBounds)).width)
1003 || (visibleRect.height != clipBounds.height))
1007 g.drawImage(image, 0, 0, this);
1012 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
1013 + 1) * av.getCharWidth();
1019 if (image == null || imgWidth != image.getWidth(this)
1020 || image.getHeight(this) != getHeight())
1024 image = new BufferedImage(imgWidth,
1025 ap.getAnnotationPanel().getHeight(),
1026 BufferedImage.TYPE_INT_RGB);
1027 } catch (OutOfMemoryError oom)
1032 } catch (Exception x)
1037 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1041 gg = (Graphics2D) image.getGraphics();
1045 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1046 RenderingHints.VALUE_ANTIALIAS_ON);
1049 gg.setFont(av.getFont());
1050 fm = gg.getFontMetrics();
1051 gg.setColor(Color.white);
1052 gg.fillRect(0, 0, imgWidth, image.getHeight());
1055 gg = (Graphics2D) image.getGraphics();
1059 drawComponent(gg, av.getRanges().getStartRes(),
1060 av.getRanges().getEndRes() + 1);
1063 g.drawImage(image, 0, 0, this);
1067 * set true to enable redraw timing debug output on stderr
1069 private final boolean debugRedraw = false;
1072 * non-Thread safe repaint
1075 * repaint with horizontal shift in alignment
1077 public void fastPaint(int horizontal)
1079 if ((horizontal == 0) || image == null
1080 || av.getAlignment().getAlignmentAnnotation() == null
1081 || av.getAlignment().getAlignmentAnnotation().length < 1
1082 || av.isCalcInProgress())
1088 int sr = av.getRanges().getStartRes();
1089 int er = av.getRanges().getEndRes() + 1;
1092 Graphics2D gg = (Graphics2D) image.getGraphics();
1094 gg.copyArea(0, 0, imgWidth, getHeight(),
1095 -horizontal * av.getCharWidth(), 0);
1097 if (horizontal > 0) // scrollbar pulled right, image to the left
1099 transX = (er - sr - horizontal) * av.getCharWidth();
1100 sr = er - horizontal;
1102 else if (horizontal < 0)
1104 er = sr - horizontal;
1107 gg.translate(transX, 0);
1109 drawComponent(gg, sr, er);
1111 gg.translate(-transX, 0);
1117 // Call repaint on alignment panel so that repaints from other alignment
1118 // panel components can be aggregated. Otherwise performance of the overview
1119 // window and others may be adversely affected.
1120 av.getAlignPanel().repaint();
1123 private volatile boolean lastImageGood = false;
1135 public void drawComponent(Graphics g, int startRes, int endRes)
1137 BufferedImage oldFaded = fadedImage;
1138 if (av.isCalcInProgress())
1142 lastImageGood = false;
1145 // We'll keep a record of the old image,
1146 // and draw a faded image until the calculation
1149 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1150 || fadedImage.getHeight() != image.getHeight()))
1152 // System.err.println("redraw faded image ("+(fadedImage==null ?
1153 // "null image" : "") + " lastGood="+lastImageGood+")");
1154 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1155 BufferedImage.TYPE_INT_RGB);
1157 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1159 fadedG.setColor(Color.white);
1160 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1162 fadedG.setComposite(
1163 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1164 fadedG.drawImage(image, 0, 0, this);
1167 // make sure we don't overwrite the last good faded image until all
1168 // calculations have finished
1169 lastImageGood = false;
1174 if (fadedImage != null)
1176 oldFaded = fadedImage;
1181 g.setColor(Color.white);
1182 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1184 g.setFont(av.getFont());
1187 fm = g.getFontMetrics();
1190 if ((av.getAlignment().getAlignmentAnnotation() == null)
1191 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1193 g.setColor(Color.white);
1194 g.fillRect(0, 0, getWidth(), getHeight());
1195 g.setColor(Color.black);
1196 if (av.validCharWidth)
1198 g.drawString(MessageManager
1199 .getString("label.alignment_has_no_annotations"), 20, 15);
1204 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1206 if (!lastImageGood && fadedImage == null)
1208 fadedImage = oldFaded;
1213 public FontMetrics getFontMetrics()
1219 public Image getFadedImage()
1225 public int getFadedImageWidth()
1230 private int[] bounds = new int[2];
1233 public int[] getVisibleVRange()
1235 if (ap != null && ap.getAlabels() != null)
1237 int sOffset = -ap.getAlabels().getScrollOffset();
1238 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1239 bounds[0] = sOffset;
1240 bounds[1] = visHeight;
1250 * Try to ensure any references held are nulled
1252 public void dispose()
1262 * I created the renderer so I will dispose of it
1264 if (renderer != null)
1271 public void propertyChange(PropertyChangeEvent evt)
1273 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1274 // Both scrolling and resizing change viewport ranges: scrolling changes
1275 // both start and end points, but resize only changes end values.
1276 // Here we only want to fastpaint on a scroll, with resize using a normal
1277 // paint, so scroll events are identified as changes to the horizontal or
1278 // vertical start value.
1279 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1281 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1283 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1285 fastPaint(((int[]) evt.getNewValue())[0]
1286 - ((int[]) evt.getOldValue())[0]);
1288 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
1295 * computes the visible height of the annotation panel
1297 * @param adjustPanelHeight
1298 * - when false, just adjust existing height according to other
1300 * @param annotationHeight
1301 * @return height to use for the ScrollerPreferredVisibleSize
1303 public int adjustForAlignFrame(boolean adjustPanelHeight,
1304 int annotationHeight)
1307 * Estimate available height in the AlignFrame for alignment +
1308 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1309 * hscroll, status bar, insets.
1311 int stuff = (ap.getViewName() != null ? 30 : 0)
1312 + (Platform.isAMacAndNotJS() ? 120 : 140);
1313 int availableHeight = ap.alignFrame.getHeight() - stuff;
1314 int rowHeight = av.getCharHeight();
1316 if (adjustPanelHeight)
1318 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1321 * If not enough vertical space, maximize annotation height while keeping
1322 * at least two rows of alignment visible
1324 if (annotationHeight + alignmentHeight > availableHeight)
1326 annotationHeight = Math.min(annotationHeight,
1327 availableHeight - 2 * rowHeight);
1332 // maintain same window layout whilst updating sliders
1333 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1334 availableHeight - 2 * rowHeight);
1336 return annotationHeight;