From: hansonr Date: Thu, 18 Jul 2019 06:15:38 +0000 (+0100) Subject: JAL-3269 JAL-3370 JalviewJS interface upgrade X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=f820a6e0b7ec599b0e040109c3361cdb3e6d9aa0;p=jalview.git JAL-3269 JAL-3370 JalviewJS interface upgrade --- diff --git a/site-resources/images/coolJalviewBackgroundFading.png b/site-resources/images/coolJalviewBackgroundFading.png new file mode 100644 index 0000000..0a6eb6c Binary files /dev/null and b/site-resources/images/coolJalviewBackgroundFading.png differ diff --git a/site-resources/images/coolVeryLightBG.png b/site-resources/images/coolVeryLightBG.png new file mode 100644 index 0000000..ce32520 Binary files /dev/null and b/site-resources/images/coolVeryLightBG.png differ diff --git a/site-resources/jalview_embedded_example1.html b/site-resources/jalview_embedded_example1.html new file mode 100644 index 0000000..9151f33 --- /dev/null +++ b/site-resources/jalview_embedded_example1.html @@ -0,0 +1,118 @@ + + + +Embedded JalviewJS Example 1 + + + + + + + + + + + + +
Demonstration of embedded JalviewJS components
+
+This simple page illustrates how one can embed the JalviewJS desktop into a web page. +The basic idea is that we have something interesting to say — some sort of scientific context — something we want to get +across to our visitors with more than just text and images. The idea is to have a dynamic page that will involve user interaction. +

+We start with a Jalview desktop. You can't see it, because I have placed it in a div tag with style width:0px;height:0px just after the period that ends this sentence. +
+
+The idea is NOT to teach visitors how to use Jalview. The idea is to seamlessly integrate components of Jalview that can be used to enrich a discussion. +Like JSmol in Proteopedia. +For example, in the space to the right, after a few seconds, you will see an alignment frame. This alignment is for 15 genes that code for one of the domains in the ferredoxin family (NIR_SIR_ferr (PF03460)). +

+What you first see is just the first few bases. Doesn't look like much of an alignment, does it? But scroll to the right. +See the big block of red color? That's the Ferredoxin folddomain. +

+Select a few alignments by left-dragging across a few rows of the alignment to make a selection box. +Then click one of the buttons below to see more information about your selected subset of the alignment. +
    +
  • +
  • +
+ +
+ +
+

+The alignment frame will appear here momentarily. When it does, you can go ahead and manipulate the alignment with your mouse. +
+
+
+



+jalview-tree-div +
+
+
+



+jalview-pca-div +
+
+ + + + + +
+
+
+
+jalview-structureviewer-div +
+
+ +
+ + + + + + + +
+
+This is System.out. clear it
Add ?j2snocore to URL to see full class list; ?j2sdebug to use uncompressed j2s/core files
get _j2sClassList.txt +
+ + diff --git a/src/jalview/analysis/scoremodels/SimilarityParams.java b/src/jalview/analysis/scoremodels/SimilarityParams.java index 5c47703..69ca4035 100644 --- a/src/jalview/analysis/scoremodels/SimilarityParams.java +++ b/src/jalview/analysis/scoremodels/SimilarityParams.java @@ -104,6 +104,7 @@ public class SimilarityParams implements SimilarityParamsI private boolean denominateByShortestLength; + /** * Constructor * @@ -124,6 +125,19 @@ public class SimilarityParams implements SimilarityParamsI denominateByShortestLength = shortestLength; } + /** + * BH added a non-Groovy "standard" set for JalviewJS + * + * @param isPCA + */ + public SimilarityParams(boolean isPCA) + { + includeGappedColumns = true; + matchGaps = !isPCA; + includeGaps = true; + denominateByShortestLength = false; + } + @Override public boolean includeGaps() { diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index 84fec45..047356b 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -34,6 +34,7 @@ import jalview.ext.so.SequenceOntology; import jalview.gui.AlignFrame; import jalview.gui.AlignViewport; import jalview.gui.AlignmentPanel; +import jalview.gui.CalculationChooser; import jalview.gui.Desktop; import jalview.gui.Preferences; import jalview.gui.PromptUserConfig; @@ -51,7 +52,6 @@ import jalview.io.IdentifyFile; import jalview.io.NewickFile; import jalview.io.gff.SequenceOntologyFactory; import jalview.javascript.JSFunctionExec; -import jalview.javascript.JalviewLiteJsApi; import jalview.javascript.MouseOverStructureListener; import jalview.renderer.seqfeatures.FeatureRenderer; import jalview.schemes.ColourSchemeI; @@ -109,7 +109,7 @@ import netscape.javascript.JSObject; * @author $author$ * @version $Revision$ */ -public class Jalview implements ApplicationSingletonI, JalviewLiteJsApi +public class Jalview implements ApplicationSingletonI, JalviewJSApi { public static Jalview getInstance() @@ -142,7 +142,7 @@ public class Jalview implements ApplicationSingletonI, JalviewLiteJsApi public String appletResourcePath; - private JalviewAppLoader appLoader; + JalviewAppLoader appLoader; protected JSFunctionExec jsFunctionExec; @@ -255,6 +255,27 @@ public class Jalview implements ApplicationSingletonI, JalviewLiteJsApi getInstance().doMain(args); } + /** + * Allow an outside entity to initiate the second half of argument parsing + * (only). + * + * @param args + * @return null is good + */ + @Override + public Object parseArguments(String[] args) + { + + try + { + ArgsParser aparser = new ArgsParser(args); + return parseArguments(aparser, false); + } catch (Throwable t) + { + return t; + } + } + private static void logClass(String name) { // BH - for event debugging in JavaScript @@ -529,6 +550,15 @@ public class Jalview implements ApplicationSingletonI, JalviewLiteJsApi } } + parseArguments(aparser, true); + } + + private Object parseArguments(ArgsParser aparser, + boolean isStartup) + { + boolean isJS = Platform.isJS(); + + Desktop desktop = (headless ? null : Desktop.getInstance()); // script to execute after all loading is // completed one way or another // extract groovy argument and execute if necessary @@ -855,6 +885,8 @@ public class Jalview implements ApplicationSingletonI, JalviewLiteJsApi } desktop.setInBatchMode(false); } + + return null; } private boolean checkStartVamas(ArgsParser aparser) @@ -938,7 +970,8 @@ public class Jalview implements ApplicationSingletonI, JalviewLiteJsApi // the Jalview specific remnants can now be imported into the new // session at the user's leisure. Cache.log.info( - "Skipping Push for import of data into existing vamsas session."); // TODO: + "Skipping Push for import of data into existing vamsas session."); + // TODO: // enable // this // when @@ -1513,169 +1546,172 @@ public class Jalview implements ApplicationSingletonI, JalviewLiteJsApi appLoader.load(app); } + /** + * + * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences() + */ @Override public String getSelectedSequences() { - // TODO Auto-generated method stub - return null; + return getSelectedSequencesFrom(getCurrentAlignFrame()); } + /** + * + * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String) + */ @Override public String getSelectedSequences(String sep) { - // TODO Auto-generated method stub - return null; + return getSelectedSequencesFrom(getCurrentAlignFrame(), sep); } + /** + * + * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui + * .AlignFrame) + */ @Override public String getSelectedSequencesFrom(AlignFrameI alf) { - // TODO Auto-generated method stub - return null; + return getSelectedSequencesFrom(alf, null); } + /** + * + * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui + * .AlignFrame, java.lang.String) + */ @Override - public String getSelectedSequencesFrom(AlignFrameI alf, - String sep) + public String getSelectedSequencesFrom(AlignFrameI alf, String sep) { - // TODO Auto-generated method stub - return null; + return appLoader.getSelectedSequencesFrom(alf, sep); } + /** + * + * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui + * .AlignFrame, java.lang.String) + */ @Override public void highlight(String sequenceId, String position, String alignedPosition) { - // TODO Auto-generated method stub - + highlightIn(getCurrentAlignFrame(), sequenceId, position, + alignedPosition); } @Override public void highlightIn(AlignFrameI alf, String sequenceId, String position, String alignedPosition) { - // TODO Auto-generated method stub - + appLoader.highlightIn(alf, sequenceId, position, + alignedPosition); } @Override public void select(String sequenceIds, String columns) { - // TODO Auto-generated method stub - + selectIn(getCurrentAlignFrame(), sequenceIds, columns, null); } @Override public void select(String sequenceIds, String columns, String sep) { - // TODO Auto-generated method stub - + selectIn(getCurrentAlignFrame(), sequenceIds, columns, sep); } @Override public void selectIn(AlignFrameI alf, String sequenceIds, String columns) { - // TODO Auto-generated method stub - + selectIn(alf, sequenceIds, columns, null); } @Override public void selectIn(AlignFrameI alf, String sequenceIds, String columns, String sep) { - // TODO Auto-generated method stub - + appLoader.selectIn(alf, sequenceIds, columns, sep); } @Override public String getSelectedSequencesAsAlignment(String format, String suffix) { - // TODO Auto-generated method stub - return null; + return getSelectedSequencesAsAlignmentFrom(getCurrentAlignFrame(), + format, suffix); } @Override public String getSelectedSequencesAsAlignmentFrom( - AlignFrameI alf, String format, String suffix) + AlignFrameI alf, String format, String sep) { - // TODO Auto-generated method stub - return null; + return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format, sep); } @Override public String getAlignmentOrder() { - // TODO Auto-generated method stub - return null; + return getAlignmentFrom(getCurrentAlignFrame(), null); } @Override public String getAlignmentOrderFrom(AlignFrameI alf) { - // TODO Auto-generated method stub - return null; + return getAlignmentFrom(alf, null); } @Override public String getAlignmentOrderFrom(AlignFrameI alf, String sep) { - // TODO Auto-generated method stub - return null; + return appLoader.getAlignmentOrderFrom(alf, sep); } @Override public String orderBy(String order, String undoName) { - // TODO Auto-generated method stub - return null; + return orderBy(order, undoName, null); } @Override public String orderBy(String order, String undoName, String sep) { - // TODO Auto-generated method stub - return null; + return orderAlignmentBy(getCurrentAlignFrame(), order, undoName, sep); } @Override public String orderAlignmentBy(AlignFrameI alf, String order, String undoName, String sep) { - // TODO Auto-generated method stub - return null; + return appLoader.orderAlignmentBy(alf, order, undoName, sep); } @Override public String getAlignment(String format) { - // TODO Auto-generated method stub - return null; + return getAlignmentFrom(null, format, null); } @Override public String getAlignmentFrom(AlignFrameI alf, String format) { - // TODO Auto-generated method stub - return null; + return getAlignmentFrom(alf, format, null); } @Override public String getAlignment(String format, String suffix) { - // TODO Auto-generated method stub - return null; + return getAlignmentFrom(getCurrentAlignFrame(), format, suffix); } @Override public String getAlignmentFrom(AlignFrameI alf, String format, String suffix) { - // TODO Auto-generated method stub - return null; + return appLoader.getAlignmentFrom(alf, format, suffix); } @Override @@ -1769,8 +1805,8 @@ public class Jalview implements ApplicationSingletonI, JalviewLiteJsApi public AlignFrameI loadAlignment(String text, String title) { - // TODO Auto-generated method stub - return null; + return appLoader.loadAlignment(text, AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT, title); } @Override @@ -1830,97 +1866,118 @@ public class Jalview implements ApplicationSingletonI, JalviewLiteJsApi public boolean addPdbFile(AlignFrameI alFrame, String sequenceId, String pdbEntryString, String pdbFile) { - // TODO Auto-generated method stub - return false; + return appLoader.addPdbFile(alFrame, sequenceId, pdbEntryString, + pdbFile); } @Override public void scrollViewToIn(AlignFrameI alf, String topRow, String leftHandColumn) { - // TODO Auto-generated method stub - + appLoader.scrollViewToIn(alf, topRow, leftHandColumn); } @Override public void scrollViewToRowIn(AlignFrameI alf, String topRow) { - // TODO Auto-generated method stub - + appLoader.scrollViewToRowIn(alf, topRow); } @Override public void scrollViewToColumnIn(AlignFrameI alf, String leftHandColumn) { - // TODO Auto-generated method stub - + appLoader.scrollViewToColumnIn(alf, leftHandColumn); } @Override public String getFeatureGroups() { - // TODO Auto-generated method stub - return null; + return getFeatureGroupsOn(getCurrentAlignFrame()); } @Override public String getFeatureGroupsOn(AlignFrameI alf) { - // TODO Auto-generated method stub - return null; + return appLoader.getFeatureGroupsOn(alf); } @Override public String getFeatureGroupsOfState(boolean visible) { - // TODO Auto-generated method stub - return null; + return getFeatureGroupsOfStateOn(getCurrentAlignFrame(), visible); } @Override public String getFeatureGroupsOfStateOn(AlignFrameI alf, boolean visible) { - // TODO Auto-generated method stub - return null; + return appLoader.getFeatureGroupsOfStateOn(alf, visible); } @Override public void setFeatureGroupStateOn(AlignFrameI alf, String groups, boolean state) { - // TODO Auto-generated method stub - + setFeatureGroupStateOn(alf, groups, state); } @Override public void setFeatureGroupState(String groups, boolean state) { - // TODO Auto-generated method stub - + appLoader.setFeatureGroupStateOn(getCurrentAlignFrame(), groups, state); } @Override public String getSeparator() { - // TODO Auto-generated method stub - return null; + return appLoader.getSeparator(); } @Override public void setSeparator(String separator) { - // TODO Auto-generated method stub - + appLoader.setSeparator(separator); } @Override public String getJsMessage(String messageclass, String viewId) { - // TODO Auto-generated method stub + // see http://www.jalview.org/examples/jalviewLiteJs.html return null; } + /** + * Open a new Tree panel on the desktop statically. Params are standard (not + * set by Groovy). No dialog is opened. + * + * @param af + * @param treeType + * @param modelName + * @return null, or the string "label.you_need_at_least_n_sequences" if number + * of sequences selected is inappropriate + */ + @Override + public Object openTreePanel(AlignFrame af, String treeType, + String modelName) + { + return CalculationChooser.openTreePanel(af, treeType, modelName, null); + } + + /** + * public static method for JalviewJS API to open a PCAPanel without + * necessarily using a dialog. + * + * @param af + * @param modelName + * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences" + * if number of sequences selected is inappropriate + */ + @Override + public Object openPcaPanel(AlignFrame af, String modelName) + { + return CalculationChooser.openPcaPanel(af, modelName, null); + } + } diff --git a/src/jalview/bin/JalviewAppLoader.java b/src/jalview/bin/JalviewAppLoader.java index 39e7c21..0d911c0 100644 --- a/src/jalview/bin/JalviewAppLoader.java +++ b/src/jalview/bin/JalviewAppLoader.java @@ -1,12 +1,26 @@ package jalview.bin; +import jalview.api.AlignFrameI; import jalview.api.JalviewApp; import jalview.api.StructureSelectionManagerProvider; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.AlignmentOrder; +import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; +import jalview.gui.AlignFrame; +import jalview.gui.AlignViewport; +import jalview.gui.Desktop; import jalview.io.AnnotationFile; +import jalview.io.AppletFormatAdapter; import jalview.io.DataSourceType; +import jalview.io.FileFormat; +import jalview.io.FileFormatI; +import jalview.io.FileFormats; +import jalview.io.IdentifyFile; import jalview.io.JPredFile; import jalview.io.JnetAnnotationMaker; import jalview.io.NewickFile; @@ -14,6 +28,8 @@ import jalview.structure.StructureSelectionManager; import jalview.util.HttpUtils; import jalview.util.MessageManager; +import java.awt.EventQueue; +import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -33,7 +49,19 @@ public class JalviewAppLoader private boolean debug; - private String separator; + String separator = "\u00AC"; // JalviewLite note: the default used to + // be '|', but many sequence IDS include + // pipes. + + public String getSeparator() + { + return separator; + } + + public void setSeparator(String separator) + { + this.separator = separator; + } public JalviewAppLoader(boolean debug) { @@ -730,4 +758,581 @@ public class JalviewAppLoader return arrayToSeparatorList(array, separator); } + public String getSelectedSequencesFrom(AlignFrameI alf, String sep) + { + StringBuffer result = new StringBuffer(""); + if (sep == null || sep.length() == 0) + { + sep = separator; // "+0x00AC; + } + AlignViewport v = ((AlignFrame) alf).getViewport(); + if (v.getSelectionGroup() != null) + { + SequenceI[] seqs = v.getSelectionGroup() + .getSequencesInOrder(v.getAlignment()); + + for (int i = 0; i < seqs.length; i++) + { + result.append(seqs[i].getName()); + result.append(sep); + } + } + + return result.toString(); + } + + public void setFeatureGroupStateOn(final AlignFrameI alf, + final String groups, boolean state) + { + java.awt.EventQueue.invokeLater(new Runnable() + { + @Override + public void run() + { + ((AlignFrame) alf).setFeatureGroupState( + separatorListToArray(groups, separator), state); + } + }); + } + + public String getFeatureGroupsOfStateOn(AlignFrameI alf, boolean visible) + { + return arrayToSeparatorList( + ((AlignFrame) alf).getFeatureGroupsOfState(visible)); + } + + public void scrollViewToIn(final AlignFrameI alf, final String topRow, + final String leftHandColumn) + { + java.awt.EventQueue.invokeLater(new Runnable() + { + @Override + public void run() + { + try + { + ((AlignFrame) alf).scrollTo(new Integer(topRow).intValue(), + new Integer(leftHandColumn).intValue()); + + } catch (Exception ex) + { + System.err.println("Couldn't parse integer arguments (topRow='" + + topRow + "' and leftHandColumn='" + leftHandColumn + + "')"); + ex.printStackTrace(); + } + } + }); + } + + public void scrollViewToRowIn(final AlignFrameI alf, final String topRow) + { + + java.awt.EventQueue.invokeLater(new Runnable() + { + @Override + public void run() + { + try + { + ((AlignFrame) alf).scrollToRow(new Integer(topRow).intValue()); + + } catch (Exception ex) + { + System.err.println("Couldn't parse integer arguments (topRow='" + + topRow + "')"); + ex.printStackTrace(); + } + + } + }); + } + + public void scrollViewToColumnIn(final AlignFrameI alf, + final String leftHandColumn) + { + java.awt.EventQueue.invokeLater(new Runnable() + { + + @Override + public void run() + { + try + { + ((AlignFrame) alf) + .scrollToColumn(new Integer(leftHandColumn).intValue()); + + } catch (Exception ex) + { + System.err.println( + "Couldn't parse integer arguments (leftHandColumn='" + + leftHandColumn + "')"); + ex.printStackTrace(); + } + } + }); + + } + + public boolean addPdbFile(AlignFrameI alf, String sequenceId, + String pdbEntryString, String pdbFile) + { + AlignFrame alFrame = (AlignFrame) alf; + SequenceI toaddpdb = alFrame.getViewport().getAlignment() + .findName(sequenceId); + boolean needtoadd = false; + if (toaddpdb != null) + { + Vector pdbe = toaddpdb.getAllPDBEntries(); + PDBEntry pdbentry = null; + if (pdbe != null && pdbe.size() > 0) + { + for (int pe = 0, peSize = pdbe.size(); pe < peSize; pe++) + { + pdbentry = pdbe.elementAt(pe); + if (!pdbentry.getId().equals(pdbEntryString) + && !pdbentry.getFile().equals(pdbFile)) + { + pdbentry = null; + } + else + { + continue; + } + } + } + if (pdbentry == null) + { + pdbentry = new PDBEntry(); + pdbentry.setId(pdbEntryString); + pdbentry.setFile(pdbFile); + needtoadd = true; // add this new entry to sequence. + } + // resolve data source + // TODO: this code should be a refactored to an io package + DataSourceType protocol = AppletFormatAdapter.resolveProtocol(pdbFile, + FileFormat.PDB); + if (protocol == null) + { + return false; + } + if (needtoadd) + { + pdbentry.setProperty("protocol", protocol); + toaddpdb.addPDBId(pdbentry); + alFrame.alignPanel.getStructureSelectionManager() + .registerPDBEntry(pdbentry); + } + } + return true; + } + + public AlignFrameI loadAlignment(String text, int width, int height, + String title) + { + AlignmentI al = null; + + try + { + FileFormatI format = new IdentifyFile().identify(text, + DataSourceType.PASTE); + al = new AppletFormatAdapter().readFile(text, DataSourceType.PASTE, + format); + if (al.getHeight() > 0) + { + return new AlignFrame(al, width, height, title); + } + } catch (IOException ex) + { + ex.printStackTrace(); + } + return null; + } + + public String getFeatureGroupsOn(AlignFrameI alf) + { + return arrayToSeparatorList( + ((AlignFrame) alf).getFeatureGroups()); + } + + public void highlightIn(final AlignFrameI alf, final String sequenceId, + final String position, final String alignedPosition) + { + // TODO: could try to highlight in all alignments if alf==null + jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher( + ((AlignFrame) alf).getViewport().getAlignment() + .getSequencesArray()); + final SequenceI sq = matcher.findIdMatch(sequenceId); + if (sq != null) + { + int apos = -1; + try + { + apos = new Integer(position).intValue(); + apos--; + } catch (NumberFormatException ex) + { + return; + } + final int pos = apos; + // use vamsas listener to broadcast to all listeners in scope + if (alignedPosition != null && (alignedPosition.trim().length() == 0 + || alignedPosition.toLowerCase().indexOf("false") > -1)) + { + java.awt.EventQueue.invokeLater(new Runnable() + { + @Override + public void run() + { + StructureSelectionManager + .getStructureSelectionManager(Desktop.getInstance()) + .mouseOverVamsasSequence(sq, sq.findIndex(pos), null); + } + }); + } + else + { + java.awt.EventQueue.invokeLater(new Runnable() + { + @Override + public void run() + { + StructureSelectionManager + .getStructureSelectionManager(Desktop.getInstance()) + .mouseOverVamsasSequence(sq, pos, null); + } + }); + } + } + } + + public void selectIn(final AlignFrameI alf, String sequenceIds, + String columns, String sep) + { + if (sep == null || sep.length() == 0) + { + sep = separator; + } + else + { + if (debug) + { + System.err.println("Selecting region using separator string '" + + separator + "'"); + } + } + // deparse fields + String[] ids = JalviewAppLoader.separatorListToArray(sequenceIds, sep); + String[] cols = JalviewAppLoader.separatorListToArray(columns, sep); + final SequenceGroup sel = new SequenceGroup(); + final ColumnSelection csel = new ColumnSelection(); + AlignmentI al = ((AlignFrame) alf).getViewport().getAlignment(); + jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher( + ((AlignFrame) alf).getViewport().getAlignment() + .getSequencesArray()); + int start = 0, end = al.getWidth(), alw = al.getWidth(); + boolean seqsfound = true; + if (ids != null && ids.length > 0) + { + seqsfound = false; + for (int i = 0; i < ids.length; i++) + { + if (ids[i].trim().length() == 0) + { + continue; + } + SequenceI sq = matcher.findIdMatch(ids[i]); + if (sq != null) + { + seqsfound = true; + sel.addSequence(sq, false); + } + } + } + boolean inseqpos = false; + if (cols != null && cols.length > 0) + { + boolean seset = false; + for (int i = 0; i < cols.length; i++) + { + String cl = cols[i].trim(); + if (cl.length() == 0) + { + continue; + } + int p; + if ((p = cl.indexOf("-")) > -1) + { + int from = -1, to = -1; + try + { + from = new Integer(cl.substring(0, p)).intValue(); + from--; + } catch (NumberFormatException ex) + { + System.err.println( + "ERROR: Couldn't parse first integer in range element column selection string '" + + cl + "' - format is 'from-to'"); + return; + } + try + { + to = new Integer(cl.substring(p + 1)).intValue(); + to--; + } catch (NumberFormatException ex) + { + System.err.println( + "ERROR: Couldn't parse second integer in range element column selection string '" + + cl + "' - format is 'from-to'"); + return; + } + if (from >= 0 && to >= 0) + { + // valid range + if (from < to) + { + int t = to; + to = from; + to = t; + } + if (!seset) + { + start = from; + end = to; + seset = true; + } + else + { + // comment to prevent range extension + if (start > from) + { + start = from; + } + if (end < to) + { + end = to; + } + } + for (int r = from; r <= to; r++) + { + if (r >= 0 && r < alw) + { + csel.addElement(r); + } + } + if (debug) + { + System.err.println("Range '" + cl + "' deparsed as [" + from + + "," + to + "]"); + } + } + else + { + System.err.println("ERROR: Invalid Range '" + cl + + "' deparsed as [" + from + "," + to + "]"); + } + } + else + { + int r = -1; + try + { + r = new Integer(cl).intValue(); + r--; + } catch (NumberFormatException ex) + { + if (cl.toLowerCase().equals("sequence")) + { + // we are in the dataset sequence's coordinate frame. + inseqpos = true; + } + else + { + System.err.println( + "ERROR: Couldn't parse integer from point selection element of column selection string '" + + cl + "'"); + return; + } + } + if (r >= 0 && r <= alw) + { + if (!seset) + { + start = r; + end = r; + seset = true; + } + else + { + // comment to prevent range extension + if (start > r) + { + start = r; + } + if (end < r) + { + end = r; + } + } + csel.addElement(r); + if (debug) + { + System.err.println("Point selection '" + cl + + "' deparsed as [" + r + "]"); + } + } + else + { + System.err.println("ERROR: Invalid Point selection '" + cl + + "' deparsed as [" + r + "]"); + } + } + } + } + if (seqsfound) + { + // we only propagate the selection when it was the null selection, or the + // given sequences were found in the alignment. + if (inseqpos && sel.getSize() > 0) + { + // assume first sequence provides reference frame ? + SequenceI rs = sel.getSequenceAt(0); + start = rs.findIndex(start); + end = rs.findIndex(end); + List cs = new ArrayList<>(csel.getSelected()); + csel.clear(); + for (Integer selectedCol : cs) + { + csel.addElement(rs.findIndex(selectedCol)); + } + } + sel.setStartRes(start); + sel.setEndRes(end); + EventQueue.invokeLater(new Runnable() + { + @Override + public void run() + { + ((AlignFrame) alf).select(sel, csel, ((AlignFrame) alf) + .getCurrentView().getAlignment().getHiddenColumns()); + } + }); + } + } + + public String getAlignmentOrderFrom(AlignFrameI alf, String sep) + { + AlignmentI alorder = ((AlignFrame) alf).getViewport().getAlignment(); + String[] order = new String[alorder.getHeight()]; + for (int i = 0; i < order.length; i++) + { + order[i] = alorder.getSequenceAt(i).getName(); + } + return arrayToSeparatorList(order); + } + + public String getSelectedSequencesAsAlignmentFrom(AlignFrameI alf, + String format, String suffix) + { + try + { + AlignViewport vp = ((AlignFrame) alf).getViewport(); + FileFormatI theFormat = FileFormats.getInstance().forName(format); + boolean seqlimits = (suffix == null + || suffix.equalsIgnoreCase("true")); + if (vp.getSelectionGroup() != null) + { + // JBPNote: getSelectionAsNewSequence behaviour has changed - this + // method now returns a full copy of sequence data + // TODO consider using getSequenceSelection instead here + String reply = new AppletFormatAdapter().formatSequences(theFormat, + new Alignment(vp.getSelectionAsNewSequence()), + seqlimits); + return reply; + } + } catch (IllegalArgumentException ex) + { + ex.printStackTrace(); + return "Error retrieving alignment, possibly invalid format specifier: " + + format; + } + return ""; + } + + public String orderAlignmentBy(AlignFrameI alf, String order, + String undoName, String sep) + { + if (sep == null || sep.length() == 0) + { + sep = separator; + } + String[] ids = JalviewAppLoader.separatorListToArray(order, sep); + SequenceI[] sqs = null; + if (ids != null && ids.length > 0) + { + jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher( + ((AlignFrame) alf).getViewport().getAlignment() + .getSequencesArray()); + int s = 0; + sqs = new SequenceI[ids.length]; + for (int i = 0; i < ids.length; i++) + { + if (ids[i].trim().length() == 0) + { + continue; + } + SequenceI sq = matcher.findIdMatch(ids[i]); + if (sq != null) + { + sqs[s++] = sq; + } + } + if (s > 0) + { + SequenceI[] sqq = new SequenceI[s]; + System.arraycopy(sqs, 0, sqq, 0, s); + sqs = sqq; + } + else + { + sqs = null; + } + } + if (sqs == null) + { + return ""; + } + ; + final AlignmentOrder aorder = new AlignmentOrder(sqs); + + if (undoName != null && undoName.trim().length() == 0) + { + undoName = null; + } + final String _undoName = undoName; + // TODO: deal with synchronization here: cannot raise any events until after + // this has returned. + return ((AlignFrame) alf).sortBy(aorder, _undoName) ? "true" : ""; + } + + public String getAlignmentFrom(AlignFrameI alf, String format, + String suffix) + { + try + { + boolean seqlimits = (suffix == null + || suffix.equalsIgnoreCase("true")); + + FileFormatI theFormat = FileFormats.getInstance().forName(format); + String reply = new AppletFormatAdapter().formatSequences(theFormat, + ((AlignFrame) alf).getViewport().getAlignment(), seqlimits); + return reply; + } catch (IllegalArgumentException ex) + { + ex.printStackTrace(); + return "Error retrieving alignment, possibly invalid format specifier: " + + format; + } + } + } \ No newline at end of file diff --git a/src/jalview/bin/JalviewJSApi.java b/src/jalview/bin/JalviewJSApi.java new file mode 100644 index 0000000..61850a4 --- /dev/null +++ b/src/jalview/bin/JalviewJSApi.java @@ -0,0 +1,41 @@ +package jalview.bin; + +import jalview.gui.AlignFrame; +import jalview.javascript.JalviewLiteJsApi; + +/** + * JAL-3369 JalviewJS API BH 2019.07.17 + * + * @author hansonr + * + */ +public interface JalviewJSApi extends JalviewLiteJsApi +{ + + Object parseArguments(String[] args); + + /** + * Open a new Tree panel on the desktop statically. Params are standard (not + * set by Groovy). No dialog is opened. + * + * @param af + * @param treeType + * @param modelName + * @return null, or the string "label.you_need_at_least_n_sequences" if number + * of sequences selected is inappropriate + */ + public Object openTreePanel(AlignFrame af, String treeType, + String modelName); + + /** + * public static method for JalviewJS API to open a PCAPanel without + * necessarily using a dialog. + * + * @param af + * @param modelName + * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences" + * if number of sequences selected is inappropriate + */ + public Object openPcaPanel(AlignFrame af, String modelName); + +} diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 0067a6b..386c39b 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -33,6 +33,7 @@ import jalview.api.AlignViewControllerGuiI; import jalview.api.AlignViewControllerI; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; +//from JalviewLite imports import jalview.api.FeatureRenderer; import jalview.api.FeatureSettingsControllerI; import jalview.api.SplitContainerI; import jalview.api.ViewStyleI; @@ -5789,6 +5790,67 @@ public class AlignFrame extends GAlignFrame } + /** + * BH 2019 from JalviewLite + * + * get sequence feature groups that are hidden or shown + * + * @param visible + * true is visible + * @return list + */ + public String[] getFeatureGroupsOfState(boolean visible) + { + jalview.api.FeatureRenderer fr = null; + if (alignPanel != null + && (fr = alignPanel + .getFeatureRenderer()) != null) + { + List gps = fr.getGroups(visible); + String[] _gps = gps.toArray(new String[gps.size()]); + return _gps; + } + return null; + } + + public void scrollTo(int row, int column) + { + alignPanel.getSeqPanel().scrollTo(row, column); + } + + public void scrollToRow(int row) + { + alignPanel.getSeqPanel().scrollToRow(row); + } + + public void scrollToColumn(int column) + { + alignPanel.getSeqPanel().scrollToColumn(column); + } + + /** + * + * @return list of feature groups on the view + */ + public String[] getFeatureGroups() + { + jalview.api.FeatureRenderer fr = null; + if (alignPanel != null + && (fr = alignPanel.getFeatureRenderer()) != null) + { + List gps = fr.getFeatureGroups(); + String[] _gps = gps.toArray(new String[gps.size()]); + return _gps; + } + return null; + } + + public void select(SequenceGroup sel, ColumnSelection csel, + HiddenColumns hidden) + { + alignPanel.getSeqPanel().selection(sel, csel, hidden, null); + } + } class PrintThread extends Thread diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index fa30e09..f723794 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -1730,4 +1730,109 @@ public class AlignmentPanel extends GAlignmentPanel implements return seqPanel.seqCanvas.getSequenceRenderer(); } + public boolean scrollTo(int ostart, int end, int seqIndex, + boolean scrollToNearest, boolean redrawOverview) + { + int startv, endv, starts, ends;// , width; + + int start = -1; + if (av.hasHiddenColumns()) + { + AlignmentI al = av.getAlignment(); + start = al.getHiddenColumns().absoluteToVisibleColumn(ostart); + end = al.getHiddenColumns().absoluteToVisibleColumn(end); + if (start == end) + { + if (!scrollToNearest && !al.getHiddenColumns().isVisible(ostart)) + { + // don't scroll - position isn't visible + return false; + } + } + } + else + { + start = ostart; + } + + ViewportRanges ranges = av.getRanges(); + if (!av.getWrapAlignment()) + { + /* + * int spos=av.getStartRes(),sqpos=av.getStartSeq(); if ((startv = + * av.getStartRes()) >= start) { spos=start-1; // seqIn // + * setScrollValues(start - 1, seqIndex); } else if ((endv = + * av.getEndRes()) <= end) { // setScrollValues(spos=startv + 1 + end - + * endv, seqIndex); spos=startv + 1 + end - endv; } else if ((starts = + * av.getStartSeq()) > seqIndex) { setScrollValues(av.getStartRes(), + * seqIndex); } else if ((ends = av.getEndSeq()) <= seqIndex) { + * setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1); } + */ + + // below is scrolling logic up to Jalview 2.8.2 + // if ((av.getStartRes() > end) + // || (av.getEndRes() < start) + // || ((av.getStartSeq() > seqIndex) || (av.getEndSeq() < seqIndex))) + // { + // if (start > av.getAlignment().getWidth() - hextent) + // { + // start = av.getAlignment().getWidth() - hextent; + // if (start < 0) + // { + // start = 0; + // } + // + // } + // if (seqIndex > av.getAlignment().getHeight() - vextent) + // { + // seqIndex = av.getAlignment().getHeight() - vextent; + // if (seqIndex < 0) + // { + // seqIndex = 0; + // } + // } + // setScrollValues(start, seqIndex); + // } + // logic copied from jalview.gui.AlignmentPanel: + if ((startv = ranges.getStartRes()) >= start) + { + /* + * Scroll left to make start of search results visible + */ + setScrollValues(start - 1, seqIndex); + } + else if ((endv = ranges.getEndRes()) <= end) + { + /* + * Scroll right to make end of search results visible + */ + setScrollValues(startv + 1 + end - endv, seqIndex); + } + else if ((starts = ranges.getStartSeq()) > seqIndex) + { + /* + * Scroll up to make start of search results visible + */ + setScrollValues(ranges.getStartRes(), seqIndex); + } + else if ((ends = ranges.getEndSeq()) <= seqIndex) + { + /* + * Scroll down to make end of search results visible + */ + setScrollValues(ranges.getStartRes(), starts + seqIndex - ends + 1); + } + /* + * Else results are already visible - no need to scroll + */ + } + else + { + ranges.scrollToWrappedVisible(start); + } + + paintAlignment(redrawOverview, false); + return true; + } + } diff --git a/src/jalview/gui/CalculationChooser.java b/src/jalview/gui/CalculationChooser.java index 45bf2d7..13506a5 100644 --- a/src/jalview/gui/CalculationChooser.java +++ b/src/jalview/gui/CalculationChooser.java @@ -62,8 +62,13 @@ import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; /** - * A dialog where a user can choose and action Tree or PCA calculation options + * A dialog where a user can choose and action Tree or PCA calculation options. + * + * Allows also for dialog-free static methods openPCAPanel(...) and + * openTreePanel(...) for scripted use. + * */ +@SuppressWarnings("serial") public class CalculationChooser extends JPanel { /* @@ -74,7 +79,7 @@ public class CalculationChooser extends JPanel */ private static boolean treeMatchGaps = true; - private static final Font VERDANA_11PT = new Font("Verdana", 0, 11); + private static Font VERDANA_11PT; private static final int MIN_TREE_SELECTION = 3; @@ -102,7 +107,7 @@ public class CalculationChooser extends JPanel private JCheckBox shorterSequence; - final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer(); + private static ComboBoxTooltipRenderer renderer; // BH was not static List tips = new ArrayList<>(); @@ -112,6 +117,37 @@ public class CalculationChooser extends JPanel private PCAPanel pcaPanel; /** + * Open a new Tree panel on the desktop statically. Params are standard (not + * set by Groovy). No dialog is opened. + * + * @param af + * @param treeType + * @param modelName + * @return null if successful; the string + * "label.you_need_at_least_n_sequences" if number of sequences + * selected is inappropriate + */ + public static Object openTreePanel(AlignFrame af, String treeType, + String modelName) + { + return openTreePanel(af, treeType, modelName, null); + } + + /** + * public static method for JalviewJS API to open a PCAPanel without + * necessarily using a dialog. + * + * @param af + * @param modelName + * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences" + * if number of sequences selected is inappropriate + */ + public static Object openPcaPanel(AlignFrame af, String modelName) + { + return openPcaPanel(af, modelName, null); + } + + /** * Constructor * * @param af @@ -232,6 +268,10 @@ public class CalculationChooser extends JPanel paramsPanel.add(includeGappedColumns); paramsPanel.add(shorterSequence); + if (VERDANA_11PT == null) + { + VERDANA_11PT = new Font("Verdana", 0, 11); + } /* * OK / Cancel buttons */ @@ -380,7 +420,11 @@ public class CalculationChooser extends JPanel */ protected JComboBox buildModelOptionsList() { - final JComboBox scoreModelsCombo = new JComboBox<>(); + JComboBox scoreModelsCombo = new JComboBox<>(); + if (renderer == null) + { + renderer = new ComboBoxTooltipRenderer(); + } scoreModelsCombo.setRenderer(renderer); /* @@ -538,6 +582,63 @@ public class CalculationChooser extends JPanel */ protected void openTreePanel(String modelName, SimilarityParamsI params) { + Object ret = openTreePanel(af, + neighbourJoining.isSelected() ? TreeBuilder.NEIGHBOUR_JOINING + : TreeBuilder.AVERAGE_DISTANCE, + modelName, params); + if (ret instanceof String) + { + JvOptionPane.showMessageDialog(this, // was opening on Desktop? + MessageManager.formatMessage( + (String) ret, + MIN_TREE_SELECTION), + MessageManager.getString("label.not_enough_sequences"), + JvOptionPane.WARNING_MESSAGE); + + } + } + + /** + * Open a new PCA panel on the desktop + * + * @param modelName + * @param params + */ + protected void openPcaPanel(String modelName, SimilarityParamsI params) + { + Object ret = openPcaPanel(af, modelName, params); + if (ret instanceof String) + { + JvOptionPane.showInternalMessageDialog(this, + MessageManager.formatMessage( + (String) ret, + MIN_PCA_SELECTION), + MessageManager + .getString("label.sequence_selection_insufficient"), + JvOptionPane.WARNING_MESSAGE); + } + else + { + // only used for test suite + pcaPanel = (PCAPanel) ret; + } + + } + + /** + * Open a new Tree panel on the desktop statically + * + * @param af + * @param treeType + * @param modelName + * @param params + * @return null, or the string "label.you_need_at_least_n_sequences" if number + * of sequences selected is inappropriate + */ + public static Object openTreePanel(AlignFrame af, String treeType, + String modelName, SimilarityParamsI params) + { + /* * gui validation shouldn't allow insufficient sequences here, but leave * this check in in case this method gets exposed programmatically in future @@ -546,56 +647,58 @@ public class CalculationChooser extends JPanel SequenceGroup sg = viewport.getSelectionGroup(); if (sg != null && sg.getSize() < MIN_TREE_SELECTION) { - JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), - MessageManager.formatMessage( - "label.you_need_at_least_n_sequences", - MIN_TREE_SELECTION), - MessageManager.getString("label.not_enough_sequences"), - JvOptionPane.WARNING_MESSAGE); - return; + return "label.you_need_at_least_n_sequences"; + } + + if (params == null) + { + params = getSimilarityParameters(false); } - String treeType = neighbourJoining.isSelected() - ? TreeBuilder.NEIGHBOUR_JOINING - : TreeBuilder.AVERAGE_DISTANCE; af.newTreePanel(treeType, modelName, params); + return null; } /** - * Open a new PCA panel on the desktop + * public static method for JalviewJS API * + * @param af * @param modelName * @param params + * @return the PCAPanel, or null if number of sequences selected is + * inappropriate */ - protected void openPcaPanel(String modelName, SimilarityParamsI params) + public static Object openPcaPanel(AlignFrame af, String modelName, + SimilarityParamsI params) { + AlignViewport viewport = af.getViewport(); /* * gui validation shouldn't allow insufficient sequences here, but leave * this check in in case this method gets exposed programmatically in future + * + * */ if (((viewport.getSelectionGroup() != null) && (viewport.getSelectionGroup().getSize() < MIN_PCA_SELECTION) && (viewport.getSelectionGroup().getSize() > 0)) || (viewport.getAlignment().getHeight() < MIN_PCA_SELECTION)) { - JvOptionPane.showInternalMessageDialog(this, - MessageManager.formatMessage( - "label.you_need_at_least_n_sequences", - MIN_PCA_SELECTION), - MessageManager - .getString("label.sequence_selection_insufficient"), - JvOptionPane.WARNING_MESSAGE); - return; + return "label.you_need_at_least_n_sequences"; + } + + if (params == null) + { + params = getSimilarityParameters(true); } /* * construct the panel and kick off its calculation thread */ - pcaPanel = new PCAPanel(af.alignPanel, modelName, params); - new Thread(pcaPanel).start(); - + PCAPanel pcap = new PCAPanel(af.alignPanel, modelName, params); + new Thread(pcap).start(); + return pcap; } /** @@ -611,6 +714,7 @@ public class CalculationChooser extends JPanel } } + /** * Returns a data bean holding parameters for similarity (or distance) model * calculation @@ -618,7 +722,8 @@ public class CalculationChooser extends JPanel * @param doPCA * @return */ - protected SimilarityParamsI getSimilarityParameters(boolean doPCA) + public static SimilarityParamsI getSimilarityParameters( + boolean doPCA) { // commented out: parameter choices read from gui widgets // SimilarityParamsI params = new SimilarityParams( @@ -639,6 +744,7 @@ public class CalculationChooser extends JPanel return new SimilarityParams(includeGapGap, matchGap, includeGapResidue, matchOnShortestLength); + } /** @@ -656,6 +762,7 @@ public class CalculationChooser extends JPanel public PCAPanel getPcaPanel() { + // only called for FreeUpMemoryTest return pcaPanel; } } diff --git a/src/jalview/gui/FeatureRenderer.java b/src/jalview/gui/FeatureRenderer.java index 0553fef..2c54906 100644 --- a/src/jalview/gui/FeatureRenderer.java +++ b/src/jalview/gui/FeatureRenderer.java @@ -30,6 +30,7 @@ import java.awt.Color; */ public class FeatureRenderer extends jalview.renderer.seqfeatures.FeatureRenderer + implements jalview.api.FeatureRenderer { Color resBoxColour; diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 2e0c1f4..de67c39 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -2770,4 +2770,45 @@ public class SeqPanel extends JPanel { return lastSearchResults; } + + /** + * scroll to the given row/column - or nearest visible location + * + * @param row + * @param column + */ + public void scrollTo(int row, int column) + { + + row = row < 0 ? ap.av.getRanges().getStartSeq() : row; + column = column < 0 ? ap.av.getRanges().getStartRes() : column; + ap.scrollTo(column, column, row, true, true); + } + + /** + * scroll to the given row - or nearest visible location + * + * @param row + */ + public void scrollToRow(int row) + { + + row = row < 0 ? ap.av.getRanges().getStartSeq() : row; + ap.scrollTo(ap.av.getRanges().getStartRes(), + ap.av.getRanges().getStartRes(), row, true, true); + } + + /** + * scroll to the given column - or nearest visible location + * + * @param column + */ + public void scrollToColumn(int column) + { + + column = column < 0 ? ap.av.getRanges().getStartRes() : column; + ap.scrollTo(column, column, ap.av.getRanges().getStartSeq(), true, + true); + } + }