From: Jim Procter Date: Fri, 17 Nov 2017 12:31:58 +0000 (+0000) Subject: Merge branch 'bug/JAL-2821' into develop X-Git-Tag: Release_2_10_3b1~21 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=8ec36c3e36ff5beb0db07345babe8c8426d0426b;hp=e249a4950d0e7f3c5ea905f3c1c02baebd92b708;p=jalview.git Merge branch 'bug/JAL-2821' into develop --- diff --git a/.classpath b/.classpath index c4a2832..d704f10 100644 --- a/.classpath +++ b/.classpath @@ -68,6 +68,6 @@ - + diff --git a/benchmarking/README b/benchmarking/README index 60b94a9..fac0bf6 100644 --- a/benchmarking/README +++ b/benchmarking/README @@ -1,11 +1,13 @@ To set up benchmarking: -1. In the jalview directory run +You will need to install Maven: https://maven.apache.org/install.html + +1. Run the makedist target of build.xml in Eclipse, or in the jalview directory run ant makedist This builds a jalview.jar file and puts it into dist/ -2. Make a lib directory in benchmarking/ if not already present. +2. Make a lib directory in benchmarking/ if not already present and cd into this directory. 3. Purge any previous maven dependencies: mvn dependency:purge-local-repository -DactTransitively=false -DreResolve=false diff --git a/benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java b/benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java index eb35e3b..d3c67d7 100644 --- a/benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java +++ b/benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java @@ -47,126 +47,128 @@ import jalview.datamodel.HiddenColumns; @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Fork(1) public class HiddenColumnsBenchmark -{ - /* - * State with multiple hidden columns and a start position set - */ - @State(Scope.Thread) - public static class HiddenColsAndStartState - { - @Param({"300", "10000", "100000"}) - public int maxcols; - - @Param({"1", "50", "90"}) - public int startpcnt; // position as percentage of maxcols - - @Param({"1","15","100"}) - public int hide; - - HiddenColumns h = new HiddenColumns(); - Random rand = new Random(); - - public int hiddenColumn; - public int visibleColumn; - - @Setup - public void setup() - { - rand.setSeed(1234); - int lastcol = 0; - while (lastcol < maxcols) - { - int count = rand.nextInt(100); - lastcol += count; - h.hideColumns(lastcol, lastcol+hide); - lastcol+=hide; - } - - // make sure column at start is hidden - hiddenColumn = (int)(maxcols * startpcnt/100.0); - h.hideColumns(hiddenColumn, hiddenColumn); - - // and column after start is visible - ColumnSelection sel = new ColumnSelection(); - h.revealHiddenColumns(hiddenColumn+hide, sel); - visibleColumn = hiddenColumn+hide; - - System.out.println("Maxcols: " + maxcols + " HiddenCol: " + hiddenColumn + " Hide: " + hide); - System.out.println("Number of hidden columns: " + h.getSize()); - } - } - - /* Convention: functions in alphabetical order */ - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public int benchAdjustForHiddenColumns(HiddenColsAndStartState tstate) - { - return tstate.h.adjustForHiddenColumns(tstate.visibleColumn); - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public int benchFindColumnPosition(HiddenColsAndStartState tstate) - { - return tstate.h.findColumnPosition(tstate.visibleColumn); - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public List benchFindHiddenRegionPositions(HiddenColsAndStartState tstate) - { - return tstate.h.findHiddenRegionPositions(); - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public ArrayList benchGetHiddenColumnsCopy(HiddenColsAndStartState tstate) - { - return tstate.h.getHiddenColumnsCopy(); - } - - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public int benchGetSize(HiddenColsAndStartState tstate) - { - return tstate.h.getSize(); - } +{ + /* + * State with multiple hidden columns and a start position set + */ + @State(Scope.Thread) + public static class HiddenColsAndStartState + { + @Param({ "300", "10000", "100000" }) + public int maxcols; - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public HiddenColumns benchHideCols(HiddenColsAndStartState tstate) - { - tstate.h.hideColumns(tstate.visibleColumn, - tstate.visibleColumn+2000); - return tstate.h; - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public boolean benchIsVisible(HiddenColsAndStartState tstate) - { - return tstate.h.isVisible(tstate.hiddenColumn); - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public HiddenColumns benchReveal(HiddenColsAndStartState tstate) - { - ColumnSelection sel = new ColumnSelection(); - tstate.h.revealHiddenColumns(tstate.hiddenColumn, sel); - return tstate.h; - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public HiddenColumns benchRevealAll(HiddenColsAndStartState tstate) + @Param({ "1", "50", "90" }) + public int startpcnt; // position as percentage of maxcols + + @Param({ "1", "15", "100" }) + public int hide; + + HiddenColumns h = new HiddenColumns(); + + Random rand = new Random(); + + public int hiddenColumn; + + public int visibleColumn; + + @Setup + public void setup() { - ColumnSelection sel = new ColumnSelection(); - tstate.h.revealAllHiddenColumns(sel); - return tstate.h; + rand.setSeed(1234); + int lastcol = 0; + while (lastcol < maxcols) + { + int count = rand.nextInt(100); + lastcol += count; + h.hideColumns(lastcol, lastcol + hide); + lastcol += hide; + } + + // make sure column at start is hidden + hiddenColumn = (int) (maxcols * startpcnt / 100.0); + h.hideColumns(hiddenColumn, hiddenColumn); + + // and column after start is visible + ColumnSelection sel = new ColumnSelection(); + h.revealHiddenColumns(hiddenColumn + hide, sel); + visibleColumn = hiddenColumn + hide; + + System.out.println("Maxcols: " + maxcols + " HiddenCol: " + + hiddenColumn + " Hide: " + hide); + System.out.println("Number of hidden columns: " + h.getSize()); } - - + } + + /* Convention: functions in alphabetical order */ + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public int benchAdjustForHiddenColumns(HiddenColsAndStartState tstate) + { + return tstate.h.adjustForHiddenColumns(tstate.visibleColumn); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public int benchFindColumnPosition(HiddenColsAndStartState tstate) + { + return tstate.h.findColumnPosition(tstate.visibleColumn); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public List benchFindHiddenRegionPositions( + HiddenColsAndStartState tstate) + { + return tstate.h.findHiddenRegionPositions(); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public ArrayList benchGetHiddenColumnsCopy( + HiddenColsAndStartState tstate) + { + return tstate.h.getHiddenColumnsCopy(); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public int benchGetSize(HiddenColsAndStartState tstate) + { + return tstate.h.getSize(); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public HiddenColumns benchHideCols(HiddenColsAndStartState tstate) + { + tstate.h.hideColumns(tstate.visibleColumn, tstate.visibleColumn + 2000); + return tstate.h; + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public boolean benchIsVisible(HiddenColsAndStartState tstate) + { + return tstate.h.isVisible(tstate.hiddenColumn); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public HiddenColumns benchReveal(HiddenColsAndStartState tstate) + { + ColumnSelection sel = new ColumnSelection(); + tstate.h.revealHiddenColumns(tstate.hiddenColumn, sel); + return tstate.h; + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public HiddenColumns benchRevealAll(HiddenColsAndStartState tstate) + { + ColumnSelection sel = new ColumnSelection(); + tstate.h.revealAllHiddenColumns(sel); + return tstate.h; + } + } \ No newline at end of file diff --git a/build.xml b/build.xml index f39fdf3..4931cfb 100755 --- a/build.xml +++ b/build.xml @@ -451,7 +451,7 @@ - + j2se version="1.7+" diff --git a/help/helpTOC.xml b/help/helpTOC.xml index 4636ea3..7ba4ee5 100755 --- a/help/helpTOC.xml +++ b/help/helpTOC.xml @@ -24,13 +24,7 @@ - - - - - - - + diff --git a/help/html/features/pdbseqfetcher.png b/help/html/features/pdbseqfetcher.png index 97a779a..2081a3d 100644 Binary files a/help/html/features/pdbseqfetcher.png and b/help/html/features/pdbseqfetcher.png differ diff --git a/help/html/features/pdbsequencefetcher.html b/help/html/features/pdbsequencefetcher.html index 2962ba6..bb63bed 100644 --- a/help/html/features/pdbsequencefetcher.html +++ b/help/html/features/pdbsequencefetcher.html @@ -37,7 +37,7 @@

To open the PDB Sequence Fetcher, select PDB as the database from any Sequence Fetcher dialog (opened via - "File →Fetch Sequences"). + "File →Fetch Sequences").

PDB sequence fetcher (introduced in Jalview 2.9) @@ -45,13 +45,18 @@

Searching the PDB Database

+

To search the PDB, begin typing in the text box. If the + 'autosearch' checkbox is enabled, then the results of your query + will be automatically updated and shown in the search results tab; + otherwise, press return to update the results. To access previous + searches, press the down-arrow or click the drop down menu icon at + the side of the search box. If you just want to paste in a list of + IDs, the 'Retrieve IDs' tab provides a batch-retrieval interface.

- To search the PDB, begin typing in the text box. The results of your - query are shown in the search results tab, which updates every time - you type in the search text box. You can sort results according to - the displayed columns, and select entries with the mouse and - keyboard. Once you have selected one or more entries, hit the OK - button to retrieve and view them in Jalview. + You can sort results according to the displayed columns, and select + entries with the mouse and keyboard. Once you have selected one or + more entries, hit the OK button to retrieve and + view them in Jalview.

    @@ -64,9 +69,8 @@ 1xyz:A
  • Bulk PDB retrieval
    Multiple PDB - IDs can be specified by separating them with a semi-colon.
    - e.g. 1xyz;2xyz;3xyz
    Hitting Return or OK will automatically - fetch those IDs, like the default Sequence Fetcher interface.
  • + IDs can be specified for retrieval via the + Retrieve IDs tab.
  • Wild card searching
    The following wild cards are supported by the EMBL-EBI PDBe query service: diff --git a/help/html/features/uniprotseqfetcher.png b/help/html/features/uniprotseqfetcher.png index a592e8e..23b55fa 100644 Binary files a/help/html/features/uniprotseqfetcher.png and b/help/html/features/uniprotseqfetcher.png differ diff --git a/help/html/features/uniprotsequencefetcher.html b/help/html/features/uniprotsequencefetcher.html index edd8995..4a64f52 100644 --- a/help/html/features/uniprotsequencefetcher.html +++ b/help/html/features/uniprotsequencefetcher.html @@ -46,10 +46,13 @@

    Searching the UniProt Database

    -

    - To search UniProt, simply begin typing in the text box. After a - short delay (about 1.5 seconds), results will be shown in the table - below. You can sort results by clicking on the displayed columns, +

    To search UniProt, simply begin typing in the text box. If the + 'autosearch' check box is enabled, then after a short delay (about + 1.5 seconds), results will be shown in the table below. Results are + also updated whenever you press Enter, and you can access previous + searches by pressing the 'Down' arrow or clicking the drop-down menu + icon at the side of the search box.

    +

    You can sort results by clicking on the displayed columns, and select entries with the mouse or keyboard. Once you have selected one or more entries, hit the OK button to retrieve the sequences. @@ -61,11 +64,8 @@

  • Bulk UniProt record retrieval
    To - retrieve several uniprot accessions at once, first select UniProt - ID from the dropdown menu, then paste in the accession IDs as a - semi-colon separated list. (e.g. fila_human; mnt_human; - mnt_mouse).
    Hitting Return or OK will automatically fetch - those IDs, like the default Sequence Fetcher interface.
  • + retrieve sequences for a list of Uniprot accessions, please enter + them via the 'Retrieve IDs' tab.
  • Complex queries with the UniProt query Syntax The text box also allows complex diff --git a/help/html/releases.html b/help/html/releases.html index 92af377..79ef877 100755 --- a/help/html/releases.html +++ b/help/html/releases.html @@ -71,7 +71,7 @@ li:before {
    2.10.3
    - 10/10/2017
    + 17/11/2017
    @@ -95,11 +95,20 @@ li:before {
  • Stop codons are excluded in CDS/Protein view from Ensembl locus cross-references
  • Start/End limits are shown in Pairwise Alignment report
  • +
  • Sequence fetcher's Free text 'autosearch' feature can be disabled
  • +
  • Retrieve IDs tab added for UniProt and PDB easier retrieval of sequences for lists of IDs
  • +
  • Short names for sequences retrieved from Uniprot
  • +
+ Scripting +
    +
  • Groovy interpreter updated to 2.4.12
  • +
  • Example groovy script for generating a matrix of percent identity scores for current alignment.
-
  • Example groovy script for generating a matrix of percent identity scores for current alignment.
Testing and Deployment -
  • Test to catch memory leaks in Jalview UI
- +
    +
  • Test to catch memory leaks in Jalview UI
  • +
+
General @@ -108,6 +117,7 @@ li:before {
  • Race condition when parsing sequence ID strings in parallel
  • Overview windows are also closed when alignment window is closed
  • Export of features doesn't always respect group visibility
  • +
  • Jumping from column 1 to column 100,000 takes a long time in Cursor mode
  • Desktop
      @@ -131,7 +141,12 @@ li:before {
    • Scale mark not shown when close to right hand end of alignment
    • Pairwise alignment only aligns selected regions of each selected sequence
    • Alignment ruler height set incorrectly after canceling the Alignment Window's Font dialog
    • -
    • Show cross-references not enabled after restoring project until a new view is created
    • +
    • Show cross-references not enabled after restoring project until a new view is created
    • +
    • Warning popup about use of SEQUENCE_ID in URL links appears when only default EMBL-EBI link is configured (since 2.10.2b2)
    • +
    • Overview redraws whole window when box position is adjusted
    • +
    • Structure viewer doesn't map all chains in a multi-chain structure when viewing alignment involving more than one chain (since 2.10)
    • +
    • Double residue highlights in cursor mode if new selection moves alignment window
    • +
    • Alignment vanishes when using arrow key in cursor mode to pass hidden column marker
    Applet
      @@ -143,6 +158,17 @@ li:before { BioJSON export does not preserve non-positional features
    + Known Java 9 Issues +
      +
    • Groovy Console very slow to open and is + not responsive when entering characters (Webstart, Java 9.01, + OSX 10.10) +
    • +
    + New Known Issues +
      +
    • +
    diff --git a/help/html/whatsNew.html b/help/html/whatsNew.html index 3475012..d92f26b 100755 --- a/help/html/whatsNew.html +++ b/help/html/whatsNew.html @@ -27,11 +27,19 @@ What's new in Jalview 2.10.3 ?

    - Version 2.10.3 is due for release in October 2017. The full list of + Version 2.10.3 was released in November 2017. The full list of bug fixes and new features can be found in the 2.10.3 Release Notes, but the highlights are below.

    +
      +
    • Faster import and more responsive UI when working with wide alignments and handling hundreds and thousands of sequence features
    • +
    • +
    • Improved usability with PDB and + UniProt Free Text Search + dialog, and new tab for retrieval of sequences for lists of IDs.
    • +
    • Short names assigned to sequences retrieved from UniProt
    • +

    Experimental Features

    diff --git a/lib/groovy-all-2.4.6-indy.jar b/lib/groovy-all-2.4.12-indy.jar similarity index 62% rename from lib/groovy-all-2.4.6-indy.jar rename to lib/groovy-all-2.4.12-indy.jar index 5f3d51c..bb246a3 100644 Binary files a/lib/groovy-all-2.4.6-indy.jar and b/lib/groovy-all-2.4.12-indy.jar differ diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 5d9bdff..94f7eff 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1319,3 +1319,6 @@ label.select_hidden_colour = Select hidden colour label.overview = Overview label.reset_to_defaults = Reset to defaults label.oview_calc = Recalculating overview... +option.enable_disable_autosearch = When ticked, search is performed automatically. +option.autosearch = Autosearch +label.retrieve_ids = Retrieve IDs \ No newline at end of file diff --git a/src/jalview/appletgui/AnnotationPanel.java b/src/jalview/appletgui/AnnotationPanel.java index c06f7b1..50a9e33 100755 --- a/src/jalview/appletgui/AnnotationPanel.java +++ b/src/jalview/appletgui/AnnotationPanel.java @@ -778,5 +778,14 @@ public class AnnotationPanel extends Panel { 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(); + } } } diff --git a/src/jalview/appletgui/IdCanvas.java b/src/jalview/appletgui/IdCanvas.java index 5eddc4f..f5ea12e 100755 --- a/src/jalview/appletgui/IdCanvas.java +++ b/src/jalview/appletgui/IdCanvas.java @@ -448,5 +448,14 @@ public class IdCanvas extends Panel implements ViewportListenerI { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } + else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ)) + { + fastPaint(((int[]) evt.getNewValue())[1] + - ((int[]) evt.getOldValue())[1]); + } + else if (propertyName.equals(ViewportRanges.MOVE_VIEWPORT)) + { + repaint(); + } } } diff --git a/src/jalview/appletgui/ScalePanel.java b/src/jalview/appletgui/ScalePanel.java index 7d4150d..04fb22b 100755 --- a/src/jalview/appletgui/ScalePanel.java +++ b/src/jalview/appletgui/ScalePanel.java @@ -468,7 +468,9 @@ public class ScalePanel extends Panel // 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)) + if (evt.getPropertyName().equals(ViewportRanges.STARTRES) + || evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ) + || evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT)) { // scroll event, repaint panel repaint(); diff --git a/src/jalview/appletgui/SeqCanvas.java b/src/jalview/appletgui/SeqCanvas.java index f59967d..2420cf7 100755 --- a/src/jalview/appletgui/SeqCanvas.java +++ b/src/jalview/appletgui/SeqCanvas.java @@ -889,15 +889,37 @@ public class SeqCanvas extends Panel implements ViewportListenerI { String eventName = evt.getPropertyName(); + if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED)) + { + fastPaint = true; + repaint(); + return; + } + else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT)) + { + fastPaint = false; + repaint(); + return; + } + if (!av.getWrapAlignment()) { int scrollX = 0; - if (eventName.equals(ViewportRanges.STARTRES)) + if (eventName.equals(ViewportRanges.STARTRES) + || eventName.equals(ViewportRanges.STARTRESANDSEQ)) { // Make sure we're not trying to draw a panel // larger than the visible window + if (eventName.equals(ViewportRanges.STARTRES)) + { + scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); + } + else + { + scrollX = ((int[]) evt.getNewValue())[0] + - ((int[]) evt.getOldValue())[0]; + } ViewportRanges vpRanges = av.getRanges(); - scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); int range = vpRanges.getEndRes() - vpRanges.getStartRes(); if (scrollX > range) { @@ -924,6 +946,10 @@ public class SeqCanvas extends Panel implements ViewportListenerI // scroll fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); } + else if (eventName.equals(ViewportRanges.STARTRESANDSEQ)) + { + fastPaint(scrollX, 0); + } } } diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index 9a61f5f..d74bbb7 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -43,7 +43,6 @@ import jalview.util.Comparison; import jalview.util.MappingUtils; import jalview.util.MessageManager; import jalview.viewmodel.AlignmentViewport; -import jalview.viewmodel.ViewportRanges; import java.awt.BorderLayout; import java.awt.Font; @@ -148,13 +147,13 @@ public class SeqPanel extends Panel implements MouseMotionListener, void setCursorRow() { seqCanvas.cursorY = getKeyboardNo1() - 1; - scrollToVisible(); + scrollToVisible(true); } void setCursorColumn() { seqCanvas.cursorX = getKeyboardNo1() - 1; - scrollToVisible(); + scrollToVisible(true); } void setCursorRowAndColumn() @@ -167,7 +166,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, { seqCanvas.cursorX = getKeyboardNo1() - 1; seqCanvas.cursorY = getKeyboardNo2() - 1; - scrollToVisible(); + scrollToVisible(true); } } @@ -176,7 +175,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY); seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1; - scrollToVisible(); + scrollToVisible(true); } void moveCursor(int dx, int dy) @@ -202,10 +201,16 @@ public class SeqPanel extends Panel implements MouseMotionListener, seqCanvas.cursorX = original; } } - scrollToVisible(); + scrollToVisible(false); } - void scrollToVisible() + /** + * Scroll to make the cursor visible in the viewport. + * + * @param jump + * just jump to the location rather than scrolling + */ + void scrollToVisible(boolean jump) { if (seqCanvas.cursorX < 0) { @@ -226,44 +231,34 @@ public class SeqPanel extends Panel implements MouseMotionListener, } endEditing(); - if (av.getWrapAlignment()) + + boolean repaintNeeded = true; + if (jump) { - av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX); + // only need to repaint if the viewport did not move, as otherwise it will + // get a repaint + repaintNeeded = !av.getRanges().setViewportLocation(seqCanvas.cursorX, + seqCanvas.cursorY); } else { - ViewportRanges ranges = av.getRanges(); - HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - while (seqCanvas.cursorY < ranges.getStartSeq()) + if (av.getWrapAlignment()) { - ranges.scrollUp(true); + av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX); } - while (seqCanvas.cursorY > ranges.getEndSeq()) - { - ranges.scrollUp(false); - } - while (seqCanvas.cursorX < hidden - .adjustForHiddenColumns(ranges.getStartRes())) - { - - if (!ranges.scrollRight(false)) - { - break; - } - } - while (seqCanvas.cursorX > hidden - .adjustForHiddenColumns(ranges.getEndRes())) + else { - if (!ranges.scrollRight(true)) - { - break; - } + av.getRanges().scrollToVisible(seqCanvas.cursorX, + seqCanvas.cursorY); } } setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY), seqCanvas.cursorX, seqCanvas.cursorY); - seqCanvas.repaint(); + if (repaintNeeded) + { + seqCanvas.repaint(); + } } void setSelectionAreaAtCursor(boolean topLeft) diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index 8c5e4ac..f268d37 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -28,6 +28,7 @@ import jalview.util.LinkedIdentityHashSet; import jalview.util.MessageManager; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; @@ -1617,40 +1618,21 @@ public class Alignment implements AlignmentI @Override public Iterable findAnnotation(String calcId) { - List aa = new ArrayList<>(); AlignmentAnnotation[] alignmentAnnotation = getAlignmentAnnotation(); if (alignmentAnnotation != null) { - for (AlignmentAnnotation a : alignmentAnnotation) - { - if (a.getCalcId() == calcId || (a.getCalcId() != null - && calcId != null && a.getCalcId().equals(calcId))) - { - aa.add(a); - } - } + return AlignmentAnnotation.findAnnotation( + Arrays.asList(getAlignmentAnnotation()), calcId); } - return aa; + return Arrays.asList(new AlignmentAnnotation[] {}); } @Override public Iterable findAnnotations(SequenceI seq, String calcId, String label) { - ArrayList aa = new ArrayList<>(); - for (AlignmentAnnotation ann : getAlignmentAnnotation()) - { - if ((calcId == null || (ann.getCalcId() != null - && ann.getCalcId().equals(calcId))) - && (seq == null || (ann.sequenceRef != null - && ann.sequenceRef == seq)) - && (label == null - || (ann.label != null && ann.label.equals(label)))) - { - aa.add(ann); - } - } - return aa; + return AlignmentAnnotation.findAnnotations( + Arrays.asList(getAlignmentAnnotation()), seq, calcId, label); } @Override diff --git a/src/jalview/datamodel/AlignmentAnnotation.java b/src/jalview/datamodel/AlignmentAnnotation.java index 09facbf..f7bf4d8 100755 --- a/src/jalview/datamodel/AlignmentAnnotation.java +++ b/src/jalview/datamodel/AlignmentAnnotation.java @@ -24,6 +24,7 @@ import jalview.analysis.Rna; import jalview.analysis.SecStrConsensus.SimpleBP; import jalview.analysis.WUSSParseException; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -1471,4 +1472,71 @@ public class AlignmentAnnotation { return graphMin < graphMax; } + + public static Iterable findAnnotations( + Iterable list, SequenceI seq, String calcId, + String label) + { + + ArrayList aa = new ArrayList<>(); + for (AlignmentAnnotation ann : list) + { + if ((calcId == null || (ann.getCalcId() != null + && ann.getCalcId().equals(calcId))) + && (seq == null || (ann.sequenceRef != null + && ann.sequenceRef == seq)) + && (label == null + || (ann.label != null && ann.label.equals(label)))) + { + aa.add(ann); + } + } + return aa; + } + + /** + * Answer true if any annotation matches the calcId passed in (if not null). + * + * @param list + * annotation to search + * @param calcId + * @return + */ + public static boolean hasAnnotation(List list, + String calcId) + { + + if (calcId != null && !"".equals(calcId)) + { + for (AlignmentAnnotation a : list) + { + if (a.getCalcId() == calcId) + { + return true; + } + } + } + return false; + } + + public static Iterable findAnnotation( + List list, String calcId) + { + + List aa = new ArrayList<>(); + if (calcId == null) + { + return aa; + } + for (AlignmentAnnotation a : list) + { + + if (a.getCalcId() == calcId || (a.getCalcId() != null + && calcId != null && a.getCalcId().equals(calcId))) + { + aa.add(a); + } + } + return aa; + } } diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index 96b0757..15d1378 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -77,11 +77,6 @@ public class Sequence extends ASequence implements SequenceI */ Vector annotation; - /** - * The index of the sequence in a MSA - */ - int index = -1; - private SequenceFeaturesI sequenceFeatureStore; /* @@ -1165,7 +1160,7 @@ public class Sequence extends ASequence implements SequenceI } @Override - public void deleteChars(int i, int j) + public void deleteChars(final int i, final int j) { int newstart = start, newend = end; if (i >= sequence.length || i < 0) @@ -1177,62 +1172,75 @@ public class Sequence extends ASequence implements SequenceI boolean createNewDs = false; // TODO: take a (second look) at the dataset creation validation method for // the very large sequence case - int eindex = -1, sindex = -1; - boolean ecalc = false, scalc = false; + int startIndex = findIndex(start) - 1; + int endIndex = findIndex(end) - 1; + int startDeleteColumn = -1; // for dataset sequence deletions + int deleteCount = 0; + for (int s = i; s < j; s++) { - if (jalview.schemes.ResidueProperties.aaIndex[sequence[s]] != 23) + if (Comparison.isGap(sequence[s])) + { + continue; + } + deleteCount++; + if (startDeleteColumn == -1) + { + startDeleteColumn = findPosition(s) - start; + } + if (createNewDs) + { + newend--; + } + else { - if (createNewDs) + if (startIndex == s) { - newend--; + /* + * deleting characters from start of sequence; new start is the + * sequence position of the next column (position to the right + * if the column position is gapped) + */ + newstart = findPosition(j); + break; } else { - if (!scalc) + if (endIndex < j) { - sindex = findIndex(start) - 1; - scalc = true; - } - if (sindex == s) - { - // delete characters including start of sequence - newstart = findPosition(j); - break; // don't need to search for any more residue characters. + /* + * deleting characters at end of sequence; new end is the sequence + * position of the column before the deletion; subtract 1 if this is + * gapped since findPosition returns the next sequence position + */ + newend = findPosition(i - 1); + if (Comparison.isGap(sequence[i - 1])) + { + newend--; + } + break; } else { - // delete characters after start. - if (!ecalc) - { - eindex = findIndex(end) - 1; - ecalc = true; - } - if (eindex < j) - { - // delete characters at end of sequence - newend = findPosition(i - 1); - break; // don't need to search for any more residue characters. - } - else - { - createNewDs = true; - newend--; // decrease end position by one for the deleted residue - // and search further - } + createNewDs = true; + newend--; } } } } - // deletion occured in the middle of the sequence + if (createNewDs && this.datasetSequence != null) { - // construct a new sequence + /* + * if deletion occured in the middle of the sequence, + * construct a new dataset sequence and delete the residues + * that were deleted from the aligned sequence + */ Sequence ds = new Sequence(datasetSequence); + ds.deleteChars(startDeleteColumn, startDeleteColumn + deleteCount); + datasetSequence = ds; // TODO: remove any non-inheritable properties ? // TODO: create a sequence mapping (since there is a relation here ?) - ds.deleteChars(i, j); - datasetSequence = ds; } start = newstart; end = newend; @@ -1682,30 +1690,6 @@ public class Sequence extends ASequence implements SequenceI } } - /** - * @return The index (zero-based) on this sequence in the MSA. It returns - * {@code -1} if this information is not available. - */ - @Override - public int getIndex() - { - return index; - } - - /** - * Defines the position of this sequence in the MSA. Use the value {@code -1} - * if this information is undefined. - * - * @param The - * position for this sequence. This value is zero-based (zero for - * this first sequence) - */ - @Override - public void setIndex(int value) - { - index = value; - } - @Override public void setRNA(RNA r) { diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java index e2f15e1..6b797d7 100755 --- a/src/jalview/datamodel/SequenceGroup.java +++ b/src/jalview/datamodel/SequenceGroup.java @@ -30,6 +30,7 @@ import java.awt.Color; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -1316,39 +1317,16 @@ public class SequenceGroup implements AnnotatedCollectionI @Override public Iterable findAnnotation(String calcId) { - List aa = new ArrayList<>(); - if (calcId == null) - { - return aa; - } - for (AlignmentAnnotation a : getAlignmentAnnotation()) - { - if (calcId.equals(a.getCalcId())) - { - aa.add(a); - } - } - return aa; + return AlignmentAnnotation.findAnnotation( + Arrays.asList(getAlignmentAnnotation()), calcId); } @Override public Iterable findAnnotations(SequenceI seq, String calcId, String label) { - ArrayList aa = new ArrayList<>(); - for (AlignmentAnnotation ann : getAlignmentAnnotation()) - { - if ((calcId == null || (ann.getCalcId() != null - && ann.getCalcId().equals(calcId))) - && (seq == null || (ann.sequenceRef != null - && ann.sequenceRef == seq)) - && (label == null - || (ann.label != null && ann.label.equals(label)))) - { - aa.add(ann); - } - } - return aa; + return AlignmentAnnotation.findAnnotations( + Arrays.asList(getAlignmentAnnotation()), seq, calcId, label); } /** @@ -1359,17 +1337,8 @@ public class SequenceGroup implements AnnotatedCollectionI */ public boolean hasAnnotation(String calcId) { - if (calcId != null && !"".equals(calcId)) - { - for (AlignmentAnnotation a : getAlignmentAnnotation()) - { - if (a.getCalcId() == calcId) - { - return true; - } - } - } - return false; + return AlignmentAnnotation + .hasAnnotation(Arrays.asList(getAlignmentAnnotation()), calcId); } /** diff --git a/src/jalview/datamodel/SequenceI.java b/src/jalview/datamodel/SequenceI.java index 6e6d1aa..2f3e925 100755 --- a/src/jalview/datamodel/SequenceI.java +++ b/src/jalview/datamodel/SequenceI.java @@ -192,13 +192,14 @@ public interface SequenceI extends ASequenceI public int findIndex(int pos); /** - * Returns the sequence position for an alignment position. + * Returns the sequence position for an alignment (column) position. If at a + * gap, returns the position of the next residue to the right. If beyond the + * end of the sequence, returns 1 more than the last residue position. * * @param i * column index in alignment (from 0.. cmb_searchTarget = new JComboBox(); @@ -98,6 +100,8 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI protected JButton btn_cancel = new JButton(); + protected JCheckBox btn_autosearch = new JCheckBox(); + protected JvCacheableInputBox txt_search; protected SequenceFetcher seqFetcher; @@ -239,16 +243,38 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI public GFTSPanel() { + this(null); + } + + public GFTSPanel(SequenceFetcher fetcher) + { try { + if (fetcher == null) + { + tabs = null; + } jbInit(); + if (fetcher != null) + { + tabs.addTab(MessageManager.getString("label.retrieve_ids"), + fetcher); + fetcher.setDatabaseChooserVisible(false); + fetcher.embedWithFTSPanel(this); + } mainFrame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); + final JPanel ftsPanel = this; mainFrame.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { - txt_search.requestFocusInWindow(); + // TODO: make selected tab gain focus in correct widget + if (tabs != null + && tabs.getSelectedComponent() == ftsPanel) + { + txt_search.requestFocusInWindow(); + } } }); mainFrame.invalidate(); @@ -331,6 +357,20 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } }); + btn_autosearch.setText(MessageManager.getString("option.autosearch")); + btn_autosearch.setToolTipText( + MessageManager.getString("option.enable_disable_autosearch")); + btn_autosearch.setSelected( + jalview.bin.Cache.getDefault(getAutosearchPreference(), true)); + btn_autosearch.addActionListener(new java.awt.event.ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + jalview.bin.Cache.setProperty(getAutosearchPreference(), + Boolean.toString(btn_autosearch.isSelected())); + } + }); btn_back.setFont(new java.awt.Font("Verdana", 0, 12)); btn_back.setText(MessageManager.getString("action.back")); btn_back.addActionListener(new java.awt.event.ActionListener() @@ -511,8 +551,14 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI if (primaryKeyName.equalsIgnoreCase(getCmbSearchTarget() .getSelectedItem().toString())) { + // TODO: nicer to show the list in the result set before + // viewing in Jalview perhaps ? transferToSequenceFetcher(getTypedText()); } + else + { + performSearchAction(); + } } } }); @@ -522,12 +568,10 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI @Override public void actionPerformed(ActionEvent e) { - String typed = getTypedText(); - if (!typed.equalsIgnoreCase(lastSearchTerm)) + if (btn_autosearch.isSelected() + || txt_search.wasEnterPressed()) { - searchAction(true); - paginatorCart.clear(); - lastSearchTerm = typed; + performSearchAction(); } } }, false); @@ -549,6 +593,15 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } }); + txt_search.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + performSearchAction(); + } + }); final String searchTabTitle = MessageManager .getString("label.search_result"); final String configureCols = MessageManager @@ -613,6 +666,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI pnl_results.add(tabbedPane); pnl_inputs.add(cmb_searchTarget); pnl_inputs.add(txt_search); + pnl_inputs.add(btn_autosearch); pnl_inputs.add(lbl_loading); pnl_inputs.add(lbl_warning); pnl_inputs.add(lbl_blank); @@ -624,7 +678,17 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI this.add(pnl_results, java.awt.BorderLayout.CENTER); this.add(pnl_actions, java.awt.BorderLayout.SOUTH); mainFrame.setVisible(true); - mainFrame.setContentPane(this); + if (tabs != null) + { + tabs.setOpaque(true); + tabs.insertTab("Free Text Search", null, this, "", 0); + mainFrame.setContentPane(tabs); + tabs.setVisible(true); + } + else + { + mainFrame.setContentPane(this); + } mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); mainFrame.addInternalFrameListener( new javax.swing.event.InternalFrameAdapter() @@ -635,8 +699,6 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI closeAction(); } }); - mainFrame.setVisible(true); - mainFrame.setContentPane(this); mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); Integer x = getTempUserPrefs().get("FTSPanel.x"); Integer y = getTempUserPrefs().get("FTSPanel.y"); @@ -698,6 +760,18 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } + void performSearchAction() + { + String typed = getTypedText(); + if (typed != null && typed.length() > 0 + && !typed.equalsIgnoreCase(lastSearchTerm)) + { + searchAction(true); + paginatorCart.clear(); + lastSearchTerm = typed; + } + } + public boolean wantedFieldsUpdated() { if (previousWantedFields == null) @@ -774,7 +848,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } } - protected void btn_back_ActionPerformed() + public void btn_back_ActionPerformed() { closeAction(); new SequenceFetcher(progressIndicator); @@ -787,7 +861,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI btn_cancel.setEnabled(false); } - protected void btn_cancel_ActionPerformed() + public void btn_cancel_ActionPerformed() { closeAction(); } diff --git a/src/jalview/fts/service/pdb/PDBFTSPanel.java b/src/jalview/fts/service/pdb/PDBFTSPanel.java index 19f7db4..053d91b 100644 --- a/src/jalview/fts/service/pdb/PDBFTSPanel.java +++ b/src/jalview/fts/service/pdb/PDBFTSPanel.java @@ -43,9 +43,11 @@ public class PDBFTSPanel extends GFTSPanel private static final String PDB_FTS_CACHE_KEY = "CACHE.PDB_FTS"; + private static final String PDB_AUTOSEARCH = "FTS.PDB.AUTOSEARCH"; + public PDBFTSPanel(SequenceFetcher fetcher) { - super(); + super(fetcher); pageLimit = PDBFTSRestClient.getInstance().getDefaultResponsePageSize(); this.seqFetcher = fetcher; this.progressIndicator = (fetcher == null) ? null @@ -281,4 +283,9 @@ public class PDBFTSPanel extends GFTSPanel return PDB_FTS_CACHE_KEY; } -} + @Override + public String getAutosearchPreference() + { + return PDB_AUTOSEARCH; + } +} \ No newline at end of file diff --git a/src/jalview/fts/service/uniprot/UniprotFTSPanel.java b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java index 020331a..df54dea 100644 --- a/src/jalview/fts/service/uniprot/UniprotFTSPanel.java +++ b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java @@ -44,9 +44,11 @@ public class UniprotFTSPanel extends GFTSPanel private static final String UNIPROT_FTS_CACHE_KEY = "CACHE.UNIPROT_FTS"; + private static final String UNIPROT_AUTOSEARCH = "FTS.UNIPROT.AUTOSEARCH"; + public UniprotFTSPanel(SequenceFetcher fetcher) { - super(); + super(fetcher); pageLimit = UniProtFTSRestClient.getInstance() .getDefaultResponsePageSize(); this.seqFetcher = fetcher; @@ -237,4 +239,10 @@ public class UniprotFTSPanel extends GFTSPanel { return UNIPROT_FTS_CACHE_KEY; } + + @Override + public String getAutosearchPreference() + { + return UNIPROT_AUTOSEARCH; + } } diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 90271c8..4d09084 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -583,58 +583,6 @@ public class AlignViewport extends AlignmentViewport .getStructureSelectionManager(Desktop.instance); } - /** - * - * @param pdbEntries - * @return an array of SequenceI arrays, one for each PDBEntry, listing which - * sequences in the alignment hold a reference to it - */ - public SequenceI[][] collateForPDB(PDBEntry[] pdbEntries) - { - List seqvectors = new ArrayList(); - for (PDBEntry pdb : pdbEntries) - { - List choosenSeqs = new ArrayList(); - for (SequenceI sq : alignment.getSequences()) - { - Vector pdbRefEntries = sq.getDatasetSequence() - .getAllPDBEntries(); - if (pdbRefEntries == null) - { - continue; - } - for (PDBEntry pdbRefEntry : pdbRefEntries) - { - if (pdbRefEntry.getId().equals(pdb.getId())) - { - if (pdbRefEntry.getChainCode() != null - && pdb.getChainCode() != null) - { - if (pdbRefEntry.getChainCode().equalsIgnoreCase( - pdb.getChainCode()) && !choosenSeqs.contains(sq)) - { - choosenSeqs.add(sq); - continue; - } - } - else - { - if (!choosenSeqs.contains(sq)) - { - choosenSeqs.add(sq); - continue; - } - } - - } - } - } - seqvectors - .add(choosenSeqs.toArray(new SequenceI[choosenSeqs.size()])); - } - return seqvectors.toArray(new SequenceI[seqvectors.size()][]); - } - @Override public boolean isNormaliseSequenceLogo() { diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java index 12d1369..438e81b 100755 --- a/src/jalview/gui/AnnotationPanel.java +++ b/src/jalview/gui/AnnotationPanel.java @@ -1185,5 +1185,14 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, { 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(); + } } } diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index 2d1ba12..128481c 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -2338,7 +2338,7 @@ public class Desktop extends jalview.jbgui.GDesktop { String link = li.next(); if (link.contains(SEQUENCE_ID) - && !link.equals(UrlConstants.DEFAULT_STRING)) + && !UrlConstants.isDefaultString(link)) { check = true; int barPos = link.indexOf("|"); diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java index a7dff86..085b259 100755 --- a/src/jalview/gui/IdCanvas.java +++ b/src/jalview/gui/IdCanvas.java @@ -564,5 +564,14 @@ public class IdCanvas extends JPanel implements ViewportListenerI { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } + else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ)) + { + fastPaint(((int[]) evt.getNewValue())[1] + - ((int[]) evt.getOldValue())[1]); + } + else if (propertyName.equals(ViewportRanges.MOVE_VIEWPORT)) + { + repaint(); + } } } diff --git a/src/jalview/gui/OverviewCanvas.java b/src/jalview/gui/OverviewCanvas.java index 7371eb5..2991889 100644 --- a/src/jalview/gui/OverviewCanvas.java +++ b/src/jalview/gui/OverviewCanvas.java @@ -183,6 +183,8 @@ public class OverviewCanvas extends JComponent @Override public void paintComponent(Graphics g) { + // super.paintComponent(g); + if (restart) { if (lastMiniMe == null) @@ -204,7 +206,8 @@ public class OverviewCanvas extends JComponent && ((getWidth() != od.getWidth()) || (getHeight() != od.getHeight()))) { - // if there is annotation, scale the alignment and annotation separately + // if there is annotation, scale the alignment and annotation + // separately if (od.getGraphHeight() > 0) { BufferedImage topImage = lastMiniMe.getSubimage(0, 0, @@ -235,25 +238,24 @@ public class OverviewCanvas extends JComponent od.setHeight(getHeight()); } - // scale lastMiniMe to the new size - g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); - // make sure the box is in the right place od.setBoxPosition(av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); } - else // not a resize - { - // fall back to normal behaviour - g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); - } + // fall back to normal behaviour + g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); } - + else + { + g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); + } + // draw the box g.setColor(Color.red); od.drawBox(g); } + public void dispose() { dispose = true; diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java index 9ddb751..43b4310 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -329,6 +329,22 @@ public class OverviewPanel extends JPanel * changed * */ + private void setBoxPositionOnly() + { + if (od != null) + { + int oldX = od.getBoxX(); + int oldY = od.getBoxY(); + int oldWidth = od.getBoxWidth(); + int oldHeight = od.getBoxHeight(); + od.setBoxPosition(av.getAlignment().getHiddenSequences(), + av.getAlignment().getHiddenColumns()); + repaint(oldX - 1, oldY - 1, oldWidth + 2, oldHeight + 2); + repaint(od.getBoxX(), od.getBoxY(), od.getBoxWidth(), + od.getBoxHeight()); + } + } + private void setBoxPosition() { if (od != null) @@ -342,7 +358,7 @@ public class OverviewPanel extends JPanel @Override public void propertyChange(PropertyChangeEvent evt) { - setBoxPosition(); + setBoxPositionOnly(); } /** diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java index e677769..798c833 100755 --- a/src/jalview/gui/ScalePanel.java +++ b/src/jalview/gui/ScalePanel.java @@ -549,7 +549,9 @@ public class ScalePanel extends JPanel // 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)) + if (evt.getPropertyName().equals(ViewportRanges.STARTRES) + || evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ) + || evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT)) { // scroll event, repaint panel repaint(); diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 881afe5..cf1375e 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -296,47 +296,48 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int transX = 0; int transY = 0; - gg.copyArea(horizontal * charWidth, vertical * charHeight, - img.getWidth(), img.getHeight(), -horizontal * charWidth, - -vertical * charHeight); + gg.copyArea(horizontal * charWidth, vertical * charHeight, + img.getWidth(), img.getHeight(), -horizontal * charWidth, + -vertical * charHeight); - if (horizontal > 0) // scrollbar pulled right, image to the left - { - transX = (endRes - startRes - horizontal) * charWidth; - startRes = endRes - horizontal; - } - else if (horizontal < 0) - { - endRes = startRes - horizontal; - } - else if (vertical > 0) // scroll down - { - startSeq = endSeq - vertical; - - if (startSeq < ranges.getStartSeq()) - { // ie scrolling too fast, more than a page at a time - startSeq = ranges.getStartSeq(); + if (horizontal > 0) // scrollbar pulled right, image to the left + { + transX = (endRes - startRes - horizontal) * charWidth; + startRes = endRes - horizontal; } - else + else if (horizontal < 0) { - transY = img.getHeight() - ((vertical + 1) * charHeight); + endRes = startRes - horizontal; } - } - else if (vertical < 0) - { - endSeq = startSeq - vertical; - if (endSeq > ranges.getEndSeq()) + if (vertical > 0) // scroll down { - endSeq = ranges.getEndSeq(); + startSeq = endSeq - vertical; + + if (startSeq < ranges.getStartSeq()) + { // ie scrolling too fast, more than a page at a time + startSeq = ranges.getStartSeq(); + } + else + { + transY = img.getHeight() - ((vertical + 1) * charHeight); + } + } + else if (vertical < 0) + { + endSeq = startSeq - vertical; + + if (endSeq > ranges.getEndSeq()) + { + endSeq = ranges.getEndSeq(); + } } - } - gg.translate(transX, transY); - drawPanel(gg, startRes, endRes, startSeq, endSeq, 0); - gg.translate(-transX, -transY); + gg.translate(transX, transY); + drawPanel(gg, startRes, endRes, startSeq, endSeq, 0); + gg.translate(-transX, -transY); - repaint(); + repaint(); } finally { fastpainting = false; @@ -410,6 +411,13 @@ public class SeqCanvas extends JComponent implements ViewportListenerI // lcimg is a local *copy* of img which we'll draw selectImage on top of BufferedImage lcimg = buildLocalImage(selectImage); g.drawImage(lcimg, 0, 0, this); + + } + + if (av.cursorMode) + { + drawCursor(g, ranges.getStartRes(), ranges.getEndRes(), + ranges.getStartSeq(), ranges.getEndSeq()); } } @@ -508,6 +516,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); g2d.drawImage(selectImage, 0, 0, this); } + g2d.dispose(); return lcimg; @@ -912,9 +921,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int canvasWidth, int canvasHeight, int startRes) { - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); - + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + // height gap above each panel int hgap = charHeight; if (av.getScaleAboveWrapped()) @@ -1141,13 +1150,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } } } - - if (av.cursorMode && cursorY == i && cursorX >= startRes - && cursorX <= endRes) - { - seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth, - offset + ((i - startSeq) * charHeight)); - } } if (av.getSelectionGroup() != null @@ -1245,6 +1247,94 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return selectionImage; } + /** + * Draw the cursor as a separate image and overlay + * + * @param startRes + * start residue of area to draw cursor in + * @param endRes + * end residue of area to draw cursor in + * @param startSeq + * start sequence of area to draw cursor in + * @param endSeq + * end sequence of are to draw cursor in + * @return a transparent image of the same size as the sequence canvas, with + * the cursor drawn on it, if any + */ + private void drawCursor(Graphics g, int startRes, int endRes, + int startSeq, + int endSeq) + { + // convert the cursorY into a position on the visible alignment + int cursor_ypos = cursorY; + + // don't do work unless we have to + if (cursor_ypos >= startSeq && cursor_ypos <= endSeq) + { + int yoffset = 0; + int xoffset = 0; + int startx = startRes; + int endx = endRes; + + // convert the cursorX into a position on the visible alignment + int cursor_xpos = av.getAlignment().getHiddenColumns() + .findColumnPosition(cursorX); + + if (av.getAlignment().getHiddenColumns().isVisible(cursorX)) + { + + if (av.getWrapAlignment()) + { + // work out the correct offsets for the cursor + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + int canvasWidth = getWidth(); + int canvasHeight = getHeight(); + + // height gap above each panel + int hgap = charHeight; + if (av.getScaleAboveWrapped()) + { + hgap += charHeight; + } + + int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) + / charWidth; + int cHeight = av.getAlignment().getHeight() * charHeight; + + endx = startx + cWidth - 1; + int ypos = hgap; // vertical offset + + // iterate down the wrapped panels + while ((ypos <= canvasHeight) && (endx < cursor_xpos)) + { + // update vertical offset + ypos += cHeight + getAnnotationHeight() + hgap; + + // update horizontal offset + startx += cWidth; + endx = startx + cWidth - 1; + } + yoffset = ypos; + xoffset = labelWidthWest; + } + + // now check if cursor is within range for x values + if (cursor_xpos >= startx && cursor_xpos <= endx) + { + // get the character the cursor is drawn at + SequenceI seq = av.getAlignment().getSequenceAt(cursorY); + char s = seq.getCharAt(cursorX); + + seqRdr.drawCursor(g, s, + xoffset + (cursor_xpos - startx) * av.getCharWidth(), + yoffset + (cursor_ypos - startSeq) * av.getCharHeight()); + } + } + } + } + + /* * Set up graphics for selection group */ @@ -1276,8 +1366,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group, int startRes, int endRes, int startSeq, int endSeq, int offset) { - int charWidth = av.getCharWidth(); - + int charWidth = av.getCharWidth(); + if (!av.hasHiddenColumns()) { drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq, @@ -1660,15 +1750,31 @@ public class SeqCanvas extends JComponent implements ViewportListenerI repaint(); return; } + else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT)) + { + fastPaint = false; + repaint(); + return; + } int scrollX = 0; - if (eventName.equals(ViewportRanges.STARTRES)) + if (eventName.equals(ViewportRanges.STARTRES) + || eventName.equals(ViewportRanges.STARTRESANDSEQ)) { // Make sure we're not trying to draw a panel // larger than the visible window + if (eventName.equals(ViewportRanges.STARTRES)) + { + scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); + } + else + { + scrollX = ((int[]) evt.getNewValue())[0] + - ((int[]) evt.getOldValue())[0]; + } ViewportRanges vpRanges = av.getRanges(); - scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); - int range = vpRanges.getViewportWidth(); + + int range = vpRanges.getEndRes() - vpRanges.getStartRes(); if (scrollX > range) { scrollX = range; @@ -1677,31 +1783,44 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { scrollX = -range; } - } - - // 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. - // scroll - startres and endres both change - if (eventName.equals(ViewportRanges.STARTRES)) - { - if (av.getWrapAlignment()) + // 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 (eventName.equals(ViewportRanges.STARTRES)) + { + if (av.getWrapAlignment()) + { + fastPaintWrapped(scrollX); + } + else + { + fastPaint(scrollX, 0); + } + } + else if (eventName.equals(ViewportRanges.STARTSEQ)) { - fastPaintWrapped(scrollX); + // scroll + fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); } - else + else if (eventName.equals(ViewportRanges.STARTRESANDSEQ)) { - fastPaint(scrollX, 0); + if (av.getWrapAlignment()) + { + fastPaintWrapped(scrollX); + } + else + { + fastPaint(scrollX, 0); + } + // bizarrely, we only need to scroll on the x value here as fastpaint + // copies the full height of the image anyway. Passing in the y value + // causes nasty repaint artefacts, which only disappear on a full + // repaint. } } - else if (eventName.equals(ViewportRanges.STARTSEQ)) - { - // scroll - fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); - } } /** @@ -1717,7 +1836,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { ViewportRanges ranges = av.getRanges(); - if (Math.abs(scrollX) > ranges.getViewportWidth()) + // if (Math.abs(scrollX) > ranges.getViewportWidth()) + // JAL-2836, 2836 temporarily removed wrapped fastpaint for release 2.10.3 + if (true) { /* * shift of more than one view width is diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 2223ee5..2cdb1d8 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -320,13 +320,13 @@ public class SeqPanel extends JPanel void setCursorRow() { seqCanvas.cursorY = getKeyboardNo1() - 1; - scrollToVisible(); + scrollToVisible(true); } void setCursorColumn() { seqCanvas.cursorX = getKeyboardNo1() - 1; - scrollToVisible(); + scrollToVisible(true); } void setCursorRowAndColumn() @@ -339,7 +339,7 @@ public class SeqPanel extends JPanel { seqCanvas.cursorX = getKeyboardNo1() - 1; seqCanvas.cursorY = getKeyboardNo2() - 1; - scrollToVisible(); + scrollToVisible(true); } } @@ -348,7 +348,7 @@ public class SeqPanel extends JPanel SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY); seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1; - scrollToVisible(); + scrollToVisible(true); } void moveCursor(int dx, int dy) @@ -376,10 +376,16 @@ public class SeqPanel extends JPanel } } - scrollToVisible(); + scrollToVisible(false); } - void scrollToVisible() + /** + * Scroll to make the cursor visible in the viewport. + * + * @param jump + * just jump to the location rather than scrolling + */ + void scrollToVisible(boolean jump) { if (seqCanvas.cursorX < 0) { @@ -400,20 +406,44 @@ public class SeqPanel extends JPanel } endEditing(); - if (av.getWrapAlignment()) + + boolean repaintNeeded = true; + if (jump) { - av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX); + // only need to repaint if the viewport did not move, as otherwise it will + // get a repaint + repaintNeeded = !av.getRanges().setViewportLocation(seqCanvas.cursorX, + seqCanvas.cursorY); } else { - av.getRanges().scrollToVisible(seqCanvas.cursorX, seqCanvas.cursorY); + if (av.getWrapAlignment()) + { + // scrollToWrappedVisible expects x-value to have hidden cols subtracted + int x = av.getAlignment().getHiddenColumns() + .findColumnPosition(seqCanvas.cursorX); + av.getRanges().scrollToWrappedVisible(x); + } + else + { + av.getRanges().scrollToVisible(seqCanvas.cursorX, + seqCanvas.cursorY); + } } - setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY), + + if (av.getAlignment().getHiddenColumns().isVisible(seqCanvas.cursorX)) + { + setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY), seqCanvas.cursorX, seqCanvas.cursorY); + } - seqCanvas.repaint(); + if (repaintNeeded) + { + seqCanvas.repaint(); + } } + void setSelectionAreaAtCursor(boolean topLeft) { SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY); diff --git a/src/jalview/gui/SequenceFetcher.java b/src/jalview/gui/SequenceFetcher.java index e05230b..8d46792 100755 --- a/src/jalview/gui/SequenceFetcher.java +++ b/src/jalview/gui/SequenceFetcher.java @@ -26,6 +26,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.fts.core.GFTSPanel; import jalview.fts.service.pdb.PDBFTSPanel; import jalview.fts.service.uniprot.UniprotFTSPanel; import jalview.io.FileFormatI; @@ -78,6 +79,8 @@ public class SequenceFetcher extends JPanel implements Runnable JButton close = new JButton(); + JButton back = new JButton(); + JPanel jPanel1 = new JPanel(); JTextArea textArea = new JTextArea(); @@ -383,6 +386,15 @@ public class SequenceFetcher extends JPanel implements Runnable .getString("label.additional_sequence_fetcher")); } + GFTSPanel parentFTSframe = null; + /** + * change the buttons so they fit with the FTS panel. + */ + public void embedWithFTSPanel(GFTSPanel toClose) + { + back.setVisible(true); + parentFTSframe = toClose; + } private void jbInit() throws Exception { this.setLayout(borderLayout2); @@ -427,7 +439,7 @@ public class SequenceFetcher extends JPanel implements Runnable example_actionPerformed(); } }); - close.setText(MessageManager.getString("action.close")); + close.setText(MessageManager.getString("action.cancel")); close.addActionListener(new ActionListener() { @Override @@ -436,6 +448,17 @@ public class SequenceFetcher extends JPanel implements Runnable close_actionPerformed(e); } }); + back.setText(MessageManager.getString("action.back")); + back.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + parentFTSframe.btn_back_ActionPerformed(); + } + }); + // back not visible unless embedded + back.setVisible(false); textArea.setFont(JvSwingUtils.getLabelFont()); textArea.setLineWrap(true); textArea.addKeyListener(new KeyAdapter() @@ -451,9 +474,10 @@ public class SequenceFetcher extends JPanel implements Runnable }); jPanel3.setLayout(borderLayout1); borderLayout1.setVgap(5); - jPanel1.add(ok); + jPanel1.add(back); jPanel1.add(example); jPanel1.add(clear); + jPanel1.add(ok); jPanel1.add(close); jPanel2.setLayout(borderLayout3); databaseButt = /*database.getDatabaseSelectorButton(); @@ -582,6 +606,10 @@ public class SequenceFetcher extends JPanel implements Runnable try { frame.setClosed(true); + if (parentFTSframe!=null) + { + parentFTSframe.btn_cancel_ActionPerformed(); + } } catch (Exception ex) { } @@ -594,7 +622,7 @@ public class SequenceFetcher extends JPanel implements Runnable textArea.setEnabled(false); ok.setEnabled(false); close.setEnabled(false); - + back.setEnabled(false); Thread worker = new Thread(this); worker.start(); } @@ -606,6 +634,7 @@ public class SequenceFetcher extends JPanel implements Runnable textArea.setEnabled(true); ok.setEnabled(true); close.setEnabled(true); + back.setEnabled(parentFTSframe != null); } @Override @@ -1087,4 +1116,9 @@ public class SequenceFetcher extends JPanel implements Runnable { frame.setVisible(false); } + + public void setDatabaseChooserVisible(boolean b) + { + databaseButt.setVisible(b); + } } diff --git a/src/jalview/gui/SequenceRenderer.java b/src/jalview/gui/SequenceRenderer.java index 0a1e8ef..81b394b 100755 --- a/src/jalview/gui/SequenceRenderer.java +++ b/src/jalview/gui/SequenceRenderer.java @@ -481,21 +481,30 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer } } - public void drawCursor(SequenceI seq, int res, int x1, int y1) + /** + * Draw a sequence canvas cursor + * + * @param g + * graphics context to draw on + * @param s + * character to draw at cursor + * @param x1 + * x position of cursor in graphics context + * @param y1 + * y position of cursor in graphics context + */ + public void drawCursor(Graphics g, char s, int x1, int y1) { int pady = av.getCharHeight() / 5; int charOffset = 0; - graphics.setColor(Color.black); - graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight()); + g.setColor(Color.black); + g.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight()); if (av.isValidCharWidth()) { - graphics.setColor(Color.white); - - char s = seq.getCharAt(res); - + g.setColor(Color.white); charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2; - graphics.drawString(String.valueOf(s), charOffset + x1, + g.drawString(String.valueOf(s), charOffset + x1, (y1 + av.getCharHeight()) - pady); } diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java index 20f4a49..37632ef 100644 --- a/src/jalview/gui/StructureChooser.java +++ b/src/jalview/gui/StructureChooser.java @@ -927,16 +927,10 @@ public class StructureChooser extends GStructureChooser } if (pdbEntriesToView.length > 1) { - ArrayList seqsMap = new ArrayList<>(); - for (SequenceI seq : sequences) - { - seqsMap.add(new SequenceI[] { seq }); - } - SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]); - - setProgressBar(MessageManager - .getString("status.fetching_3d_structures_for_selected_entries"), progressId); - sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel); + setProgressBar(MessageManager.getString( + "status.fetching_3d_structures_for_selected_entries"), + progressId); + sViewer.viewStructures(pdbEntriesToView, sequences, alignPanel); } else { diff --git a/src/jalview/gui/StructureViewer.java b/src/jalview/gui/StructureViewer.java index e58b378..b142613 100644 --- a/src/jalview/gui/StructureViewer.java +++ b/src/jalview/gui/StructureViewer.java @@ -29,19 +29,24 @@ import jalview.structure.StructureSelectionManager; import java.awt.Rectangle; import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; /** - * proxy for handling structure viewers. - * - * this allows new views to be created with the currently configured viewer, the - * preferred viewer to be set/read and existing views created previously with a - * particular viewer to be recovered + * A proxy for handling structure viewers, that orchestrates adding selected + * structures, associated with sequences in Jalview, to an existing viewer, or + * opening a new one. Currently supports either Jmol or Chimera as the structure + * viewer. * * @author jprocter */ public class StructureViewer { + private static final String UNKNOWN_VIEWER_TYPE = "Unknown structure viewer type "; + StructureSelectionManager ssm; public enum ViewerType @@ -49,6 +54,16 @@ public class StructureViewer JMOL, CHIMERA }; + /** + * Constructor + * + * @param structureSelectionManager + */ + public StructureViewer(StructureSelectionManager structureSelectionManager) + { + ssm = structureSelectionManager; + } + public ViewerType getViewerType() { String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY, @@ -61,135 +76,157 @@ public class StructureViewer Cache.setProperty(Preferences.STRUCTURE_DISPLAY, type.name()); } - public StructureViewer( - StructureSelectionManager structureSelectionManager) - { - ssm = structureSelectionManager; - } - /** * View multiple PDB entries, each with associated sequences * * @param pdbs - * @param seqsForPdbs + * @param seqs * @param ap * @return */ public JalviewStructureDisplayI viewStructures(PDBEntry[] pdbs, - SequenceI[][] seqsForPdbs, AlignmentPanel ap) + SequenceI[] seqs, AlignmentPanel ap) { - JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqsForPdbs, ap); + JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqs, ap); if (viewer != null) { + /* + * user added structure to an existing viewer - all done + */ return viewer; } - return viewStructures(getViewerType(), pdbs, seqsForPdbs, ap); + + ViewerType viewerType = getViewerType(); + + Map seqsForPdbs = getSequencesForPdbs(pdbs, + seqs); + PDBEntry[] pdbsForFile = seqsForPdbs.keySet().toArray( + new PDBEntry[seqsForPdbs.size()]); + SequenceI[][] theSeqs = seqsForPdbs.values().toArray( + new SequenceI[seqsForPdbs.size()][]); + JalviewStructureDisplayI sview = null; + if (viewerType.equals(ViewerType.JMOL)) + { + sview = new AppJmol(ap, pdbsForFile, theSeqs); + } + else if (viewerType.equals(ViewerType.CHIMERA)) + { + sview = new ChimeraViewFrame(pdbsForFile, theSeqs, ap); + } + else + { + Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString()); + } + return sview; } /** - * A strictly temporary method pending JAL-1761 refactoring. Determines if all - * the passed PDB entries are the same (this is the case if selected sequences - * to view structure for are chains of the same structure). If so, calls the - * single-pdb version of viewStructures and returns the viewer, else returns - * null. + * Converts the list of selected PDB entries (possibly including duplicates + * for multiple chains), and corresponding sequences, into a map of sequences + * for each distinct PDB file. Returns null if either argument is null, or + * their lengths do not match. * * @param pdbs - * @param seqsForPdbs - * @param ap + * @param seqs * @return */ - private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs, - SequenceI[][] seqsForPdbs, AlignmentPanel ap) + Map getSequencesForPdbs(PDBEntry[] pdbs, + SequenceI[] seqs) { - List seqs = new ArrayList(); - if (pdbs == null || pdbs.length == 0) + if (pdbs == null || seqs == null || pdbs.length != seqs.length) { return null; } - int i = 0; - String firstFile = pdbs[0].getFile(); - for (PDBEntry pdb : pdbs) + + /* + * we want only one 'representative' PDBEntry per distinct file name + * (there may be entries for distinct chains) + */ + Map pdbsSeen = new HashMap<>(); + + /* + * LinkedHashMap preserves order of PDB entries (significant if they + * will get superimposed to the first structure) + */ + Map> pdbSeqs = new LinkedHashMap<>(); + for (int i = 0; i < pdbs.length; i++) { + PDBEntry pdb = pdbs[i]; + SequenceI seq = seqs[i]; String pdbFile = pdb.getFile(); - if (pdbFile == null || !pdbFile.equals(firstFile)) + if (!pdbsSeen.containsKey(pdbFile)) { - return null; + pdbsSeen.put(pdbFile, pdb); + pdbSeqs.put(pdb, new ArrayList()); + } + else + { + pdb = pdbsSeen.get(pdbFile); } - SequenceI[] pdbseqs = seqsForPdbs[i++]; - if (pdbseqs != null) + List seqsForPdb = pdbSeqs.get(pdb); + if (!seqsForPdb.contains(seq)) { - for (SequenceI sq : pdbseqs) - { - seqs.add(sq); - } + seqsForPdb.add(seq); } } - return viewStructures(pdbs[0], seqs.toArray(new SequenceI[seqs.size()]), - ap); - } - - public JalviewStructureDisplayI viewStructures(PDBEntry pdb, - SequenceI[] seqsForPdb, AlignmentPanel ap) - { - return viewStructures(getViewerType(), pdb, seqsForPdb, ap); - } - protected JalviewStructureDisplayI viewStructures(ViewerType viewerType, - PDBEntry[] pdbs, SequenceI[][] seqsForPdbs, AlignmentPanel ap) - { - PDBEntry[] pdbsForFile = getUniquePdbFiles(pdbs); - JalviewStructureDisplayI sview = null; - if (viewerType.equals(ViewerType.JMOL)) - { - sview = new AppJmol(ap, pdbsForFile, - ap.av.collateForPDB(pdbsForFile)); - } - else if (viewerType.equals(ViewerType.CHIMERA)) + /* + * convert to Map + */ + Map result = new LinkedHashMap<>(); + for (Entry> entry : pdbSeqs.entrySet()) { - sview = new ChimeraViewFrame(pdbsForFile, - ap.av.collateForPDB(pdbsForFile), ap); + List theSeqs = entry.getValue(); + result.put(entry.getKey(), + theSeqs.toArray(new SequenceI[theSeqs.size()])); } - else - { - Cache.log.error("Unknown structure viewer type " - + getViewerType().toString()); - } - return sview; + + return result; } /** - * Convert the array of PDBEntry into an array with no filename repeated + * A strictly temporary method pending JAL-1761 refactoring. Determines if all + * the passed PDB entries are the same (this is the case if selected sequences + * to view structure for are chains of the same structure). If so, calls the + * single-pdb version of viewStructures and returns the viewer, else returns + * null. * * @param pdbs + * @param seqsForPdbs + * @param ap * @return */ - static PDBEntry[] getUniquePdbFiles(PDBEntry[] pdbs) + private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs, + SequenceI[] seqsForPdbs, AlignmentPanel ap) { - if (pdbs == null) + List seqs = new ArrayList(); + if (pdbs == null || pdbs.length == 0) { return null; } - List uniques = new ArrayList(); - List filesSeen = new ArrayList(); - for (PDBEntry entry : pdbs) + int i = 0; + String firstFile = pdbs[0].getFile(); + for (PDBEntry pdb : pdbs) { - String file = entry.getFile(); - if (file == null) + String pdbFile = pdb.getFile(); + if (pdbFile == null || !pdbFile.equals(firstFile)) { - uniques.add(entry); + return null; } - else if (!filesSeen.contains(file)) + SequenceI pdbseq = seqsForPdbs[i++]; + if (pdbseq != null) { - uniques.add(entry); - filesSeen.add(file); + seqs.add(pdbseq); } } - return uniques.toArray(new PDBEntry[uniques.size()]); + return viewStructures(pdbs[0], seqs.toArray(new SequenceI[seqs.size()]), + ap); } - protected JalviewStructureDisplayI viewStructures(ViewerType viewerType, - PDBEntry pdb, SequenceI[] seqsForPdb, AlignmentPanel ap) + public JalviewStructureDisplayI viewStructures(PDBEntry pdb, + SequenceI[] seqsForPdb, AlignmentPanel ap) { + ViewerType viewerType = getViewerType(); JalviewStructureDisplayI sview = null; if (viewerType.equals(ViewerType.JMOL)) { @@ -201,8 +238,7 @@ public class StructureViewer } else { - Cache.log.error("Unknown structure viewer type " - + getViewerType().toString()); + Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString()); } return sview; } @@ -242,7 +278,7 @@ public class StructureViewer "Unsupported structure viewer type " + type.toString()); break; default: - Cache.log.error("Unknown structure viewer type " + type.toString()); + Cache.log.error(UNKNOWN_VIEWER_TYPE + type.toString()); } return sview; } diff --git a/src/jalview/io/AlignFile.java b/src/jalview/io/AlignFile.java index a603cca..2340283 100755 --- a/src/jalview/io/AlignFile.java +++ b/src/jalview/io/AlignFile.java @@ -174,11 +174,6 @@ public abstract class AlignFile extends FileParse } parseCalled = true; parse(); - // sets the index of each sequence in the alignment - for (int i = 0, c = seqs.size(); i < c; i++) - { - seqs.get(i).setIndex(i); - } } /** diff --git a/src/jalview/io/cache/JvCacheableInputBox.java b/src/jalview/io/cache/JvCacheableInputBox.java index 3d0daed..a837512 100644 --- a/src/jalview/io/cache/JvCacheableInputBox.java +++ b/src/jalview/io/cache/JvCacheableInputBox.java @@ -28,6 +28,7 @@ import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -70,11 +71,49 @@ public class JvCacheableInputBox extends JComboBox private JMenuItem menuItemClearCache = new JMenuItem(); + volatile boolean enterWasPressed = false; + + /** + * @return flag indicating if the most recent keypress was enter + */ + public boolean wasEnterPressed() + { + return enterWasPressed; + } + public JvCacheableInputBox(String newCacheKey) { super(); this.cacheKey = newCacheKey; setEditable(true); + addKeyListener(new KeyListener() + { + + @Override + public void keyTyped(KeyEvent e) + { + enterWasPressed = false; + if (e.getKeyCode() == KeyEvent.VK_ENTER) + { + enterWasPressed = true; + } + // let event bubble up + } + + @Override + public void keyReleased(KeyEvent e) + { + // TODO Auto-generated method stub + + } + + @Override + public void keyPressed(KeyEvent e) + { + // TODO Auto-generated method stub + + } + }); setPrototypeDisplayValue( "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); appCache = AppCache.getInstance(); diff --git a/src/jalview/urls/CustomUrlProvider.java b/src/jalview/urls/CustomUrlProvider.java index 0d5ef99..86d5660 100644 --- a/src/jalview/urls/CustomUrlProvider.java +++ b/src/jalview/urls/CustomUrlProvider.java @@ -160,10 +160,24 @@ public class CustomUrlProvider extends UrlProviderImpl */ private void upgradeOldLinks(HashMap urls) { + boolean upgrade = false; // upgrade old SRS link if (urls.containsKey(SRS_LABEL)) { urls.remove(SRS_LABEL); + upgrade = true; + } + // upgrade old EBI link - easier just to remove and re-add than faffing + // around checking exact url + if (urls.containsKey(UrlConstants.DEFAULT_LABEL)) + { + // note because this is called separately for selected and nonselected + // urls, the default url will not always be present + urls.remove(UrlConstants.DEFAULT_LABEL); + upgrade = true; + } + if (upgrade) + { UrlLink link = new UrlLink(UrlConstants.DEFAULT_STRING); link.setLabel(UrlConstants.DEFAULT_LABEL); urls.put(UrlConstants.DEFAULT_LABEL, link); diff --git a/src/jalview/util/UrlConstants.java b/src/jalview/util/UrlConstants.java index d6ece8d..e5cfaee 100644 --- a/src/jalview/util/UrlConstants.java +++ b/src/jalview/util/UrlConstants.java @@ -57,10 +57,20 @@ public class UrlConstants public static final String DEFAULT_STRING = DEFAULT_LABEL + "|https://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$"; + private static final String COLON = ":"; + /* * not instantiable */ private UrlConstants() { } + + public static boolean isDefaultString(String link) + { + String sublink = link.substring(link.indexOf(COLON) + 1); + String subdefault = DEFAULT_STRING + .substring(DEFAULT_STRING.indexOf(COLON) + 1); + return sublink.equalsIgnoreCase(subdefault); + } } diff --git a/src/jalview/viewmodel/OverviewDimensionsHideHidden.java b/src/jalview/viewmodel/OverviewDimensionsHideHidden.java index c525bc6..c158ce7 100644 --- a/src/jalview/viewmodel/OverviewDimensionsHideHidden.java +++ b/src/jalview/viewmodel/OverviewDimensionsHideHidden.java @@ -132,9 +132,7 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions } } - // update viewport - ranges.setStartRes(xAsRes); - ranges.setStartSeq(yAsSeq); + ranges.setStartResAndSeq(xAsRes, yAsSeq); } @Override diff --git a/src/jalview/viewmodel/OverviewDimensionsShowHidden.java b/src/jalview/viewmodel/OverviewDimensionsShowHidden.java index 0bda56e..9dde16e 100644 --- a/src/jalview/viewmodel/OverviewDimensionsShowHidden.java +++ b/src/jalview/viewmodel/OverviewDimensionsShowHidden.java @@ -176,8 +176,7 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions } // update viewport - ranges.setStartRes(visXAsRes); - ranges.setStartSeq(visYAsSeq); + ranges.setStartResAndSeq(visXAsRes, visYAsSeq); } /** diff --git a/src/jalview/viewmodel/ViewportRanges.java b/src/jalview/viewmodel/ViewportRanges.java index 24ff57f..c7a3fa1 100644 --- a/src/jalview/viewmodel/ViewportRanges.java +++ b/src/jalview/viewmodel/ViewportRanges.java @@ -24,11 +24,10 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.HiddenColumns; /** - * Slightly less embryonic class which: Supplies and updates viewport properties - * relating to position such as: start and end residues and sequences; ideally - * will serve hidden columns/rows too. Intention also to support calculations - * for positioning, scrolling etc. such as finding the middle of the viewport, - * checking for scrolls off screen + * Supplies and updates viewport properties relating to position such as: start + * and end residues and sequences; ideally will serve hidden columns/rows too. + * Intention also to support calculations for positioning, scrolling etc. such + * as finding the middle of the viewport, checking for scrolls off screen */ public class ViewportRanges extends ViewportProperties { @@ -40,6 +39,10 @@ public class ViewportRanges extends ViewportProperties public static final String ENDSEQ = "endseq"; + public static final String STARTRESANDSEQ = "startresandseq"; + + public static final String MOVE_VIEWPORT = "move_viewport"; + private boolean wrappedMode = false; // start residue of viewport @@ -130,6 +133,31 @@ public class ViewportRanges extends ViewportProperties */ public void setStartEndRes(int start, int end) { + int[] oldvalues = updateStartEndRes(start, end); + int oldstartres = oldvalues[0]; + int oldendres = oldvalues[1]; + + changeSupport.firePropertyChange(STARTRES, oldstartres, startRes); + if (oldstartres == startRes) + { + // event won't be fired if start positions are same + // fire an event for the end positions in case they changed + changeSupport.firePropertyChange(ENDRES, oldendres, endRes); + } + } + + /** + * Update start and end residue values, adjusting for width constraints if + * necessary + * + * @param start + * start residue + * @param end + * end residue + * @return array containing old start and end residue values + */ + private int[] updateStartEndRes(int start, int end) + { int oldstartres = this.startRes; /* @@ -162,14 +190,7 @@ public class ViewportRanges extends ViewportProperties { endRes = end; } - - changeSupport.firePropertyChange(STARTRES, oldstartres, startRes); - if (oldstartres == startRes) - { - // event won't be fired if start positions are same - // fire an event for the end positions in case they changed - changeSupport.firePropertyChange(ENDRES, oldendres, endRes); - } + return new int[] { oldstartres, oldendres }; } /** @@ -203,6 +224,31 @@ public class ViewportRanges extends ViewportProperties */ public void setStartEndSeq(int start, int end) { + int[] oldvalues = updateStartEndSeq(start, end); + int oldstartseq = oldvalues[0]; + int oldendseq = oldvalues[1]; + + changeSupport.firePropertyChange(STARTSEQ, oldstartseq, startSeq); + if (oldstartseq == startSeq) + { + // event won't be fired if start positions are the same + // fire in case the end positions changed + changeSupport.firePropertyChange(ENDSEQ, oldendseq, endSeq); + } + } + + /** + * Update start and end sequence values, adjusting for height constraints if + * necessary + * + * @param start + * start sequence + * @param end + * end sequence + * @return array containing old start and end sequence values + */ + private int[] updateStartEndSeq(int start, int end) + { int oldstartseq = this.startSeq; int visibleHeight = getVisibleAlignmentHeight(); if (start > visibleHeight - 1) @@ -231,14 +277,7 @@ public class ViewportRanges extends ViewportProperties { endSeq = end; } - - changeSupport.firePropertyChange(STARTSEQ, oldstartseq, startSeq); - if (oldstartseq == startSeq) - { - // event won't be fired if start positions are the same - // fire in case the end positions changed - changeSupport.firePropertyChange(ENDSEQ, oldendseq, endSeq); - } + return new int[] { oldstartseq, oldendseq }; } /** @@ -255,6 +294,34 @@ public class ViewportRanges extends ViewportProperties } /** + * Set start residue and start sequence together (fires single event). The + * event supplies a pair of old values and a pair of new values: [old start + * residue, old start sequence] and [new start residue, new start sequence] + * + * @param res + * the start residue + * @param seq + * the start sequence + */ + public void setStartResAndSeq(int res, int seq) + { + int width = getViewportWidth(); + int[] oldresvalues = updateStartEndRes(res, res + width - 1); + + int startseq = seq; + int height = getViewportHeight(); + if (startseq + height - 1 > getVisibleAlignmentHeight() - 1) + { + startseq = getVisibleAlignmentHeight() - height; + } + int[] oldseqvalues = updateStartEndSeq(startseq, startseq + height - 1); + + int[] old = new int[] { oldresvalues[0], oldseqvalues[0] }; + int[] newresseq = new int[] { startRes, startSeq }; + changeSupport.firePropertyChange(STARTRESANDSEQ, old, newresseq); + } + + /** * Get start residue of viewport */ public int getStartRes() @@ -477,18 +544,33 @@ public class ViewportRanges extends ViewportProperties * the startRes changed, else false. * * @param res - * residue position to scroll to + * residue position to scroll to NB visible position not absolute + * alignment position * @return */ public boolean scrollToWrappedVisible(int res) { - int oldStartRes = startRes; - int width = getViewportWidth(); - - if (res >= oldStartRes && res < oldStartRes + width) + int newStartRes = calcWrappedStartResidue(res); + if (newStartRes == startRes) { return false; } + setStartRes(newStartRes); + + return true; + } + + /** + * Calculate wrapped start residue from visible start residue + * + * @param res + * visible start residue + * @return left column of panel res will be located in + */ + private int calcWrappedStartResidue(int res) + { + int oldStartRes = startRes; + int width = getViewportWidth(); boolean up = res < oldStartRes; int widthsToScroll = Math.abs((res - oldStartRes) / width); @@ -504,19 +586,16 @@ public class ViewportRanges extends ViewportProperties { newStartRes = 0; } - - setStartRes(newStartRes); - - return true; + return newStartRes; } /** * Scroll so that (x,y) is visible. Fires a property change event. * * @param x - * x position in alignment + * x position in alignment (absolute position) * @param y - * y position in alignment + * y position in alignment (absolute position) */ public void scrollToVisible(int x, int y) { @@ -528,7 +607,7 @@ public class ViewportRanges extends ViewportProperties { scrollUp(false); } - + HiddenColumns hidden = al.getHiddenColumns(); while (x < hidden.adjustForHiddenColumns(startRes)) { @@ -547,6 +626,62 @@ public class ViewportRanges extends ViewportProperties } /** + * Set the viewport location so that a position is visible + * + * @param x + * column to be visible: absolute position in alignment + * @param y + * row to be visible: absolute position in alignment + */ + public boolean setViewportLocation(int x, int y) + { + boolean changedLocation = false; + + // convert the x,y location to visible coordinates + int visX = al.getHiddenColumns().findColumnPosition(x); + int visY = al.getHiddenSequences().findIndexWithoutHiddenSeqs(y); + + // if (vis_x,vis_y) is already visible don't do anything + if (startRes > visX || visX > endRes + || startSeq > visY && visY > endSeq) + { + int[] old = new int[] { startRes, startSeq }; + int[] newresseq; + if (wrappedMode) + { + int newstartres = calcWrappedStartResidue(visX); + setStartRes(newstartres); + newresseq = new int[] { startRes, startSeq }; + } + else + { + // set the viewport x location to contain vis_x + int newstartres = visX; + int width = getViewportWidth(); + if (newstartres + width - 1 > getVisibleAlignmentWidth() - 1) + { + newstartres = getVisibleAlignmentWidth() - width; + } + updateStartEndRes(newstartres, newstartres + width - 1); + + // set the viewport y location to contain vis_y + int newstartseq = visY; + int height = getViewportHeight(); + if (newstartseq + height - 1 > getVisibleAlignmentHeight() - 1) + { + newstartseq = getVisibleAlignmentHeight() - height; + } + updateStartEndSeq(newstartseq, newstartseq + height - 1); + + newresseq = new int[] { startRes, startSeq }; + } + changedLocation = true; + changeSupport.firePropertyChange(MOVE_VIEWPORT, old, newresseq); + } + return changedLocation; + } + + /** * Adjust sequence position for page up. Fires a property change event. */ public void pageUp() diff --git a/src/jalview/ws/dbsources/Uniprot.java b/src/jalview/ws/dbsources/Uniprot.java index c9beb8e..73775cf 100644 --- a/src/jalview/ws/dbsources/Uniprot.java +++ b/src/jalview/ws/dbsources/Uniprot.java @@ -310,23 +310,18 @@ public class Uniprot extends DbSourceProxyImpl /** * * @param entry - * UniportEntry + * UniprotEntry * @return The accession id(s) and name(s) delimited by '|'. */ public static String getUniprotEntryId(UniprotEntry entry) { StringBuilder name = new StringBuilder(32); - // name.append("UniProt/Swiss-Prot"); - // use 'canonicalised' name for optimal id matching - name.append(DBRefSource.UNIPROT); - for (String accessionId : entry.getAccession()) - { - name.append(BAR_DELIMITER); - name.append(accessionId); - } for (String n : entry.getName()) { - name.append(BAR_DELIMITER); + if (name.length() > 0) + { + name.append(BAR_DELIMITER); + } name.append(n); } return name.toString(); diff --git a/test/jalview/datamodel/SequenceTest.java b/test/jalview/datamodel/SequenceTest.java index c0cb09c..a084a8e 100644 --- a/test/jalview/datamodel/SequenceTest.java +++ b/test/jalview/datamodel/SequenceTest.java @@ -1803,4 +1803,82 @@ public class SequenceTest sq.checkValidRange(); assertEquals(22, sq.getEnd()); } + + @Test(groups = { "Functional" }) + public void testDeleteChars_withGaps() + { + /* + * delete gaps only + */ + SequenceI sq = new Sequence("test/8-10", "A-B-C"); + sq.createDatasetSequence(); + assertEquals("ABC", sq.getDatasetSequence().getSequenceAsString()); + sq.deleteChars(1, 2); // delete first gap + assertEquals("AB-C", sq.getSequenceAsString()); + assertEquals(8, sq.getStart()); + assertEquals(10, sq.getEnd()); + assertEquals("ABC", sq.getDatasetSequence().getSequenceAsString()); + + /* + * delete gaps and residues at start (no new dataset sequence) + */ + sq = new Sequence("test/8-10", "A-B-C"); + sq.createDatasetSequence(); + sq.deleteChars(0, 3); // delete A-B + assertEquals("-C", sq.getSequenceAsString()); + assertEquals(10, sq.getStart()); + assertEquals(10, sq.getEnd()); + assertEquals("ABC", sq.getDatasetSequence().getSequenceAsString()); + + /* + * delete gaps and residues at end (no new dataset sequence) + */ + sq = new Sequence("test/8-10", "A-B-C"); + sq.createDatasetSequence(); + sq.deleteChars(2, 5); // delete B-C + assertEquals("A-", sq.getSequenceAsString()); + assertEquals(8, sq.getStart()); + assertEquals(8, sq.getEnd()); + assertEquals("ABC", sq.getDatasetSequence().getSequenceAsString()); + + /* + * delete gaps and residues internally (new dataset sequence) + * first delete from gap to residue + */ + sq = new Sequence("test/8-10", "A-B-C"); + sq.createDatasetSequence(); + sq.deleteChars(1, 3); // delete -B + assertEquals("A-C", sq.getSequenceAsString()); + assertEquals(8, sq.getStart()); + assertEquals(9, sq.getEnd()); + assertEquals("AC", sq.getDatasetSequence().getSequenceAsString()); + assertEquals(8, sq.getDatasetSequence().getStart()); + assertEquals(9, sq.getDatasetSequence().getEnd()); + + /* + * internal delete from gap to gap + */ + sq = new Sequence("test/8-10", "A-B-C"); + sq.createDatasetSequence(); + sq.deleteChars(1, 4); // delete -B- + assertEquals("AC", sq.getSequenceAsString()); + assertEquals(8, sq.getStart()); + assertEquals(9, sq.getEnd()); + assertEquals("AC", sq.getDatasetSequence().getSequenceAsString()); + assertEquals(8, sq.getDatasetSequence().getStart()); + assertEquals(9, sq.getDatasetSequence().getEnd()); + + /* + * internal delete from residue to residue + */ + sq = new Sequence("test/8-10", "A-B-C"); + sq.createDatasetSequence(); + sq.deleteChars(2, 3); // delete B + assertEquals("A--C", sq.getSequenceAsString()); + assertEquals(8, sq.getStart()); + assertEquals(9, sq.getEnd()); + assertEquals("AC", sq.getDatasetSequence().getSequenceAsString()); + assertEquals(8, sq.getDatasetSequence().getStart()); + assertEquals(9, sq.getDatasetSequence().getEnd()); + } } diff --git a/test/jalview/gui/AlignViewportTest.java b/test/jalview/gui/AlignViewportTest.java index 812fd8f..5ed0cac 100644 --- a/test/jalview/gui/AlignViewportTest.java +++ b/test/jalview/gui/AlignViewportTest.java @@ -96,57 +96,6 @@ public class AlignViewportTest testee = new AlignViewport(al); } - @Test(groups = { "Functional" }) - public void testCollateForPdb() - { - // JBP: What behaviour is this supposed to test ? - /* - * Set up sequence pdb ids - */ - PDBEntry pdb1 = new PDBEntry("1ABC", "B", Type.PDB, "1ABC.pdb"); - PDBEntry pdb2 = new PDBEntry("2ABC", "C", Type.PDB, "2ABC.pdb"); - PDBEntry pdb3 = new PDBEntry("3ABC", "D", Type.PDB, "3ABC.pdb"); - - /* - * seq1 and seq3 refer to 1abcB, seq2 to 2abcC, none to 3abcD - */ - al.getSequenceAt(0).getDatasetSequence() - .addPDBId(new PDBEntry("1ABC", "B", Type.PDB, "1ABC.pdb")); - al.getSequenceAt(2).getDatasetSequence() - .addPDBId(new PDBEntry("1ABC", "B", Type.PDB, "1ABC.pdb")); - al.getSequenceAt(1).getDatasetSequence() - .addPDBId(new PDBEntry("2ABC", "C", Type.PDB, "2ABC.pdb")); - /* - * Add a second chain PDB xref to Seq2 - should not result in a duplicate in - * the results - */ - al.getSequenceAt(1).getDatasetSequence() - .addPDBId(new PDBEntry("2ABC", "D", Type.PDB, "2ABC.pdb")); - /* - * Seq3 refers to 3abc - this does not match 3ABC (as the code stands) - */ - al.getSequenceAt(2).getDatasetSequence() - .addPDBId(new PDBEntry("3abc", "D", Type.PDB, "3ABC.pdb")); - - /* - * run method under test - */ - SequenceI[][] seqs = testee.collateForPDB(new PDBEntry[] { pdb1, pdb2, - pdb3 }); - - // seq1 and seq3 refer to PDBEntry[0] - assertEquals(2, seqs[0].length); - assertSame(al.getSequenceAt(0), seqs[0][0]); - assertSame(al.getSequenceAt(2), seqs[0][1]); - - // seq2 refers to PDBEntry[1] - assertEquals(1, seqs[1].length); - assertSame(al.getSequenceAt(1), seqs[1][0]); - - // no sequence refers to PDBEntry[2] - assertEquals(0, seqs[2].length); - } - /** * Test that a mapping is not deregistered when a second view is closed but * the first still holds a reference to the mapping diff --git a/test/jalview/gui/StructureViewerTest.java b/test/jalview/gui/StructureViewerTest.java index c1c1d5c..4d5b114 100644 --- a/test/jalview/gui/StructureViewerTest.java +++ b/test/jalview/gui/StructureViewerTest.java @@ -1,10 +1,17 @@ package jalview.gui; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; import jalview.datamodel.PDBEntry; import jalview.datamodel.PDBEntry.Type; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; + +import java.util.Map; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -20,9 +27,11 @@ public class StructureViewerTest } @Test(groups = "Functional") - public void testGetUniquePdbFiles() + public void testGetSequencesForPdbs() { - assertNull(StructureViewer.getUniquePdbFiles(null)); + StructureViewer sv = new StructureViewer(null); + + assertNull(sv.getSequencesForPdbs(null, null)); PDBEntry pdbe1 = new PDBEntry("1A70", "A", Type.PDB, "path1"); PDBEntry pdbe2 = new PDBEntry("3A6S", "A", Type.PDB, "path2"); @@ -30,13 +39,45 @@ public class StructureViewerTest PDBEntry pdbe4 = new PDBEntry("1GAQ", "A", Type.PDB, null); PDBEntry pdbe5 = new PDBEntry("3A6S", "B", Type.PDB, "path2"); PDBEntry pdbe6 = new PDBEntry("1GAQ", "B", Type.PDB, null); + PDBEntry[] pdbs = new PDBEntry[] { pdbe1, pdbe2, pdbe3, pdbe4, pdbe5, + pdbe6 }; + + /* + * seq1 ... seq6 associated with pdbe1 ... pdbe6 + */ + SequenceI[] seqs = new SequenceI[pdbs.length]; + for (int i = 0; i < seqs.length; i++) + { + seqs[i] = new Sequence("Seq" + i, "abc"); + } /* - * pdbe2 and pdbe5 get removed as having a duplicate file path + * pdbe3/5/6 should get removed as having a duplicate file path */ - PDBEntry[] uniques = StructureViewer.getUniquePdbFiles(new PDBEntry[] { - pdbe1, pdbe2, pdbe3, pdbe4, pdbe5, pdbe6 }); - assertEquals(uniques, - new PDBEntry[] { pdbe1, pdbe2, pdbe4, pdbe6 }); + Map uniques = sv.getSequencesForPdbs(pdbs, seqs); + assertTrue(uniques.containsKey(pdbe1)); + assertTrue(uniques.containsKey(pdbe2)); + assertFalse(uniques.containsKey(pdbe3)); + assertTrue(uniques.containsKey(pdbe4)); + assertFalse(uniques.containsKey(pdbe5)); + assertFalse(uniques.containsKey(pdbe6)); + + // 1A70 associates with seq1 and seq3 + SequenceI[] ss = uniques.get(pdbe1); + assertEquals(ss.length, 2); + assertSame(seqs[0], ss[0]); + assertSame(seqs[2], ss[1]); + + // 3A6S has seq2 and seq5 + ss = uniques.get(pdbe2); + assertEquals(ss.length, 2); + assertSame(seqs[1], ss[0]); + assertSame(seqs[4], ss[1]); + + // 1GAQ has seq4 and seq6 + ss = uniques.get(pdbe4); + assertEquals(ss.length, 2); + assertSame(seqs[3], ss[0]); + assertSame(seqs[5], ss[1]); } } diff --git a/test/jalview/viewmodel/ViewportRangesTest.java b/test/jalview/viewmodel/ViewportRangesTest.java index c0cb4ba..41a313f 100644 --- a/test/jalview/viewmodel/ViewportRangesTest.java +++ b/test/jalview/viewmodel/ViewportRangesTest.java @@ -85,7 +85,6 @@ public class ViewportRangesTest { vr.setEndSeq(al.getHeight()); assertEquals(vr.getEndSeq(), al.getHeight() - 1); - // vr.setEndRes(al.getHeight() - 1); vr.setEndSeq(al.getHeight() - 1); assertEquals(vr.getEndSeq(), al.getHeight() - 1); } @@ -169,6 +168,24 @@ public class ViewportRangesTest { } @Test(groups = { "Functional" }) + public void testSetStartResAndSeq() + { + ViewportRanges vr = new ViewportRanges(al); + vr.setViewportHeight(10); + vr.setStartResAndSeq(3, 6); + assertEquals(vr.getStartRes(), 3); + assertEquals(vr.getStartSeq(), 6); + assertEquals(vr.getEndRes(), 3 + vr.getViewportWidth() - 1); + assertEquals(vr.getEndSeq(), 6 + vr.getViewportHeight() - 1); + + vr.setStartResAndSeq(10, 25); + assertEquals(vr.getStartRes(), 10); + assertEquals(vr.getStartSeq(), 19); + assertEquals(vr.getEndRes(), 10 + vr.getViewportWidth() - 1); + assertEquals(vr.getEndSeq(), 19 + vr.getViewportHeight() - 1); + } + + @Test(groups = { "Functional" }) public void testSetViewportHeight() { ViewportRanges vr = new ViewportRanges(al); @@ -385,14 +402,13 @@ public class ViewportRangesTest { assertEquals(vr.getEndRes(), 52); } - // leave until JAL-2388 is merged and we can do without viewport - /*@Test(groups = { "Functional" }) + @Test(groups = { "Functional" }) public void testScrollToVisible() { ViewportRanges vr = new ViewportRanges(al); vr.setViewportStartAndWidth(12,5); vr.setViewportStartAndHeight(10,6); - vr.scrollToVisible(13,14) + vr.scrollToVisible(13, 14); // no change assertEquals(vr.getStartRes(), 12); @@ -403,7 +419,15 @@ public class ViewportRangesTest { assertEquals(vr.getStartSeq(), 6); // test for hidden columns too - }*/ + al.getHiddenColumns().hideColumns(1, 3); + vr.scrollToVisible(13, 3); + assertEquals(vr.getStartRes(), 6); + assertEquals(vr.getStartSeq(), 3); + + vr.scrollToVisible(2, 9); + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getStartSeq(), 4); + } @Test(groups = { "Functional" }) public void testEventFiring() @@ -418,7 +442,7 @@ public class ViewportRangesTest { // one event fired when startRes is called with new value vr.setStartRes(4); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); l.reset(); // no event fired for same value @@ -427,7 +451,7 @@ public class ViewportRangesTest { l.reset(); vr.setStartSeq(4); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.setStartSeq(4); @@ -435,7 +459,7 @@ public class ViewportRangesTest { l.reset(); vr.setEndSeq(10); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.setEndSeq(10); @@ -443,7 +467,7 @@ public class ViewportRangesTest { l.reset(); vr.setStartEndRes(2, 15); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); l.reset(); vr.setStartEndRes(2, 15); @@ -452,16 +476,18 @@ public class ViewportRangesTest { // check new value fired by event is corrected startres vr.setStartEndRes(-1, 5); - assertTrue(l.verify(1, Arrays.asList("startres"), Arrays.asList(0))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES), + Arrays.asList(0))); l.reset(); // check new value fired by event is corrected endres vr.setStartEndRes(0, -1); - assertTrue(l.verify(1, Arrays.asList("endres"), Arrays.asList(0))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDRES), + Arrays.asList(0))); l.reset(); vr.setStartEndSeq(2, 15); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.setStartEndSeq(2, 15); @@ -474,12 +500,14 @@ public class ViewportRangesTest { // check new value fired by event is corrected startseq vr.setStartEndSeq(-1, 5); - assertTrue(l.verify(1, Arrays.asList("startseq"), Arrays.asList(0))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ), + Arrays.asList(0))); l.reset(); // check new value fired by event is corrected endseq vr.setStartEndSeq(0, -1); - assertTrue(l.verify(1, Arrays.asList("endseq"), Arrays.asList(0))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDSEQ), + Arrays.asList(0))); l.reset(); // reset for later tests @@ -488,51 +516,52 @@ public class ViewportRangesTest { // test viewport height and width setting triggers event vr.setViewportHeight(10); - assertTrue(l.verify(1, Arrays.asList("endseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDSEQ))); l.reset(); vr.setViewportWidth(18); - assertTrue(l.verify(1, Arrays.asList("endres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDRES))); l.reset(); // already has seq start set to 2, so triggers endseq vr.setViewportStartAndHeight(2, 16); - assertTrue(l.verify(1, Arrays.asList("endseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDSEQ))); l.reset(); vr.setViewportStartAndWidth(1, 14); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); l.reset(); // test page up/down triggers event vr.pageUp(); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.pageDown(); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); // test scrolling triggers event vr.scrollUp(true); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.scrollUp(false); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.scrollRight(true); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); l.reset(); vr.scrollRight(false); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); l.reset(); vr.scrollToVisible(10, 10); assertTrue(l.verify(4, - Arrays.asList("startseq", "startseq", "startseq", "startseq"))); + Arrays.asList(ViewportRanges.STARTSEQ, ViewportRanges.STARTSEQ, + ViewportRanges.STARTSEQ, ViewportRanges.STARTSEQ))); l.reset(); /* @@ -544,7 +573,15 @@ public class ViewportRangesTest { l.reset(); vr.scrollToWrappedVisible(25); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); + l.reset(); + + // test setStartResAndSeq triggers one event + vr.setStartResAndSeq(5, 7); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRESANDSEQ), + Arrays.asList(5, 7))); + + l.reset(); } @Test(groups = { "Functional" }) @@ -823,6 +860,76 @@ public class ViewportRangesTest { assertEquals(vr.getStartSeq(), 1); assertEquals(vr.getStartRes(), 43); } + + @Test(groups = { "Functional" }) + public void testSetViewportLocation() + { + AlignmentI al2 = gen.generate(60, 80, 1, 0, 0); + + ViewportRanges vr = new ViewportRanges(al2); + + // start with viewport on 5-14 + vr.setViewportStartAndWidth(5, 10); + assertEquals(vr.getStartRes(), 5); + assertEquals(vr.getEndRes(), 14); + + vr.setViewportStartAndHeight(3, 13); + assertEquals(vr.getStartSeq(), 3); + assertEquals(vr.getEndSeq(), 15); + + // set location to (8,5) - no change + vr.setViewportLocation(8, 5); + assertEquals(vr.getStartRes(), 5); + assertEquals(vr.getEndRes(), 14); + assertEquals(vr.getStartSeq(), 3); + assertEquals(vr.getEndSeq(), 15); + + // set location to (40,50) - change to top left (40,50) + vr.setViewportLocation(40, 50); + assertEquals(vr.getStartRes(), 40); + assertEquals(vr.getEndRes(), 49); + assertEquals(vr.getStartSeq(), 50); + assertEquals(vr.getEndSeq(), 62); + + // set location past end of alignment - resets to leftmost pos + vr.setViewportLocation(63, 85); + assertEquals(vr.getStartRes(), 50); + assertEquals(vr.getEndRes(), 59); + assertEquals(vr.getStartSeq(), 67); + assertEquals(vr.getEndSeq(), 79); + + // hide some columns + al2.getHiddenColumns().hideColumns(20, 50); + vr.setViewportLocation(55, 4); + assertEquals(vr.getStartRes(), 19); + assertEquals(vr.getEndRes(), 28); + assertEquals(vr.getStartSeq(), 4); + assertEquals(vr.getEndSeq(), 16); + + // hide some sequences + al2.getHiddenSequences().hideSequence(al2.getSequenceAt(3)); + al2.getHiddenSequences().hideSequence(al2.getSequenceAt(4)); + vr.setViewportLocation(17, 5); + assertEquals(vr.getStartRes(), 17); + assertEquals(vr.getEndRes(), 26); + assertEquals(vr.getStartSeq(), 3); + assertEquals(vr.getEndSeq(), 15); + + // set wrapped mode + vr.setWrappedMode(true); + vr.setViewportLocation(1, 8); + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 9); + assertEquals(vr.getStartSeq(), 3); + assertEquals(vr.getEndSeq(), 15); + + // try further down the alignment + vr.setViewportLocation(57, 5); + assertEquals(vr.getStartRes(), 20); + assertEquals(vr.getEndRes(), 29); + assertEquals(vr.getStartSeq(), 3); + assertEquals(vr.getEndSeq(), 15); + } } // mock listener for property change events @@ -844,7 +951,15 @@ class MockPropChangeListener implements ViewportListenerI { firecount++; events.add(evt.getPropertyName()); - newvalues.add((Integer) evt.getNewValue()); + if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ)) + { + newvalues.add(((int[]) evt.getNewValue())[0]); + newvalues.add(((int[]) evt.getNewValue())[1]); + } + else + { + newvalues.add((Integer) evt.getNewValue()); + } } public boolean verify(int count, List eventslist, diff --git a/test/jalview/ws/dbsources/UniprotTest.java b/test/jalview/ws/dbsources/UniprotTest.java index 2d4be71..f98ef85 100644 --- a/test/jalview/ws/dbsources/UniprotTest.java +++ b/test/jalview/ws/dbsources/UniprotTest.java @@ -163,11 +163,11 @@ public class UniprotTest new StringReader(UNIPROT_XML)).get(0); /* - * name formatted as source | accession ids | names - * source database converted to Jalview canonical name + * name formatted with Uniprot Entry name */ - String expectedName = "UNIPROT|A9CKP4|A9CKP5|A9CKP4_AGRT5|A9CKP4_AGRT6"; - assertEquals(expectedName, Uniprot.getUniprotEntryId(entry)); + String expectedName = "A9CKP4_AGRT5|A9CKP4_AGRT6"; + assertEquals(expectedName, + Uniprot.getUniprotEntryId(entry)); } /**