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.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.JColorChooser;
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;
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 * Responds to a mouse wheel movement by scrolling the annotations up or down.
181 * Annotation labels are scrolled via method adjustmentValueChanged when the
182 * vertical scrollbar is adjusted.
184 * If shift is pressed, then scrolling is left or right instead, and is
185 * delegated to AlignmentPanel, so that both sequences and annotations are
188 * This object is a MouseWheelListener to AnnotationLabels, so mouse wheel
189 * events over the labels are delegated to this method.
191 * Note that this method may also be fired by scrolling with a gesture on a
195 public void mouseWheelMoved(MouseWheelEvent e)
199 ap.getSeqPanel().mouseWheelMoved(e);
203 // TODO: find the correct way to let the event bubble up to
204 // ap.annotationScroller
205 for (MouseWheelListener mwl : _mwl)
209 mwl.mouseWheelMoved(e);
220 public Dimension getPreferredScrollableViewportSize()
222 Dimension ps = getPreferredSize();
223 return new Dimension(ps.width, adjustForAlignFrame(false, ps.height));
227 public int getScrollableBlockIncrement(Rectangle visibleRect,
228 int orientation, int direction)
234 public boolean getScrollableTracksViewportHeight()
240 public boolean getScrollableTracksViewportWidth()
246 public int getScrollableUnitIncrement(Rectangle visibleRect,
247 int orientation, int direction)
256 * java.awt.event.AdjustmentListener#adjustmentValueChanged(java.awt.event
260 public void adjustmentValueChanged(AdjustmentEvent evt)
262 // update annotation label display
263 ap.getAlabels().setScrollOffset(-evt.getValue());
267 * Calculates the height of the annotation displayed in the annotation panel.
268 * Callers should normally call the ap.adjustAnnotationHeight method to ensure
269 * all annotation associated components are updated correctly.
272 public int adjustPanelHeight()
274 int height = av.calcPanelHeight();
275 this.setPreferredSize(new Dimension(1, height));
278 // revalidate only when the alignment panel is fully constructed
292 public void actionPerformed(ActionEvent evt)
294 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
299 Annotation[] anot = aa[activeRow].annotations;
301 if (anot.length < av.getColumnSelection().getMax())
303 Annotation[] temp = new Annotation[av.getColumnSelection().getMax()
305 System.arraycopy(anot, 0, temp, 0, anot.length);
307 aa[activeRow].annotations = anot;
310 String action = evt.getActionCommand();
311 if (action.equals(REMOVE))
313 for (int index : av.getColumnSelection().getSelected())
315 if (av.getAlignment().getHiddenColumns().isVisible(index))
321 else if (action.equals(LABEL))
323 String exMesg = collectAnnotVals(anot, LABEL);
324 String label = JvOptionPane.showInputDialog(this,
325 MessageManager.getString("label.enter_label"), exMesg);
332 if ((label.length() > 0) && !aa[activeRow].hasText)
334 aa[activeRow].hasText = true;
337 for (int index : av.getColumnSelection().getSelected())
339 if (!av.getAlignment().getHiddenColumns().isVisible(index))
344 if (anot[index] == null)
346 anot[index] = new Annotation(label, "", ' ', 0);
350 anot[index].displayCharacter = label;
354 else if (action.equals(COLOUR))
356 Color col = JColorChooser.showDialog(this,
357 MessageManager.getString("label.select_foreground_colour"),
360 for (int index : av.getColumnSelection().getSelected())
362 if (!av.getAlignment().getHiddenColumns().isVisible(index))
367 if (anot[index] == null)
369 anot[index] = new Annotation("", "", ' ', 0);
372 anot[index].colour = col;
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 ap.getScalePanel().mouseEntered(evt);
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;
1005 public void paintComponent(Graphics g)
1007 super.paintComponent(g);
1009 g.setColor(Color.white);
1010 g.fillRect(0, 0, getWidth(), getHeight());
1014 if (fastPaint || (getVisibleRect().width != g.getClipBounds().width)
1015 || (getVisibleRect().height != g.getClipBounds().height))
1017 g.drawImage(image, 0, 0, this);
1022 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
1023 + 1) * av.getCharWidth();
1028 if (image == null || imgWidth != image.getWidth(this)
1029 || image.getHeight(this) != getHeight())
1033 image = new BufferedImage(imgWidth,
1034 ap.getAnnotationPanel().getHeight(),
1035 BufferedImage.TYPE_INT_RGB);
1036 } catch (OutOfMemoryError oom)
1041 } catch (Exception x)
1046 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1050 gg = (Graphics2D) image.getGraphics();
1054 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1055 RenderingHints.VALUE_ANTIALIAS_ON);
1058 gg.setFont(av.getFont());
1059 fm = gg.getFontMetrics();
1060 gg.setColor(Color.white);
1061 gg.fillRect(0, 0, imgWidth, image.getHeight());
1065 drawComponent(gg, av.getRanges().getStartRes(),
1066 av.getRanges().getEndRes() + 1);
1068 g.drawImage(image, 0, 0, this);
1072 * set true to enable redraw timing debug output on stderr
1074 private final boolean debugRedraw = false;
1077 * non-Thread safe repaint
1080 * repaint with horizontal shift in alignment
1082 public void fastPaint(int horizontal)
1084 if ((horizontal == 0) || gg == null
1085 || av.getAlignment().getAlignmentAnnotation() == null
1086 || av.getAlignment().getAlignmentAnnotation().length < 1
1087 || av.isCalcInProgress())
1093 int sr = av.getRanges().getStartRes();
1094 int er = av.getRanges().getEndRes() + 1;
1097 gg.copyArea(0, 0, imgWidth, getHeight(),
1098 -horizontal * av.getCharWidth(), 0);
1100 if (horizontal > 0) // scrollbar pulled right, image to the left
1102 transX = (er - sr - horizontal) * av.getCharWidth();
1103 sr = er - horizontal;
1105 else if (horizontal < 0)
1107 er = sr - horizontal;
1110 gg.translate(transX, 0);
1112 drawComponent(gg, sr, er);
1114 gg.translate(-transX, 0);
1118 // Call repaint on alignment panel so that repaints from other alignment
1119 // panel components can be aggregated. Otherwise performance of the overview
1120 // window and others may be adversely affected.
1121 av.getAlignPanel().repaint();
1124 private volatile boolean lastImageGood = false;
1136 public void drawComponent(Graphics g, int startRes, int endRes)
1138 BufferedImage oldFaded = fadedImage;
1139 if (av.isCalcInProgress())
1143 lastImageGood = false;
1146 // We'll keep a record of the old image,
1147 // and draw a faded image until the calculation
1150 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1151 || fadedImage.getHeight() != image.getHeight()))
1153 // System.err.println("redraw faded image ("+(fadedImage==null ?
1154 // "null image" : "") + " lastGood="+lastImageGood+")");
1155 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1156 BufferedImage.TYPE_INT_RGB);
1158 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1160 fadedG.setColor(Color.white);
1161 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1163 fadedG.setComposite(
1164 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1165 fadedG.drawImage(image, 0, 0, this);
1168 // make sure we don't overwrite the last good faded image until all
1169 // calculations have finished
1170 lastImageGood = false;
1175 if (fadedImage != null)
1177 oldFaded = fadedImage;
1182 g.setColor(Color.white);
1183 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1185 g.setFont(av.getFont());
1188 fm = g.getFontMetrics();
1191 if ((av.getAlignment().getAlignmentAnnotation() == null)
1192 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1194 g.setColor(Color.white);
1195 g.fillRect(0, 0, getWidth(), getHeight());
1196 g.setColor(Color.black);
1197 if (av.validCharWidth)
1199 g.drawString(MessageManager
1200 .getString("label.alignment_has_no_annotations"), 20, 15);
1205 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1207 if (!lastImageGood && fadedImage == null)
1209 fadedImage = oldFaded;
1214 public FontMetrics getFontMetrics()
1220 public Image getFadedImage()
1226 public int getFadedImageWidth()
1231 private int[] bounds = new int[2];
1234 public int[] getVisibleVRange()
1236 if (ap != null && ap.getAlabels() != null)
1238 int sOffset = -ap.getAlabels().getScrollOffset();
1239 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1240 bounds[0] = sOffset;
1241 bounds[1] = visHeight;
1251 * Try to ensure any references held are nulled
1253 public void dispose()
1263 * I created the renderer so I will dispose of it
1265 if (renderer != null)
1272 public void propertyChange(PropertyChangeEvent evt)
1274 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1275 // Both scrolling and resizing change viewport ranges: scrolling changes
1276 // both start and end points, but resize only changes end values.
1277 // Here we only want to fastpaint on a scroll, with resize using a normal
1278 // paint, so scroll events are identified as changes to the horizontal or
1279 // vertical start value.
1280 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1282 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1284 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1286 fastPaint(((int[]) evt.getNewValue())[0]
1287 - ((int[]) evt.getOldValue())[0]);
1289 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
1296 * computes the visible height of the annotation panel
1298 * @param adjustPanelHeight
1299 * - when false, just adjust existing height according to other
1301 * @param annotationHeight
1302 * @return height to use for the ScrollerPreferredVisibleSize
1304 public int adjustForAlignFrame(boolean adjustPanelHeight,
1305 int annotationHeight)
1308 * Estimate available height in the AlignFrame for alignment +
1309 * annotations. Deduct an estimate for title bar, menu bar, scale panel,
1310 * hscroll, status bar, insets.
1312 int stuff = (ap.getViewName() != null ? 30 : 0)
1313 + (Platform.isAMac() ? 120 : 140);
1314 int availableHeight = ap.alignFrame.getHeight() - stuff;
1315 int rowHeight = av.getCharHeight();
1317 if (adjustPanelHeight)
1319 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
1322 * If not enough vertical space, maximize annotation height while keeping
1323 * at least two rows of alignment visible
1325 if (annotationHeight + alignmentHeight > availableHeight)
1327 annotationHeight = Math.min(annotationHeight,
1328 availableHeight - 2 * rowHeight);
1333 // maintain same window layout whilst updating sliders
1334 annotationHeight = Math.min(ap.annotationScroller.getSize().height,
1335 availableHeight - 2 * rowHeight);
1337 return annotationHeight;