From ed2283c5f54da377a2a2fdbdb7aec75ed7041714 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 8 Apr 2015 15:21:23 +0100 Subject: [PATCH] JAL-1691 applet SplitFrame scrolling; pull up of 5 fields to AlignmentViewport --- src/jalview/api/AlignViewportI.java | 23 ++- src/jalview/appletgui/AlignFrame.java | 13 +- src/jalview/appletgui/AlignViewport.java | 109 ++++------- src/jalview/appletgui/AlignmentPanel.java | 257 ++++++++++++++++++++------ src/jalview/appletgui/FeatureRenderer.java | 3 +- src/jalview/appletgui/Finder.java | 3 +- src/jalview/appletgui/IdPanel.java | 3 +- src/jalview/appletgui/RotatableCanvas.java | 6 +- src/jalview/appletgui/SeqCanvas.java | 3 +- src/jalview/appletgui/SeqPanel.java | 5 +- src/jalview/appletgui/SplitFrame.java | 32 +++- src/jalview/appletgui/TreeCanvas.java | 3 +- src/jalview/gui/AlignFrame.java | 4 +- src/jalview/gui/AlignViewport.java | 203 ++------------------ src/jalview/gui/AlignmentPanel.java | 13 +- src/jalview/gui/IdPanel.java | 3 +- src/jalview/gui/Jalview2XML.java | 4 +- src/jalview/gui/SeqPanel.java | 36 ++-- src/jalview/util/MappingUtils.java | 20 +- src/jalview/viewmodel/AlignmentViewport.java | 159 ++++++++++++++++ 20 files changed, 517 insertions(+), 385 deletions(-) diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index c49ee39..1a1cba2 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -20,6 +20,11 @@ */ package jalview.api; +import java.awt.Color; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + import jalview.analysis.Conservation; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; @@ -31,11 +36,6 @@ import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.schemes.ColourSchemeI; -import java.awt.Color; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; - /** * @author jimp * @@ -413,4 +413,17 @@ public interface AlignViewportI extends ViewStyleI * @return */ String getViewId(); + + /** + * Return true if view should scroll to show the highlighted region of a + * sequence + * + * @return + */ + boolean isFollowHighlight(); + + /** + * Set whether view should scroll to show the highlighted region of a sequence + */ + void setFollowHighlight(boolean b); } diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index 5c8135d..ad1626c 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -103,6 +103,7 @@ import jalview.structure.StructureSelectionManager; import jalview.structures.models.AAStructureBindingModel; import jalview.util.MappingUtils; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; public class AlignFrame extends EmbmenuFrame implements ActionListener, ItemListener, KeyListener, AlignViewControllerGuiI @@ -197,7 +198,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, viewport.updateConsensus(alignPanel); displayNonconservedMenuItem.setState(viewport.getShowUnconserved()); - followMouseOverFlag.setState(viewport.getFollowHighlight()); + followMouseOverFlag.setState(viewport.isFollowHighlight()); showGroupConsensus.setState(viewport.isShowGroupConsensus()); showGroupConservation.setState(viewport.isShowGroupConservation()); showConsensusHistogram.setState(viewport.isShowConsensusHistogram()); @@ -926,7 +927,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, private void mouseOverFlag_stateChanged() { - viewport.followHighlight = followMouseOverFlag.getState(); + viewport.setFollowHighlight(followMouseOverFlag.getState()); // TODO: could kick the scrollTo mechanism to reset view for current // searchresults. } @@ -1594,7 +1595,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, viewport.addToRedoList(command); command.undoCommand(null); - AlignViewport originalSource = getOriginatingSource(command); + AlignmentViewport originalSource = getOriginatingSource(command); // JBPNote Test if (originalSource != viewport) { @@ -1626,7 +1627,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, viewport.addToHistoryList(command); command.doCommand(null); - AlignViewport originalSource = getOriginatingSource(command); + AlignmentViewport originalSource = getOriginatingSource(command); // JBPNote Test if (originalSource != viewport) { @@ -1642,9 +1643,9 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, .getAlignment().getSequences()); } - AlignViewport getOriginatingSource(CommandI command) + AlignmentViewport getOriginatingSource(CommandI command) { - AlignViewport originalSource = null; + AlignmentViewport originalSource = null; // For sequence removal and addition, we need to fire // the property change event FROM the viewport where the // original alignment was altered diff --git a/src/jalview/appletgui/AlignViewport.java b/src/jalview/appletgui/AlignViewport.java index b0e29da..fdc6a0c 100644 --- a/src/jalview/appletgui/AlignViewport.java +++ b/src/jalview/appletgui/AlignViewport.java @@ -28,6 +28,7 @@ import jalview.bin.JalviewLite; import jalview.commands.CommandI; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; +import jalview.datamodel.SearchResults; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; @@ -42,14 +43,6 @@ import jalview.viewmodel.AlignmentViewport; public class AlignViewport extends AlignmentViewport implements AlignViewportI, SelectionSource, VamsasSource, CommandListener { - int startRes; - - int endRes; - - int startSeq; - - int endSeq; - boolean cursorMode = false; Font font = new Font("SansSerif", Font.PLAIN, 10); @@ -103,7 +96,7 @@ public class AlignViewport extends AlignmentViewport implements + widthScale + "). Ignoring."); widthScale = 1; } - if (applet.debug) + if (JalviewLite.debug) { System.err .println("Alignment character width scaling factor is now " @@ -126,7 +119,7 @@ public class AlignViewport extends AlignmentViewport implements + heightScale + "). Ignoring."); heightScale = 1; } - if (applet.debug) + if (JalviewLite.debug) { System.err .println("Alignment character height scaling factor is now " @@ -170,9 +163,9 @@ public class AlignViewport extends AlignmentViewport implements } sortByTree = applet.getDefaultParameter("sortByTree", sortByTree); - followHighlight = applet.getDefaultParameter("automaticScrolling", - followHighlight); - followSelection = followHighlight; + setFollowHighlight(applet.getDefaultParameter("automaticScrolling", + isFollowHighlight())); + followSelection = isFollowHighlight(); showSequenceLogo = applet.getDefaultParameter("showSequenceLogo", showSequenceLogo); @@ -262,64 +255,6 @@ public class AlignViewport extends AlignmentViewport implements return sq; } - public int getStartRes() - { - return startRes; - } - - public int getEndRes() - { - return endRes; - } - - public int getStartSeq() - { - return startSeq; - } - - public void setStartRes(int res) - { - this.startRes = res; - } - - public void setStartSeq(int seq) - { - this.startSeq = seq; - } - - public void setEndRes(int res) - { - if (res > alignment.getWidth() - 1) - { - // log.System.out.println(" Corrected res from " + res + " to maximum " + - // (alignment.getWidth()-1)); - res = alignment.getWidth() - 1; - } - if (res < 0) - { - res = 0; - } - this.endRes = res; - } - - public void setEndSeq(int seq) - { - if (seq > alignment.getHeight()) - { - seq = alignment.getHeight(); - } - if (seq < 0) - { - seq = 0; - } - this.endSeq = seq; - } - - public int getEndSeq() - { - return endSeq; - } - java.awt.Frame nullFrame; protected FeatureSettings featureSettings = null; @@ -376,13 +311,6 @@ public class AlignViewport extends AlignmentViewport implements return centreColumnLabels; } - public boolean followHighlight = true; - - public boolean getFollowHighlight() - { - return followHighlight; - } - public boolean followSelection = true; /** @@ -514,4 +442,29 @@ public class AlignViewport extends AlignmentViewport implements return this; } + /** + * If this viewport has a (Protein/cDNA) complement, then scroll the + * complementary alignment to match this one. + */ + public void scrollComplementaryAlignment(AlignmentPanel complementPanel) + { + if (complementPanel == null) + { + return; + } + + /* + * Populate a SearchResults object with the mapped location to scroll to. If + * there is no complement, or it is not following highlights, or no mapping + * is found, the result will be empty. + */ + SearchResults sr = new SearchResults(); + int seqOffset = findComplementScrollTarget(sr); + if (!sr.isEmpty()) + { + complementPanel.setFollowingComplementScroll(true); + complementPanel.scrollToCentre(sr, seqOffset); + } + } + } diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java index c74914f..5a2c5ef 100644 --- a/src/jalview/appletgui/AlignmentPanel.java +++ b/src/jalview/appletgui/AlignmentPanel.java @@ -32,10 +32,12 @@ import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; +import java.util.List; import jalview.analysis.AnnotationSorter; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; +import jalview.bin.JalviewLite; import jalview.datamodel.AlignmentI; import jalview.datamodel.SearchResults; import jalview.datamodel.SequenceI; @@ -313,7 +315,21 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, public boolean scrollToPosition(SearchResults results, boolean redrawOverview) { + return scrollToPosition(results, redrawOverview, false); + } + /** + * scroll the view to show the position of the highlighted region in results + * (if any) + * + * @param results + * @param redrawOverview + * - when set, the overview will be recalculated (takes longer) + * @return false if results were not found + */ + public boolean scrollToPosition(SearchResults results, + boolean redrawOverview, boolean centre) + { // do we need to scroll the panel? if (results != null && results.getSize() > 0) { @@ -327,7 +343,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, int[] r = results.getResults(seq, 0, alignment.getWidth()); if (r == null) { - if (av.applet.debug) + if (JalviewLite.debug) {// DEBUG System.out .println("DEBUG: scroll didn't happen - results not within alignment : " @@ -335,7 +351,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, } return false; } - if (av.applet.debug) + if (JalviewLite.debug) { // DEBUG /* @@ -347,6 +363,18 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, } int start = r[0]; int end = r[1]; + + /* + * To centre results, scroll to positions half the visible width + * left/right of the start/end positions + */ + if (centre) + { + int offset = (av.getEndRes() - av.getStartRes() + 1) / 2 - 1; + start = Math.max(start - offset, 0); + end = Math.min(end + offset, seq.getEnd() - 1); + } + if (start < 0) { return false; @@ -394,33 +422,64 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, * av.getStartSeq()) > seqIndex) { setScrollValues(av.getStartRes(), * seqIndex); } else if ((ends = av.getEndSeq()) <= seqIndex) { * setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1); } - * - * /* */ - if ((av.getStartRes() > end) - || (av.getEndRes() < start) - || ((av.getStartSeq() > seqIndex) || (av.getEndSeq() < seqIndex))) - { - if (start > av.getAlignment().getWidth() - hextent) - { - start = av.getAlignment().getWidth() - hextent; - if (start < 0) - { - start = 0; - } + // below is scrolling logic up to Jalview 2.8.2 + // if ((av.getStartRes() > end) + // || (av.getEndRes() < start) + // || ((av.getStartSeq() > seqIndex) || (av.getEndSeq() < seqIndex))) + // { + // if (start > av.getAlignment().getWidth() - hextent) + // { + // start = av.getAlignment().getWidth() - hextent; + // if (start < 0) + // { + // start = 0; + // } + // + // } + // if (seqIndex > av.getAlignment().getHeight() - vextent) + // { + // seqIndex = av.getAlignment().getHeight() - vextent; + // if (seqIndex < 0) + // { + // seqIndex = 0; + // } + // } + // setScrollValues(start, seqIndex); + // } + // logic copied from jalview.gui.AlignmentPanel: + if ((startv = av.getStartRes()) >= start) + { + /* + * Scroll left to make start of search results visible + */ + setScrollValues(start - 1, seqIndex); + } + else if ((endv = av.getEndRes()) <= end) + { + /* + * Scroll right to make end of search results visible + */ + setScrollValues(startv + 1 + end - endv, seqIndex); + } + else if ((starts = av.getStartSeq()) > seqIndex) + { + /* + * Scroll up to make start of search results visible + */ + setScrollValues(av.getStartRes(), seqIndex); } - if (seqIndex > av.getAlignment().getHeight() - vextent) + else if ((ends = av.getEndSeq()) <= seqIndex) { - seqIndex = av.getAlignment().getHeight() - vextent; - if (seqIndex < 0) - { - seqIndex = 0; - } + /* + * Scroll down to make end of search results visible + */ + setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1); } - // System.out.println("trying to scroll to: "+start+" "+seqIndex); - setScrollValues(start, seqIndex); - }/**/ + /* + * Else results are already visible - no need to scroll + */ } else { @@ -793,6 +852,95 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, } sendViewPosition(); + /* + * If there is one, scroll the (Protein/cDNA) complementary alignment to + * match, unless we are ourselves doing that. + */ + if (isFollowingComplementScroll()) + { + setFollowingComplementScroll(false); + } + else + { + AlignmentPanel ap = getComplementPanel(); + av.scrollComplementaryAlignment(ap); + } + + } + + /** + * A helper method to return the AlignmentPanel in the other (complementary) + * half of a SplitFrame view. Returns null if not in a SplitFrame. + * + * @return + */ + private AlignmentPanel getComplementPanel() + { + AlignmentPanel ap = null; + if (alignFrame != null) + { + SplitFrame sf = alignFrame.getSplitFrame(); + if (sf != null) + { + AlignFrame other = sf.getComplement(alignFrame); + if (other != null) + { + ap = other.alignPanel; + } + } + } + return ap; + } + + /** + * Follow a scrolling change in the (cDNA/Protein) complementary alignment. + * The aim is to keep the two alignments 'lined up' on their centre columns. + * + * @param sr + * holds mapped region(s) of this alignment that we are scrolling + * 'to'; may be modified for sequence offset by this method + * @param seqOffset + * the number of visible sequences to show above the mapped region + */ + protected void scrollToCentre(SearchResults sr, int seqOffset) + { + /* + * To avoid jumpy vertical scrolling (if some sequences are gapped or not + * mapped), we can make the scroll-to location a sequence above the one + * actually mapped. + */ + SequenceI mappedTo = sr.getResultSequence(0); + List seqs = av.getAlignment().getSequences(); + + /* + * This is like AlignmentI.findIndex(seq) but here we are matching the + * dataset sequence not the aligned sequence + */ + int sequenceIndex = 0; + boolean matched = false; + for (SequenceI seq : seqs) + { + if (mappedTo == seq.getDatasetSequence()) + { + matched = true; + break; + } + sequenceIndex++; + } + if (!matched) + { + return; // failsafe, shouldn't happen + } + sequenceIndex = Math.max(0, sequenceIndex - seqOffset); + sr.getResults().get(0) + .setSequence(av.getAlignment().getSequenceAt(sequenceIndex)); + + /* + * Scroll to position but centring the target residue. Also set a state flag + * to prevent adjustmentValueChanged performing this recursively. + */ + setFollowingComplementScroll(true); + scrollToPosition(sr, true, true); } private void sendViewPosition() @@ -884,72 +1032,58 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, protected Panel seqPanelHolder = new Panel(); - BorderLayout borderLayout1 = new BorderLayout(); - - BorderLayout borderLayout3 = new BorderLayout(); - protected Panel scalePanelHolder = new Panel(); protected Panel idPanelHolder = new Panel(); - BorderLayout borderLayout5 = new BorderLayout(); - protected Panel idSpaceFillerPanel1 = new Panel(); public Panel annotationSpaceFillerHolder = new Panel(); - BorderLayout borderLayout6 = new BorderLayout(); - - BorderLayout borderLayout7 = new BorderLayout(); - - Panel hscrollHolder = new Panel(); - - BorderLayout borderLayout10 = new BorderLayout(); - protected Panel hscrollFillerPanel = new Panel(); - BorderLayout borderLayout11 = new BorderLayout(); - - BorderLayout borderLayout4 = new BorderLayout(); - - BorderLayout borderLayout2 = new BorderLayout(); - Panel annotationPanelHolder = new Panel(); protected Scrollbar apvscroll = new Scrollbar(); - BorderLayout borderLayout12 = new BorderLayout(); + /* + * Flag set while scrolling to follow complementary cDNA/protein scroll. When + * true, suppresses invoking the same method recursively. + */ + private boolean followingComplementScroll; private void jbInit() throws Exception { // idPanelHolder.setPreferredSize(new Dimension(70, 10)); - this.setLayout(borderLayout7); + this.setLayout(new BorderLayout()); // sequenceHolderPanel.setPreferredSize(new Dimension(150, 150)); - sequenceHolderPanel.setLayout(borderLayout3); - seqPanelHolder.setLayout(borderLayout1); + sequenceHolderPanel.setLayout(new BorderLayout()); + seqPanelHolder.setLayout(new BorderLayout()); scalePanelHolder.setBackground(Color.white); // scalePanelHolder.setPreferredSize(new Dimension(10, 30)); - scalePanelHolder.setLayout(borderLayout6); - idPanelHolder.setLayout(borderLayout5); + scalePanelHolder.setLayout(new BorderLayout()); + idPanelHolder.setLayout(new BorderLayout()); idSpaceFillerPanel1.setBackground(Color.white); // idSpaceFillerPanel1.setPreferredSize(new Dimension(10, 30)); - idSpaceFillerPanel1.setLayout(borderLayout11); + idSpaceFillerPanel1.setLayout(new BorderLayout()); annotationSpaceFillerHolder.setBackground(Color.white); // annotationSpaceFillerHolder.setPreferredSize(new Dimension(10, 80)); - annotationSpaceFillerHolder.setLayout(borderLayout4); + annotationSpaceFillerHolder.setLayout(new BorderLayout()); hscroll.setOrientation(Scrollbar.HORIZONTAL); - hscrollHolder.setLayout(borderLayout10); + + Panel hscrollHolder = new Panel(); + hscrollHolder.setLayout(new BorderLayout()); hscrollFillerPanel.setBackground(Color.white); apvscroll.setOrientation(Scrollbar.VERTICAL); apvscroll.setVisible(true); apvscroll.addAdjustmentListener(this); annotationPanelHolder.setBackground(Color.white); - annotationPanelHolder.setLayout(borderLayout12); + annotationPanelHolder.setLayout(new BorderLayout()); annotationPanelHolder.add(apvscroll, BorderLayout.EAST); // hscrollFillerPanel.setPreferredSize(new Dimension(70, 10)); hscrollHolder.setBackground(Color.white); @@ -1020,4 +1154,19 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, error.printStackTrace(); } + /** + * Set a flag to say we are scrolling to follow a (cDNA/protein) complement. + * + * @param b + */ + protected void setFollowingComplementScroll(boolean b) + { + this.followingComplementScroll = b; + } + + protected boolean isFollowingComplementScroll() + { + return this.followingComplementScroll; + } + } diff --git a/src/jalview/appletgui/FeatureRenderer.java b/src/jalview/appletgui/FeatureRenderer.java index 6963ca7..9751514 100644 --- a/src/jalview/appletgui/FeatureRenderer.java +++ b/src/jalview/appletgui/FeatureRenderer.java @@ -28,6 +28,7 @@ import jalview.datamodel.*; import jalview.schemes.AnnotationColourGradient; import jalview.schemes.GraduatedColor; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import jalview.viewmodel.seqfeatures.FeaturesDisplayed; /** @@ -49,7 +50,7 @@ public class FeatureRenderer extends jalview.renderer.seqfeatures.FeatureRendere * @param av * DOCUMENT ME! */ - public FeatureRenderer(AlignViewport av) + public FeatureRenderer(AlignmentViewport av) { super(); this.av = av; diff --git a/src/jalview/appletgui/Finder.java b/src/jalview/appletgui/Finder.java index 3f8a54d..5412472 100644 --- a/src/jalview/appletgui/Finder.java +++ b/src/jalview/appletgui/Finder.java @@ -24,6 +24,7 @@ import jalview.datamodel.SearchResults; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import java.awt.Button; import java.awt.Checkbox; @@ -43,7 +44,7 @@ import java.util.Vector; public class Finder extends Panel implements ActionListener { - AlignViewport av; + AlignmentViewport av; AlignmentPanel ap; diff --git a/src/jalview/appletgui/IdPanel.java b/src/jalview/appletgui/IdPanel.java index 6cf245f..af0b2fa 100755 --- a/src/jalview/appletgui/IdPanel.java +++ b/src/jalview/appletgui/IdPanel.java @@ -25,6 +25,7 @@ import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.util.UrlLink; +import jalview.viewmodel.AlignmentViewport; import java.awt.BorderLayout; import java.awt.Panel; @@ -41,7 +42,7 @@ public class IdPanel extends Panel implements MouseListener, protected IdCanvas idCanvas; - protected AlignViewport av; + protected AlignmentViewport av; protected AlignmentPanel alignPanel; diff --git a/src/jalview/appletgui/RotatableCanvas.java b/src/jalview/appletgui/RotatableCanvas.java index 760e801..377186a 100755 --- a/src/jalview/appletgui/RotatableCanvas.java +++ b/src/jalview/appletgui/RotatableCanvas.java @@ -21,7 +21,6 @@ package jalview.appletgui; import java.util.*; - import java.awt.*; import java.awt.event.*; @@ -29,6 +28,7 @@ import jalview.api.RotatableCanvasI; import jalview.datamodel.*; import jalview.math.*; import jalview.util.*; +import jalview.viewmodel.AlignmentViewport; public class RotatableCanvas extends Panel implements MouseListener, MouseMotionListener, KeyListener, RotatableCanvasI @@ -99,11 +99,11 @@ public class RotatableCanvas extends Panel implements MouseListener, float scalefactor = 1; - AlignViewport av; + AlignmentViewport av; boolean showLabels = false; - public RotatableCanvas(AlignViewport av) + public RotatableCanvas(AlignmentViewport av) { this.av = av; } diff --git a/src/jalview/appletgui/SeqCanvas.java b/src/jalview/appletgui/SeqCanvas.java index bf54c66..522151c 100755 --- a/src/jalview/appletgui/SeqCanvas.java +++ b/src/jalview/appletgui/SeqCanvas.java @@ -24,6 +24,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.SearchResults; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; +import jalview.viewmodel.AlignmentViewport; import java.awt.Color; import java.awt.FontMetrics; @@ -72,7 +73,7 @@ public class SeqCanvas extends Panel avcharWidth = av.getCharWidth(); } - public AlignViewport getViewport() + public AlignmentViewport getViewport() { return av; } diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index ae4ae10..86dedfc 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -50,6 +50,7 @@ import jalview.structure.StructureSelectionManager; import jalview.structure.VamsasSource; import jalview.util.MappingUtils; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; public class SeqPanel extends Panel implements MouseMotionListener, MouseListener, SequenceListener, SelectionListener @@ -742,7 +743,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, public void highlightSequence(SearchResults results) { - if (av.followHighlight) + if (av.isFollowHighlight()) { if (ap.scrollToPosition(results, true)) { @@ -1797,7 +1798,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, // TODO: extend config options to allow user to control if selections may be // shared between viewports. if (av != null - && (av == source || !av.followSelection || (source instanceof AlignViewport && ((AlignViewport) source) + && (av == source || !av.followSelection || (source instanceof AlignViewport && ((AlignmentViewport) source) .getSequenceSetId().equals(av.getSequenceSetId())))) { return; diff --git a/src/jalview/appletgui/SplitFrame.java b/src/jalview/appletgui/SplitFrame.java index 4fbf65a..836d70c 100644 --- a/src/jalview/appletgui/SplitFrame.java +++ b/src/jalview/appletgui/SplitFrame.java @@ -11,6 +11,7 @@ import jalview.api.ViewStyleI; import jalview.bin.JalviewLite; import jalview.datamodel.AlignmentI; import jalview.structure.StructureSelectionManager; +import jalview.viewmodel.AlignmentViewport; public class SplitFrame extends EmbmenuFrame { @@ -49,9 +50,9 @@ public class SplitFrame extends EmbmenuFrame final AlignViewport bottomViewport = bottomFrame.viewport; final AlignmentI topAlignment = topViewport.getAlignment(); final AlignmentI bottomAlignment = bottomViewport.getAlignment(); - AlignViewport cdna = topAlignment.isNucleotide() ? topViewport + AlignmentViewport cdna = topAlignment.isNucleotide() ? topViewport : (bottomAlignment.isNucleotide() ? bottomViewport : null); - AlignViewport protein = !topAlignment.isNucleotide() ? topViewport + AlignmentViewport protein = !topAlignment.isNucleotide() ? topViewport : (!bottomAlignment.isNucleotide() ? bottomViewport : null); boolean mapped = AlignmentUtils.mapProteinToCdna( @@ -99,9 +100,9 @@ public class SplitFrame extends EmbmenuFrame */ protected void adjustLayout() { - AlignViewport cdna = topFrame.getAlignViewport().getAlignment() + AlignmentViewport cdna = topFrame.getAlignViewport().getAlignment() .isNucleotide() ? topFrame.viewport : bottomFrame.viewport; - AlignViewport protein = cdna == topFrame.viewport ? bottomFrame.viewport + AlignmentViewport protein = cdna == topFrame.viewport ? bottomFrame.viewport : topFrame.viewport; /* @@ -190,11 +191,30 @@ public class SplitFrame extends EmbmenuFrame else { this.add(outermost); - int width = Math.max(topFrame.frameWidth, - bottomFrame.frameWidth); + int width = Math.max(topFrame.frameWidth, bottomFrame.frameWidth); int height = topFrame.frameHeight + bottomFrame.frameHeight; jalview.bin.JalviewLite .addFrame(this, this.getTitle(), width, height); } } + + /** + * Returns the contained AlignFrame complementary to the one given (or null if + * no match to top or bottom component). + * + * @param af + * @return + */ + public AlignFrame getComplement(AlignFrame af) + { + if (topFrame == af) + { + return bottomFrame; + } + else if (bottomFrame == af) + { + return topFrame; + } + return null; + } } diff --git a/src/jalview/appletgui/TreeCanvas.java b/src/jalview/appletgui/TreeCanvas.java index 7ba9977..edcd961 100755 --- a/src/jalview/appletgui/TreeCanvas.java +++ b/src/jalview/appletgui/TreeCanvas.java @@ -33,6 +33,7 @@ import jalview.schemes.ResidueProperties; import jalview.schemes.UserColourScheme; import jalview.util.Format; import jalview.util.MappingUtils; +import jalview.viewmodel.AlignmentViewport; import java.awt.Color; import java.awt.Dimension; @@ -715,7 +716,7 @@ public class TreeCanvas extends Panel implements MouseListener, ap.updateAnnotation(); if (av.getCodingComplement() != null) { - ((AlignViewport) av.getCodingComplement()).firePropertyChange( + ((AlignmentViewport) av.getCodingComplement()).firePropertyChange( "alignment", null, ap.av.getAlignment().getSequences()); } } diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index c7792a5..bb8eec3 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -2849,7 +2849,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * Set the 'follow' flag on the Viewport (and scroll to position if now * true). */ - if (viewport.followHighlight = this.followHighlightMenuItem.getState()) + final boolean state = this.followHighlightMenuItem.getState(); + viewport.setFollowHighlight(state); + if (state) { alignPanel.scrollToPosition( alignPanel.getSeqPanel().seqCanvas.searchResults, false); diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index d84d3a3..5e477c7 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -44,7 +44,6 @@ import java.awt.Font; import java.awt.Rectangle; import java.util.ArrayList; import java.util.Hashtable; -import java.util.List; import java.util.Set; import java.util.Vector; @@ -62,7 +61,6 @@ import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; -import jalview.datamodel.HiddenSequences; import jalview.datamodel.PDBEntry; import jalview.datamodel.SearchResults; import jalview.datamodel.Sequence; @@ -74,8 +72,6 @@ import jalview.structure.CommandListener; import jalview.structure.SelectionSource; import jalview.structure.StructureSelectionManager; import jalview.structure.VamsasSource; -import jalview.util.Comparison; -import jalview.util.MappingUtils; import jalview.util.MessageManager; import jalview.viewmodel.AlignmentViewport; import jalview.ws.params.AutoCalcSetting; @@ -89,14 +85,6 @@ import jalview.ws.params.AutoCalcSetting; public class AlignViewport extends AlignmentViewport implements SelectionSource, AlignViewportI, CommandListener { - int startRes; - - int endRes; - - int startSeq; - - int endSeq; - Font font; NJTree currentTree = null; @@ -371,112 +359,6 @@ public class AlignViewport extends AlignmentViewport implements return sq; } - /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public int getStartRes() - { - return startRes; - } - - /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public int getEndRes() - { - return endRes; - } - - /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public int getStartSeq() - { - return startSeq; - } - - /** - * DOCUMENT ME! - * - * @param res - * DOCUMENT ME! - */ - public void setStartRes(int res) - { - this.startRes = res; - } - - /** - * DOCUMENT ME! - * - * @param seq - * DOCUMENT ME! - */ - public void setStartSeq(int seq) - { - this.startSeq = seq; - } - - /** - * DOCUMENT ME! - * - * @param res - * DOCUMENT ME! - */ - public void setEndRes(int res) - { - if (res > (alignment.getWidth() - 1)) - { - // log.System.out.println(" Corrected res from " + res + " to maximum " + - // (alignment.getWidth()-1)); - res = alignment.getWidth() - 1; - } - - if (res < 0) - { - res = 0; - } - - this.endRes = res; - } - - /** - * DOCUMENT ME! - * - * @param seq - * DOCUMENT ME! - */ - public void setEndSeq(int seq) - { - if (seq > alignment.getHeight()) - { - seq = alignment.getHeight(); - } - - if (seq < 0) - { - seq = 0; - } - - this.endSeq = seq; - } - - /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public int getEndSeq() - { - return endSeq; - } - boolean validCharWidth; /** @@ -674,21 +556,6 @@ public class AlignViewport extends AlignmentViewport implements return false; } - /** - * when set, view will scroll to show the highlighted position - */ - public boolean followHighlight = true; - - /** - * @return true if view should scroll to show the highlighted region of a - * sequence - * @return - */ - public boolean getFollowHighlight() - { - return followHighlight; - } - public boolean followSelection = true; /** @@ -1139,70 +1006,22 @@ public class AlignViewport extends AlignmentViewport implements /** * If this viewport has a (Protein/cDNA) complement, then scroll the * complementary alignment to match this one. - * - * @param horizontal - * true for horizontal scroll event, false for vertical */ - public void scrollComplementaryAlignment(boolean horizontal) + public void scrollComplementaryAlignment() { /* - * If no complement, or it is not following scrolling, do nothing. + * Populate a SearchResults object with the mapped location to scroll to. If + * there is no complement, or it is not following highlights, or no mapping + * is found, the result will be empty. */ - // TODO pull up followHighlight to AlignmentViewport/AlignViewportI - final AlignViewport codingComplement = (AlignViewport) getCodingComplement(); - if (codingComplement == null || !codingComplement.followHighlight) - { - return; - } - boolean iAmProtein = !getAlignment().isNucleotide(); - AlignmentI proteinAlignment = iAmProtein ? getAlignment() - : codingComplement.getAlignment(); - if (proteinAlignment == null) + SearchResults sr = new SearchResults(); + int seqOffset = findComplementScrollTarget(sr); + if (!sr.isEmpty()) { - return; - } - final Set mappings = proteinAlignment - .getCodonFrames(); - - /* - * Heuristic: find the first mapped sequence (if any) with a non-gapped - * residue in the middle column of the visible region. Scroll the - * complementary alignment to line up the corresponding residue. - */ - int seqOffset = 0; - SequenceI sequence = null; - int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2; - final HiddenSequences hiddenSequences = getAlignment() - .getHiddenSequences(); - for (int seqNo = getStartSeq(); seqNo < getEndSeq(); seqNo++, seqOffset++) - { - sequence = getAlignment().getSequenceAt(seqNo); - if (hiddenSequences != null && hiddenSequences.isHidden(sequence)) - { - continue; - } - if (Comparison.isGap(sequence.getCharAt(middleColumn))) - { - continue; - } - List seqMappings = MappingUtils - .findMappingsForSequence(sequence, mappings); - if (!seqMappings.isEmpty()) - { - break; - } - } - - if (sequence == null) - { - /* - * No ungapped mapped sequence in middle column - do nothing - */ - return; + // TODO would like next line without cast but needs more refactoring... + final AlignmentPanel complementPanel = ((AlignViewport) getCodingComplement()).getAlignPanel(); + complementPanel.setFollowingComplementScroll(true); + complementPanel.scrollToCentre(sr, seqOffset); } - SearchResults sr = MappingUtils.buildSearchResults(sequence, - sequence.findPosition(middleColumn), mappings); - codingComplement.getAlignPanel().scrollAsComplement(sr, seqOffset, - horizontal); } } diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index 851c58b..c7ef3ce 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -338,7 +338,7 @@ public class AlignmentPanel extends GAlignmentPanel implements // TODO: properly locate search results in view when large numbers of hidden // columns exist before highlighted region // do we need to scroll the panel? - // TODO: tons of nullpointereexceptions raised here. + // TODO: tons of nullpointerexceptions raised here. if (results != null && results.getSize() > 0 && av != null && av.getAlignment() != null) { @@ -814,7 +814,7 @@ public class AlignmentPanel extends GAlignmentPanel implements } else { - av.scrollComplementaryAlignment(evt.getSource() == hscroll); + av.scrollComplementaryAlignment(); } } @@ -1721,11 +1721,8 @@ public class AlignmentPanel extends GAlignmentPanel implements * 'to'; may be modified for sequence offset by this method * @param seqOffset * the number of visible sequences to show above the mapped region - * @param horizontal - * if true, horizontal scrolling, else vertical */ - public void scrollAsComplement(SearchResults sr, int seqOffset, - boolean horizontal) + public void scrollToCentre(SearchResults sr, int seqOffset) { /* * To avoid jumpy vertical scrolling (if some sequences are gapped or not @@ -1759,10 +1756,8 @@ public class AlignmentPanel extends GAlignmentPanel implements .setSequence(av.getAlignment().getSequenceAt(sequenceIndex)); /* - * Scroll to position but centring the target residue. Also set a state flag - * to prevent adjustmentValueChanged performing this recursively. + * Scroll to position but centring the target residue. */ - setFollowingComplementScroll(true); scrollToPosition(sr, true, true); } diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index 4ea35ca..88d0c69 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -27,6 +27,7 @@ import jalview.datamodel.SequenceI; import jalview.io.SequenceAnnotationReport; import jalview.util.MessageManager; import jalview.util.UrlLink; +import jalview.viewmodel.AlignmentViewport; import java.awt.BorderLayout; import java.awt.event.MouseEvent; @@ -54,7 +55,7 @@ public class IdPanel extends JPanel implements MouseListener, { private IdCanvas idCanvas; - protected AlignViewport av; + protected AlignmentViewport av; protected AlignmentPanel alignPanel; diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index 2528841..8e77111 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -1133,7 +1133,7 @@ public class Jalview2XML view.setShowGroupConservation(av.isShowGroupConservation()); view.setShowNPfeatureTooltip(av.isShowNPFeats()); view.setShowDbRefTooltip(av.isShowDBRefs()); - view.setFollowHighlight(av.followHighlight); + view.setFollowHighlight(av.isFollowHighlight()); view.setFollowSelection(av.followSelection); view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus()); if (av.getFeaturesDisplayed() != null) @@ -3844,7 +3844,7 @@ public class Jalview2XML } if (view.hasFollowHighlight()) { - af.viewport.followHighlight = view.getFollowHighlight(); + af.viewport.setFollowHighlight(view.getFollowHighlight()); } if (view.hasFollowSelection()) { diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index afd3242..6cd5b70 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -20,6 +20,23 @@ */ package jalview.gui; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.util.List; +import java.util.Vector; + +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.ToolTipManager; + import jalview.api.AlignViewportI; import jalview.commands.EditCommand; import jalview.commands.EditCommand.Action; @@ -43,23 +60,6 @@ import jalview.util.MappingUtils; import jalview.util.MessageManager; import jalview.viewmodel.AlignmentViewport; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Point; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; -import java.util.List; -import java.util.Vector; - -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.ToolTipManager; - /** * DOCUMENT ME! * @@ -647,7 +647,7 @@ public class SeqPanel extends JPanel implements MouseListener, @Override public void highlightSequence(SearchResults results) { - if (av.followHighlight) + if (av.isFollowHighlight()) { if (ap.scrollToPosition(results, false)) { diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java index c6cbdc5..4aed258 100644 --- a/src/jalview/util/MappingUtils.java +++ b/src/jalview/util/MappingUtils.java @@ -250,8 +250,23 @@ public final class MappingUtils public static SearchResults buildSearchResults(SequenceI seq, int index, Set seqmappings) { - SearchResults results; - results = new SearchResults(); + SearchResults results = new SearchResults(); + addSearchResults(results, seq, index, seqmappings); + return results; + } + + /** + * Adds entries to a SearchResults object describing the mapped region + * corresponding to the specified sequence position. + * + * @param results + * @param seq + * @param index + * @param seqmappings + */ + public static void addSearchResults(SearchResults results, SequenceI seq, + int index, Set seqmappings) + { if (index >= seq.getStart() && index <= seq.getEnd()) { for (AlignedCodonFrame acf : seqmappings) @@ -259,7 +274,6 @@ public final class MappingUtils acf.markMappedRegion(seq, index, results); } } - return results; } /** diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index c0e86cb..d6aa400 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -46,6 +46,8 @@ import jalview.datamodel.AlignmentView; import jalview.datamodel.Annotation; import jalview.datamodel.CigarArray; import jalview.datamodel.ColumnSelection; +import jalview.datamodel.HiddenSequences; +import jalview.datamodel.SearchResults; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceGroup; @@ -57,6 +59,8 @@ import jalview.schemes.ResidueProperties; import jalview.structure.CommandListener; import jalview.structure.StructureSelectionManager; import jalview.structure.VamsasSource; +import jalview.util.Comparison; +import jalview.util.MappingUtils; import jalview.viewmodel.styles.ViewStyle; import jalview.workers.AlignCalcManager; import jalview.workers.ComplementConsensusThread; @@ -1196,6 +1200,20 @@ public abstract class AlignmentViewport implements AlignViewportI, protected boolean showAutocalculatedAbove; /** + * when set, view will scroll to show the highlighted position + */ + private boolean followHighlight = true; + + // TODO private with getters and setters? + public int startRes; + + public int endRes; + + public int startSeq; + + public int endSeq; + + /** * Property change listener for changes in alignment * * @param listener @@ -2423,4 +2441,145 @@ public abstract class AlignmentViewport implements AlignViewportI, { viewStyle.setScaleProteinAsCdna(b); } + + /** + * @return true if view should scroll to show the highlighted region of a + * sequence + * @return + */ + @Override + public final boolean isFollowHighlight() + { + return followHighlight; + } + + @Override + public final void setFollowHighlight(boolean b) + { + this.followHighlight = b; + } + + public int getStartRes() + { + return startRes; + } + + public int getEndRes() + { + return endRes; + } + + public int getStartSeq() + { + return startSeq; + } + + public void setStartRes(int res) + { + this.startRes = res; + } + + public void setStartSeq(int seq) + { + this.startSeq = seq; + } + + public void setEndRes(int res) + { + if (res > alignment.getWidth() - 1) + { + // log.System.out.println(" Corrected res from " + res + " to maximum " + + // (alignment.getWidth()-1)); + res = alignment.getWidth() - 1; + } + if (res < 0) + { + res = 0; + } + this.endRes = res; + } + + public void setEndSeq(int seq) + { + if (seq > alignment.getHeight()) + { + seq = alignment.getHeight(); + } + if (seq < 0) + { + seq = 0; + } + this.endSeq = seq; + } + + public int getEndSeq() + { + return endSeq; + } + + /** + * Helper method to populate the SearchResults with the location in the + * complementary alignment to scroll to, in order to match this one. + * + * @param sr + * the SearchResults to add to + * @return the offset (below top of visible region) of the matched sequence + */ + protected int findComplementScrollTarget(SearchResults sr) + { + final AlignViewportI codingComplement = getCodingComplement(); + if (codingComplement == null || !codingComplement.isFollowHighlight()) + { + return 0; + } + boolean iAmProtein = !getAlignment().isNucleotide(); + AlignmentI proteinAlignment = iAmProtein ? getAlignment() + : codingComplement.getAlignment(); + if (proteinAlignment == null) + { + return 0; + } + final Set mappings = proteinAlignment + .getCodonFrames(); + + /* + * Heuristic: find the first mapped sequence (if any) with a non-gapped + * residue in the middle column of the visible region. Scroll the + * complementary alignment to line up the corresponding residue. + */ + int seqOffset = 0; + SequenceI sequence = null; + int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2; + final HiddenSequences hiddenSequences = getAlignment() + .getHiddenSequences(); + for (int seqNo = getStartSeq(); seqNo < getEndSeq(); seqNo++, seqOffset++) + { + sequence = getAlignment().getSequenceAt(seqNo); + if (hiddenSequences != null && hiddenSequences.isHidden(sequence)) + { + continue; + } + if (Comparison.isGap(sequence.getCharAt(middleColumn))) + { + continue; + } + List seqMappings = MappingUtils + .findMappingsForSequence(sequence, mappings); + if (!seqMappings.isEmpty()) + { + break; + } + } + + if (sequence == null) + { + /* + * No ungapped mapped sequence in middle column - do nothing + */ + return 0; + } + MappingUtils.addSearchResults(sr, sequence, + sequence.findPosition(middleColumn), mappings); + return seqOffset; + } } -- 1.7.10.2