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.renderer.AnnotationRenderer;
29 import jalview.renderer.AwtRenderPanelI;
30 import jalview.schemes.ResidueProperties;
31 import jalview.util.Comparison;
32 import jalview.util.MessageManager;
33 import jalview.viewmodel.ViewportListenerI;
34 import jalview.viewmodel.ViewportRanges;
36 import java.awt.AlphaComposite;
37 import java.awt.Color;
38 import java.awt.Dimension;
39 import java.awt.FontMetrics;
40 import java.awt.Graphics;
41 import java.awt.Graphics2D;
42 import java.awt.Image;
43 import java.awt.Rectangle;
44 import java.awt.RenderingHints;
45 import java.awt.event.ActionEvent;
46 import java.awt.event.ActionListener;
47 import java.awt.event.AdjustmentEvent;
48 import java.awt.event.AdjustmentListener;
49 import java.awt.event.MouseEvent;
50 import java.awt.event.MouseListener;
51 import java.awt.event.MouseMotionListener;
52 import java.awt.event.MouseWheelEvent;
53 import java.awt.event.MouseWheelListener;
54 import java.awt.image.BufferedImage;
55 import java.beans.PropertyChangeEvent;
56 import java.util.ArrayList;
57 import java.util.Collections;
58 import java.util.List;
60 import javax.swing.JColorChooser;
61 import javax.swing.JMenuItem;
62 import javax.swing.JPanel;
63 import javax.swing.JPopupMenu;
64 import javax.swing.Scrollable;
65 import javax.swing.ToolTipManager;
68 * AnnotationPanel displays visible portion of annotation rows below unwrapped
74 public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
75 MouseListener, MouseWheelListener, MouseMotionListener,
76 ActionListener, AdjustmentListener, Scrollable, ViewportListenerI
80 Select, Resize, Undefined
83 String HELIX = MessageManager.getString("label.helix");
85 String SHEET = MessageManager.getString("label.sheet");
88 * For RNA secondary structure "stems" aka helices
90 String STEM = MessageManager.getString("label.rna_helix");
92 String LABEL = MessageManager.getString("label.label");
94 String REMOVE = MessageManager.getString("label.remove_annotation");
96 String COLOUR = MessageManager.getString("action.colour");
98 public final Color HELIX_COLOUR = Color.red.darker();
100 public final Color SHEET_COLOUR = Color.green.darker().darker();
102 public final Color STEM_COLOUR = Color.blue.darker();
105 public AlignViewport av;
109 public int activeRow = -1;
111 public BufferedImage image;
113 public volatile BufferedImage fadedImage;
117 public FontMetrics fm;
119 public int imgWidth = 0;
121 boolean fastPaint = false;
123 // Used For mouse Dragging and resizing graphs
124 int graphStretch = -1;
126 int mouseDragLastX = -1;
128 int mouseDragLastY = -1;
130 DragMode dragMode = DragMode.Undefined;
132 boolean mouseDragging = false;
134 // for editing cursor
139 public final AnnotationRenderer renderer;
141 private MouseWheelListener[] _mwl;
144 * Creates a new AnnotationPanel object.
149 public AnnotationPanel(AlignmentPanel ap)
151 ToolTipManager.sharedInstance().registerComponent(this);
152 ToolTipManager.sharedInstance().setInitialDelay(0);
153 ToolTipManager.sharedInstance().setDismissDelay(10000);
156 this.setLayout(null);
157 addMouseListener(this);
158 addMouseMotionListener(this);
159 ap.annotationScroller.getVerticalScrollBar()
160 .addAdjustmentListener(this);
161 // save any wheel listeners on the scroller, so we can propagate scroll
163 _mwl = ap.annotationScroller.getMouseWheelListeners();
164 // and then set our own listener to consume all mousewheel events
165 ap.annotationScroller.addMouseWheelListener(this);
166 renderer = new AnnotationRenderer();
168 av.getRanges().addPropertyChangeListener(this);
171 public AnnotationPanel(AlignViewport av)
174 renderer = new AnnotationRenderer();
178 public void mouseWheelMoved(MouseWheelEvent e)
183 double wheelRotation = e.getPreciseWheelRotation();
184 if (wheelRotation > 0)
186 av.getRanges().scrollRight(true);
188 else if (wheelRotation < 0)
190 av.getRanges().scrollRight(false);
195 // TODO: find the correct way to let the event bubble up to
196 // ap.annotationScroller
197 for (MouseWheelListener mwl : _mwl)
201 mwl.mouseWheelMoved(e);
212 public Dimension getPreferredScrollableViewportSize()
214 return getPreferredSize();
218 public int getScrollableBlockIncrement(Rectangle visibleRect,
219 int orientation, int direction)
225 public boolean getScrollableTracksViewportHeight()
231 public boolean getScrollableTracksViewportWidth()
237 public int getScrollableUnitIncrement(Rectangle visibleRect,
238 int orientation, int direction)
247 * java.awt.event.AdjustmentListener#adjustmentValueChanged(java.awt.event
251 public void adjustmentValueChanged(AdjustmentEvent evt)
253 // update annotation label display
254 ap.getAlabels().setScrollOffset(-evt.getValue());
258 * Calculates the height of the annotation displayed in the annotation panel.
259 * Callers should normally call the ap.adjustAnnotationHeight method to ensure
260 * all annotation associated components are updated correctly.
263 public int adjustPanelHeight()
265 int height = av.calcPanelHeight();
266 this.setPreferredSize(new Dimension(1, height));
269 // revalidate only when the alignment panel is fully constructed
283 public void actionPerformed(ActionEvent evt)
285 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
290 Annotation[] anot = aa[activeRow].annotations;
292 if (anot.length < av.getColumnSelection().getMax())
294 Annotation[] temp = new Annotation[av.getColumnSelection().getMax()
296 System.arraycopy(anot, 0, temp, 0, anot.length);
298 aa[activeRow].annotations = anot;
301 String action = evt.getActionCommand();
302 if (action.equals(REMOVE))
304 for (int index : av.getColumnSelection().getSelected())
306 if (av.getAlignment().getHiddenColumns().isVisible(index))
312 else if (action.equals(LABEL))
314 String exMesg = collectAnnotVals(anot, LABEL);
315 String label = JvOptionPane.showInputDialog(this,
316 MessageManager.getString("label.enter_label"), exMesg);
323 if ((label.length() > 0) && !aa[activeRow].hasText)
325 aa[activeRow].hasText = true;
328 for (int index : av.getColumnSelection().getSelected())
330 if (!av.getAlignment().getHiddenColumns().isVisible(index))
335 if (anot[index] == null)
337 anot[index] = new Annotation(label, "", ' ', 0);
341 anot[index].displayCharacter = label;
345 else if (action.equals(COLOUR))
347 Color col = JColorChooser.showDialog(this,
348 MessageManager.getString("label.select_foreground_colour"),
351 for (int index : av.getColumnSelection().getSelected())
353 if (!av.getAlignment().getHiddenColumns().isVisible(index))
358 if (anot[index] == null)
360 anot[index] = new Annotation("", "", ' ', 0);
363 anot[index].colour = col;
367 // HELIX, SHEET or STEM
370 String symbol = "\u03B1"; // alpha
372 if (action.equals(HELIX))
376 else if (action.equals(SHEET))
379 symbol = "\u03B2"; // beta
382 // Added by LML to color stems
383 else if (action.equals(STEM))
386 int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
387 symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
390 if (!aa[activeRow].hasIcons)
392 aa[activeRow].hasIcons = true;
395 String label = JvOptionPane.showInputDialog(MessageManager
396 .getString("label.enter_label_for_the_structure"), symbol);
403 if ((label.length() > 0) && !aa[activeRow].hasText)
405 aa[activeRow].hasText = true;
406 if (action.equals(STEM))
408 aa[activeRow].showAllColLabels = true;
411 for (int index : av.getColumnSelection().getSelected())
413 if (!av.getAlignment().getHiddenColumns().isVisible(index))
418 if (anot[index] == null)
420 anot[index] = new Annotation(label, "", type, 0);
423 anot[index].secondaryStructure = type != 'S' ? type
424 : label.length() == 0 ? ' ' : label.charAt(0);
425 anot[index].displayCharacter = label;
430 av.getAlignment().validateAnnotation(aa[activeRow]);
431 ap.alignmentChanged();
432 ap.alignFrame.setMenusForViewport();
440 * Returns any existing annotation concatenated as a string. For each
441 * annotation, takes the description, if any, else the secondary structure
442 * character (if type is HELIX, SHEET or STEM), else the display character (if
449 private String collectAnnotVals(Annotation[] anots, String type)
451 // TODO is this method wanted? why? 'last' is not used
453 StringBuilder collatedInput = new StringBuilder(64);
455 ColumnSelection viscols = av.getColumnSelection();
456 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
459 * the selection list (read-only view) is in selection order, not
460 * column order; make a copy so we can sort it
462 List<Integer> selected = new ArrayList<>(viscols.getSelected());
463 Collections.sort(selected);
464 for (int index : selected)
466 // always check for current display state - just in case
467 if (!hidden.isVisible(index))
471 String tlabel = null;
472 if (anots[index] != null)
473 { // LML added stem code
474 if (type.equals(HELIX) || type.equals(SHEET) || type.equals(STEM)
475 || type.equals(LABEL))
477 tlabel = anots[index].description;
478 if (tlabel == null || tlabel.length() < 1)
480 if (type.equals(HELIX) || type.equals(SHEET)
481 || type.equals(STEM))
483 tlabel = "" + anots[index].secondaryStructure;
487 tlabel = "" + anots[index].displayCharacter;
491 if (tlabel != null && !tlabel.equals(last))
493 if (last.length() > 0)
495 collatedInput.append(" ");
497 collatedInput.append(tlabel);
501 return collatedInput.toString();
505 * Action on right mouse pressed on Mac is to show a pop-up menu for the
506 * annotation. Action on left mouse pressed is to find which annotation is
507 * pressed and mark the start of a column selection or graph resize operation.
512 public void mousePressed(MouseEvent evt)
515 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
520 mouseDragLastX = evt.getX();
521 mouseDragLastY = evt.getY();
524 * add visible annotation heights until we reach the y
525 * position, to find which annotation it is in
530 final int y = evt.getY();
531 for (int i = 0; i < aa.length; i++)
535 height += aa[i].height;
544 else if (aa[i].graph > 0)
547 * we have clicked on a resizable graph annotation
556 * isPopupTrigger fires in mousePressed on Mac,
557 * not until mouseRelease on Windows
559 if (evt.isPopupTrigger() && activeRow != -1)
561 showPopupMenu(y, evt.getX());
565 ap.getScalePanel().mousePressed(evt);
569 * Construct and display a context menu at the right-click position
574 void showPopupMenu(final int y, int x)
576 if (av.getColumnSelection() == null
577 || av.getColumnSelection().isEmpty())
582 JPopupMenu pop = new JPopupMenu(
583 MessageManager.getString("label.structure_type"));
586 * Just display the needed structure options
588 if (av.getAlignment().isNucleotide())
590 item = new JMenuItem(STEM);
591 item.addActionListener(this);
596 item = new JMenuItem(HELIX);
597 item.addActionListener(this);
599 item = new JMenuItem(SHEET);
600 item.addActionListener(this);
603 item = new JMenuItem(LABEL);
604 item.addActionListener(this);
606 item = new JMenuItem(COLOUR);
607 item.addActionListener(this);
609 item = new JMenuItem(REMOVE);
610 item.addActionListener(this);
612 pop.show(this, x, y);
616 * Action on mouse up is to clear mouse drag data and call mouseReleased on
617 * ScalePanel, to deal with defining the selection group (if any) defined by
623 public void mouseReleased(MouseEvent evt)
628 mouseDragging = false;
629 dragMode = DragMode.Undefined;
630 ap.getScalePanel().mouseReleased(evt);
633 * isPopupTrigger is set in mouseReleased on Windows
634 * (in mousePressed on Mac)
636 if (evt.isPopupTrigger() && activeRow != -1)
638 showPopupMenu(evt.getY(), evt.getX());
650 public void mouseEntered(MouseEvent evt)
652 ap.getScalePanel().mouseEntered(evt);
662 public void mouseExited(MouseEvent evt)
664 ap.getScalePanel().mouseExited(evt);
674 public void mouseDragged(MouseEvent evt)
677 * todo: if dragMode is Undefined:
678 * - set to Select if dx > dy
679 * - set to Resize if dy > dx
680 * - do nothing if dx == dy
682 final int x = evt.getX();
683 final int y = evt.getY();
684 if (dragMode == DragMode.Undefined)
686 int dx = Math.abs(x - mouseDragLastX);
687 int dy = Math.abs(y - mouseDragLastY);
688 if (graphStretch == -1 || dx > dy)
691 * mostly horizontal drag, or not a graph annotation
693 dragMode = DragMode.Select;
698 * mostly vertical drag
700 dragMode = DragMode.Resize;
704 if (dragMode == DragMode.Undefined)
707 * drag is diagonal - defer deciding whether to
708 * treat as up/down or left/right
715 if (dragMode == DragMode.Resize)
718 * resize graph annotation if mouse was dragged up or down
720 int deltaY = mouseDragLastY - evt.getY();
723 AlignmentAnnotation graphAnnotation = av.getAlignment()
724 .getAlignmentAnnotation()[graphStretch];
725 int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
726 graphAnnotation.graphHeight = newHeight;
728 ap.paintAlignment(false, false);
734 * for mouse drag left or right, delegate to
735 * ScalePanel to adjust the column selection
737 ap.getScalePanel().mouseDragged(evt);
747 * Constructs the tooltip, and constructs and displays a status message, for
748 * the current mouse position
753 public void mouseMoved(MouseEvent evt)
755 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
759 this.setToolTipText(null);
766 for (int i = 0; i < aa.length; i++)
770 height += aa[i].height;
773 if (evt.getY() < height)
782 this.setToolTipText(null);
786 int column = (evt.getX() / av.getCharWidth())
787 + av.getRanges().getStartRes();
789 if (av.hasHiddenColumns())
791 column = av.getAlignment().getHiddenColumns()
792 .visibleToAbsoluteColumn(column);
795 AlignmentAnnotation ann = aa[row];
796 if (row > -1 && ann.annotations != null
797 && column < ann.annotations.length)
799 buildToolTip(ann, column, aa);
800 setStatusMessage(column, ann);
804 this.setToolTipText(null);
805 ap.alignFrame.statusBar.setText(" ");
810 * Builds a tooltip for the annotation at the current mouse position.
816 void buildToolTip(AlignmentAnnotation ann, int column,
817 AlignmentAnnotation[] anns)
819 if (ann.graphGroup > -1)
821 StringBuilder tip = new StringBuilder(32);
822 tip.append("<html>");
823 for (int i = 0; i < anns.length; i++)
825 if (anns[i].graphGroup == ann.graphGroup
826 && anns[i].annotations[column] != null)
828 tip.append(anns[i].label);
829 String description = anns[i].annotations[column].description;
830 if (description != null && description.length() > 0)
832 tip.append(" ").append(description);
837 if (tip.length() != 6)
839 tip.setLength(tip.length() - 4);
840 this.setToolTipText(tip.toString() + "</html>");
843 else if (ann.annotations[column] != null)
845 String description = ann.annotations[column].description;
846 if (description != null && description.length() > 0)
848 this.setToolTipText(JvSwingUtils.wrapTooltip(true, description));
852 this.setToolTipText(null); // no tooltip if null or empty description
857 // clear the tooltip.
858 this.setToolTipText(null);
863 * Constructs and displays the status bar message
868 void setStatusMessage(int column, AlignmentAnnotation ann)
871 * show alignment column and annotation description if any
873 StringBuilder text = new StringBuilder(32);
874 text.append(MessageManager.getString("label.column")).append(" ")
877 if (ann.annotations[column] != null)
879 String description = ann.annotations[column].description;
880 if (description != null && description.trim().length() > 0)
882 text.append(" ").append(description);
887 * if the annotation is sequence-specific, show the sequence number
888 * in the alignment, and (if not a gap) the residue and position
890 SequenceI seqref = ann.sequenceRef;
893 int seqIndex = av.getAlignment().findIndex(seqref);
896 text.append(", ").append(MessageManager.getString("label.sequence"))
897 .append(" ").append(seqIndex + 1);
898 char residue = seqref.getCharAt(column);
899 if (!Comparison.isGap(residue))
903 if (av.getAlignment().isNucleotide())
905 name = ResidueProperties.nucleotideName
906 .get(String.valueOf(residue));
907 text.append(" Nucleotide: ")
908 .append(name != null ? name : residue);
912 name = 'X' == residue ? "X"
913 : ('*' == residue ? "STOP"
914 : ResidueProperties.aa2Triplet
915 .get(String.valueOf(residue)));
916 text.append(" Residue: ").append(name != null ? name : residue);
918 int residuePos = seqref.findPosition(column);
919 text.append(" (").append(residuePos).append(")");
924 ap.alignFrame.statusBar.setText(text.toString());
934 public void mouseClicked(MouseEvent evt)
936 // if (activeRow != -1)
938 // AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
939 // AlignmentAnnotation anot = aa[activeRow];
943 // TODO mouseClicked-content and drawCursor are quite experimental!
944 public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
947 int pady = av.getCharHeight() / 5;
949 graphics.setColor(Color.black);
950 graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
952 if (av.validCharWidth)
954 graphics.setColor(Color.white);
956 char s = seq.getCharAt(res);
958 charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
959 graphics.drawString(String.valueOf(s), charOffset + x1,
960 (y1 + av.getCharHeight()) - pady);
965 private volatile boolean imageFresh = false;
974 public void paintComponent(Graphics g)
976 super.paintComponent(g);
978 g.setColor(Color.white);
979 g.fillRect(0, 0, getWidth(), getHeight());
983 if (fastPaint || (getVisibleRect().width != g.getClipBounds().width)
984 || (getVisibleRect().height != g.getClipBounds().height))
986 g.drawImage(image, 0, 0, this);
991 imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
992 + 1) * av.getCharWidth();
997 if (image == null || imgWidth != image.getWidth(this)
998 || image.getHeight(this) != getHeight())
1002 image = new BufferedImage(imgWidth,
1003 ap.getAnnotationPanel().getHeight(),
1004 BufferedImage.TYPE_INT_RGB);
1005 } catch (OutOfMemoryError oom)
1010 } catch (Exception x)
1015 "Couldn't allocate memory to redraw screen. Please restart Jalview",
1019 gg = (Graphics2D) image.getGraphics();
1023 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1024 RenderingHints.VALUE_ANTIALIAS_ON);
1027 gg.setFont(av.getFont());
1028 fm = gg.getFontMetrics();
1029 gg.setColor(Color.white);
1030 gg.fillRect(0, 0, imgWidth, image.getHeight());
1034 drawComponent(gg, av.getRanges().getStartRes(),
1035 av.getRanges().getEndRes() + 1);
1037 g.drawImage(image, 0, 0, this);
1041 * set true to enable redraw timing debug output on stderr
1043 private final boolean debugRedraw = false;
1046 * non-Thread safe repaint
1049 * repaint with horizontal shift in alignment
1051 public void fastPaint(int horizontal)
1053 if ((horizontal == 0) || gg == null
1054 || av.getAlignment().getAlignmentAnnotation() == null
1055 || av.getAlignment().getAlignmentAnnotation().length < 1
1056 || av.isCalcInProgress())
1062 int sr = av.getRanges().getStartRes();
1063 int er = av.getRanges().getEndRes() + 1;
1066 gg.copyArea(0, 0, imgWidth, getHeight(),
1067 -horizontal * av.getCharWidth(), 0);
1069 if (horizontal > 0) // scrollbar pulled right, image to the left
1071 transX = (er - sr - horizontal) * av.getCharWidth();
1072 sr = er - horizontal;
1074 else if (horizontal < 0)
1076 er = sr - horizontal;
1079 gg.translate(transX, 0);
1081 drawComponent(gg, sr, er);
1083 gg.translate(-transX, 0);
1087 // Call repaint on alignment panel so that repaints from other alignment
1088 // panel components can be aggregated. Otherwise performance of the overview
1089 // window and others may be adversely affected.
1090 av.getAlignPanel().repaint();
1093 private volatile boolean lastImageGood = false;
1105 public void drawComponent(Graphics g, int startRes, int endRes)
1107 BufferedImage oldFaded = fadedImage;
1108 if (av.isCalcInProgress())
1112 lastImageGood = false;
1115 // We'll keep a record of the old image,
1116 // and draw a faded image until the calculation
1119 && (fadedImage == null || fadedImage.getWidth() != imgWidth
1120 || fadedImage.getHeight() != image.getHeight()))
1122 // System.err.println("redraw faded image ("+(fadedImage==null ?
1123 // "null image" : "") + " lastGood="+lastImageGood+")");
1124 fadedImage = new BufferedImage(imgWidth, image.getHeight(),
1125 BufferedImage.TYPE_INT_RGB);
1127 Graphics2D fadedG = (Graphics2D) fadedImage.getGraphics();
1129 fadedG.setColor(Color.white);
1130 fadedG.fillRect(0, 0, imgWidth, image.getHeight());
1132 fadedG.setComposite(
1133 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
1134 fadedG.drawImage(image, 0, 0, this);
1137 // make sure we don't overwrite the last good faded image until all
1138 // calculations have finished
1139 lastImageGood = false;
1144 if (fadedImage != null)
1146 oldFaded = fadedImage;
1151 g.setColor(Color.white);
1152 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
1154 g.setFont(av.getFont());
1157 fm = g.getFontMetrics();
1160 if ((av.getAlignment().getAlignmentAnnotation() == null)
1161 || (av.getAlignment().getAlignmentAnnotation().length < 1))
1163 g.setColor(Color.white);
1164 g.fillRect(0, 0, getWidth(), getHeight());
1165 g.setColor(Color.black);
1166 if (av.validCharWidth)
1168 g.drawString(MessageManager
1169 .getString("label.alignment_has_no_annotations"), 20, 15);
1174 lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
1176 if (!lastImageGood && fadedImage == null)
1178 fadedImage = oldFaded;
1183 public FontMetrics getFontMetrics()
1189 public Image getFadedImage()
1195 public int getFadedImageWidth()
1200 private int[] bounds = new int[2];
1203 public int[] getVisibleVRange()
1205 if (ap != null && ap.getAlabels() != null)
1207 int sOffset = -ap.getAlabels().getScrollOffset();
1208 int visHeight = sOffset + ap.annotationSpaceFillerHolder.getHeight();
1209 bounds[0] = sOffset;
1210 bounds[1] = visHeight;
1220 * Try to ensure any references held are nulled
1222 public void dispose()
1232 * I created the renderer so I will dispose of it
1234 if (renderer != null)
1241 public void propertyChange(PropertyChangeEvent evt)
1243 // Respond to viewport range changes (e.g. alignment panel was scrolled)
1244 // Both scrolling and resizing change viewport ranges: scrolling changes
1245 // both start and end points, but resize only changes end values.
1246 // Here we only want to fastpaint on a scroll, with resize using a normal
1247 // paint, so scroll events are identified as changes to the horizontal or
1248 // vertical start value.
1249 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
1251 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
1253 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
1255 fastPaint(((int[]) evt.getNewValue())[0]
1256 - ((int[]) evt.getOldValue())[0]);
1258 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))