X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fjalview%2Fgui%2FSeqPanel.java;h=d8d674fa975da1ea28916db3d59c0e7b89fc0806;hb=e2c4c31f9a5cdbc22f0f2cbd1c377b8ede26baf1;hp=328dfdc0671f601eeb4592aec47f1e9ca9795b75;hpb=9e2b4ccd9c95ffa75130bf808b0037ea6a1054ca;p=jalview.git diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 328dfdc..d8d674f 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -62,7 +62,6 @@ import java.awt.event.MouseWheelListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.ListIterator; import javax.swing.JPanel; import javax.swing.SwingUtilities; @@ -85,6 +84,16 @@ public class SeqPanel extends JPanel /** 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; @@ -171,6 +180,9 @@ public class SeqPanel extends JPanel ssm.addStructureViewerListener(this); ssm.addSelectionListener(this); } + + lastMouseColumn = -1; + lastMouseSeq = -1; } int startWrapBlock = -1; @@ -203,8 +215,8 @@ public class SeqPanel extends JPanel + 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) @@ -390,7 +402,10 @@ public class SeqPanel extends JPanel endEditing(); if (av.getWrapAlignment()) { - av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX); + // scrollToWrappedVisible expects x-value to have hidden cols subtracted + int x = av.getAlignment().getHiddenColumns() + .findColumnPosition(seqCanvas.cursorX); + av.getRanges().scrollToWrappedVisible(x); } else { @@ -472,7 +487,7 @@ public class SeqPanel extends JPanel av.setSelectionGroup(sg); } - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -669,6 +684,8 @@ public class SeqPanel extends JPanel } lastSearchResults = results; + boolean wasScrolled = false; + if (av.isFollowHighlight()) { // don't allow highlight of protein/cDNA to also scroll a complementary @@ -676,14 +693,19 @@ public class SeqPanel extends JPanel // 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 @@ -700,10 +722,12 @@ public class SeqPanel extends JPanel } /** - * 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) @@ -716,11 +740,22 @@ public class SeqPanel extends JPanel } 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); @@ -771,11 +806,7 @@ public class SeqPanel extends JPanel if (av.isShowSequenceFeatures()) { List 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()); } @@ -786,45 +817,13 @@ public class SeqPanel extends JPanel } 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 features, - final int column, SequenceI sequence) - { - // TODO should this be an AlignViewController method (and reused by applet)? - ListIterator 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; } } } @@ -859,11 +858,12 @@ public class SeqPanel extends JPanel /** * 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 @@ -883,19 +883,48 @@ public class SeqPanel extends JPanel * 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 + * + *
+   * 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)
+   * 
+ * + * @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()); @@ -904,13 +933,12 @@ public class SeqPanel extends JPanel /* * 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); @@ -924,11 +952,9 @@ public class SeqPanel extends JPanel 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; } /** @@ -956,12 +982,9 @@ public class SeqPanel extends JPanel 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; } } @@ -1041,7 +1064,7 @@ public class SeqPanel extends JPanel } if (newWidth > 0) { - ap.paintAlignment(false); + ap.paintAlignment(false, false); if (copyChanges) { /* @@ -1095,7 +1118,7 @@ public class SeqPanel extends JPanel } mouseDragging = true; - if (scrollThread != null) + if ((scrollThread != null) && (scrollThread.isRunning())) { scrollThread.setEvent(evt); } @@ -1258,7 +1281,7 @@ public class SeqPanel extends JPanel // 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; @@ -1521,9 +1544,9 @@ public class SeqPanel extends JPanel oldSeq = 0; } - if (scrollThread != null) + if ((scrollThread != null) && (scrollThread.isRunning())) { - scrollThread.running = false; + scrollThread.stopScrolling(); scrollThread = null; } } @@ -1542,7 +1565,7 @@ public class SeqPanel extends JPanel return; } - if (mouseDragging) + if (mouseDragging && scrollThread == null) { scrollThread = new ScrollThread(); } @@ -1568,19 +1591,13 @@ public class SeqPanel extends JPanel } 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 features = seqCanvas.getFeatureRenderer() - .findFeaturesAtRes(sequence.getDatasetSequence(), - sequence.findPosition(column)); - if (isGapped) - { - removeAdjacentFeatures(features, column, sequence); - } + .findFeaturesAtColumn(sequence, column + 1); if (!features.isEmpty()) { @@ -1588,9 +1605,9 @@ public class SeqPanel extends JPanel * 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, @@ -1599,7 +1616,8 @@ public class SeqPanel extends JPanel List 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 } } } @@ -1615,7 +1633,7 @@ public class SeqPanel extends JPanel av.getRanges().scrollRight(true); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(false); } @@ -1626,12 +1644,18 @@ public class SeqPanel extends JPanel { 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); } /** @@ -1645,7 +1669,7 @@ public class SeqPanel extends JPanel final int res = findColumn(evt); final int seq = findSeq(evt); oldSeq = seq; - needOverviewUpdate = false; + updateOverviewAndStructs = false; startWrapBlock = wrappedBlock; @@ -1768,12 +1792,11 @@ public class SeqPanel extends JPanel */ 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 allFeatures = ap.getFeatureRenderer() - .findFeaturesAtRes(sequence.getDatasetSequence(), - sequence.findPosition(res)); + .findFeaturesAtColumn(sequence, column + 1); List links = new ArrayList<>(); for (SequenceFeature sf : allFeatures) { @@ -1811,7 +1834,7 @@ public class SeqPanel extends JPanel // 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) { @@ -1831,8 +1854,10 @@ public class SeqPanel extends JPanel } } 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; @@ -1886,7 +1911,7 @@ public class SeqPanel extends JPanel if (res > (stretchGroup.getStartRes() - 1)) { stretchGroup.setEndRes(res); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } } else if (changeStartRes) @@ -1894,7 +1919,7 @@ public class SeqPanel extends JPanel if (res < (stretchGroup.getEndRes() + 1)) { stretchGroup.setStartRes(res); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } } @@ -1928,7 +1953,7 @@ public class SeqPanel extends JPanel if (stretchGroup.getSequences(null).contains(nextSeq)) { stretchGroup.deleteSequence(seq, false); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } else { @@ -1938,7 +1963,7 @@ public class SeqPanel extends JPanel } stretchGroup.addSequence(nextSeq, false); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } } @@ -1949,7 +1974,7 @@ public class SeqPanel extends JPanel mouseDragging = true; - if (scrollThread != null) + if ((scrollThread != null) && (scrollThread.isRunning())) { scrollThread.setEvent(evt); } @@ -1959,9 +1984,9 @@ public class SeqPanel extends JPanel { if (evt == null) { - if (scrollThread != null) + if ((scrollThread != null) && (scrollThread.isRunning())) { - scrollThread.running = false; + scrollThread.stopScrolling(); scrollThread = null; } mouseDragging = false; @@ -1984,7 +2009,7 @@ public class SeqPanel extends JPanel { MouseEvent evt; - boolean running = false; + private volatile boolean threadRunning = true; public ScrollThread() { @@ -1998,37 +2023,40 @@ public class SeqPanel extends JPanel 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); } }