X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FAnnotationPanel.java;h=5b5a884dbab045138996bee684cd374dde88638c;hb=9d30955a72847c8906b55fd458380762d84ea8ee;hp=e5bed74ed72292dd7276cfdf2bb7a207591681d0;hpb=ad15cff29620f960119f80176f1fd443da9f6763;p=jalview.git diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java index e5bed74..5b5a884 100755 --- a/src/jalview/gui/AnnotationPanel.java +++ b/src/jalview/gui/AnnotationPanel.java @@ -21,12 +21,19 @@ package jalview.gui; import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; import jalview.datamodel.ColumnSelection; +import jalview.datamodel.HiddenColumns; import jalview.datamodel.SequenceI; import jalview.renderer.AnnotationRenderer; import jalview.renderer.AwtRenderPanelI; +import jalview.schemes.ResidueProperties; +import jalview.util.Comparison; import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.viewmodel.ViewportListenerI; +import jalview.viewmodel.ViewportRanges; import java.awt.AlphaComposite; import java.awt.Color; @@ -47,14 +54,16 @@ import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import javax.swing.JColorChooser; import javax.swing.JMenuItem; -import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.Scrollable; -import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; /** @@ -66,8 +75,13 @@ import javax.swing.ToolTipManager; */ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, MouseListener, MouseWheelListener, MouseMotionListener, - ActionListener, AdjustmentListener, Scrollable + ActionListener, AdjustmentListener, Scrollable, ViewportListenerI { + enum DragMode + { + Select, Resize, Undefined + }; + String HELIX = MessageManager.getString("label.helix"); String SHEET = MessageManager.getString("label.sheet"); @@ -111,16 +125,14 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, // Used For mouse Dragging and resizing graphs int graphStretch = -1; - int graphStretchY = -1; + int mouseDragLastX = -1; - int min; // used by mouseDragged to see if user + int mouseDragLastY = -1; - int max; // used by mouseDragged to see if user + DragMode dragMode = DragMode.Undefined; boolean mouseDragging = false; - boolean MAC = false; - // for editing cursor int cursorX = 0; @@ -138,9 +150,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, */ public AnnotationPanel(AlignmentPanel ap) { - - MAC = new jalview.util.Platform().isAMac(); - ToolTipManager.sharedInstance().registerComponent(this); ToolTipManager.sharedInstance().setInitialDelay(0); ToolTipManager.sharedInstance().setDismissDelay(10000); @@ -157,6 +166,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, // and then set our own listener to consume all mousewheel events ap.annotationScroller.addMouseWheelListener(this); renderer = new AnnotationRenderer(); + + av.getRanges().addPropertyChangeListener(this); } public AnnotationPanel(AlignViewport av) @@ -165,20 +176,27 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, renderer = new AnnotationRenderer(); } + /** + * Responds to a mouse wheel movement by scrolling the annotations up or down. + * Annotation labels are scrolled via method adjustmentValueChanged when the + * vertical scrollbar is adjusted. + *
+ * If shift is pressed, then scrolling is left or right instead, and is + * delegated to AlignmentPanel, so that both sequences and annotations are + * scrolled together. + *
+ * This object is a MouseWheelListener to AnnotationLabels, so mouse wheel + * events over the labels are delegated to this method. + *
+ * Note that this method may also be fired by scrolling with a gesture on a
+ * trackpad.
+ */
@Override
public void mouseWheelMoved(MouseWheelEvent e)
{
if (e.isShiftDown())
{
- e.consume();
- if (e.getWheelRotation() > 0)
- {
- ap.scrollRight(true);
- }
- else
- {
- ap.scrollRight(false);
- }
+ ap.getSeqPanel().mouseWheelMoved(e);
}
else
{
@@ -201,7 +219,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
@Override
public Dimension getPreferredScrollableViewportSize()
{
- return getPreferredSize();
+ Dimension ps = getPreferredSize();
+ return new Dimension(ps.width, adjustForAlignFrame(false, ps.height));
}
@Override
@@ -281,23 +300,28 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
if (anot.length < av.getColumnSelection().getMax())
{
- Annotation[] temp = new Annotation[av.getColumnSelection().getMax() + 2];
+ Annotation[] temp = new Annotation[av.getColumnSelection().getMax()
+ + 2];
System.arraycopy(anot, 0, temp, 0, anot.length);
anot = temp;
aa[activeRow].annotations = anot;
}
- if (evt.getActionCommand().equals(REMOVE))
+ String action = evt.getActionCommand();
+ if (action.equals(REMOVE))
{
- for (int i = 0; i < av.getColumnSelection().size(); i++)
+ for (int index : av.getColumnSelection().getSelected())
{
- anot[av.getColumnSelection().columnAt(i)] = null;
+ if (av.getAlignment().getHiddenColumns().isVisible(index))
+ {
+ anot[index] = null;
+ }
}
}
- else if (evt.getActionCommand().equals(LABEL))
+ else if (action.equals(LABEL))
{
- String exMesg = collectAnnotVals(anot, av.getColumnSelection(), LABEL);
- String label = JOptionPane.showInputDialog(this,
+ String exMesg = collectAnnotVals(anot, LABEL);
+ String label = JvOptionPane.showInputDialog(this,
MessageManager.getString("label.enter_label"), exMesg);
if (label == null)
@@ -310,21 +334,16 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
aa[activeRow].hasText = true;
}
- for (int i = 0; i < av.getColumnSelection().size(); i++)
+ for (int index : av.getColumnSelection().getSelected())
{
- int index = av.getColumnSelection().columnAt(i);
-
- if (!av.getColumnSelection().isVisible(index))
+ if (!av.getAlignment().getHiddenColumns().isVisible(index))
{
continue;
}
if (anot[index] == null)
{
- anot[index] = new Annotation(label, "", ' ', 0); // TODO: verify that
- // null exceptions
- // aren't raised
- // elsewhere.
+ anot[index] = new Annotation(label, "", ' ', 0);
}
else
{
@@ -332,16 +351,15 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
}
}
}
- else if (evt.getActionCommand().equals(COLOUR))
+ else if (action.equals(COLOUR))
{
Color col = JColorChooser.showDialog(this,
- MessageManager.getString("label.select_foreground_colour"), Color.black);
+ MessageManager.getString("label.select_foreground_colour"),
+ Color.black);
- for (int i = 0; i < av.getColumnSelection().size(); i++)
+ for (int index : av.getColumnSelection().getSelected())
{
- int index = av.getColumnSelection().columnAt(i);
-
- if (!av.getColumnSelection().isVisible(index))
+ if (!av.getAlignment().getHiddenColumns().isVisible(index))
{
continue;
}
@@ -355,26 +373,27 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
}
}
else
- // HELIX OR SHEET
+ // HELIX, SHEET or STEM
{
char type = 0;
- String symbol = "\u03B1";
+ String symbol = "\u03B1"; // alpha
- if (evt.getActionCommand().equals(HELIX))
+ if (action.equals(HELIX))
{
type = 'H';
}
- else if (evt.getActionCommand().equals(SHEET))
+ else if (action.equals(SHEET))
{
type = 'E';
- symbol = "\u03B2";
+ symbol = "\u03B2"; // beta
}
// Added by LML to color stems
- else if (evt.getActionCommand().equals(STEM))
+ else if (action.equals(STEM))
{
type = 'S';
- symbol = "\u03C3";
+ int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
+ symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
}
if (!aa[activeRow].hasIcons)
@@ -382,7 +401,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
aa[activeRow].hasIcons = true;
}
- String label = JOptionPane.showInputDialog(MessageManager
+ String label = JvOptionPane.showInputDialog(MessageManager
.getString("label.enter_label_for_the_structure"), symbol);
if (label == null)
@@ -393,16 +412,14 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
if ((label.length() > 0) && !aa[activeRow].hasText)
{
aa[activeRow].hasText = true;
- if (evt.getActionCommand().equals(STEM))
+ if (action.equals(STEM))
{
aa[activeRow].showAllColLabels = true;
}
}
- for (int i = 0; i < av.getColumnSelection().size(); i++)
+ for (int index : av.getColumnSelection().getSelected())
{
- int index = av.getColumnSelection().columnAt(i);
-
- if (!av.getColumnSelection().isVisible(index))
+ if (!av.getAlignment().getHiddenColumns().isVisible(index))
{
continue;
}
@@ -412,54 +429,71 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
anot[index] = new Annotation(label, "", type, 0);
}
-
- anot[index].secondaryStructure = type != 'S' ? type : label
- .length() == 0 ? ' ' : label.charAt(0);
+ anot[index].secondaryStructure = type != 'S' ? type
+ : label.length() == 0 ? ' ' : label.charAt(0);
anot[index].displayCharacter = label;
}
}
+
av.getAlignment().validateAnnotation(aa[activeRow]);
ap.alignmentChanged();
-
+ ap.alignFrame.setMenusForViewport();
adjustPanelHeight();
repaint();
return;
}
- private String collectAnnotVals(Annotation[] anot,
- ColumnSelection columnSelection, String label2)
+ /**
+ * Returns any existing annotation concatenated as a string. For each
+ * annotation, takes the description, if any, else the secondary structure
+ * character (if type is HELIX, SHEET or STEM), else the display character (if
+ * type is LABEL).
+ *
+ * @param anots
+ * @param type
+ * @return
+ */
+ private String collectAnnotVals(Annotation[] anots, String type)
{
- String collatedInput = "";
+ // TODO is this method wanted? why? 'last' is not used
+
+ StringBuilder collatedInput = new StringBuilder(64);
String last = "";
ColumnSelection viscols = av.getColumnSelection();
- // TODO: refactor and save av.getColumnSelection for efficiency
- for (int i = 0; i < columnSelection.size(); i++)
+ HiddenColumns hidden = av.getAlignment().getHiddenColumns();
+
+ /*
+ * the selection list (read-only view) is in selection order, not
+ * column order; make a copy so we can sort it
+ */
+ List
");
+ tip.append(" ").append(description);
}
- }
- if (tip.length() != 6)
- {
- tip.setLength(tip.length() - 4);
- this.setToolTipText(tip.toString() + "");
+ tip.append("
");
}
}
- else if (aa[row].annotations[res] != null
- && aa[row].annotations[res].description != null
- && aa[row].annotations[res].description.length() > 0)
+ if (tip.length() != 6)
{
- this.setToolTipText(JvSwingUtils
- .wrapTooltip(true, aa[row].annotations[res].description));
+ tip.setLength(tip.length() - 4);
+ tooltip = tip.toString() + "";
+ }
+ }
+ else if (column < ann.annotations.length
+ && ann.annotations[column] != null)
+ {
+ String description = ann.annotations[column].description;
+ if (description != null && description.length() > 0)
+ {
+ tooltip = JvSwingUtils.wrapTooltip(true, description);
}
else
{
- // clear the tooltip.
- this.setToolTipText(null);
+ tooltip = null; // no tooltip if null or empty description
}
+ }
+ else
+ {
+ // clear the tooltip.
+ tooltip = null;
+ }
+ return tooltip;
+ }
- if (aa[row].annotations[res] != null)
+ /**
+ * Constructs and returns the status bar message
+ *
+ * @param al
+ * @param column
+ * @param ann
+ */
+ static String getStatusMessage(AlignmentI al, int column,
+ AlignmentAnnotation ann)
+ {
+ /*
+ * show alignment column and annotation description if any
+ */
+ StringBuilder text = new StringBuilder(32);
+ text.append(MessageManager.getString("label.column")).append(" ")
+ .append(column + 1);
+
+ if (column < ann.annotations.length && ann.annotations[column] != null)
+ {
+ String description = ann.annotations[column].description;
+ if (description != null && description.trim().length() > 0)
{
- StringBuffer text = new StringBuffer("Sequence position "
- + (res + 1));
+ text.append(" ").append(description);
+ }
+ }
- if (aa[row].annotations[res].description != null)
+ /*
+ * if the annotation is sequence-specific, show the sequence number
+ * in the alignment, and (if not a gap) the residue and position
+ */
+ SequenceI seqref = ann.sequenceRef;
+ if (seqref != null)
+ {
+ int seqIndex = al.findIndex(seqref);
+ if (seqIndex != -1)
+ {
+ text.append(", ").append(MessageManager.getString("label.sequence"))
+ .append(" ").append(seqIndex + 1);
+ char residue = seqref.getCharAt(column);
+ if (!Comparison.isGap(residue))
{
- text.append(" " + aa[row].annotations[res].description);
+ text.append(" ");
+ String name;
+ if (al.isNucleotide())
+ {
+ name = ResidueProperties.nucleotideName
+ .get(String.valueOf(residue));
+ text.append(" Nucleotide: ")
+ .append(name != null ? name : residue);
+ }
+ else
+ {
+ name = 'X' == residue ? "X"
+ : ('*' == residue ? "STOP"
+ : ResidueProperties.aa2Triplet
+ .get(String.valueOf(residue)));
+ text.append(" Residue: ").append(name != null ? name : residue);
+ }
+ int residuePos = seqref.findPosition(column);
+ text.append(" (").append(residuePos).append(")");
}
-
- ap.alignFrame.statusBar.setText(text.toString());
}
}
- else
- {
- this.setToolTipText(null);
- }
+
+ return text.toString();
}
/**
@@ -757,10 +975,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1,
int y1)
{
- int pady = av.charHeight / 5;
+ int pady = av.getCharHeight() / 5;
int charOffset = 0;
graphics.setColor(Color.black);
- graphics.fillRect(x1, y1, av.charWidth, av.charHeight);
+ graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
if (av.validCharWidth)
{
@@ -768,9 +986,9 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
char s = seq.getCharAt(res);
- charOffset = (av.charWidth - fm.charWidth(s)) / 2;
+ charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
graphics.drawString(String.valueOf(s), charOffset + x1,
- (y1 + av.charHeight) - pady);
+ (y1 + av.getCharHeight()) - pady);
}
}
@@ -786,6 +1004,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
@Override
public void paintComponent(Graphics g)
{
+ super.paintComponent(g);
+
g.setColor(Color.white);
g.fillRect(0, 0, getWidth(), getHeight());
@@ -799,7 +1019,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
return;
}
}
- imgWidth = (av.endRes - av.startRes + 1) * av.charWidth;
+ imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
+ + 1) * av.getCharWidth();
if (imgWidth < 1)
{
return;
@@ -809,7 +1030,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
{
try
{
- image = new BufferedImage(imgWidth, ap.getAnnotationPanel().getHeight(),
+ image = new BufferedImage(imgWidth,
+ ap.getAnnotationPanel().getHeight(),
BufferedImage.TYPE_INT_RGB);
} catch (OutOfMemoryError oom)
{
@@ -839,8 +1061,9 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
gg.fillRect(0, 0, imgWidth, image.getHeight());
imageFresh = true;
}
-
- drawComponent(gg, av.startRes, av.endRes + 1);
+
+ drawComponent(gg, av.getRanges().getStartRes(),
+ av.getRanges().getEndRes() + 1);
imageFresh = false;
g.drawImage(image, 0, 0, this);
}
@@ -866,16 +1089,17 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
repaint();
return;
}
- long stime = System.currentTimeMillis();
- gg.copyArea(0, 0, imgWidth, getHeight(), -horizontal * av.charWidth, 0);
- long mtime = System.currentTimeMillis();
- int sr = av.startRes;
- int er = av.endRes + 1;
+
+ int sr = av.getRanges().getStartRes();
+ int er = av.getRanges().getEndRes() + 1;
int transX = 0;
+ gg.copyArea(0, 0, imgWidth, getHeight(),
+ -horizontal * av.getCharWidth(), 0);
+
if (horizontal > 0) // scrollbar pulled right, image to the left
{
- transX = (er - sr - horizontal) * av.charWidth;
+ transX = (er - sr - horizontal) * av.getCharWidth();
sr = er - horizontal;
}
else if (horizontal < 0)
@@ -888,17 +1112,13 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
drawComponent(gg, sr, er);
gg.translate(-transX, 0);
- long dtime = System.currentTimeMillis();
+
fastPaint = true;
- repaint();
- long rtime = System.currentTimeMillis();
- if (debugRedraw)
- {
- System.err.println("Scroll:\t" + horizontal + "\tCopyArea:\t"
- + (mtime - stime) + "\tDraw component:\t" + (dtime - mtime)
- + "\tRepaint call:\t" + (rtime - dtime));
- }
+ // Call repaint on alignment panel so that repaints from other alignment
+ // panel components can be aggregated. Otherwise performance of the overview
+ // window and others may be adversely affected.
+ av.getAlignPanel().repaint();
}
private volatile boolean lastImageGood = false;
@@ -927,8 +1147,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
// and draw a faded image until the calculation
// has completed
if (lastImageGood
- && (fadedImage == null || fadedImage.getWidth() != imgWidth || fadedImage
- .getHeight() != image.getHeight()))
+ && (fadedImage == null || fadedImage.getWidth() != imgWidth
+ || fadedImage.getHeight() != image.getHeight()))
{
// System.err.println("redraw faded image ("+(fadedImage==null ?
// "null image" : "") + " lastGood="+lastImageGood+")");
@@ -940,8 +1160,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
fadedG.setColor(Color.white);
fadedG.fillRect(0, 0, imgWidth, image.getHeight());
- fadedG.setComposite(AlphaComposite.getInstance(
- AlphaComposite.SRC_OVER, .3f));
+ fadedG.setComposite(
+ AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .3f));
fadedG.drawImage(image, 0, 0, this);
}
@@ -960,7 +1180,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
}
g.setColor(Color.white);
- g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getHeight());
+ g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(), getHeight());
g.setFont(av.getFont());
if (fm == null)
@@ -982,8 +1202,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
return;
}
- lastImageGood = renderer.drawComponent(this, av, g, activeRow,
- startRes, endRes);
+ lastImageGood = renderer.drawComponent(this, av, g, activeRow, startRes,
+ endRes);
if (!lastImageGood && fadedImage == null)
{
fadedImage = oldFaded;
@@ -1026,4 +1246,94 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
return null;
}
}
+
+ /**
+ * Try to ensure any references held are nulled
+ */
+ public void dispose()
+ {
+ av = null;
+ ap = null;
+ image = null;
+ fadedImage = null;
+ gg = null;
+ _mwl = null;
+
+ /*
+ * I created the renderer so I will dispose of it
+ */
+ if (renderer != null)
+ {
+ renderer.dispose();
+ }
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ // Respond to viewport range changes (e.g. alignment panel was scrolled)
+ // Both scrolling and resizing change viewport ranges: scrolling changes
+ // both start and end points, but resize only changes end values.
+ // Here we only want to fastpaint on a scroll, with resize using a normal
+ // paint, so scroll events are identified as changes to the horizontal or
+ // vertical start value.
+ if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
+ {
+ fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
+ }
+ else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
+ {
+ fastPaint(((int[]) evt.getNewValue())[0]
+ - ((int[]) evt.getOldValue())[0]);
+ }
+ else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
+ {
+ repaint();
+ }
+ }
+
+ /**
+ * computes the visible height of the annotation panel
+ *
+ * @param adjustPanelHeight
+ * - when false, just adjust existing height according to other
+ * windows
+ * @param annotationHeight
+ * @return height to use for the ScrollerPreferredVisibleSize
+ */
+ public int adjustForAlignFrame(boolean adjustPanelHeight,
+ int annotationHeight)
+ {
+ /*
+ * Estimate available height in the AlignFrame for alignment +
+ * annotations. Deduct an estimate for title bar, menu bar, scale panel,
+ * hscroll, status bar, insets.
+ */
+ int stuff = (ap.getViewName() != null ? 30 : 0)
+ + (Platform.isAMac() ? 120 : 140);
+ int availableHeight = ap.alignFrame.getHeight() - stuff;
+ int rowHeight = av.getCharHeight();
+
+ if (adjustPanelHeight)
+ {
+ int alignmentHeight = rowHeight * av.getAlignment().getHeight();
+
+ /*
+ * If not enough vertical space, maximize annotation height while keeping
+ * at least two rows of alignment visible
+ */
+ if (annotationHeight + alignmentHeight > availableHeight)
+ {
+ annotationHeight = Math.min(annotationHeight,
+ availableHeight - 2 * rowHeight);
+ }
+ }
+ else
+ {
+ // maintain same window layout whilst updating sliders
+ annotationHeight = Math.min(ap.annotationScroller.getSize().height,
+ availableHeight - 2 * rowHeight);
+ }
+ return annotationHeight;
+ }
}