import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.ListIterator;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/** DOCUMENT ME!! */
public AlignmentPanel ap;
+ /*
+ * last column position for mouseMoved event
+ */
+ private int lastMouseColumn;
+
+ /*
+ * last sequence offset for mouseMoved event
+ */
+ private int lastMouseSeq;
+
protected int lastres;
protected int startseq;
ssm.addStructureViewerListener(this);
ssm.addSelectionListener(this);
}
+
+ lastMouseColumn = -1;
+ lastMouseSeq = -1;
}
int startWrapBlock = -1;
+ hgap + seqCanvas.getAnnotationHeight();
int y = evt.getY();
- y -= hgap;
- x = Math.max(0, x - seqCanvas.LABEL_WEST);
+ y = Math.max(0, y - hgap);
+ x = Math.max(0, x - seqCanvas.getLabelWidthWest());
int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
if (cwidth < 1)
av.setSelectionGroup(sg);
}
- ap.paintAlignment(false);
+ ap.paintAlignment(false, false);
av.sendSelection();
}
}
lastSearchResults = results;
+ boolean wasScrolled = false;
+
if (av.isFollowHighlight())
{
// don't allow highlight of protein/cDNA to also scroll a complementary
// over residue to change abruptly, causing highlighted residue in panel 2
// to change, causing a scroll in panel 1 etc)
ap.setToScrollComplementPanel(false);
- if (ap.scrollToPosition(results, false))
+ wasScrolled = ap.scrollToPosition(results, false);
+ if (wasScrolled)
{
seqCanvas.revalidate();
}
ap.setToScrollComplementPanel(true);
}
- setStatusMessage(results);
- seqCanvas.highlightSearchResults(results);
+
+ boolean noFastPaint = wasScrolled && av.getWrapAlignment();
+ if (seqCanvas.highlightSearchResults(results, noFastPaint))
+ {
+ setStatusMessage(results);
+ }
}
@Override
}
/**
- * DOCUMENT ME!
+ * Action on mouse movement is to update the status bar to show the current
+ * sequence position, and (if features are shown) to show any features at the
+ * position in a tooltip. Does nothing if the mouse move does not change
+ * residue position.
*
* @param evt
- * DOCUMENT ME!
*/
@Override
public void mouseMoved(MouseEvent evt)
}
final int column = findColumn(evt);
- int seq = findSeq(evt);
+ final int seq = findSeq(evt);
+
if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
{
+ lastMouseSeq = -1;
return;
}
+ if (column == lastMouseColumn && seq == lastMouseSeq)
+ {
+ /*
+ * just a pixel move without change of residue
+ */
+ return;
+ }
+ lastMouseColumn = column;
+ lastMouseSeq = seq;
SequenceI sequence = av.getAlignment().getSequenceAt(seq);
if (av.isShowSequenceFeatures())
{
List<SequenceFeature> features = ap.getFeatureRenderer()
- .findFeaturesAtRes(sequence.getDatasetSequence(), pos);
- if (isGapped)
- {
- removeAdjacentFeatures(features, column + 1, sequence);
- }
+ .findFeaturesAtColumn(sequence, column + 1);
seqARep.appendFeatures(tooltipText, pos, features,
this.ap.getSeqPanel().seqCanvas.fr.getMinMax());
}
}
else
{
- if (lastTooltip == null
- || !lastTooltip.equals(tooltipText.toString()))
+ String textString = tooltipText.toString();
+ if (lastTooltip == null || !lastTooltip.equals(textString))
{
- String formatedTooltipText = JvSwingUtils.wrapTooltip(true,
- tooltipText.toString());
- // String formatedTooltipText = tooltipText.toString();
- setToolTipText(formatedTooltipText);
- lastTooltip = tooltipText.toString();
- }
-
- }
-
- }
-
- /**
- * Removes from the list of features any that start after, or end before, the
- * given column position. This allows us to retain only those features
- * adjacent to a gapped position that straddle the position. Contact features
- * that 'straddle' the position are also removed, since they are not 'at' the
- * position.
- *
- * @param features
- * @param column
- * alignment column (1..)
- * @param sequence
- */
- protected void removeAdjacentFeatures(List<SequenceFeature> features,
- final int column, SequenceI sequence)
- {
- // TODO should this be an AlignViewController method (and reused by applet)?
- ListIterator<SequenceFeature> it = features.listIterator();
- while (it.hasNext())
- {
- SequenceFeature sf = it.next();
- if (sf.isContactFeature()
- || sequence.findIndex(sf.getBegin()) > column
- || sequence.findIndex(sf.getEnd()) < column)
- {
- it.remove();
+ String formattedTooltipText = JvSwingUtils.wrapTooltip(true,
+ textString);
+ setToolTipText(formattedTooltipText);
+ lastTooltip = textString;
}
}
}
/**
* set when the current UI interaction has resulted in a change that requires
- * overview shading to be recalculated. this could be changed to something
- * more expressive that indicates what actually has changed, so selective
- * redraws can be applied
+ * shading in overviews and structures to be recalculated. this could be
+ * changed to a something more expressive that indicates what actually has
+ * changed, so selective redraws can be applied (ie. only structures, only
+ * overview, etc)
*/
- private boolean needOverviewUpdate = false; // TODO: refactor to avcontroller
+ private boolean updateOverviewAndStructs = false; // TODO: refactor to avcontroller
/**
* set if av.getSelectionGroup() refers to a group that is defined on the
* aligned sequence object
* @param column
* alignment column
- * @param seq
+ * @param seqIndex
* index of sequence in alignment
* @return sequence position of residue at column, or adjacent residue if at a
* gap
*/
- int setStatusMessage(SequenceI sequence, final int column, int seq)
+ int setStatusMessage(SequenceI sequence, final int column, int seqIndex)
+ {
+ char sequenceChar = sequence.getCharAt(column);
+ int pos = sequence.findPosition(column);
+ setStatusMessage(sequence, seqIndex, sequenceChar, pos);
+
+ return pos;
+ }
+
+ /**
+ * Builds the status message for the current cursor location and writes it to
+ * the status bar, for example
+ *
+ * <pre>
+ * Sequence 3 ID: FER1_SOLLC
+ * Sequence 5 ID: FER1_PEA Residue: THR (4)
+ * Sequence 5 ID: FER1_PEA Residue: B (3)
+ * Sequence 6 ID: O.niloticus.3 Nucleotide: Uracil (2)
+ * </pre>
+ *
+ * @param sequence
+ * @param seqIndex
+ * sequence position in the alignment (1..)
+ * @param sequenceChar
+ * the character under the cursor
+ * @param residuePos
+ * the sequence residue position (if not over a gap)
+ */
+ protected void setStatusMessage(SequenceI sequence, int seqIndex,
+ char sequenceChar, int residuePos)
{
StringBuilder text = new StringBuilder(32);
/*
* Sequence number (if known), and sequence name.
*/
- String seqno = seq == -1 ? "" : " " + (seq + 1);
+ String seqno = seqIndex == -1 ? "" : " " + (seqIndex + 1);
text.append("Sequence").append(seqno).append(" ID: ")
.append(sequence.getName());
/*
* Try to translate the display character to residue name (null for gap).
*/
- final String displayChar = String.valueOf(sequence.getCharAt(column));
- boolean isGapped = Comparison.isGap(sequence.getCharAt(column));
- int pos = sequence.findPosition(column);
+ boolean isGapped = Comparison.isGap(sequenceChar);
if (!isGapped)
{
boolean nucleotide = av.getAlignment().isNucleotide();
+ String displayChar = String.valueOf(sequenceChar);
if (nucleotide)
{
residue = ResidueProperties.nucleotideName.get(displayChar);
text.append(" ").append(nucleotide ? "Nucleotide" : "Residue")
.append(": ").append(residue == null ? displayChar : residue);
- text.append(" (").append(Integer.toString(pos)).append(")");
+ text.append(" (").append(Integer.toString(residuePos)).append(")");
}
ap.alignFrame.statusBar.setText(text.toString());
-
- return pos;
}
/**
if (seq == ds)
{
- /*
- * Convert position in sequence (base 1) to sequence character array
- * index (base 0)
- */
- int start = m.getStart() - m.getSequence().getStart();
- setStatusMessage(seq, start, sequenceIndex);
+ int start = m.getStart();
+ setStatusMessage(seq, sequenceIndex, seq.getCharAt(start - 1),
+ start);
return;
}
}
}
if (newWidth > 0)
{
- ap.paintAlignment(false);
+ ap.paintAlignment(false, false);
if (copyChanges)
{
/*
}
mouseDragging = true;
- if (scrollThread != null)
+ if ((scrollThread != null) && (scrollThread.isRunning()))
{
scrollThread.setEvent(evt);
}
// Find the next gap before the end
// of the visible region boundary
boolean blank = false;
- for (fixedRight = fixedRight; fixedRight > lastres; fixedRight--)
+ for (; fixedRight > lastres; fixedRight--)
{
blank = true;
oldSeq = 0;
}
- if (scrollThread != null)
+ if ((scrollThread != null) && (scrollThread.isRunning()))
{
- scrollThread.running = false;
+ scrollThread.stopScrolling();
scrollThread = null;
}
}
return;
}
- if (mouseDragging)
+ if (mouseDragging && scrollThread == null)
{
scrollThread = new ScrollThread();
}
}
int column = findColumn(evt);
- boolean isGapped = Comparison.isGap(sequence.getCharAt(column));
/*
* find features at the position (if not gapped), or straddling
* the position (if at a gap)
*/
List<SequenceFeature> features = seqCanvas.getFeatureRenderer()
- .findFeaturesAtRes(sequence.getDatasetSequence(),
- sequence.findPosition(column));
- if (isGapped)
- {
- removeAdjacentFeatures(features, column, sequence);
- }
+ .findFeaturesAtColumn(sequence, column + 1);
if (!features.isEmpty())
{
* highlight the first feature at the position on the alignment
*/
SearchResultsI highlight = new SearchResults();
- highlight.addResult(sequence, features.get(0).getBegin(),
- features.get(0).getEnd());
- seqCanvas.highlightSearchResults(highlight);
+ highlight.addResult(sequence, features.get(0).getBegin(), features
+ .get(0).getEnd());
+ seqCanvas.highlightSearchResults(highlight, false);
/*
* open the Amend Features dialog; clear highlighting afterwards,
List<SequenceI> seqs = Collections.singletonList(sequence);
seqCanvas.getFeatureRenderer().amendFeatures(seqs, features, false,
ap);
- seqCanvas.highlightSearchResults(null);
+ av.setSearchResults(null); // clear highlighting
+ seqCanvas.repaint(); // draw new/amended features
}
}
}
av.getRanges().scrollRight(true);
}
- else if (!av.getWrapAlignment())
+ else
{
av.getRanges().scrollUp(false);
}
{
av.getRanges().scrollRight(false);
}
- else if (!av.getWrapAlignment())
+ else
{
av.getRanges().scrollUp(true);
}
}
- // TODO Update tooltip for new position.
+
+ /*
+ * update status bar and tooltip for new position
+ * (need to synthesize a mouse movement to refresh tooltip)
+ */
+ mouseMoved(e);
+ ToolTipManager.sharedInstance().mouseMoved(e);
}
/**
final int res = findColumn(evt);
final int seq = findSeq(evt);
oldSeq = seq;
- needOverviewUpdate = false;
+ updateOverviewAndStructs = false;
startWrapBlock = wrappedBlock;
if (stretchGroup == null)
{
- // Only if left mouse button do we want to change group sizes
+ createStretchGroup(res, sequence);
+ }
- // define a new group here
- SequenceGroup sg = new SequenceGroup();
- sg.setStartRes(res);
- sg.setEndRes(res);
- sg.addSequence(sequence, false);
- av.setSelectionGroup(sg);
- stretchGroup = sg;
+ if (stretchGroup != null)
+ {
+ stretchGroup.addPropertyChangeListener(seqCanvas);
+ }
- if (av.getConservationSelected())
- {
- SliderPanel.setConservationSlider(ap, av.getResidueShading(),
- ap.getViewName());
- }
+ seqCanvas.repaint();
+ }
- if (av.getAbovePIDThreshold())
- {
- SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
- ap.getViewName());
- }
- // TODO: stretchGroup will always be not null. Is this a merge error ?
- if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
- {
- // Edit end res position of selected group
- changeEndRes = true;
- }
- else if ((stretchGroup != null)
- && (stretchGroup.getStartRes() == res))
- {
- // Edit end res position of selected group
- changeStartRes = true;
- }
- stretchGroup.getWidth();
+ private void createStretchGroup(int res, SequenceI sequence)
+ {
+ // Only if left mouse button do we want to change group sizes
+ // define a new group here
+ SequenceGroup sg = new SequenceGroup();
+ sg.setStartRes(res);
+ sg.setEndRes(res);
+ sg.addSequence(sequence, false);
+ av.setSelectionGroup(sg);
+ stretchGroup = sg;
+
+ if (av.getConservationSelected())
+ {
+ SliderPanel.setConservationSlider(ap, av.getResidueShading(),
+ ap.getViewName());
}
- seqCanvas.repaint();
+ if (av.getAbovePIDThreshold())
+ {
+ SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
+ ap.getViewName());
+ }
+ // TODO: stretchGroup will always be not null. Is this a merge error ?
+ // or is there a threading issue here?
+ if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
+ {
+ // Edit end res position of selected group
+ changeEndRes = true;
+ }
+ else if ((stretchGroup != null) && (stretchGroup.getStartRes() == res))
+ {
+ // Edit end res position of selected group
+ changeStartRes = true;
+ }
+ stretchGroup.getWidth();
+
}
/**
*/
void showPopupMenu(MouseEvent evt)
{
- final int res = findColumn(evt);
+ final int column = findColumn(evt);
final int seq = findSeq(evt);
SequenceI sequence = av.getAlignment().getSequenceAt(seq);
List<SequenceFeature> allFeatures = ap.getFeatureRenderer()
- .findFeaturesAtRes(sequence.getDatasetSequence(),
- sequence.findPosition(res));
+ .findFeaturesAtColumn(sequence, column + 1);
List<String> links = new ArrayList<>();
for (SequenceFeature sf : allFeatures)
{
{
return;
}
+
+ stretchGroup.removePropertyChangeListener(seqCanvas);
+
// always do this - annotation has own state
// but defer colourscheme update until hidden sequences are passed in
boolean vischange = stretchGroup.recalcConservation(true);
- needOverviewUpdate |= vischange && av.isSelectionDefinedGroup()
+ updateOverviewAndStructs |= vischange && av.isSelectionDefinedGroup()
&& afterDrag;
if (stretchGroup.cs != null)
{
}
}
PaintRefresher.Refresh(this, av.getSequenceSetId());
- ap.paintAlignment(needOverviewUpdate);
- needOverviewUpdate = false;
+ // TODO: structure colours only need updating if stretchGroup used to or now
+ // does contain sequences with structure views
+ ap.paintAlignment(updateOverviewAndStructs, updateOverviewAndStructs);
+ updateOverviewAndStructs = false;
changeEndRes = false;
changeStartRes = false;
stretchGroup = null;
if (res > (stretchGroup.getStartRes() - 1))
{
stretchGroup.setEndRes(res);
- needOverviewUpdate |= av.isSelectionDefinedGroup();
+ updateOverviewAndStructs |= av.isSelectionDefinedGroup();
}
}
else if (changeStartRes)
if (res < (stretchGroup.getEndRes() + 1))
{
stretchGroup.setStartRes(res);
- needOverviewUpdate |= av.isSelectionDefinedGroup();
+ updateOverviewAndStructs |= av.isSelectionDefinedGroup();
}
}
if (stretchGroup.getSequences(null).contains(nextSeq))
{
stretchGroup.deleteSequence(seq, false);
- needOverviewUpdate |= av.isSelectionDefinedGroup();
+ updateOverviewAndStructs |= av.isSelectionDefinedGroup();
}
else
{
}
stretchGroup.addSequence(nextSeq, false);
- needOverviewUpdate |= av.isSelectionDefinedGroup();
+ updateOverviewAndStructs |= av.isSelectionDefinedGroup();
}
}
mouseDragging = true;
- if (scrollThread != null)
+ if ((scrollThread != null) && (scrollThread.isRunning()))
{
scrollThread.setEvent(evt);
}
-
- seqCanvas.repaint();
}
void scrollCanvas(MouseEvent evt)
{
if (evt == null)
{
- if (scrollThread != null)
+ if ((scrollThread != null) && (scrollThread.isRunning()))
{
- scrollThread.running = false;
+ scrollThread.stopScrolling();
scrollThread = null;
}
mouseDragging = false;
{
MouseEvent evt;
- boolean running = false;
+ private volatile boolean threadRunning = true;
public ScrollThread()
{
public void stopScrolling()
{
- running = false;
+ threadRunning = false;
+ }
+
+ public boolean isRunning()
+ {
+ return threadRunning;
}
@Override
public void run()
{
- running = true;
-
- while (running)
+ while (threadRunning)
{
if (evt != null)
{
if (mouseDragging && (evt.getY() < 0)
&& (av.getRanges().getStartSeq() > 0))
{
- running = av.getRanges().scrollUp(true);
+ av.getRanges().scrollUp(true);
}
if (mouseDragging && (evt.getY() >= getHeight()) && (av
.getAlignment().getHeight() > av.getRanges().getEndSeq()))
{
- running = av.getRanges().scrollUp(false);
+ av.getRanges().scrollUp(false);
}
if (mouseDragging && (evt.getX() < 0))
{
- running = av.getRanges().scrollRight(false);
+ av.getRanges().scrollRight(false);
}
else if (mouseDragging && (evt.getX() >= getWidth()))
{
- running = av.getRanges().scrollRight(true);
+ av.getRanges().scrollRight(true);
}
}