This commit was manufactured by cvs2svn to create tag 'Release_2_1_1'. Release_2_1_1
authorjprocter <Jim Procter>
Fri, 9 Feb 2007 18:02:18 +0000 (18:02 +0000)
committerjprocter <Jim Procter>
Fri, 9 Feb 2007 18:02:18 +0000 (18:02 +0000)
Sprout from master 2006-09-12 10:01:52 UTC andrew 'Copy consensus'
Cherrypick from master 2007-02-09 18:02:17 UTC jprocter 'running jalview from the command line.':
    doc/AddingGroovySupport.html
    help/html/align.jpg
    help/html/colourSchemes/textcolour.gif
    help/html/colourSchemes/textcolour.html
    help/html/features/amendfeature.gif
    help/html/features/clarguments.html
    help/html/features/codingfeatures.html
    help/html/features/commandline.html
    help/html/features/creatinFeatures.html
    help/html/features/crnewfeature.gif
    help/html/features/das.gif
    help/html/features/editingFeatures.html
    help/html/features/multipleViews.html
    help/html/features/newkeystrokes.html
    help/html/features/seqmappings.html
    help/html/menus/alwformat.html
    help/html/menus/alwselect.html
    lib/vamsas-client.jar
    src/jalview/analysis/Finder.java
    src/jalview/appletgui/EditNameDialog.java
    src/jalview/commands/ChangeCaseCommand.java
    src/jalview/commands/CommandI.java
    src/jalview/commands/EditCommand.java
    src/jalview/commands/OrderCommand.java
    src/jalview/commands/RemoveGapColCommand.java
    src/jalview/commands/RemoveGapsCommand.java
    src/jalview/commands/TrimRegionCommand.java
    src/jalview/gui/EditNameDialog.java
    src/jalview/gui/TextColourChooser.java
    src/jalview/gui/UserQuestionnaireCheck.java
    src/jalview/gui/VamsasClient.java
    src/jalview/io/AMSAFile.java
    src/jalview/io/VamsasDatastore.java
    src/jalview/schemes/ScoreMatrix.java
    src/jalview/util/MapList.java
    src/jalview/ws/JPredThread.java

36 files changed:
doc/AddingGroovySupport.html [new file with mode: 0644]
help/html/align.jpg [new file with mode: 0644]
help/html/colourSchemes/textcolour.gif [new file with mode: 0644]
help/html/colourSchemes/textcolour.html [new file with mode: 0644]
help/html/features/amendfeature.gif [new file with mode: 0644]
help/html/features/clarguments.html [new file with mode: 0644]
help/html/features/codingfeatures.html [new file with mode: 0644]
help/html/features/commandline.html [new file with mode: 0644]
help/html/features/creatinFeatures.html [new file with mode: 0644]
help/html/features/crnewfeature.gif [new file with mode: 0644]
help/html/features/das.gif [new file with mode: 0644]
help/html/features/editingFeatures.html [new file with mode: 0644]
help/html/features/multipleViews.html [new file with mode: 0644]
help/html/features/newkeystrokes.html [new file with mode: 0644]
help/html/features/seqmappings.html [new file with mode: 0644]
help/html/menus/alwformat.html [new file with mode: 0644]
help/html/menus/alwselect.html [new file with mode: 0644]
lib/vamsas-client.jar [new file with mode: 0644]
src/jalview/analysis/Finder.java [new file with mode: 0644]
src/jalview/appletgui/EditNameDialog.java [new file with mode: 0644]
src/jalview/commands/ChangeCaseCommand.java [new file with mode: 0644]
src/jalview/commands/CommandI.java [new file with mode: 0644]
src/jalview/commands/EditCommand.java [new file with mode: 0644]
src/jalview/commands/OrderCommand.java [new file with mode: 0644]
src/jalview/commands/RemoveGapColCommand.java [new file with mode: 0644]
src/jalview/commands/RemoveGapsCommand.java [new file with mode: 0644]
src/jalview/commands/TrimRegionCommand.java [new file with mode: 0644]
src/jalview/gui/EditNameDialog.java [new file with mode: 0644]
src/jalview/gui/TextColourChooser.java [new file with mode: 0644]
src/jalview/gui/UserQuestionnaireCheck.java [new file with mode: 0644]
src/jalview/gui/VamsasClient.java [new file with mode: 0755]
src/jalview/io/AMSAFile.java [new file with mode: 0644]
src/jalview/io/VamsasDatastore.java [new file with mode: 0755]
src/jalview/schemes/ScoreMatrix.java [new file with mode: 0644]
src/jalview/util/MapList.java [new file with mode: 0644]
src/jalview/ws/JPredThread.java [new file with mode: 0644]

diff --git a/doc/AddingGroovySupport.html b/doc/AddingGroovySupport.html
new file mode 100644 (file)
index 0000000..6726612
--- /dev/null
@@ -0,0 +1,122 @@
+<html>
+<title>Adding Groovy Support to Jalview
+</title>
+<body>
+<h1>
+Adding Groovy Support to Jalview
+</h1>
+<p>
+There is currently no scripting language 
+extension within Jalview, in part because a 
+scripting API has not been developed.
+</p>
+<p>It is, however, really easy to embed scripting
+engines within Jalview. We haven't done it
+with the Bean Scripting Framework, but the
+code snippets below show you how to get going
+with groovy.
+</p>
+<h2>Modifications</h2>
+<p>
+For each class below, add the following objects and methods to their definitions.
+</p>
+<ul><li>
+jalview.jbgui.GDesktop
+<pre>
+..
+protected JMenuItem groovyShell = new JMenuItem();
+..
+jbInit() {
+..
+groovyShell.setText("Groovy Shell...");
+groovyShell.addActionListener(new ActionListener() 
+{
+    public void actionPerformed(ActionEvent e) {
+        groovyShell_actionPerformed(e);
+    }  
+});
+..
+}
+..
+protected void groovyShell_actionPerformed(ActionEvent e) 
+{
+}
+..
+</pre></li>
+<li>jalview.gui.Desktop
+<pre>
+..
+/** 
+ * Accessor method to quickly get all the AlignmentFrames
+ * loaded.  
+ */    
+protected AlignFrame[] getAlignframes() {
+    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+
+    if (frames == null)
+    {
+        return null;
+    }
+    Vector avp=new Vector();
+    try
+    {
+        //REVERSE ORDER
+        for (int i = frames.length - 1; i > -1; i--)
+        {
+            if (frames[i] instanceof AlignFrame)
+            {
+                AlignFrame af = (AlignFrame) frames[i];
+                avp.addElement(af);
+            }
+        }
+    }
+    catch (Exception ex)
+    {
+        ex.printStackTrace();
+    }
+    if (avp.size()==0)
+    {
+        return null;
+    }
+    AlignFrame afs[] = new AlignFrame[avp.size()];
+    for (int i=0,j=avp.size(); i&lt;j; i++) {
+        afs[i] = (AlignFrame) avp.elementAt(i);
+    }
+    avp.clear();
+    return afs;
+}
+
+/**
+  * Add Groovy Support to Jalview
+  */
+public void groovyShell_actionPerformed(ActionEvent e) {
+    Console gc = new Console();
+    gc.setVariable("Jalview", this);
+    gc.run();
+}
+..
+</pre>
+</li>
+</ul>
+<p>
+Finally, compile and run with the groovy-all-*.jar (get the jar 
+from the <em>embedded</em> directory within the <a 
+href="http://dist.codehaus.org/groovy/distributions"/>groovy distribution</a>).
+Then, you should be able to open the Groovy shell 
+window from the Desktop's Tools menu. To check things are working,
+try a simple test script :<br>
+<pre>
+  
+  print Jalview.getAlignframes()[0].getTitle();
+</pre>
+Executing this will print the title of the first alignment loaded into Jalview.</p>
+</hr>
+<h2>TODO</h2>
+<p>
+Using Java class methods from Groovy is straightforward, but currently, there isn't a set of easy to use methods for the jalview objects. A Jalview Scripting API needs to be developed to make this easier.</p>
+<h3>Making it easier</h3>
+<p>jalview.bin.JalviewScript could be a top level jalview instance of a script execution thread, creating and maintaining the context for scripts operating on the jalview datamodel and interfacing with the Jalview GUI.
+</p> 
+</body>
+</html>
+
diff --git a/help/html/align.jpg b/help/html/align.jpg
new file mode 100644 (file)
index 0000000..451ccaa
Binary files /dev/null and b/help/html/align.jpg differ
diff --git a/help/html/colourSchemes/textcolour.gif b/help/html/colourSchemes/textcolour.gif
new file mode 100644 (file)
index 0000000..a659156
Binary files /dev/null and b/help/html/colourSchemes/textcolour.gif differ
diff --git a/help/html/colourSchemes/textcolour.html b/help/html/colourSchemes/textcolour.html
new file mode 100644 (file)
index 0000000..f40116a
--- /dev/null
@@ -0,0 +1,20 @@
+<html>
+<head>
+Background Dependent Text Colour
+</head>
+<body>
+<strong>Background Dependent Text Colour</strong>
+<p>The <strong>Colour&#8594;Text Colour</strong> menu entry opens
+the <strong>&quot;Adjust Foreground Text Colour Threshold&quot;</strong>
+dialog box, allowing the colour of symbols rendered on dark or light
+backgrounds to be set for the current selection or the whole alignment.
+</p>
+<p><img src="textcolour.gif"></p>
+<p>The dialog box contains a slider, and two colour icons showing
+the text colour for dark backgrounds (left hand end of slider), and
+light backgrounds (right hand end of slider). Drag the slider to change
+the threshold for transitioning between dark and light background
+colours, and select either of the colour boxes to open a colour chooser
+to select a different text colour.</p>
+</body>
+</html>
diff --git a/help/html/features/amendfeature.gif b/help/html/features/amendfeature.gif
new file mode 100644 (file)
index 0000000..39cc53a
Binary files /dev/null and b/help/html/features/amendfeature.gif differ
diff --git a/help/html/features/clarguments.html b/help/html/features/clarguments.html
new file mode 100644 (file)
index 0000000..45dfab5
--- /dev/null
@@ -0,0 +1,82 @@
+<html>
+<title>Jalview Command Line Arguments
+</title>
+<body>
+ <p><strong>The Jalview Executable's Command Line
+ Arguments</strong></p>
+ See <a href="commandline.html">running Jalview from the command
+ line</a> for more information.<br>
+ <table width="100%" border="1" cellspacing="0" cellpadding="0">
+ <tr > 
+ <td width="27%"><div align="center">-nodisplay</div></td>
+ <td width="73%"><div align="left">Run Jalview without User Interface.</div></td>
+ </tr>
+
+ <tr> 
+ <td><div align="center">-props FILE</div></td>
+ <td><div align="left">Use the given Jalview properties file instead 
+ of users default.</div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-features FILE</div></td>
+ <td><div align="left"> 
+ <p>Use the given file to add sequence features to an alignment. 
+ See <a href="featuresFormat.html" target="NEW">Features 
+ File</a> (Known as Groups file prior to 2.08) description. </p>
+
+ </div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-annotations FILE</div></td>
+ <td>Add precalculated annotations to the alignment. See <a href="annotationsFormat.html" target="NEW">Annotation 
+ File</a> description.</td>
+ </tr>
+ <tr> 
+ <td><div align="center">-fasta FILE</div></td>
+
+ <td><div align="left">Create alignment file FILE in Fasta format.</div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-clustal FILE</div></td>
+ <td><div align="left">Create alignment file FILE in Clustal format.</div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-msf FILE</div></td>
+
+ <td><div align="left">Create alignment file FILE in MSF format.</div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-pileup FILE</div></td>
+ <td><div align="left">Create alignment file FILE in Pileup format.</div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-pir FILE</div></td>
+
+ <td><div align="left">Create alignment file FILE in PIR format.</div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-blc FILE</div></td>
+ <td><div align="left">Create alignment file FILE in BLC format.</div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-jalview FILE</div></td>
+
+ <td><div align="left">Create alignment file FILE in Jalview format.</div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-png FILE</div></td>
+ <td><div align="left">Create PNG image FILE from alignment.</div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-imgMap FILE</div></td>
+
+ <td><div align="left">Create HTML file FILE with image map of PNG 
+ image.</div></td>
+ </tr>
+ <tr> 
+ <td><div align="center">-eps FILE</div></td>
+ <td><div align="left">Create EPS file FILE from alignment.</div></td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/help/html/features/codingfeatures.html b/help/html/features/codingfeatures.html
new file mode 100644 (file)
index 0000000..a828545
--- /dev/null
@@ -0,0 +1,16 @@
+<html>
+<head>
+<title>DNA Sequence Coding Region Definition</title>
+</head>
+<body>
+<p><strong>DNA Sequence Coding Region Definition</strong></p>
+<p>Jalview includes the standard DNA codon translation table in\r
+order to be able to dynamically translate cDNA to its expressed\r
+protein sequence. DNA Sequence Coding Regions are sequence \r
+features that can be defined on any DNA sequence in order to\r
+mark stretches of cDNA that will be concatenated to form the\r
+series of codons that are translated by the <strong>&quot;\r
+Calculate&#8594;Translate cDNA&quot;</strong> menu function.    \r
+</p>\r
+</body>
+</html>
\ No newline at end of file
diff --git a/help/html/features/commandline.html b/help/html/features/commandline.html
new file mode 100644 (file)
index 0000000..fadddce
--- /dev/null
@@ -0,0 +1,35 @@
+<html>\r
+<head><title>Running Jalview from the command line</title></head>\r
+<body>\r
+ <p><strong>Running Jalview from the command line</strong></p>\r
+ <p>Jalview is most easily run from the command line if you have built\r
+ it from source, or via the 'Jalview' executable created from the\r
+ InstallAnywhere jalview installation. Both of these mechanisms allow\r
+ true command line execution of jalview - allowing you to provide\r
+ additional options.</p><p>\r
+The Java Webstart version of\r
+ jalview can be executed from the command line using something like\r
+ :\r
+ <pre>javaws http://www.jalview.org/webstart/jalview.jnlp -open\r
+ <em>yourFileName</em></pre>\r
+ But, this is not guaranteed to work on all versions of webstart on all\r
+ operating systems, and doesn't let you execute Jalview with any\r
+ additional parameters.\r
+ <p><strong>Running jalview from the InstallAnywhere\r
+ installation</strong></p>\r
+ <p>If you install with InstallAnywhere you can use several more commands. \r
+ However, if you call the application with the link provided by InstallAnywhere \r
+ any output from the application will be sent to output.txt, not standard \r
+ out.<br>\r
+ The jalview application also requires a number of additional\r
+ libraries on the class path. The command line below adds the Jalview\r
+ installation's 'lib' directory to the list of directories that are\r
+ searched for jars to be added to the classpath:</p>\r
+<pre>java -Djava.ext.dirs=$INSTALL_DIR$/lib -cp $INSTALL_DIR$/jalview.jar jalview.bin.Jalview -open [FILE] </pre>\r
+ <p>Use '-help' to get more information on the <a\r
+ href="clarguments.html">command line arguments</a> that Jalview\r
+ accepts.</p>\r
+ <p>&nbsp;</p>\r
+ <p>&nbsp;</p>\r
+ </body>\r
+ </html>\r
diff --git a/help/html/features/creatinFeatures.html b/help/html/features/creatinFeatures.html
new file mode 100644 (file)
index 0000000..0ebb52f
--- /dev/null
@@ -0,0 +1,38 @@
+<html>
+<head>
+<title>Creating Sequence Features</title>
+</head>
+<body>
+<strong>Creating Sequence Features</strong>
+<p>Jalview can create sequence features from the matches of a <a
+       href="search.html">regular expression search</a>, or from the currently
+selected area via the <strong>&quot;selection&#8594;Create
+sequence feature&quot;</strong> entry in the <a href="../menus/popupMenu.html">selection area popup menu</a>. In both
+cases, the <strong>Create Features</strong> dialog box will then be
+opened:</p>
+<p><img src="crnewfeature.gif"></p>
+<p>Select or enter the attributes for the features being created,
+and then press OK to create the new features.</p>
+<p>Each attribute is described below:
+<ul>
+       <li><strong>Sequence Feature Name</strong>
+       <p>Either give the new features a new name or use the menu to
+       re-use an existing feature name.</p>
+       </li>
+       <li><strong>Feature group</strong>
+       <p>Enter a new group name, or re-use an existing group from the
+       pull-down menu.</p>
+       </li>
+       <li><strong>Feature Colour</strong>
+       <p>Keep the existing colour for this feature's name and group, or
+       select the colour box to open a colour chooser to pick a different one.</p>
+       </li>
+       <li><strong>Description</strong>
+       <p>Enter a description for all the features being created. Each
+       feature defined on a sequence may have its own description that will be
+       displayed in the tooltip for the feature in that region.</p>
+       </li>
+</ul>
+</p><p><em>Sequence Feature Creation was introduced in Jalview Version 2.2</em></p>
+</body>
+</html>
\ No newline at end of file
diff --git a/help/html/features/crnewfeature.gif b/help/html/features/crnewfeature.gif
new file mode 100644 (file)
index 0000000..5aa6edb
Binary files /dev/null and b/help/html/features/crnewfeature.gif differ
diff --git a/help/html/features/das.gif b/help/html/features/das.gif
new file mode 100644 (file)
index 0000000..1f884cb
Binary files /dev/null and b/help/html/features/das.gif differ
diff --git a/help/html/features/editingFeatures.html b/help/html/features/editingFeatures.html
new file mode 100644 (file)
index 0000000..ff4d5ca
--- /dev/null
@@ -0,0 +1,26 @@
+<html>
+<head>
+<title>Amending or Deleting Sequence Features</title>
+</head>
+<body>
+<strong>Amending or Deleting Sequence Features</strong>
+<p>Double clicking a position in the alignment with one or more
+displayed sequence features opens the <strong>&quot;Amend/Delete
+Features&quot;</strong> dialog box.</p>
+<p><img src="amendfeature.gif"></p>
+<p>The dialog box only allows one of the features at the
+double-clicked position to be edited or deleted at a time, and it will
+also be highlighted in black in the alignment window.</p>
+<p>Choose which feature is to be modified by selecting it from the <strong>Sequence
+Feature Name</strong> <!-- and <strong>Feature Group</strong>  --> pull down
+menu. In addition to the Name, group, colour and description attributes
+described for the <a href="creatinFeatures.html">new feature dialog
+box</a>, a feature's start and end position can be changed either by
+entering a new position directly or by using the adjacent up and down
+buttons.</p>
+<p>Select <strong>Amend</strong> to update the feature, <strong>Delete</strong>
+to remove the selected feature, or <strong>Cancel</strong> to leave the
+feature unchanged.</p>
+<p><em>Sequence feature editing was implemented in Jalview 2.2</em></p>
+</body>
+</html>
diff --git a/help/html/features/multipleViews.html b/help/html/features/multipleViews.html
new file mode 100644 (file)
index 0000000..da811d4
--- /dev/null
@@ -0,0 +1,58 @@
+<html>\r
+<head>\r
+<title>Multiple Alignment Views</title>\r
+</head>\r
+<body>\r
+<p><strong>Multiple Alignment Views</strong></p>\r
+<p>Multiple alignment views allows the same alignment to be viewed\r
+independently in many different ways simultaneously. Each view is an\r
+independent visualization of the same alignment, so each may have a\r
+different ordering, colouring, row and column hiding and seuqence\r
+feature and annotation display setting, but alignment, feature and\r
+annotation edits are common to all, since this affects the underlying\r
+data.</p>\r
+<p>Create a new view using the <strong>&quot;View&#8594;New\r
+View&quot;</strong> menu item, or by pressing <strong>Control+T</strong>. A newly\r
+created view will be identical to the view it was created from, but any\r
+changes to the way the alignment is coloured or displayed will only\r
+affect the new view.</p>\r
+<p>A particular view may focus on some specific aspect of an\r
+alignment - for example, hiding all but the region of an alignment\r
+containing a particular domain. <strong>Right-clicking</strong> a view's\r
+tab opens the View Name dialog box, allowing it to be renamed to\r
+something more meaningful.</p>\r
+<p><strong>Viewing Multiple Views Simultaneously</strong></p>\r
+<p>Multiple views of an alignment are, by default, gathered together\r
+as tabs within a single alignment window. They can be viewed\r
+simultanously by pressing <strong>X</strong> (or via <strong>&quot;View&#8594;Expand&quot;</strong>)\r
+to expand each view into its own linked alignment window. Expanded views\r
+are gathered back into into a single tabbed alignment window by pressing\r
+<strong>G</strong>, or by selecting <strong>&quot;View&#8594;Gather&quot;</strong>).\r
+</p>\r
+<p><strong>Hidden Sequence Representatives and Multiple\r
+Views</strong></p>\r
+<p>There are some unexpected interactions between hidden sequence\r
+representatives and their display in multiple views. See the\r
+corresponding entry in the <a href="hiddenRegions.html">documentation\r
+for hidden regions</a>.</p>\r
+<p><strong>Structure and Analysis Viewers and Multiple\r
+Views</strong></p>\r
+<p>A tree calculated on a particular view, or loaded onto it, is by\r
+default associated with just that view. However, the <a\r
+       href="../calculations/treeviewer.html">Tree Viewer's</a> <strong>&quot;View&#8594;Associate\r
+leaves&quot;</strong> submenu allows a tree's view association to be changed to\r
+to any or all other views.</p>\r
+<p>The results of a <a href="../calculations/pca.html">PCA\r
+calculation</a> on a particular view may also be associated with other\r
+views, using the PCA Viewer's <strong>&quot;View&#8594;Associate\r
+Nodes&quot;</strong> submenu.</p>\r
+<p>Currently, a <a href="pdbviewer.html">PDB Structure Viewer</a>\r
+opened on a structure associated with a sequence in a particular view\r
+will only be associated with the seuqence as displayed in that view. <br>\r
+<em>This will be resolved in a future release</em><!-- also, by default, only be associated\r
+with the sequence as it is displayed in that view. The\r
+&quot;View&#8594;Associate View&quot; submenu allows the association of\r
+alternative views.</p> -->\r
+<p><em>Multiple Views were introduced in Jalview 2.2</em></p>\r
+</body>\r
+</html>\r
diff --git a/help/html/features/newkeystrokes.html b/help/html/features/newkeystrokes.html
new file mode 100644 (file)
index 0000000..12ea967
--- /dev/null
@@ -0,0 +1,33 @@
+<html>
+<title>New Key Strokes and Menus</title>
+<body>
+<strong>New Key Strokes and Menus</strong>
+<p>Many new <a href="../keys.html">keyboard shortcuts</a> have been
+added in Jalview 2.2 to make editing, selecting and navigating an
+alignment even easier. The selection commands in the <strong>Edit</strong>
+menu, and the alignment formatting controls within the <strong>View</strong>
+menu have also been moved into their own respective <strong>Select</strong>
+and <strong>Format</strong> menus.</p>
+<p>Some of the more important new keystrokes are shown below :
+<ul>
+       <li><strong>Page Up</strong> and <strong>Page Down</strong>
+       scrolls through the alignment view.</li>
+       <li><strong>Control I</strong> inverts the currently selected
+       sequence set, and <strong>Control Alt I</strong> will invert the
+       currently selected set of columns.
+       <li><strong>Control V</strong> will paste the contents of the
+       clipboard to the current alignment window, and <strong>Control
+       Shift V</strong> pastes the data to a new window.</li>
+       <li><strong>Control O</strong> opens the file browser for loading
+       a new alignment or Jalview archive.</li>
+       <li><strong>Control S</strong> saves the alignment with the
+       current filename and format, and <strong>Control Shift S</strong> opens
+       the <strong>Save As...</strong> dialog box.</li>
+       <li><strong>Control T</strong> creates a new alignment view, and <strong>Control
+       W</strong> closes the current view, or if none remain, then the whole alignment.</li>
+       <li><strong>Control E</strong> will remove gapped columns in the alignment.</li>
+       <li><strong>Control D</strong> opens the <strong>Remove Redundancy</strong> dialog box.</li>
+</ul>
+</p>
+</body>
+</html>
diff --git a/help/html/features/seqmappings.html b/help/html/features/seqmappings.html
new file mode 100644 (file)
index 0000000..46fcd59
--- /dev/null
@@ -0,0 +1,22 @@
+<html>
+<head>\r
+<title>Mapping Between Different Sequences</title>\r
+</head>\r
+<body>
+<p><strong>Mapping Between Different Sequences</strong></p>\r
+<p>A new feature in Jalview 2.8 is the ability to map \r
+between sequences in different domains, based on alignment, \r
+or by the use of explicit mappings provided by databases.\r
+</p>\r
+<p>The most familiar mapping is the one used to identify\r
+the coordinates corresponding to a displayed sequence when\r
+viewing a PDB file associated with a sequence (see \r
+<a href="viewingpdbs.html">&quot;Viewing PDB Files&quot;</a> \r
+for more information.</p>\r
+<p>The newest form of mapping supported by Jalview is the \r
+correspondence between DNA and protein sequences. This mapping\r
+can be imported directly from EMBL and EMBLCDS database records \r
+retrieved by the <a href="seqfetch.html">Sequence Fetcher</a>,\r
+or by the definition of <a href="codingfeatures.html">coding regions</a>. \r
+</body>
+</html>
\ No newline at end of file
diff --git a/help/html/menus/alwformat.html b/help/html/menus/alwformat.html
new file mode 100644 (file)
index 0000000..d7224b3
--- /dev/null
@@ -0,0 +1,56 @@
+<html>\r
+<head>\r
+<p><strong>Alignment Window Format Menu</strong></p>\r
+<ul>\r
+  <li><strong>Font...</strong><br>\r
+    <em>Opens the &quot;Choose Font&quot; dialog box, in order to change the font \r
+    of the display and enable or disable 'smooth fonts' (anti-aliasing) for faster \r
+    alignment rendering. </em></em></li>\r
+  <li><strong>Wrap<br>\r
+    </strong><em>When ticked, the alignment display is &quot;<a\r
+               href="../features/wrap.html">wrapped</a>&quot; to the width of the alignment \r
+    window. This is useful if your alignment has only a few sequences to view \r
+    its full width at once.<br>\r
+    Additional options for display of sequence numbering and scales are also visible \r
+    in wrapped layout mode:</em>\r
+    <ul>\r
+      <li><strong>Scale Left</strong><br>\r
+        <em>Show the sequence position for the first aligned residue in each row \r
+        in the left column of the alignment.</em></li>\r
+      <li><strong>Scale Right</strong><br>\r
+        <em>Show the sequence position for the last aligned residue in each row \r
+        in the right-most column of the alignment.</em></li>\r
+    </ul>\r
+  \r
+  <li><strong>Scale Above</strong><br>\r
+    <em>Show the alignment column position scale.</em></li>\r
+  <li><strong>Show Sequence Limits<br>\r
+    </strong><em>If this box is selected the sequence name will have the start \r
+    and end position of the sequence appended to the name, in the format NAME/START-END</em></li>\r
+  <li><strong>Right Align Sequence ID<br>\r
+    </strong> <em>If this box is selected then the sequence names displayed in \r
+    the sequence label area will be aligned against the left-hand edge of the \r
+    alignment display, rather than the left-hand edge of the alignment window.</em></li>\r
+  <li><strong>Show Hidden Markers<br>\r
+    </strong><em>When this box is selected, positions in the alignment where rows \r
+    and columns are hidden will be marked by blue arrows.</em></li>\r
+  <li><strong>Boxes</strong><em><br>\r
+    If this is selected the background of a residue will be coloured using the \r
+    selected background colour. Useful if used in conjunction with &quot;Colour \r
+    Text.&quot; </em></li>\r
+  <li><strong>Text<br>\r
+    </strong><em>If this is selected the residues will be displayed using the \r
+    standard 1 character amino acid alphabet.</em></li>\r
+  <li><strong>Colour Text<br>\r
+    </strong><em>If this is selected the residues will be coloured according to \r
+    the background colour associated with that residue. The colour is slightly \r
+    darker than background so the amino acid symbol remains visible. </em></li>\r
+  <li><strong>Show Gaps<br>\r
+    </strong><em>When this is selected, gap characters will be displayed as &quot;.&quot; \r
+    or &quot;-&quot;. If unselected, then gap characters will appear as blank \r
+    spaces. <br>\r
+    You may set the default gap character in <a\r
+               href="../features/preferences.html">preferences</a>.</em></li>\r
+</ul>\r
+</body>\r
+</html>\r
diff --git a/help/html/menus/alwselect.html b/help/html/menus/alwselect.html
new file mode 100644 (file)
index 0000000..34438cd
--- /dev/null
@@ -0,0 +1,29 @@
+<html>\r
+<head><body>\r
+<p><strong>Alignment Window Select Menu</strong></p>\r
+<ul>\r
+       \r
+  <li><a href="../features/search.html"><strong>Find... (Control F)</strong></a><br>\r
+    <em>Opens the Find dialog box to search for residues, sequence name or residue \r
+    position within the alignment and create new sequence features from the queries. \r
+    </em>\r
+  <li><strong>Select All (Control A)</strong><strong><br>\r
+    </strong><em>Selects all the sequences and residues in the alignment. <br>\r
+    Use &lt;CTRL&gt; and A (&lt;APPLE&gt; and A on a MacOSX) to select all.</em></em></li>\r
+       <li><strong>Deselect All (Escape)<br>\r
+       </strong><em>Removes the current selection box (red dashed box) from the\r
+       alignment window. All selected sequences, residues and marked columns\r
+       will be deselected. </em><em> <br>\r
+       Use &lt;ESCAPE&gt; to deselect all.</em></li>\r
+       <li><strong>Invert Sequence Selection (Control I)<br>\r
+       </strong><em>Any sequence ids currently not selected will replace the\r
+       current selection. </em></li>\r
+       <li><strong>Invert Column Selection (Control Alt I)<br>\r
+       </strong><em>Any columns currently not selected will replace the current\r
+       column selection. </em></li>\r
+       <li><strong>Undefine Groups (Control U)<br>\r
+       </strong><em>The alignment will be reset with no defined groups.<br>\r
+       <strong>WARNING</strong>: This cannot be undone.</em></li>\r
+</ul>\r
+</body>\r
+</html>\r
diff --git a/lib/vamsas-client.jar b/lib/vamsas-client.jar
new file mode 100644 (file)
index 0000000..e0c7d95
Binary files /dev/null and b/lib/vamsas-client.jar differ
diff --git a/src/jalview/analysis/Finder.java b/src/jalview/analysis/Finder.java
new file mode 100644 (file)
index 0000000..2fa09aa
--- /dev/null
@@ -0,0 +1,308 @@
+package jalview.analysis;\r
+\r
+import java.util.*;\r
+\r
+import jalview.datamodel.*;\r
+\r
+public class Finder\r
+{\r
+  /**\r
+   * Implements the search algorithms for the Find dialog box.\r
+   */\r
+  SearchResults searchResults;\r
+  AlignmentI alignment;\r
+  jalview.datamodel.SequenceGroup selection = null;\r
+  Vector idMatch = null;\r
+  boolean caseSensitive = false;\r
+  boolean findAll = false;\r
+  com.stevesoft.pat.Regex regex = null;\r
+  /**\r
+   * hold's last-searched position between calles to find(false)\r
+   */\r
+  int seqIndex = 0, resIndex = 0;\r
+  public Finder(AlignmentI alignment, SequenceGroup selection)\r
+  {\r
+    this.alignment = alignment;\r
+    this.selection = selection;\r
+  }\r
+\r
+  public Finder(AlignmentI alignment, SequenceGroup selectionGroup,\r
+                int seqIndex, int resIndex)\r
+  {\r
+    this(alignment, selectionGroup);\r
+    this.seqIndex = seqIndex;\r
+    this.resIndex = resIndex;\r
+  }\r
+\r
+  public boolean find(String searchString)\r
+  {\r
+    boolean hasResults = false;\r
+    if (!caseSensitive)\r
+    {\r
+      searchString = searchString.toUpperCase();\r
+    }\r
+    regex = new com.stevesoft.pat.Regex(searchString);\r
+    searchResults = new SearchResults();\r
+    idMatch = new Vector();\r
+    Sequence seq;\r
+    String item = null;\r
+    boolean found = false;\r
+\r
+    ////// is the searchString a residue number?\r
+    try\r
+    {\r
+      int res = Integer.parseInt(searchString);\r
+      found = true;\r
+      if (selection == null || selection.getSize() < 1)\r
+      {\r
+        seq = (Sequence) alignment.getSequenceAt(0);\r
+      }\r
+      else\r
+      {\r
+        seq = (Sequence) (selection.getSequenceAt(0));\r
+      }\r
+\r
+      searchResults.addResult(seq, res, res);\r
+      hasResults = true;\r
+    }\r
+    catch (NumberFormatException ex)\r
+    {\r
+    }\r
+\r
+    ///////////////////////////////////////////////\r
+\r
+    int end = alignment.getHeight();\r
+\r
+    if (selection != null)\r
+    {\r
+      if ( (selection.getSize() < 1) ||\r
+          ( (selection.getEndRes() - selection.getStartRes()) < 2))\r
+      {\r
+        selection = null;\r
+      }\r
+    }\r
+\r
+    while (!found && (seqIndex < end))\r
+    {\r
+      seq = (Sequence) alignment.getSequenceAt(seqIndex);\r
+\r
+      if ( (selection != null) && !selection.getSequences(null).contains(seq))\r
+      {\r
+        seqIndex++;\r
+        resIndex = 0;\r
+\r
+        continue;\r
+      }\r
+\r
+      item = seq.getSequenceAsString();\r
+      // JBPNote - check if this toUpper which is present in the application implementation makes a difference\r
+      //if(!caseSensitive)\r
+      //  item = item.toUpperCase();\r
+\r
+      if ( (selection != null) &&\r
+          (selection.getEndRes() < alignment.getWidth() - 1))\r
+      {\r
+        item = item.substring(0, selection.getEndRes() + 1);\r
+      }\r
+\r
+      ///Shall we ignore gaps???? - JBPNote: Add Flag for forcing this or not\r
+      StringBuffer noGapsSB = new StringBuffer();\r
+      int insertCount = 0;\r
+      Vector spaces = new Vector();\r
+\r
+      for (int j = 0; j < item.length(); j++)\r
+      {\r
+        if (!jalview.util.Comparison.isGap(item.charAt(j)))\r
+        {\r
+          noGapsSB.append(item.charAt(j));\r
+          spaces.addElement(new Integer(insertCount));\r
+        }\r
+        else\r
+        {\r
+          insertCount++;\r
+        }\r
+      }\r
+\r
+      String noGaps = noGapsSB.toString();\r
+\r
+      for (int r = resIndex; r < noGaps.length(); r++)\r
+      {\r
+\r
+        if (regex.searchFrom(noGaps, r))\r
+        {\r
+          resIndex = regex.matchedFrom();\r
+\r
+          if ( (selection != null) &&\r
+              ( (resIndex +\r
+                 Integer.parseInt(spaces.elementAt(resIndex).toString())) <\r
+               selection.getStartRes()))\r
+          {\r
+            continue;\r
+          }\r
+\r
+          int sres = seq.findPosition(resIndex +\r
+                                      Integer.parseInt(spaces.elementAt(\r
+              resIndex)\r
+              .toString()));\r
+          int eres = seq.findPosition(regex.matchedTo() - 1 +\r
+                                      Integer.parseInt(spaces.elementAt(regex.\r
+              matchedTo() -\r
+              1).toString()));\r
+\r
+          searchResults.addResult(seq, sres, eres);\r
+          hasResults = true;\r
+          if (!findAll)\r
+          {\r
+            // thats enough, break and display the result\r
+            found = true;\r
+            resIndex++;\r
+\r
+            break;\r
+          }\r
+\r
+          r = resIndex;\r
+        }\r
+        else\r
+        {\r
+          break;\r
+        }\r
+      }\r
+\r
+      if (!found)\r
+      {\r
+        seqIndex++;\r
+        resIndex = 0;\r
+      }\r
+    }\r
+\r
+    for (int id = 0; id < alignment.getHeight(); id++)\r
+    {\r
+      if (regex.search(alignment.getSequenceAt(id).getName()))\r
+      {\r
+        idMatch.addElement(alignment.getSequenceAt(id));\r
+        hasResults = true;\r
+      }\r
+    }\r
+    return hasResults;\r
+  }\r
+\r
+  /**\r
+   * @return the alignment\r
+   */\r
+  public AlignmentI getAlignment()\r
+  {\r
+    return alignment;\r
+  }\r
+\r
+  /**\r
+   * @param alignment the alignment to set\r
+   */\r
+  public void setAlignment(AlignmentI alignment)\r
+  {\r
+    this.alignment = alignment;\r
+  }\r
+\r
+  /**\r
+   * @return the caseSensitive\r
+   */\r
+  public boolean isCaseSensitive()\r
+  {\r
+    return caseSensitive;\r
+  }\r
+\r
+  /**\r
+   * @param caseSensitive the caseSensitive to set\r
+   */\r
+  public void setCaseSensitive(boolean caseSensitive)\r
+  {\r
+    this.caseSensitive = caseSensitive;\r
+  }\r
+\r
+  /**\r
+   * @return the findAll\r
+   */\r
+  public boolean isFindAll()\r
+  {\r
+    return findAll;\r
+  }\r
+\r
+  /**\r
+   * @param findAll the findAll to set\r
+   */\r
+  public void setFindAll(boolean findAll)\r
+  {\r
+    this.findAll = findAll;\r
+  }\r
+\r
+  /**\r
+   * @return the selection\r
+   */\r
+  public jalview.datamodel.SequenceGroup getSelection()\r
+  {\r
+    return selection;\r
+  }\r
+\r
+  /**\r
+   * @param selection the selection to set\r
+   */\r
+  public void setSelection(jalview.datamodel.SequenceGroup selection)\r
+  {\r
+    this.selection = selection;\r
+  }\r
+\r
+  /**\r
+   * @return the idMatch\r
+   */\r
+  public Vector getIdMatch()\r
+  {\r
+    return idMatch;\r
+  }\r
+\r
+  /**\r
+   * @return the regex\r
+   */\r
+  public com.stevesoft.pat.Regex getRegex()\r
+  {\r
+    return regex;\r
+  }\r
+\r
+  /**\r
+   * @return the searchResults\r
+   */\r
+  public SearchResults getSearchResults()\r
+  {\r
+    return searchResults;\r
+  }\r
+\r
+  /**\r
+   * @return the resIndex\r
+   */\r
+  public int getResIndex()\r
+  {\r
+    return resIndex;\r
+  }\r
+\r
+  /**\r
+   * @param resIndex the resIndex to set\r
+   */\r
+  public void setResIndex(int resIndex)\r
+  {\r
+    this.resIndex = resIndex;\r
+  }\r
+\r
+  /**\r
+   * @return the seqIndex\r
+   */\r
+  public int getSeqIndex()\r
+  {\r
+    return seqIndex;\r
+  }\r
+\r
+  /**\r
+   * @param seqIndex the seqIndex to set\r
+   */\r
+  public void setSeqIndex(int seqIndex)\r
+  {\r
+    this.seqIndex = seqIndex;\r
+  }\r
+}\r
diff --git a/src/jalview/appletgui/EditNameDialog.java b/src/jalview/appletgui/EditNameDialog.java
new file mode 100644 (file)
index 0000000..3e197f0
--- /dev/null
@@ -0,0 +1,111 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+\r
+package jalview.appletgui;\r
+\r
+import java.awt.*;\r
+import java.awt.event.*;\r
+\r
+public class EditNameDialog\r
+    extends Dialog implements ActionListener\r
+{\r
+  TextField id, description;\r
+  Button ok = new Button("Accept");\r
+  Button cancel = new Button("Cancel");\r
+  boolean accept = false;\r
+\r
+  public String getName()\r
+  {\r
+    return id.getText();\r
+  }\r
+\r
+  public String getDescription()\r
+  {\r
+    if (description.getText().length() < 1)\r
+    {\r
+      return null;\r
+    }\r
+    else\r
+    {\r
+      return description.getText();\r
+    }\r
+  }\r
+\r
+  public EditNameDialog(String name,\r
+                        String desc,\r
+                        String label1,\r
+                        String label2,\r
+                        AlignmentPanel ap,\r
+                        String title)\r
+  {\r
+    super(ap.alignFrame, title, true);\r
+\r
+    id = new TextField(name, 40);\r
+    description = new TextField(desc, 40);\r
+    Panel panel = new Panel(new BorderLayout());\r
+    Panel panel2 = new Panel(new BorderLayout());\r
+    Label label = new Label(label1);\r
+    label.setFont(new Font("Monospaced", Font.PLAIN, 12));\r
+    panel2.add(label, BorderLayout.WEST);\r
+    panel2.add(id, BorderLayout.CENTER);\r
+    panel.add(panel2, BorderLayout.NORTH);\r
+    panel2 = new Panel(new BorderLayout());\r
+    label = new Label(label2);\r
+    label.setFont(new Font("Monospaced", Font.PLAIN, 12));\r
+    panel2.add(label, BorderLayout.WEST);\r
+    panel2.add(description, BorderLayout.CENTER);\r
+    panel.add(panel2, BorderLayout.CENTER);\r
+\r
+    panel2 = new Panel(new FlowLayout());\r
+\r
+    panel2.add(ok);\r
+    panel2.add(cancel);\r
+    ok.addActionListener(this);\r
+    cancel.addActionListener(this);\r
+\r
+    panel.add(panel2, BorderLayout.SOUTH);\r
+\r
+    add(panel, BorderLayout.NORTH);\r
+\r
+    int width = 500, height = 100;\r
+\r
+    pack();\r
+\r
+    height += getInsets().top + getInsets().bottom;\r
+\r
+    setBounds(ap.alignFrame.getBounds().x\r
+              + (ap.alignFrame.getSize().width - width) / 2,\r
+              ap.alignFrame.getBounds().y\r
+              + (ap.alignFrame.getSize().height - height) / 2,\r
+              width, height);\r
+\r
+    show();\r
+\r
+  }\r
+\r
+  public void actionPerformed(ActionEvent evt)\r
+  {\r
+    if (evt.getSource() == ok)\r
+    {\r
+      accept = true;\r
+    }\r
+\r
+    setVisible(false);\r
+  }\r
+}\r
diff --git a/src/jalview/commands/ChangeCaseCommand.java b/src/jalview/commands/ChangeCaseCommand.java
new file mode 100644 (file)
index 0000000..d34552d
--- /dev/null
@@ -0,0 +1,133 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+package jalview.commands;\r
+\r
+import jalview.datamodel.*;\r
+\r
+public class ChangeCaseCommand\r
+    implements CommandI\r
+{\r
+  String description;\r
+  public static int TO_LOWER = 0;\r
+  public static int TO_UPPER = 1;\r
+  public static int TOGGLE_CASE = 2;\r
+  int caseChange = -1;\r
+  SequenceI[] seqs;\r
+  int[][] regions;\r
+  public ChangeCaseCommand(String description,\r
+                           SequenceI[] seqs,\r
+                           int[][] regions,\r
+                           int caseChange)\r
+  {\r
+    this.description = description;\r
+    this.seqs = seqs;\r
+    this.regions = regions;\r
+    this.caseChange = caseChange;\r
+    doCommand();\r
+  }\r
+\r
+  public String getDescription()\r
+  {\r
+    return description;\r
+  }\r
+\r
+  public int getSize()\r
+  {\r
+    return 1;\r
+  }\r
+\r
+  public void doCommand()\r
+  {\r
+    changeCase(true);\r
+  }\r
+\r
+  public void undoCommand()\r
+  {\r
+    changeCase(false);\r
+  }\r
+\r
+  void changeCase(boolean doCommand)\r
+  {\r
+    String sequence;\r
+    int start, end;\r
+    char nextChar;\r
+    for (int r = 0; r < regions.length; r++)\r
+    {\r
+      start = regions[r][0];\r
+      for (int s = 0; s < seqs.length; s++)\r
+      {\r
+        sequence = seqs[s].getSequenceAsString();\r
+        StringBuffer newSeq = new StringBuffer();\r
+\r
+        if (regions[r][1] > sequence.length())\r
+        {\r
+          end = sequence.length();\r
+        }\r
+        else\r
+        {\r
+          end = regions[r][1];\r
+        }\r
+\r
+        if (start > 0)\r
+        {\r
+          newSeq.append(sequence.substring(0, start));\r
+        }\r
+\r
+        if ( (caseChange == TO_UPPER && doCommand)\r
+            || (caseChange == TO_LOWER && !doCommand))\r
+        {\r
+          newSeq.append(sequence.substring(start, end).toUpperCase());\r
+        }\r
+\r
+        else if ( (caseChange == TO_LOWER && doCommand)\r
+                 || (caseChange == TO_UPPER && !doCommand))\r
+        {\r
+          newSeq.append(sequence.substring(start, end).toLowerCase());\r
+        }\r
+\r
+        else //TOGGLE CASE\r
+        {\r
+          for (int c = start; c < end; c++)\r
+          {\r
+            nextChar = sequence.charAt(c);\r
+            if ('a' <= nextChar && nextChar <= 'z')\r
+            {\r
+              // TO UPPERCASE !!!\r
+              nextChar -= ('a' - 'A');\r
+            }\r
+            else if ('A' <= nextChar && nextChar <= 'Z')\r
+            {\r
+              // TO LOWERCASE !!!\r
+              nextChar += ('a' - 'A');\r
+            }\r
+            newSeq.append(nextChar);\r
+          }\r
+        }\r
+\r
+        if (end < sequence.length())\r
+        {\r
+          newSeq.append(sequence.substring(end));\r
+        }\r
+\r
+        seqs[s].setSequence(newSeq.toString());\r
+      }\r
+    }\r
+  }\r
+\r
+}\r
diff --git a/src/jalview/commands/CommandI.java b/src/jalview/commands/CommandI.java
new file mode 100644 (file)
index 0000000..d97a6bb
--- /dev/null
@@ -0,0 +1,30 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+package jalview.commands;\r
+\r
+public interface CommandI\r
+{\r
+  public void doCommand();\r
+\r
+  public void undoCommand();\r
+\r
+  public String getDescription();\r
+\r
+  public int getSize();\r
+}\r
diff --git a/src/jalview/commands/EditCommand.java b/src/jalview/commands/EditCommand.java
new file mode 100644 (file)
index 0000000..d0a2e03
--- /dev/null
@@ -0,0 +1,650 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+package jalview.commands;\r
+\r
+import java.util.*;\r
+\r
+import jalview.datamodel.*;\r
+\r
+/**\r
+ *\r
+ * <p>Title: EditCommmand</p>\r
+ *\r
+ * <p>Description: Essential information for performing\r
+ * undo and redo for cut/paste insert/delete gap\r
+ * which can be stored in the HistoryList </p>\r
+ *\r
+ * <p>Copyright: Copyright (c) 2006</p>\r
+ *\r
+ * <p>Company: Dundee University</p>\r
+ *\r
+ * @author not attributable\r
+ * @version 1.0\r
+ */\r
+public class EditCommand\r
+    implements CommandI\r
+{\r
+  public static final int INSERT_GAP = 0;\r
+  public static final int DELETE_GAP = 1;\r
+  public static final int CUT = 2;\r
+  public static final int PASTE = 3;\r
+\r
+  Edit[] edits;\r
+\r
+  String description;\r
+\r
+  public EditCommand()\r
+  {}\r
+\r
+  public EditCommand(String description)\r
+  {\r
+    this.description = description;\r
+  }\r
+\r
+  public EditCommand(String description,\r
+                     int command,\r
+                     SequenceI[] seqs,\r
+                     int position,\r
+                     int number,\r
+                     AlignmentI al)\r
+  {\r
+    this.description = description;\r
+    if (command == CUT || command == PASTE)\r
+    {\r
+      edits = new Edit[]\r
+          {\r
+          new Edit(command, seqs, position, number, al)};\r
+    }\r
+\r
+    performEdit(0);\r
+  }\r
+\r
+  final public String getDescription()\r
+  {\r
+    return description;\r
+  }\r
+\r
+  public int getSize()\r
+  {\r
+    return edits == null ? 0 : edits.length;\r
+  }\r
+\r
+  final public AlignmentI getAlignment()\r
+  {\r
+    return edits[0].al;\r
+  }\r
+\r
+  final public void appendEdit(int command,\r
+                               SequenceI[] seqs,\r
+                               int position,\r
+                               int number,\r
+                               AlignmentI al,\r
+                               boolean performEdit)\r
+  {\r
+    Edit edit = new Edit(command, seqs, position, number, al.getGapCharacter());\r
+    if (al.getHeight() == seqs.length)\r
+    {\r
+      edit.al = al;\r
+      edit.fullAlignmentHeight = true;\r
+    }\r
+\r
+    if (edits != null)\r
+    {\r
+      Edit[] temp = new Edit[edits.length + 1];\r
+      System.arraycopy(edits, 0, temp, 0, edits.length);\r
+      edits = temp;\r
+      edits[edits.length - 1] = edit;\r
+    }\r
+    else\r
+    {\r
+      edits = new Edit[]\r
+          {\r
+          edit};\r
+    }\r
+\r
+    if (performEdit)\r
+    {\r
+      performEdit(edits.length - 1);\r
+    }\r
+  }\r
+\r
+  final void performEdit(int commandIndex)\r
+  {\r
+    int eSize = edits.length;\r
+    for (int e = commandIndex; e < eSize; e++)\r
+    {\r
+      if (edits[e].command == INSERT_GAP)\r
+      {\r
+        insertGap(edits[e]);\r
+      }\r
+      else if (edits[e].command == DELETE_GAP)\r
+      {\r
+        deleteGap(edits[e]);\r
+      }\r
+      else if (edits[e].command == CUT)\r
+      {\r
+        cut(edits[e]);\r
+      }\r
+      else if (edits[e].command == PASTE)\r
+      {\r
+        paste(edits[e]);\r
+      }\r
+    }\r
+  }\r
+\r
+  final public void doCommand()\r
+  {\r
+    performEdit(0);\r
+  }\r
+\r
+  final public void undoCommand()\r
+  {\r
+    int e = 0, eSize = edits.length;\r
+    for (e = eSize - 1; e > -1; e--)\r
+    {\r
+      if (edits[e].command == INSERT_GAP)\r
+      {\r
+        deleteGap(edits[e]);\r
+      }\r
+      else if (edits[e].command == DELETE_GAP)\r
+      {\r
+        insertGap(edits[e]);\r
+      }\r
+      else if (edits[e].command == CUT)\r
+      {\r
+        paste(edits[e]);\r
+      }\r
+      else if (edits[e].command == PASTE)\r
+      {\r
+        cut(edits[e]);\r
+      }\r
+    }\r
+  }\r
+\r
+  final void insertGap(Edit command)\r
+  {\r
+    for (int s = 0; s < command.seqs.length; s++)\r
+    {\r
+      command.seqs[s].insertCharAt(command.position,\r
+                                   command.number,\r
+                                   command.gapChar);\r
+    }\r
+\r
+    adjustAnnotations(command, true);\r
+  }\r
+\r
+  final void deleteGap(Edit command)\r
+  {\r
+    for (int s = 0; s < command.seqs.length; s++)\r
+    {\r
+      command.seqs[s].deleteChars(command.position,\r
+                                  command.position + command.number);\r
+    }\r
+\r
+    adjustAnnotations(command, false);\r
+  }\r
+\r
+  void cut(Edit command)\r
+  {\r
+    command.string = new char[command.seqs.length][];\r
+\r
+    for (int i = 0; i < command.seqs.length; i++)\r
+    {\r
+      if (command.seqs[i].getLength() > command.position)\r
+      {\r
+        command.string[i] = command.seqs[i].getSequence(command.position,\r
+            command.position + command.number);\r
+\r
+        if (command.seqs[i].getDatasetSequence() != null\r
+            || command.seqs[i].getSequenceFeatures() != null)\r
+        {\r
+          for (int s = command.position; s < command.position + command.number;\r
+               s++)\r
+          {\r
+            if (jalview.schemes.ResidueProperties\r
+                .aaIndex[command.seqs[i].getCharAt(s)] != 23)\r
+            {\r
+              adjustFeatures(command, i,\r
+                             command.seqs[i].findPosition(command.position),\r
+                             command.seqs[i].findPosition(command.position +\r
+                  command.number),\r
+                             false);\r
+              break;\r
+            }\r
+          }\r
+        }\r
+        command.seqs[i].deleteChars(command.position,\r
+                                    command.position + command.number);\r
+      }\r
+\r
+      if (command.seqs[i].getLength() < 1)\r
+      {\r
+        command.al.deleteSequence(command.seqs[i]);\r
+      }\r
+    }\r
+\r
+    adjustAnnotations(command, false);\r
+  }\r
+\r
+  void paste(Edit command)\r
+  {\r
+    StringBuffer tmp;\r
+    boolean newDSNeeded;\r
+    int start = 0, end = 0;\r
+\r
+    for (int i = 0; i < command.seqs.length; i++)\r
+    {\r
+      newDSNeeded = false;\r
+      if (command.seqs[i].getLength() < 1)\r
+      {\r
+        // ie this sequence was deleted, we need to\r
+        // read it to the alignment\r
+        if (command.alIndex[i] < command.al.getHeight())\r
+        {\r
+          command.al.getSequences().insertElementAt(command.seqs[i],\r
+              command.alIndex[i]);\r
+        }\r
+        else\r
+        {\r
+          command.al.addSequence(command.seqs[i]);\r
+        }\r
+      }\r
+      tmp = new StringBuffer();\r
+      tmp.append(command.seqs[i].getSequence());\r
+\r
+      if (command.string != null && command.string[i] != null)\r
+      {\r
+        if (command.position >= tmp.length())\r
+        {\r
+          //This occurs if padding is on, and residues\r
+          //are removed from end of alignment\r
+          int length = command.position - tmp.length();\r
+          while (length > 0)\r
+          {\r
+            tmp.append(command.gapChar);\r
+            length--;\r
+          }\r
+        }\r
+        tmp.insert(command.position, command.string[i]);\r
+\r
+        for (int s = 0; s < command.string[i].length; s++)\r
+        {\r
+          if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] !=\r
+              23)\r
+          {\r
+            newDSNeeded = true;\r
+            start = command.seqs[i].findPosition(command.position);\r
+            end = command.seqs[i].findPosition(command.position +\r
+                                               command.number);\r
+            break;\r
+          }\r
+        }\r
+        command.string[i] = null;\r
+      }\r
+\r
+      command.seqs[i].setSequence(tmp.toString());\r
+\r
+      if (newDSNeeded)\r
+      {\r
+        if (command.seqs[i].getDatasetSequence() != null)\r
+        {\r
+          Sequence ds = new Sequence(command.seqs[i].getName(),\r
+                                     jalview.analysis.AlignSeq.extractGaps(\r
+                                         jalview.util.Comparison.GapChars,\r
+                                         command.seqs[i].getSequenceAsString()\r
+                                     ),\r
+                                     command.seqs[i].getStart(),\r
+                                     command.seqs[i].getEnd());\r
+          ds.setDescription(command.seqs[i].getDescription());\r
+          command.seqs[i].setDatasetSequence(ds);\r
+        }\r
+\r
+        adjustFeatures(command, i, start, end, true);\r
+      }\r
+    }\r
+\r
+    adjustAnnotations(command, true);\r
+\r
+    command.string = null;\r
+  }\r
+\r
+  final void adjustAnnotations(Edit command, boolean insert)\r
+  {\r
+\r
+    AlignmentAnnotation[] annotations = null;\r
+\r
+    if (command.fullAlignmentHeight)\r
+    {\r
+      annotations = command.al.getAlignmentAnnotation();\r
+    }\r
+    else\r
+    {\r
+      int aSize = 0;\r
+      AlignmentAnnotation[] tmp;\r
+      for (int s = 0; s < command.seqs.length; s++)\r
+      {\r
+        if (command.seqs[s].getAnnotation() == null)\r
+        {\r
+          continue;\r
+        }\r
+\r
+        if (aSize == 0)\r
+        {\r
+          annotations = command.seqs[s].getAnnotation();\r
+        }\r
+        else\r
+        {\r
+          tmp = new AlignmentAnnotation\r
+              [aSize + command.seqs[s].getAnnotation().length];\r
+\r
+          System.arraycopy(annotations, 0, tmp, 0, aSize);\r
+\r
+          System.arraycopy(command.seqs[s].getAnnotation(),\r
+                           0, tmp, aSize,\r
+                           command.seqs[s].getAnnotation().length);\r
+\r
+          annotations = tmp;\r
+        }\r
+\r
+        aSize = annotations.length;\r
+      }\r
+    }\r
+\r
+    if (annotations == null)\r
+    {\r
+      return;\r
+    }\r
+\r
+    if (!insert)\r
+    {\r
+      command.deletedAnnotations = new Hashtable();\r
+    }\r
+\r
+    int aSize;\r
+    Annotation[] temp;\r
+    for (int a = 0; a < annotations.length; a++)\r
+    {\r
+      if (annotations[a].autoCalculated)\r
+      {\r
+        continue;\r
+      }\r
+\r
+      int tSize = 0;\r
+\r
+      aSize = annotations[a].annotations.length;\r
+      if (insert)\r
+      {\r
+        temp = new Annotation[aSize + command.number];\r
+      }\r
+      else\r
+      {\r
+        if (command.position < aSize)\r
+        {\r
+          if (command.position + command.number > aSize)\r
+          {\r
+            tSize = aSize;\r
+          }\r
+          else\r
+          {\r
+            tSize = aSize - command.number + command.position;\r
+          }\r
+        }\r
+        else\r
+        {\r
+          tSize = aSize;\r
+        }\r
+\r
+        if (tSize < 0)\r
+        {\r
+          tSize = aSize;\r
+        }\r
+        temp = new Annotation[tSize];\r
+\r
+      }\r
+\r
+      if (insert)\r
+      {\r
+        if (command.position < annotations[a].annotations.length)\r
+        {\r
+          System.arraycopy(annotations[a].annotations,\r
+                           0, temp, 0, command.position);\r
+\r
+          if (command.deletedAnnotations != null\r
+              &&\r
+              command.deletedAnnotations.containsKey(annotations[a].\r
+              annotationId))\r
+          {\r
+            Annotation[] restore = (Annotation[])\r
+                command.deletedAnnotations.get(annotations[a].annotationId);\r
+\r
+            System.arraycopy(restore,\r
+                             0,\r
+                             temp,\r
+                             command.position,\r
+                             command.number);\r
+\r
+          }\r
+\r
+          System.arraycopy(annotations[a].annotations,\r
+                           command.position, temp,\r
+                           command.position + command.number,\r
+                           aSize - command.position);\r
+        }\r
+        else\r
+        {\r
+          if (command.deletedAnnotations != null\r
+              &&\r
+              command.deletedAnnotations.containsKey(annotations[a].\r
+              annotationId))\r
+          {\r
+            Annotation[] restore = (Annotation[])\r
+                command.deletedAnnotations.get(annotations[a].annotationId);\r
+\r
+            temp = new Annotation[annotations[a].annotations.length +\r
+                restore.length];\r
+            System.arraycopy(annotations[a].annotations,\r
+                             0, temp, 0,\r
+                             annotations[a].annotations.length);\r
+            System.arraycopy(restore, 0, temp,\r
+                             annotations[a].annotations.length, restore.length);\r
+          }\r
+          else\r
+          {\r
+            temp = annotations[a].annotations;\r
+          }\r
+        }\r
+      }\r
+      else\r
+      {\r
+        if (tSize != aSize || command.position < 2)\r
+        {\r
+          System.arraycopy(annotations[a].annotations,\r
+                           0, temp, 0, command.position);\r
+\r
+          Annotation[] deleted = new Annotation[command.number];\r
+          System.arraycopy(annotations[a].annotations,\r
+                           command.position, deleted, 0, command.number);\r
+\r
+          command.deletedAnnotations.put(annotations[a].annotationId,\r
+                                         deleted);\r
+\r
+          System.arraycopy(annotations[a].annotations,\r
+                           command.position + command.number,\r
+                           temp, command.position,\r
+                           aSize - command.position - command.number);\r
+        }\r
+        else\r
+        {\r
+          int dSize = aSize - command.position;\r
+\r
+          if (dSize > 0)\r
+          {\r
+            Annotation[] deleted = new Annotation[command.number];\r
+            System.arraycopy(annotations[a].annotations,\r
+                             command.position, deleted, 0, dSize);\r
+\r
+            command.deletedAnnotations.put(annotations[a].annotationId,\r
+                                           deleted);\r
+\r
+            tSize = Math.min(annotations[a].annotations.length,\r
+                             command.position);\r
+            temp = new Annotation[tSize];\r
+            System.arraycopy(annotations[a].annotations,\r
+                             0, temp, 0, tSize);\r
+          }\r
+          else\r
+          {\r
+            temp = annotations[a].annotations;\r
+          }\r
+        }\r
+      }\r
+\r
+      annotations[a].annotations = temp;\r
+    }\r
+  }\r
+\r
+  final void adjustFeatures(Edit command, int index, int i, int j,\r
+                            boolean insert)\r
+  {\r
+    SequenceI seq = command.seqs[index];\r
+    SequenceI sequence = seq.getDatasetSequence();\r
+    if (sequence == null)\r
+    {\r
+      sequence = seq;\r
+    }\r
+\r
+    if (insert)\r
+    {\r
+      if (command.editedFeatures != null\r
+          && command.editedFeatures.containsKey(seq))\r
+      {\r
+        sequence.setSequenceFeatures(\r
+            (SequenceFeature[]) command.editedFeatures.get(seq)\r
+            );\r
+      }\r
+\r
+      return;\r
+    }\r
+\r
+    SequenceFeature[] sf = sequence.getSequenceFeatures();\r
+\r
+    if (sf == null)\r
+    {\r
+      return;\r
+    }\r
+\r
+    SequenceFeature[] oldsf = new SequenceFeature[sf.length];\r
+\r
+    int cSize = j - i;\r
+\r
+    for (int s = 0; s < sf.length; s++)\r
+    {\r
+      SequenceFeature copy = new SequenceFeature(sf[s]);\r
+\r
+      oldsf[s] = copy;\r
+\r
+      if (sf[s].getEnd() < i)\r
+      {\r
+        continue;\r
+      }\r
+\r
+      if (sf[s].getBegin() > j)\r
+      {\r
+        sf[s].setBegin(copy.getBegin() - cSize);\r
+        sf[s].setEnd(copy.getEnd() - cSize);\r
+        continue;\r
+      }\r
+\r
+      if (sf[s].getBegin() >= i)\r
+      {\r
+        sf[s].setBegin(i);\r
+      }\r
+\r
+      if (sf[s].getEnd() < j)\r
+      {\r
+        sf[s].setEnd(j - 1);\r
+      }\r
+\r
+      sf[s].setEnd(sf[s].getEnd() - (cSize));\r
+\r
+      if (sf[s].getBegin() > sf[s].getEnd())\r
+      {\r
+        sequence.deleteFeature(sf[s]);\r
+      }\r
+    }\r
+\r
+    if (command.editedFeatures == null)\r
+    {\r
+      command.editedFeatures = new Hashtable();\r
+    }\r
+\r
+    command.editedFeatures.put(seq, oldsf);\r
+\r
+  }\r
+\r
+  class Edit\r
+  {\r
+    boolean fullAlignmentHeight = false;\r
+    Hashtable deletedAnnotations;\r
+    Hashtable editedFeatures;\r
+    AlignmentI al;\r
+    int command;\r
+    char[][] string;\r
+    SequenceI[] seqs;\r
+    int[] alIndex;\r
+    int position, number;\r
+    char gapChar;\r
+\r
+    Edit(int command,\r
+         SequenceI[] seqs,\r
+         int position,\r
+         int number,\r
+         char gapChar)\r
+    {\r
+      this.command = command;\r
+      this.seqs = seqs;\r
+      this.position = position;\r
+      this.number = number;\r
+      this.gapChar = gapChar;\r
+    }\r
+\r
+    Edit(int command,\r
+         SequenceI[] seqs,\r
+         int position,\r
+         int number,\r
+         AlignmentI al)\r
+    {\r
+      this.gapChar = al.getGapCharacter();\r
+      this.command = command;\r
+      this.seqs = seqs;\r
+      this.position = position;\r
+      this.number = number;\r
+      this.al = al;\r
+\r
+      alIndex = new int[seqs.length];\r
+      for (int i = 0; i < seqs.length; i++)\r
+      {\r
+        alIndex[i] = al.findIndex(seqs[i]);\r
+      }\r
+\r
+      fullAlignmentHeight = (al.getHeight() == seqs.length);\r
+    }\r
+  }\r
+\r
+}\r
diff --git a/src/jalview/commands/OrderCommand.java b/src/jalview/commands/OrderCommand.java
new file mode 100644 (file)
index 0000000..57097c0
--- /dev/null
@@ -0,0 +1,62 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+package jalview.commands;\r
+\r
+import jalview.analysis.*;\r
+import jalview.datamodel.*;\r
+\r
+public class OrderCommand\r
+    implements CommandI\r
+{\r
+  String description;\r
+  SequenceI[] seqs;\r
+  SequenceI[] seqs2;\r
+  AlignmentI al;\r
+\r
+  public OrderCommand(String description,\r
+                      SequenceI[] seqs,\r
+                      AlignmentI al)\r
+  {\r
+    this.description = description;\r
+    this.seqs = seqs;\r
+    this.seqs2 = al.getSequencesArray();\r
+    this.al = al;\r
+    doCommand();\r
+  }\r
+\r
+  public String getDescription()\r
+  {\r
+    return description;\r
+  }\r
+\r
+  public int getSize()\r
+  {\r
+    return 1;\r
+  }\r
+\r
+  public void doCommand()\r
+  {\r
+    AlignmentSorter.setOrder(al, seqs2);\r
+  }\r
+\r
+  public void undoCommand()\r
+  {\r
+    AlignmentSorter.setOrder(al, seqs);\r
+  }\r
+}\r
diff --git a/src/jalview/commands/RemoveGapColCommand.java b/src/jalview/commands/RemoveGapColCommand.java
new file mode 100644 (file)
index 0000000..1e9b832
--- /dev/null
@@ -0,0 +1,106 @@
+package jalview.commands;\r
+\r
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+\r
+import jalview.datamodel.*;\r
+\r
+public class RemoveGapColCommand\r
+    extends EditCommand\r
+{\r
+  int columnsDeleted;\r
+  public RemoveGapColCommand(String description,\r
+                             SequenceI[] seqs,\r
+                             int start, int end, AlignmentI al)\r
+  {\r
+    this.description = description;\r
+\r
+    int j, jSize = seqs.length;\r
+\r
+    int startCol = -1, endCol = -1;\r
+    columnsDeleted = 0;\r
+\r
+    edits = new Edit[0];\r
+\r
+    boolean delete = true;\r
+    for (int i = start; i <= end; i++)\r
+    {\r
+      delete = true;\r
+\r
+      for (j = 0; j < jSize; j++)\r
+      {\r
+        if (seqs[j].getLength() > i)\r
+        {\r
+          if (!jalview.util.Comparison.isGap(seqs[j].getCharAt(i)))\r
+          {\r
+            if (delete)\r
+            {\r
+              endCol = i;\r
+            }\r
+\r
+            delete = false;\r
+            break;\r
+          }\r
+        }\r
+      }\r
+\r
+      if (delete && startCol == -1)\r
+      {\r
+        startCol = i;\r
+      }\r
+\r
+      if (!delete && startCol > -1)\r
+      {\r
+        this.appendEdit(DELETE_GAP, seqs,\r
+                        startCol - columnsDeleted,\r
+                        endCol - startCol,\r
+                        al,\r
+                        false);\r
+\r
+        columnsDeleted += (endCol - startCol);\r
+        startCol = -1;\r
+        endCol = -1;\r
+      }\r
+    }\r
+\r
+    if (delete && startCol > -1)\r
+    {\r
+      //This is for empty columns at the\r
+      //end of the alignment\r
+\r
+      this.appendEdit(DELETE_GAP, seqs,\r
+                      startCol - columnsDeleted,\r
+                      end - startCol + 1,\r
+                      al,\r
+                      false);\r
+\r
+      columnsDeleted += (end - startCol + 1);\r
+    }\r
+\r
+    performEdit(0);\r
+  }\r
+\r
+  public int getSize()\r
+  {\r
+    //We're interested in the number of columns deleted,\r
+    //Not the number of sequence edits.\r
+    return columnsDeleted;\r
+  }\r
+\r
+}\r
diff --git a/src/jalview/commands/RemoveGapsCommand.java b/src/jalview/commands/RemoveGapsCommand.java
new file mode 100644 (file)
index 0000000..ccc8008
--- /dev/null
@@ -0,0 +1,120 @@
+package jalview.commands;\r
+\r
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+\r
+import jalview.datamodel.*;\r
+\r
+public class RemoveGapsCommand\r
+    extends EditCommand\r
+{\r
+  public RemoveGapsCommand(String description,\r
+                           SequenceI[] seqs, AlignmentI al)\r
+  {\r
+    this.description = description;\r
+    int width = 0;\r
+    for (int i = 0; i < seqs.length; i++)\r
+    {\r
+      if (seqs[i].getLength() > width)\r
+      {\r
+        width = seqs[i].getLength();\r
+      }\r
+    }\r
+\r
+    findGaps(seqs, 0, width, al);\r
+  }\r
+\r
+  public RemoveGapsCommand(String description,\r
+                           SequenceI[] seqs,\r
+                           int start, int end, AlignmentI al)\r
+  {\r
+    this.description = description;\r
+    findGaps(seqs, start, end, al);\r
+  }\r
+\r
+  void findGaps(SequenceI[] seqs, int start, int end, AlignmentI al)\r
+  {\r
+\r
+    int startCol = -1, endCol = -1;\r
+    int deletedCols = 0;\r
+\r
+    int j, jSize;\r
+\r
+    edits = new Edit[0];\r
+\r
+    boolean delete = true;\r
+    char[] sequence;\r
+\r
+    for (int s = 0; s < seqs.length; s++)\r
+    {\r
+      deletedCols = 0;\r
+      startCol = -1;\r
+      endCol = -1;\r
+      sequence = seqs[s].getSequence(start, end + 1);\r
+\r
+      jSize = sequence.length;\r
+      for (j = 0; j < jSize; j++)\r
+      {\r
+        delete = true;\r
+\r
+        if (!jalview.util.Comparison.isGap(sequence[j]))\r
+        {\r
+          if (delete)\r
+          {\r
+            endCol = j;\r
+          }\r
+\r
+          delete = false;\r
+        }\r
+\r
+        if (delete && startCol == -1)\r
+        {\r
+          startCol = j;\r
+        }\r
+\r
+        if (!delete && startCol > -1)\r
+        {\r
+          this.appendEdit(DELETE_GAP, new SequenceI[]\r
+                          {seqs[s]},\r
+                          start + startCol - deletedCols,\r
+                          endCol - startCol,\r
+                          al,\r
+                          false);\r
+\r
+          deletedCols += (endCol - startCol);\r
+          startCol = -1;\r
+          endCol = -1;\r
+        }\r
+      }\r
+      if (delete && startCol > -1)\r
+      {\r
+        this.appendEdit(DELETE_GAP, new SequenceI[]\r
+                        {seqs[s]},\r
+                        start + startCol - deletedCols,\r
+                        jSize - startCol,\r
+                        al,\r
+                        false);\r
+      }\r
+\r
+    }\r
+\r
+    performEdit(0);\r
+  }\r
+\r
+}\r
diff --git a/src/jalview/commands/TrimRegionCommand.java b/src/jalview/commands/TrimRegionCommand.java
new file mode 100644 (file)
index 0000000..4c1dea0
--- /dev/null
@@ -0,0 +1,178 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+package jalview.commands;\r
+\r
+import java.util.*;\r
+\r
+import jalview.datamodel.*;\r
+import jalview.util.*;\r
+\r
+public class TrimRegionCommand\r
+    extends EditCommand\r
+{\r
+  public static String TRIM_LEFT = "TrimLeft";\r
+  public static String TRIM_RIGHT = "TrimRight";\r
+\r
+  public ColumnSelection colSel = null;\r
+\r
+  int[] start;\r
+\r
+  ShiftList shiftList;\r
+\r
+  SequenceGroup selectionGroup;\r
+\r
+  Vector deletedHiddenColumns;\r
+\r
+  int columnsDeleted;\r
+\r
+  public TrimRegionCommand(String description,\r
+                           String command,\r
+                           SequenceI[] seqs,\r
+                           int column,\r
+                           AlignmentI al,\r
+                           ColumnSelection colSel,\r
+                           SequenceGroup selectedRegion)\r
+  {\r
+    this.description = description;\r
+    this.selectionGroup = selectedRegion;\r
+    this.colSel = colSel;\r
+    if (command.equalsIgnoreCase(TRIM_LEFT))\r
+    {\r
+      if (column == 0)\r
+      {\r
+        return;\r
+      }\r
+\r
+      columnsDeleted = column;\r
+\r
+      edits = new Edit[]\r
+          {\r
+          new Edit(CUT, seqs, 0, column, al)};\r
+    }\r
+    else if (command.equalsIgnoreCase(TRIM_RIGHT))\r
+    {\r
+      int width = al.getWidth() - column - 1;\r
+      if (width < 2)\r
+      {\r
+        return;\r
+      }\r
+\r
+      columnsDeleted = width - 1;\r
+\r
+      edits = new Edit[]\r
+          {\r
+          new Edit(CUT, seqs, column + 1, width, al)};\r
+    }\r
+\r
+    //We need to keep a record of the sequence start\r
+    //in order to restore the state after a redo\r
+    int i, isize = edits[0].seqs.length;\r
+    start = new int[isize];\r
+    for (i = 0; i < isize; i++)\r
+    {\r
+      start[i] = edits[0].seqs[i].getStart();\r
+    }\r
+\r
+    performEdit(0);\r
+  }\r
+\r
+  void cut(Edit command)\r
+  {\r
+    int column, j, jSize = command.seqs.length;\r
+    for (j = 0; j < jSize; j++)\r
+    {\r
+      if (command.position == 0)\r
+      {\r
+        //This is a TRIM_LEFT command\r
+        column = command.seqs[j].findPosition(command.number);\r
+        command.seqs[j].setStart(column);\r
+      }\r
+      else\r
+      {\r
+        //This is a TRIM_RIGHT command\r
+        column = command.seqs[j].findPosition(command.position) - 1;\r
+        command.seqs[j].setEnd(column);\r
+      }\r
+    }\r
+\r
+    super.cut(command);\r
+\r
+    if (command.position == 0)\r
+    {\r
+      deletedHiddenColumns = colSel.compensateForEdit(0, command.number);\r
+      if (selectionGroup != null)\r
+      {\r
+        selectionGroup.adjustForRemoveLeft(command.number);\r
+      }\r
+    }\r
+    else\r
+    {\r
+      deletedHiddenColumns = colSel.compensateForEdit(command.position,\r
+          command.number);\r
+      if (selectionGroup != null)\r
+      {\r
+        selectionGroup.adjustForRemoveRight(command.position);\r
+      }\r
+    }\r
+  }\r
+\r
+  void paste(Edit command)\r
+  {\r
+    super.paste(command);\r
+    int column, j, jSize = command.seqs.length;\r
+    for (j = 0; j < jSize; j++)\r
+    {\r
+      if (command.position == 0)\r
+      {\r
+        command.seqs[j].setStart(start[j]);\r
+      }\r
+      else\r
+      {\r
+        column = command.seqs[j]\r
+            .findPosition(command.number + command.position) - 1;\r
+        command.seqs[j].setEnd(column);\r
+      }\r
+    }\r
+\r
+    if (command.position == 0)\r
+    {\r
+      colSel.compensateForEdit(0, -command.number);\r
+      if (selectionGroup != null)\r
+      {\r
+        selectionGroup.adjustForRemoveLeft( -command.number);\r
+      }\r
+    }\r
+\r
+    if (deletedHiddenColumns != null)\r
+    {\r
+      int[] region;\r
+      for (int i = 0; i < deletedHiddenColumns.size(); i++)\r
+      {\r
+        region = (int[]) deletedHiddenColumns.elementAt(i);\r
+        colSel.hideColumns(region[0], region[1]);\r
+      }\r
+    }\r
+  }\r
+\r
+  public int getSize()\r
+  {\r
+    return columnsDeleted;\r
+  }\r
+\r
+}\r
diff --git a/src/jalview/gui/EditNameDialog.java b/src/jalview/gui/EditNameDialog.java
new file mode 100644 (file)
index 0000000..b6cc5a5
--- /dev/null
@@ -0,0 +1,80 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+\r
+package jalview.gui;\r
+\r
+import java.awt.*;\r
+import javax.swing.*;\r
+\r
+public class EditNameDialog\r
+{\r
+  JTextField id, description;\r
+  JButton ok = new JButton("Accept");\r
+  JButton cancel = new JButton("Cancel");\r
+  boolean accept = false;\r
+\r
+  public String getName()\r
+  {\r
+    return id.getText();\r
+  }\r
+\r
+  public String getDescription()\r
+  {\r
+    if (description.getText().length() < 1)\r
+    {\r
+      return null;\r
+    }\r
+    else\r
+    {\r
+      return description.getText();\r
+    }\r
+  }\r
+\r
+  public EditNameDialog(String name,\r
+                        String desc,\r
+                        String label1,\r
+                        String label2,\r
+                        String title)\r
+  {\r
+    JLabel idlabel = new JLabel(label1);\r
+    JLabel desclabel = new JLabel(label2);\r
+    idlabel.setFont(new Font("Courier", Font.PLAIN, 12));\r
+    desclabel.setFont(new Font("Courier", Font.PLAIN, 12));\r
+    id = new JTextField(name, 40);\r
+    description = new JTextField(desc, 40);\r
+    JPanel panel = new JPanel(new BorderLayout());\r
+    JPanel panel2 = new JPanel(new BorderLayout());\r
+    panel2.add(idlabel, BorderLayout.WEST);\r
+    panel2.add(id, BorderLayout.CENTER);\r
+    panel.add(panel2, BorderLayout.NORTH);\r
+    panel2 = new JPanel(new BorderLayout());\r
+    panel2.add(desclabel, BorderLayout.WEST);\r
+    panel2.add(description, BorderLayout.CENTER);\r
+    panel.add(panel2, BorderLayout.SOUTH);\r
+\r
+    int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,\r
+        panel, title,\r
+        JOptionPane.OK_CANCEL_OPTION);\r
+\r
+    if (reply == JOptionPane.OK_OPTION)\r
+    {\r
+      accept = true;\r
+    }\r
+  }\r
+}\r
diff --git a/src/jalview/gui/TextColourChooser.java b/src/jalview/gui/TextColourChooser.java
new file mode 100644 (file)
index 0000000..504e1d5
--- /dev/null
@@ -0,0 +1,213 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+\r
+package jalview.gui;\r
+\r
+import java.util.*;\r
+\r
+import java.awt.*;\r
+import java.awt.event.*;\r
+import javax.swing.*;\r
+import javax.swing.event.*;\r
+\r
+import jalview.datamodel.*;\r
+\r
+public class TextColourChooser\r
+{\r
+  AlignmentPanel ap;\r
+  SequenceGroup sg;\r
+\r
+  public void chooseColour(AlignmentPanel ap, SequenceGroup sg)\r
+  {\r
+    this.ap = ap;\r
+    this.sg = sg;\r
+\r
+    int original1, original2, originalThreshold;\r
+    if (sg == null)\r
+    {\r
+      original1 = ap.av.textColour.getRGB();\r
+      original2 = ap.av.textColour2.getRGB();\r
+      originalThreshold = ap.av.thresholdTextColour;\r
+    }\r
+    else\r
+    {\r
+      original1 = sg.textColour.getRGB();\r
+      original2 = sg.textColour2.getRGB();\r
+      originalThreshold = sg.thresholdTextColour;\r
+    }\r
+\r
+    final JSlider slider = new JSlider(0, 750, originalThreshold);\r
+    final JPanel col1 = new JPanel();\r
+    col1.setPreferredSize(new Dimension(40, 20));\r
+    col1.setBorder(BorderFactory.createEtchedBorder());\r
+    col1.setToolTipText("Dark Colour");\r
+    col1.setBackground(new Color(original1));\r
+    final JPanel col2 = new JPanel();\r
+    col2.setPreferredSize(new Dimension(40, 20));\r
+    col2.setBorder(BorderFactory.createEtchedBorder());\r
+    col2.setToolTipText("Light Colour");\r
+    col2.setBackground(new Color(original2));\r
+    final JPanel bigpanel = new JPanel(new BorderLayout());\r
+    JPanel panel = new JPanel();\r
+    bigpanel.add(panel, BorderLayout.CENTER);\r
+    bigpanel.add(new JLabel(\r
+        "<html><i>Select a dark and light text colour, then set the threshold to"\r
+        + "<br>switch between colours, based on background colour</i></html>"),\r
+                 BorderLayout.NORTH);\r
+    panel.add(col1);\r
+    panel.add(slider);\r
+    panel.add(col2);\r
+\r
+    col1.addMouseListener(new MouseAdapter()\r
+    {\r
+      public void mousePressed(MouseEvent e)\r
+      {\r
+        Color col = JColorChooser.showDialog(bigpanel,\r
+                                             "Select Colour for Text",\r
+                                             col1.getBackground());\r
+        if (col != null)\r
+        {\r
+          colour1Changed(col);\r
+          col1.setBackground(col);\r
+        }\r
+      }\r
+    });\r
+\r
+    col2.addMouseListener(new MouseAdapter()\r
+    {\r
+      public void mousePressed(MouseEvent e)\r
+      {\r
+        Color col = JColorChooser.showDialog(bigpanel,\r
+                                             "Select Colour for Text",\r
+                                             col2.getBackground());\r
+        if (col != null)\r
+        {\r
+          colour2Changed(col);\r
+          col2.setBackground(col);\r
+        }\r
+      }\r
+    });\r
+\r
+    slider.addChangeListener(new ChangeListener()\r
+    {\r
+      public void stateChanged(ChangeEvent evt)\r
+      {\r
+        thresholdChanged(slider.getValue());\r
+      }\r
+    });\r
+\r
+    int reply = JOptionPane.showInternalOptionDialog(\r
+        ap,\r
+        bigpanel,\r
+        "Adjust Foreground Text Colour Threshold",\r
+        JOptionPane.OK_CANCEL_OPTION,\r
+        JOptionPane.QUESTION_MESSAGE,\r
+        null,\r
+        null, null);\r
+\r
+    if (reply == JOptionPane.CANCEL_OPTION)\r
+    {\r
+      if (sg == null)\r
+      {\r
+        ap.av.textColour = new Color(original1);\r
+        ap.av.textColour2 = new Color(original2);\r
+        ap.av.thresholdTextColour = originalThreshold;\r
+      }\r
+      else\r
+      {\r
+        sg.textColour = new Color(original1);\r
+        sg.textColour2 = new Color(original2);\r
+        sg.thresholdTextColour = originalThreshold;\r
+      }\r
+    }\r
+  }\r
+\r
+  void colour1Changed(Color col)\r
+  {\r
+    if (sg == null)\r
+    {\r
+      ap.av.textColour = col;\r
+      if (ap.av.colourAppliesToAllGroups)\r
+      {\r
+        setGroupTextColour();\r
+      }\r
+    }\r
+    else\r
+    {\r
+      sg.textColour = col;\r
+    }\r
+\r
+    ap.repaint();\r
+  }\r
+\r
+  void colour2Changed(Color col)\r
+  {\r
+    if (sg == null)\r
+    {\r
+      ap.av.textColour2 = col;\r
+      if (ap.av.colourAppliesToAllGroups)\r
+      {\r
+        setGroupTextColour();\r
+      }\r
+    }\r
+    else\r
+    {\r
+      sg.textColour2 = col;\r
+    }\r
+\r
+    ap.repaint();\r
+  }\r
+\r
+  void thresholdChanged(int value)\r
+  {\r
+    if (sg == null)\r
+    {\r
+      ap.av.thresholdTextColour = value;\r
+      if (ap.av.colourAppliesToAllGroups)\r
+      {\r
+        setGroupTextColour();\r
+      }\r
+    }\r
+    else\r
+    {\r
+      sg.thresholdTextColour = value;\r
+    }\r
+\r
+    ap.repaint();\r
+  }\r
+\r
+  void setGroupTextColour()\r
+  {\r
+    if (ap.av.alignment.getGroups() == null)\r
+    {\r
+      return;\r
+    }\r
+\r
+    Vector groups = ap.av.alignment.getGroups();\r
+\r
+    for (int i = 0; i < groups.size(); i++)\r
+    {\r
+      SequenceGroup sg = (SequenceGroup) groups.elementAt(i);\r
+      sg.textColour = ap.av.textColour;\r
+      sg.textColour2 = ap.av.textColour2;\r
+      sg.thresholdTextColour = ap.av.thresholdTextColour;\r
+    }\r
+  }\r
+\r
+}\r
diff --git a/src/jalview/gui/UserQuestionnaireCheck.java b/src/jalview/gui/UserQuestionnaireCheck.java
new file mode 100644 (file)
index 0000000..0accc9c
--- /dev/null
@@ -0,0 +1,140 @@
+package jalview.gui;
+
+import java.io.*;
+import java.net.*;
+
+import javax.swing.*;
+
+public class UserQuestionnaireCheck
+    implements Runnable
+{
+  /**
+   * Implements the client side machinery for detecting a new questionnaire,
+   * checking if the user has responded to an existing one,
+   * and prompting the user for responding to a questionnaire.
+   * This is intended to work with the perl CGI scripts checkresponder.pl and
+   * questionnaire.pl
+   */
+  String url = null;
+  UserQuestionnaireCheck(String url)
+  {
+    if (url.indexOf("questionnaire.pl") == -1)
+    {
+      jalview.bin.Cache.log.error("'" + url +
+          "' is an Invalid URL for the checkForQuestionnaire() method.\n"
+                                  + "This argument is only for questionnaires derived from jalview's questionnaire.pl cgi interface.");
+    }
+    else
+    {
+      this.url = url;
+    }
+  }
+
+  String qid = null, rid = null;
+  private boolean checkresponse(URL qurl)
+      throws Exception
+  {
+    jalview.bin.Cache.log.debug("Checking Response for : " + qurl);
+    boolean prompt = false;
+    // see if we have already responsed to this questionnaire or get a new qid/rid pair
+    BufferedReader br = new BufferedReader(new InputStreamReader(qurl.
+        openStream()));
+    String qresp;
+    while ( (qresp = br.readLine()) != null)
+    {
+      if (qresp.indexOf("NOTYET:") == 0)
+      {
+        prompt = true; // not yet responded under that ID
+      }
+      else
+      {
+        if (qresp.indexOf("QUESTIONNAIRE:") == 0)
+        {
+          // QUESTIONNAIRE:qid:rid for the latest questionnaire.
+          int p = qresp.indexOf(':', 14);
+          if (p > -1)
+          {
+            rid = null;
+            qid = qresp.substring(14, p);
+            if (p < (qresp.length() - 1))
+            {
+              rid = qresp.substring(p + 1);
+              prompt = true; // this is a new qid/rid pair
+            }
+          }
+        }
+      }
+    }
+    return prompt;
+  }
+
+  public void run()
+  {
+    if (url == null)
+    {
+      return;
+    }
+    boolean prompt = false;
+    try
+    {
+      // First - check to see if wee have an old questionnaire/response id pair.
+      String lastq = jalview.bin.Cache.getProperty("QUESTIONNAIRE");
+      if (lastq == null)
+      {
+        prompt = checkresponse(new URL(url + (url.indexOf('?') > -1 ? "&" : "?") +
+                                       "checkresponse=1"));
+      }
+      else
+      {
+        String qurl = url + (url.indexOf('?') > -1 ? "&" : "?") +
+            "checkresponse=1";
+        // query the server with the old qid/id pair
+        String qqid = lastq.indexOf(':') > -1 ?
+            lastq.substring(0, lastq.indexOf(':')) : null;
+        if (qqid != null && qqid != "null" && qqid.length() > 0)
+        {
+          qurl += "&qid=" + qqid;
+          qid = qqid;
+          String qrid = lastq.substring(lastq.indexOf(':') + 1); // retrieve old rid
+          if (qrid != null && !qrid.equals("null"))
+          {
+            rid = qrid;
+            qurl += "&rid=" + qrid;
+          }
+        }
+        // see if we have already responsed to this questionnaire.
+        prompt = checkresponse(new URL(qurl));
+      }
+      if (qid != null && rid != null)
+      {
+        // Update our local property cache with latest qid and rid
+        jalview.bin.Cache.setProperty("QUESTIONNAIRE", qid + ":" + rid);
+      }
+      if (prompt)
+      {
+        String qurl = url + (url.indexOf('?') > -1 ? "&" : "?") + "qid=" + qid +
+            "&rid=" + rid;
+        jalview.bin.Cache.log.info("Prompting user for questionnaire at " +
+                                   qurl);
+        int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
+            "There is a new Questionnaire available." +
+            "Would you like to complete it now ?\n",
+            "Jalview User Survey",
+            JOptionPane.YES_NO_OPTION,
+            JOptionPane.QUESTION_MESSAGE);
+
+        if (reply == JOptionPane.YES_OPTION)
+        {
+          jalview.bin.Cache.log.debug("Opening " + qurl);
+          jalview.util.BrowserLauncher.openURL(qurl);
+        }
+      }
+    }
+    catch (Exception e)
+    {
+      jalview.bin.Cache.log.warn("When trying to access questionnaire URL " +
+                                 url, e);
+    }
+  }
+
+}
diff --git a/src/jalview/gui/VamsasClient.java b/src/jalview/gui/VamsasClient.java
new file mode 100755 (executable)
index 0000000..7c37bd3
--- /dev/null
@@ -0,0 +1,275 @@
+/**
+ *
+ */
+package jalview.gui;
+
+import java.io.File;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Vector;
+import java.util.jar.JarOutputStream;
+
+import javax.swing.JInternalFrame;
+
+import jalview.bin.Cache;
+import jalview.io.VamsasDatastore;
+
+import org.vamsas.client.UserHandle;
+import org.vamsas.client.simpleclient.FileWatcher;
+import org.vamsas.client.simpleclient.VamsasArchive;
+import org.vamsas.client.simpleclient.VamsasFile;
+import org.vamsas.objects.core.Entry;
+import org.vamsas.objects.core.VamsasDocument;
+import org.vamsas.test.simpleclient.ArchiveClient;
+import org.vamsas.test.simpleclient.ClientDoc;
+
+/**
+ * @author jimp
+ *
+ */
+public class VamsasClient
+    extends ArchiveClient
+{
+  // Cache.preferences for vamsas client session arena
+  // preferences for check for default session at startup.
+  // user and organisation stuff.
+  public VamsasClient(Desktop jdesktop,
+                      File sessionPath)
+  {
+    super(System.getProperty("user.name"), System.getProperty("host.name"),
+          "jalview", "2.7",
+          sessionPath);
+  }
+
+  public void initial_update()
+  {
+    Cache.log.info("Jalview loading the Vamsas Session.");
+    // load in the vamsas archive for the first time
+    ClientDoc cdoc = this.getUpdateable();
+    updateJalview(cdoc);
+    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+
+    if (frames == null)
+    {
+      return;
+    }
+
+    try
+    {
+      //REVERSE ORDER
+      for (int i = frames.length - 1; i > -1; i--)
+      {
+        if (frames[i] instanceof AlignFrame)
+        {
+          AlignFrame af = (AlignFrame) frames[i];
+          af.alignPanel.alignmentChanged();
+        }
+      }
+    }
+    catch (Exception e)
+    {
+      Cache.log.warn(
+          "Exception whilst refreshing jalview windows after a vamsas document update.",
+          e);
+    }
+    doUpdate(cdoc);
+    cdoc.closeDoc();
+  }
+
+  /**
+   * this will close all windows currently in Jalview.
+   *
+
+    protected void closeWindows() {
+     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+
+        if (frames == null)
+        {
+            return;
+        }
+
+        try
+        {
+            for (int i = frames.length - 1; i > -1; i--) {
+             frames[i].dispose();
+            }
+        } catch (Exception e) {
+         Cache.log.error("Whilst closing windows",e);
+        }
+
+    }
+
+    public void get_update(VamsasArchive doc) {
+     // Close windows - load update.
+     Cache.log.info("Jalview updating from Vamsas Session.");
+    }
+   */
+  VamsasClientWatcher watcher = null;
+  public void push_update()
+  {
+    watchForChange = false;
+    try
+    {
+      Thread.sleep(WATCH_SLEEP);
+    }
+    catch (Exception e)
+    {
+
+    }
+    ;
+    ClientDoc cdoc = getUpdateable();
+    updateVamsasDocument(cdoc);
+    doUpdate(cdoc);
+    cdoc.closeDoc();
+    cdoc = null;
+    watchForChange = true;
+    if (watcher != null)
+    {
+      watcher.start();
+    }
+    // collect all uncached alignments and put them into the vamsas dataset.
+    // store them.
+    Cache.log.info("Jalview updating the Vamsas Session.");
+  }
+
+  public void end_session()
+  {
+    //   stop any update/watcher thread.
+    watchForChange = false; // this makes any watch(long) loops return.
+    // we should also wait arount for this.WATCH_SLEEP to really make sure the watcher thread has stopped.
+    try
+    {
+      Thread.sleep(WATCH_SLEEP);
+    }
+    catch (Exception e)
+    {
+
+    }
+    ;
+    Cache.log.info("Jalview disconnecting from the Vamsas Session.");
+  }
+
+  public void updateJalview(ClientDoc cdoc)
+  {
+    ensureJvVamsas();
+    VamsasDatastore vds = new VamsasDatastore(cdoc, vobj2jv, jv2vobj,
+                                              baseProvEntry());
+    vds.updateToJalview();
+  }
+
+  private void ensureJvVamsas()
+  {
+    if (jv2vobj == null)
+    {
+      jv2vobj = new IdentityHashMap();
+      vobj2jv = new Hashtable();
+    }
+  }
+
+  /**
+   * jalview object binding to VorbaIds
+   */
+  IdentityHashMap jv2vobj = null;
+  Hashtable vobj2jv = null;
+  public void updateVamsasDocument(ClientDoc doc)
+  {
+    ensureJvVamsas();
+    VamsasDatastore vds = new VamsasDatastore(doc, vobj2jv, jv2vobj,
+                                              baseProvEntry());
+    // wander through frames
+    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+
+    if (frames == null)
+    {
+      return;
+    }
+
+    try
+    {
+      //REVERSE ORDER
+      for (int i = frames.length - 1; i > -1; i--)
+      {
+        if (frames[i] instanceof AlignFrame)
+        {
+          AlignFrame af = (AlignFrame) frames[i];
+
+          // update alignment and root from frame.
+          vds.storeVAMSAS(af.getViewport(), af.getTitle());
+        }
+      }
+    }
+    catch (Exception e)
+    {
+      Cache.log.error("Vamsas Document store exception", e);
+    }
+  }
+
+  private Entry baseProvEntry()
+  {
+    org.vamsas.objects.core.Entry pentry = new org.vamsas.objects.core.Entry();
+    pentry.setUser(this.getProvenanceUser());
+    pentry.setApp(this.getClientHandle().getClientName());
+    pentry.setDate(new org.exolab.castor.types.Date(new java.util.Date()));
+    pentry.setAction("created");
+    return pentry;
+  }
+
+  protected class VamsasClientWatcher
+      extends Thread
+  {
+    /* (non-Javadoc)
+     * @see java.lang.Thread#run()
+     */
+    VamsasClient client = null;
+    VamsasClientWatcher(VamsasClient client)
+    {
+      this.client = client;
+    }
+
+    boolean running = false;
+    public void run()
+    {
+      running = true;
+      while (client.watchForChange)
+      {
+        ClientDoc docio = client.watch(0);
+        if (docio != null)
+        {
+          client.disableGui(true);
+          Cache.log.debug("Updating jalview from changed vamsas document.");
+          client.updateJalview(docio);
+          Cache.log.debug("Finished updating from document change.");
+          docio.closeDoc();
+          docio = null;
+          client.disableGui(false);
+        }
+      }
+      running = false;
+
+    }
+
+  }
+
+  public void disableGui(boolean b)
+  {
+    Desktop.instance.setVamsasUpdate(b);
+  }
+
+  public void startWatcher()
+  {
+    if (watcher == null)
+    {
+      watcher = new VamsasClientWatcher(this);
+    }
+    Thread thr = new Thread()
+    {
+      public void run()
+      {
+        watcher.start();
+      }
+    };
+    thr.start();
+  }
+}
diff --git a/src/jalview/io/AMSAFile.java b/src/jalview/io/AMSAFile.java
new file mode 100644 (file)
index 0000000..1f3fca0
--- /dev/null
@@ -0,0 +1,96 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+package jalview.io;\r
+\r
+import jalview.datamodel.*;\r
+\r
+public class AMSAFile\r
+    extends jalview.io.FastaFile\r
+{\r
+\r
+  AlignmentI al;\r
+  /**\r
+   * Creates a new AMSAFile object for output.\r
+   */\r
+  public AMSAFile(AlignmentI al)\r
+  {\r
+    this.al = al;\r
+  }\r
+\r
+  /**\r
+   * DOCUMENT ME!\r
+   *\r
+   * @return DOCUMENT ME!\r
+   */\r
+  public String print()\r
+  {\r
+    super.print(getSeqsAsArray());\r
+\r
+    AlignmentAnnotation aa;\r
+    if (al.getAlignmentAnnotation() != null)\r
+    {\r
+      for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
+      {\r
+        aa = al.getAlignmentAnnotation()[i];\r
+        if (aa.autoCalculated || !aa.visible)\r
+        {\r
+          continue;\r
+        }\r
+\r
+        out.append(">#_" + aa.label);\r
+        if (aa.description != null)\r
+        {\r
+          out.append(" " + aa.description);\r
+        }\r
+\r
+        out.append("\n");\r
+\r
+        int nochunks = (aa.annotations.length / len) + 1;\r
+\r
+        for (int j = 0; j < nochunks; j++)\r
+        {\r
+          int start = j * len;\r
+          int end = start + len;\r
+          if (end > aa.annotations.length)\r
+          {\r
+            end = aa.annotations.length;\r
+          }\r
+\r
+          String ch;\r
+          for (int k = start; k < end; k++)\r
+          {\r
+            if (aa.annotations[k] == null)\r
+            {\r
+              ch = " ";\r
+            }\r
+            else\r
+            {\r
+              ch = aa.annotations[k].displayCharacter;\r
+            }\r
+\r
+            out.append(ch);\r
+\r
+          }\r
+          out.append("\n");\r
+        }\r
+      }\r
+    }\r
+    return out.toString();\r
+  }\r
+}\r
diff --git a/src/jalview/io/VamsasDatastore.java b/src/jalview/io/VamsasDatastore.java
new file mode 100755 (executable)
index 0000000..f1cba4f
--- /dev/null
@@ -0,0 +1,1900 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer
+ * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+package jalview.io;
+
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
+import jalview.gui.Desktop;
+import jalview.gui.TreePanel;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Vector;
+
+import org.vamsas.client.Vobject;
+import org.vamsas.client.VorbaId;
+import org.vamsas.objects.core.Alignment;
+import org.vamsas.objects.core.AlignmentSequence;
+import org.vamsas.objects.core.AlignmentSequenceAnnotation;
+import org.vamsas.objects.core.AnnotationElement;
+import org.vamsas.objects.core.DataSet;
+import org.vamsas.objects.core.DataSetAnnotations;
+import org.vamsas.objects.core.DbRef;
+import org.vamsas.objects.core.Entry;
+import org.vamsas.objects.core.Glyph;
+import org.vamsas.objects.core.Input;
+import org.vamsas.objects.core.Link;
+import org.vamsas.objects.core.Newick;
+import org.vamsas.objects.core.Param;
+import org.vamsas.objects.core.Property;
+import org.vamsas.objects.core.Provenance;
+import org.vamsas.objects.core.RangeAnnotation;
+import org.vamsas.objects.core.RangeType;
+import org.vamsas.objects.core.Seg;
+import org.vamsas.objects.core.Sequence;
+import org.vamsas.objects.core.Tree;
+import org.vamsas.objects.core.VAMSAS;
+import org.vamsas.test.simpleclient.ClientDoc;
+
+/*
+ *
+ * static {
+ * org.exolab.castor.util.LocalConfiguration.getInstance().getProperties().setProperty(
+ * "org.exolab.castor.serializer", "org.apache.xml.serialize.XMLSerilazizer"); }
+ *
+ */
+
+public class VamsasDatastore
+{
+  Entry provEntry = null;
+
+  // AlignViewport av;
+
+  org.exolab.castor.types.Date date = new org.exolab.castor.types.Date(
+      new java.util.Date());
+
+  ClientDoc cdoc;
+
+  Hashtable vobj2jv;
+
+  IdentityHashMap jv2vobj;
+
+  public VamsasDatastore(ClientDoc cdoc, Hashtable vobj2jv,
+                         IdentityHashMap jv2vobj, Entry provEntry)
+  {
+    this.cdoc = cdoc;
+    this.vobj2jv = vobj2jv;
+    this.jv2vobj = jv2vobj;
+    this.provEntry = provEntry;
+  }
+
+  /*
+   * public void storeJalview(String file, AlignFrame af) { try { // 1. Load the
+   * mapping information from the file Mapping map = new
+   * Mapping(getClass().getClassLoader()); java.net.URL url =
+   * getClass().getResource("/jalview_mapping.xml"); map.loadMapping(url); // 2.
+   * Unmarshal the data // Unmarshaller unmar = new Unmarshaller();
+   * //unmar.setIgnoreExtraElements(true); //unmar.setMapping(map); // uni =
+   * (UniprotFile) unmar.unmarshal(new FileReader(file)); // 3. marshal the data
+   * with the total price back and print the XML in the console Marshaller
+   * marshaller = new Marshaller( new FileWriter(file) );
+   *
+   * marshaller.setMapping(map); marshaller.marshal(af); } catch (Exception e) {
+   * e.printStackTrace(); } }
+   *
+   *
+   */
+  /**
+   * @return the Vobject bound to Jalview datamodel object
+   */
+  protected Vobject getjv2vObj(Object jvobj)
+  {
+    if (jv2vobj.containsKey(jvobj))
+    {
+      return cdoc.getObject( (VorbaId) jv2vobj.get(jvobj));
+    }
+    return null;
+  }
+
+  /**
+   *
+   * @param vobj
+   * @return Jalview datamodel object bound to the vamsas document object
+   */
+  protected Object getvObj2jv(org.vamsas.client.Vobject vobj)
+  {
+    VorbaId id = vobj.getVorbaId();
+    if (id == null)
+    {
+      id = cdoc.registerObject(vobj);
+      Cache.log
+          .debug("Registering new object and returning null for getvObj2jv");
+      return null;
+    }
+    if (vobj2jv.containsKey(vobj.getVorbaId()))
+    {
+      return vobj2jv.get(vobj.getVorbaId());
+    }
+    return null;
+  }
+
+  protected void bindjvvobj(Object jvobj, org.vamsas.client.Vobject vobj)
+  {
+    VorbaId id = vobj.getVorbaId();
+    if (id == null)
+    {
+      id = cdoc.registerObject(vobj);
+      if (id == null || vobj.getVorbaId() == null)
+      {
+        Cache.log.error("Failed to get id for " +
+                        (vobj.isRegisterable() ? "registerable" :
+                         "unregisterable") + " object " + vobj);
+      }
+    }
+
+    if (vobj2jv.containsKey(vobj.getVorbaId()) &&
+        ! ( (VorbaId) vobj2jv.get(vobj.getVorbaId())).equals(jvobj))
+    {
+      Cache.log.debug("Warning? Overwriting existing vamsas id binding for " +
+                      vobj.getVorbaId(),
+                      new Exception("Overwriting vamsas id binding."));
+    }
+    else if (jv2vobj.containsKey(jvobj) &&
+             ! ( (VorbaId) jv2vobj.get(jvobj)).equals(vobj.getVorbaId()))
+    {
+      Cache.log.debug(
+          "Warning? Overwriting existing jalview object binding for " + jvobj,
+          new Exception("Overwriting jalview object binding."));
+    }
+    /* Cache.log.error("Attempt to make conflicting object binding! "+vobj+" id " +vobj.getVorbaId()+" already bound to "+getvObj2jv(vobj)+" and "+jvobj+" already bound to "+getjv2vObj(jvobj),new Exception("Excessive call to bindjvvobj"));
+         }*/
+    // we just update the hash's regardless!
+    vobj2jv.put(vobj.getVorbaId(), jvobj);
+    // JBPNote - better implementing a hybrid invertible hash.
+    jv2vobj.put(jvobj, vobj.getVorbaId());
+  }
+
+  /**
+   * put the alignment viewed by AlignViewport into cdoc.
+   *
+   * @param av alignViewport to be stored
+   * @param aFtitle title for alignment
+   */
+  public void storeVAMSAS(AlignViewport av, String aFtitle)
+  {
+    try
+    {
+      jalview.datamodel.AlignmentI jal = av.getAlignment();
+      boolean nw = false;
+      VAMSAS root = null; // will be resolved based on Dataset Parent.
+      // /////////////////////////////////////////
+      // SAVE THE DATASET
+      if (jal.getDataset() == null)
+      {
+        Cache.log.warn("Creating new dataset for an alignment.");
+        jal.setDataset(null);
+      }
+      DataSet dataset = (DataSet) getjv2vObj(jal.getDataset());
+      if (dataset == null)
+      {
+        root = cdoc.getVamsasRoots()[0]; // default vamsas root for modifying.
+        dataset = new DataSet();
+        root.addDataSet(dataset);
+        bindjvvobj(jal.getDataset(), dataset);
+        dataset.setProvenance(dummyProvenance());
+        dataset.getProvenance().addEntry(provEntry);
+        nw = true;
+      }
+      else
+      {
+        root = (VAMSAS) dataset.getV_parent();
+      }
+      // update dataset
+      Sequence sequence;
+      DbRef dbref;
+      // set new dataset and alignment sequences based on alignment Nucleotide
+      // flag.
+      // this *will* break when alignment contains both nucleotide and amino
+      // acid sequences.
+      String dict = jal.isNucleotide() ?
+          org.vamsas.objects.utils.SymbolDictionary.STANDARD_NA
+          : org.vamsas.objects.utils.SymbolDictionary.STANDARD_AA;
+      for (int i = 0; i < jal.getHeight(); i++)
+      {
+        SequenceI sq = jal.getSequenceAt(i).getDatasetSequence(); // only insert
+        // referenced
+        // sequences
+        // to dataset.
+        sequence = (Sequence) getjv2vObj(sq);
+        if (sequence == null)
+        {
+          sequence = new Sequence();
+          bindjvvobj(sq, sequence);
+          sq.setVamsasId(sequence.getVorbaId().getId());
+          sequence.setSequence(sq.getSequenceAsString());
+          sequence.setDictionary(dict);
+          sequence.setName(jal.getDataset().getSequenceAt(i).getName());
+          sequence.setStart(jal.getDataset().getSequenceAt(i).getStart());
+          sequence.setEnd(jal.getDataset().getSequenceAt(i).getEnd());
+          dataset.addSequence(sequence);
+        }
+        else
+        {
+          // verify principal attributes. and update any new
+          // features/references.
+          System.out.println("update dataset sequence object.");
+        }
+        if (sq.getSequenceFeatures() != null)
+        {
+          int sfSize = sq.getSequenceFeatures().length;
+
+          for (int sf = 0; sf < sfSize; sf++)
+          {
+            jalview.datamodel.SequenceFeature feature = (jalview.datamodel.
+                SequenceFeature) sq
+                .getSequenceFeatures()[sf];
+
+            DataSetAnnotations dsa = (DataSetAnnotations) getjv2vObj(feature);
+            if (dsa == null)
+            {
+              dsa = (DataSetAnnotations) getDSAnnotationFromJalview(
+                  new DataSetAnnotations(), feature);
+              if (dsa.getProvenance() == null)
+              {
+                dsa.setProvenance(new Provenance());
+              }
+              addProvenance(dsa.getProvenance(), "created"); // JBPNote - need
+              // to update
+              dsa.setSeqRef(sequence);
+              bindjvvobj(feature, dsa);
+              dataset.addDataSetAnnotations(dsa);
+            }
+            else
+            {
+              // todo: verify and update dataset annotations for sequence
+              System.out.println("update dataset sequence annotations.");
+            }
+          }
+        }
+
+        if (sq.getDBRef() != null)
+        {
+          DBRefEntry[] entries = sq.getDBRef();
+          jalview.datamodel.DBRefEntry dbentry;
+          for (int db = 0; db < entries.length; db++)
+          {
+            dbentry = entries[db];
+            dbref = (DbRef) getjv2vObj(dbentry);
+            if (dbref == null)
+            {
+              dbref = new DbRef();
+              bindjvvobj(dbentry, dbref);
+              dbref.setAccessionId(dbentry.getAccessionId());
+              dbref.setSource(dbentry.getSource());
+              dbref.setVersion(dbentry.getVersion());
+              /*
+               * TODO: Maps are not yet supported by Jalview. Map vMap = new
+               * Map(); vMap.set dbref.addMap(vMap);
+               */
+              sequence.addDbRef(dbref);
+            }
+            else
+            {
+              // TODO: verify and update dbrefs in vamsas document
+              // there will be trouble when a dataset sequence is modified to
+              // contain more residues than were originally referenced - we must
+              // then make a number of dataset sequence entries
+              System.out
+                  .println("update dataset sequence database references.");
+            }
+          }
+
+        }
+      }
+      // dataset.setProvenance(getVamsasProvenance(jal.getDataset().getProvenance()));
+      // ////////////////////////////////////////////
+
+      // ////////////////////////////////////////////
+      // Save the Alignments
+
+      Alignment alignment = (Alignment) getjv2vObj(av); // this is so we can get the alignviewport back
+      if (alignment == null)
+      {
+        alignment = new Alignment();
+        bindjvvobj(av, alignment);
+        if (alignment.getProvenance() == null)
+        {
+          alignment.setProvenance(new Provenance());
+        }
+        addProvenance(alignment.getProvenance(), "added"); // TODO: insert some
+        // sensible source
+        // here
+        dataset.addAlignment(alignment);
+        {
+          Property title = new Property();
+          title.setName("jalview:AlTitle");
+          title.setType("string");
+          title.setContent(aFtitle);
+          alignment.addProperty(title);
+        }
+        alignment.setGapChar(String.valueOf(av.getGapCharacter()));
+        AlignmentSequence alseq = null;
+        for (int i = 0; i < jal.getHeight(); i++)
+        {
+          alseq = new AlignmentSequence();
+          // TODO: VAMSAS: translate lowercase symbols to annotation ?
+          alseq.setSequence(jal.getSequenceAt(i).getSequenceAsString());
+          alseq.setName(jal.getSequenceAt(i).getName());
+          alseq.setStart(jal.getSequenceAt(i).getStart());
+          alseq.setEnd(jal.getSequenceAt(i).getEnd());
+          alseq.setRefid(getjv2vObj(jal.getSequenceAt(i).getDatasetSequence()));
+          alignment.addAlignmentSequence(alseq);
+          bindjvvobj(jal.getSequenceAt(i), alseq);
+        }
+      }
+      else
+      {
+        // todo: verify and update mutable alignment props.
+        if (alignment.getModifiable())
+        {
+          System.out.println("update alignment in document.");
+        }
+        else
+        {
+          System.out
+              .println("update edited alignment to new alignment in document.");
+        }
+      }
+      // ////////////////////////////////////////////
+      // SAVE Alignment Sequence Features
+      for (int i = 0, iSize = alignment.getAlignmentSequenceCount(); i < iSize;
+           i++)
+      {
+        AlignmentSequence valseq;
+        SequenceI alseq = (SequenceI) getvObj2jv(valseq = alignment
+                                                 .getAlignmentSequence(i));
+        if (alseq != null && alseq.getSequenceFeatures() != null)
+        {
+          jalview.datamodel.SequenceFeature[] features = alseq
+              .getSequenceFeatures();
+          for (int f = 0; f < features.length; f++)
+          {
+            if (features[f] != null)
+            {
+              AlignmentSequenceAnnotation valseqf = (
+                  AlignmentSequenceAnnotation) getjv2vObj(features[i]);
+              if (valseqf == null)
+              {
+
+                valseqf = (AlignmentSequenceAnnotation)
+                    getDSAnnotationFromJalview(
+                        new AlignmentSequenceAnnotation(), features[i]);
+                if (valseqf.getProvenance() == null)
+                {
+                  valseqf.setProvenance(new Provenance());
+                }
+                addProvenance(valseqf.getProvenance(), "created"); // JBPNote -
+                // need to
+                // update
+                bindjvvobj(features[i], valseqf);
+                valseq.addAlignmentSequenceAnnotation(valseqf);
+              }
+            }
+
+          }
+        }
+      }
+
+      // ////////////////////////////////////////////
+      // SAVE ANNOTATIONS
+      if (jal.getAlignmentAnnotation() != null)
+      {
+        jalview.datamodel.AlignmentAnnotation[] aa = jal
+            .getAlignmentAnnotation();
+        java.util.HashMap AlSeqMaps = new HashMap(); // stores int maps from
+        // alignment columns to
+        // sequence positions.
+        for (int i = 0; i < aa.length; i++)
+        {
+          if (aa[i] == null || isJalviewOnly(aa[i]))
+          {
+            continue;
+          }
+          if (aa[i].sequenceRef != null)
+          {
+            org.vamsas.objects.core.AlignmentSequence alsref = (org.vamsas.
+                objects.core.AlignmentSequence) getjv2vObj(aa[i].sequenceRef);
+            org.vamsas.objects.core.AlignmentSequenceAnnotation an = (org.
+                vamsas.objects.core.AlignmentSequenceAnnotation) getjv2vObj(aa[
+                i]);
+            int[] gapMap = null;
+            if (AlSeqMaps.containsKey(aa[i].sequenceRef))
+            {
+              gapMap = (int[]) AlSeqMaps.get(aa[i].sequenceRef);
+            }
+            else
+            {
+              gapMap = new int[aa[i].sequenceRef.getLength()];
+              // map from alignment position to sequence position.
+              int[] sgapMap = aa[i].sequenceRef.gapMap();
+              for (int a = 0; a < sgapMap.length; a++)
+              {
+                gapMap[sgapMap[a]] = a;
+              }
+            }
+            if (an == null)
+            {
+              an = new org.vamsas.objects.core.AlignmentSequenceAnnotation();
+              Seg vSeg = new Seg();
+              vSeg.setStart(1);
+              vSeg.setInclusive(true);
+              vSeg.setEnd(gapMap.length);
+              an.addSeg(vSeg);
+              an.setType("jalview:SecondaryStructurePrediction"); // TODO: better fix this rough guess ;)
+              alsref.addAlignmentSequenceAnnotation(an);
+              bindjvvobj(aa[i], an);
+              // LATER: much of this is verbatim from the alignmentAnnotation
+              // method below. suggests refactoring to make rangeAnnotation the
+              // base class
+              an.setDescription(aa[i].description);
+              if (aa[i].graph > 0)
+              {
+                an.setGraph(true); // aa[i].graph);
+              }
+              else
+              {
+                an.setGraph(false);
+              }
+              an.setLabel(aa[i].label);
+              an.setProvenance(dummyProvenance()); // get provenance as user
+              // created, or jnet, or
+              // something else.
+              an.setGroup(Integer.toString(aa[i].graphGroup)); // // JBPNote -
+              // originally we
+              // were going to
+              // store
+              // graphGroup in
+              // the Jalview
+              // specific
+              // bits.
+              AnnotationElement ae;
+              for (int a = 0; a < aa[i].annotations.length; a++)
+              {
+                if (aa[i].annotations[a] == null)
+                {
+                  continue;
+                }
+
+                ae = new AnnotationElement();
+                ae.setDescription(aa[i].annotations[a].description);
+                ae.addGlyph(new Glyph());
+                ae.getGlyph(0)
+                    .setContent(aa[i].annotations[a].displayCharacter); // assume
+                // jax-b
+                // takes
+                // care
+                // of
+                // utf8
+                // translation
+                if (aa[i].graph !=
+                    jalview.datamodel.AlignmentAnnotation.NO_GRAPH)
+                {
+                  ae.addValue(aa[i].annotations[a].value);
+                }
+                ae.setPosition(gapMap[a] + 1); // position w.r.t. AlignmentSequence
+                // symbols
+                if (aa[i].annotations[a].secondaryStructure != ' ')
+                {
+                  // we only write an annotation where it really exists.
+                  Glyph ss = new Glyph();
+                  ss
+                      .setDict(org.vamsas.objects.utils.GlyphDictionary.
+                               PROTEIN_SS_3STATE);
+                  ss.setContent(String
+                                .valueOf(aa[i].annotations[a].
+                                         secondaryStructure));
+                  ae.addGlyph(ss);
+                }
+                an.addAnnotationElement(ae);
+              }
+            }
+            else
+            {
+              // update reference sequence Annotation
+              if (an.getModifiable())
+              {
+                // verify existing alignment sequence annotation is up to date
+                System.out.println("update alignment sequence annotation.");
+              }
+              else
+              {
+                // verify existing alignment sequence annotation is up to date
+                System.out
+                    .println(
+                    "make new alignment sequence annotation if modification has happened.");
+              }
+            }
+          }
+          else
+          {
+            // add Alignment Annotation
+            org.vamsas.objects.core.AlignmentAnnotation an = (org.vamsas.
+                objects.core.AlignmentAnnotation) getjv2vObj(aa[i]);
+            if (an == null)
+            {
+              an = new org.vamsas.objects.core.AlignmentAnnotation();
+              an.setType("jalview:AnnotationRow");
+              an.setDescription(aa[i].description);
+              alignment.addAlignmentAnnotation(an);
+              Seg vSeg = new Seg();
+              vSeg.setStart(1);
+              vSeg.setInclusive(true);
+              vSeg.setEnd(jal.getWidth());
+              an.addSeg(vSeg);
+              if (aa[i].graph > 0)
+              {
+                an.setGraph(true); // aa[i].graph);
+              }
+              an.setLabel(aa[i].label);
+              an.setProvenance(dummyProvenance());
+              if (aa[i].graph != aa[i].NO_GRAPH)
+              {
+                an.setGroup(Integer.toString(aa[i].graphGroup)); // // JBPNote -
+                // originally we
+                // were going to
+                // store
+                // graphGroup in
+                // the Jalview
+                // specific
+                // bits.
+                an.setGraph(true);
+              }
+              else
+              {
+                an.setGraph(false);
+              }
+              AnnotationElement ae;
+
+              for (int a = 0; a < aa[i].annotations.length; a++)
+              {
+                if ( (aa[i] == null) || (aa[i].annotations[a] == null))
+                {
+                  continue;
+                }
+
+                ae = new AnnotationElement();
+                ae.setDescription(aa[i].annotations[a].description);
+                ae.addGlyph(new Glyph());
+                ae.getGlyph(0)
+                    .setContent(aa[i].annotations[a].displayCharacter); // assume
+                // jax-b
+                // takes
+                // care
+                // of
+                // utf8
+                // translation
+                ae.addValue(aa[i].annotations[a].value);
+                ae.setPosition(a + 1);
+                if (aa[i].annotations[a].secondaryStructure != ' ')
+                {
+                  Glyph ss = new Glyph();
+                  ss
+                      .setDict(org.vamsas.objects.utils.GlyphDictionary.
+                               PROTEIN_SS_3STATE);
+                  ss.setContent(String
+                                .valueOf(aa[i].annotations[a].
+                                         secondaryStructure));
+                  ae.addGlyph(ss);
+                }
+                an.addAnnotationElement(ae);
+              }
+              if (aa[i].editable)
+              {
+                //an.addProperty(newProperty("jalview:editable", null, "true"));
+                an.setModifiable(true);
+              }
+              if (aa[i].graph != jalview.datamodel.AlignmentAnnotation.NO_GRAPH)
+              {
+                an.setGraph(true);
+                an.setGroup(Integer.toString(aa[i].graphGroup));
+                an.addProperty(newProperty("jalview:graphType", null,
+                                           ( (aa[i].graph ==
+                                              jalview.datamodel.AlignmentAnnotation.
+                                              BAR_GRAPH) ? "BAR_GRAPH" :
+                                            "LINE_GRAPH")));
+
+                /** and on and on..
+                 vProperty=new Property();
+                  vProperty.setName("jalview:graphThreshhold");
+                  vProperty.setContent(aa[i].threshold);
+                 */
+
+              }
+            }
+            else
+            {
+              if (an.getModifiable())
+              {
+                // verify annotation - update (perhaps)
+                Cache.log.info(
+                    "update alignment sequence annotation. not yet implemented.");
+              }
+              else
+              {
+                // verify annotation - update (perhaps)
+                Cache.log.info("updated alignment sequence annotation added.");
+              }
+            }
+          }
+        }
+      }
+      // /////////////////////////////////////////////////////
+
+      // //////////////////////////////////////////////
+      // /SAVE THE TREES
+      // /////////////////////////////////
+      // FIND ANY ASSOCIATED TREES
+      if (Desktop.desktop != null)
+      {
+        javax.swing.JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+
+        for (int t = 0; t < frames.length; t++)
+        {
+          if (frames[t] instanceof TreePanel)
+          {
+            TreePanel tp = (TreePanel) frames[t];
+
+            if (tp.getAlignment() == jal)
+            {
+              Tree tree = (Tree) getjv2vObj(tp);
+              if (tree == null)
+              {
+                tree = new Tree();
+                bindjvvobj(tp, tree);
+                tree.setTitle(tp.getTitle());
+                Newick newick = new Newick();
+                // TODO: translate sequenceI to leaf mappings to vamsas
+                // references - see tree specification in schema.
+                newick.setContent(tp.getTree().toString());
+                newick.setTitle(tp.getTitle());
+                tree.addNewick(newick);
+                tree.setProvenance(makeTreeProvenance(jal, tp));
+                alignment.addTree(tree);
+              }
+              else
+              {
+                if (tree.getModifiable())
+                {
+                  // verify any changes.
+                  System.out.println("Update tree in document.");
+                }
+                else
+                {
+                  System.out
+                      .println("Add modified tree as new tree in document.");
+                }
+              }
+            }
+          }
+        }
+      }
+      // Store Jalview specific stuff in the Jalview appData
+      // not implemented in the SimpleDoc interface.
+    }
+
+    catch (Exception ex)
+    {
+      ex.printStackTrace();
+    }
+
+  }
+
+  private Property newProperty(String name, String type, String content)
+  {
+    Property vProperty = new Property();
+    vProperty.setName(name);
+    if (type != null)
+    {
+      vProperty.setType(type);
+    }
+    else
+    {
+      vProperty.setType("String");
+    }
+    vProperty.setContent(content);
+    return vProperty;
+  }
+
+  /**
+   * correctly create a RangeAnnotation from a jalview sequence feature
+   *
+   * @param dsa
+   *          (typically DataSetAnnotations or AlignmentSequenceAnnotation)
+   * @param feature
+   *          (the feature to be mapped from)
+   * @return
+   */
+  private RangeAnnotation getDSAnnotationFromJalview(RangeAnnotation dsa,
+      SequenceFeature feature)
+  {
+    dsa.setType(feature.getType());
+    Seg vSeg = new Seg();
+    vSeg.setStart(feature.getBegin());
+    vSeg.setEnd(feature.getEnd());
+    vSeg.setInclusive(true);
+    dsa.addSeg(vSeg);
+    dsa.setDescription(feature.getDescription());
+    dsa.setStatus(feature.getStatus());
+    if (feature.links != null && feature.links.size() > 0)
+    {
+      for (int i = 0, iSize = feature.links.size(); i < iSize; i++)
+      {
+        String link = (String) feature.links.elementAt(i);
+        int sep = link.indexOf('|');
+        if (sep > -1)
+        {
+          Link vLink = new Link();
+          if (sep > 0)
+          {
+            vLink.setContent(link.substring(0, sep - 1));
+          }
+          else
+          {
+            vLink.setContent("");
+          }
+          vLink.setHref(link.substring(sep + 1)); // TODO: validate href.
+          dsa.addLink(vLink);
+        }
+      }
+    }
+    dsa.setGroup(feature.getFeatureGroup());
+    return dsa;
+  }
+
+  /**
+   * correctly creates provenance for trees calculated on an alignment by
+   * jalview.
+   *
+   * @param jal
+   * @param tp
+   * @return
+   */
+  private Provenance makeTreeProvenance(AlignmentI jal, TreePanel tp)
+  {
+    Provenance prov = new Provenance();
+    prov.addEntry(new Entry());
+    prov.getEntry(0).setAction("imported " + tp.getTitle());
+    prov.getEntry(0).setUser(provEntry.getUser());
+    prov.getEntry(0).setApp(provEntry.getApp());
+    prov.getEntry(0).setDate(provEntry.getDate());
+    if (tp.getTree().hasOriginalSequenceData())
+    {
+      Input vInput = new Input();
+      // LATER: check to see if tree input data is contained in this alignment -
+      // or just correctly resolve the tree's seqData to the correct alignment in
+      // the document.
+      // vInput.setObjRef(getjv2vObj(jal));
+      vInput.setObjRef(getjv2vObj(tp.getViewPort()));
+      prov.getEntry(0).setAction("created " + tp.getTitle());
+      prov.getEntry(0).addInput(vInput);
+      vInput.setName("jalview:seqdist");
+      prov.getEntry(0).addParam(new Param());
+      prov.getEntry(0).getParam(0).setName("treeType");
+      prov.getEntry(0).getParam(0).setType("utf8");
+      prov.getEntry(0).getParam(0).setContent("NJ");
+
+      int ranges[] = tp.getTree().seqData.getVisibleContigs();
+      // VisibleContigs are with respect to alignment coordinates. Still need offsets
+      int start = tp.getTree().seqData.getAlignmentOrigin();
+      for (int r = 0; r < ranges.length; r += 2)
+      {
+        Seg visSeg = new Seg();
+        visSeg.setStart(1 + start + ranges[r]);
+        visSeg.setEnd(start + ranges[r + 1]);
+        visSeg.setInclusive(true);
+        vInput.addSeg(visSeg);
+      }
+    }
+    return prov;
+  }
+
+  /**
+   *
+   * @param tp
+   * @return Object[] { AlignmentView, AlignmentI - reference alignment for
+   *         input }
+   */
+  private Object[] recoverInputData(Provenance tp)
+  {
+    for (int pe = 0; pe < tp.getEntryCount(); pe++)
+    {
+      if (tp.getEntry(pe).getInputCount() > 0)
+      {
+        if (tp.getEntry(pe).getInputCount() > 1)
+        {
+          Cache.log.warn("Ignoring additional input spec in provenance entry "
+                         + tp.getEntry(pe).toString());
+        }
+        // LATER: deal sensibly with multiple inputs.
+        Input vInput = tp.getEntry(pe).getInput(0);
+        if (vInput.getObjRef() instanceof org.vamsas.objects.core.Alignment)
+        {
+          // recover an AlignmentView for the input data
+          AlignViewport javport = (AlignViewport) getvObj2jv( (org.vamsas.
+              client.Vobject) vInput
+              .getObjRef());
+          jalview.datamodel.AlignmentI jal = javport.getAlignment();
+          jalview.datamodel.CigarArray view = javport.getAlignment().
+              getCompactAlignment();
+          int from = 1, to = jal.getWidth();
+          int offset = 0; // deleteRange modifies its frame of reference
+          for (int r = 0, s = vInput.getSegCount(); r < s; r++)
+          {
+            Seg visSeg = vInput.getSeg(r);
+            int se[] = getSegRange(visSeg, true); // jalview doesn't do bidirection alignments yet.
+            if (to < se[1])
+            {
+              Cache.log.warn("Ignoring invalid segment in InputData spec.");
+            }
+            else
+            {
+              if (se[0] > from)
+              {
+                view.deleteRange(offset + from - 1, offset + se[0] - 2);
+                offset -= se[0] - from;
+              }
+              from = se[1] + 1;
+            }
+          }
+          if (from < to)
+          {
+            view.deleteRange(offset + from - 1, offset + to - 1); // final deletion - TODO: check off by
+            // one for to
+          }
+          return new Object[]
+              {
+              new AlignmentView(view), jal};
+        }
+      }
+    }
+    Cache.log.debug("Returning null for input data recovery from provenance.");
+    return null;
+  }
+
+  /**
+   * get start<end range of segment, adjusting for inclusivity flag and
+   * polarity.
+   *
+   * @param visSeg
+   * @param ensureDirection when true - always ensure start is less than end.
+   * @return int[] { start, end, direction} where direction==1 for range running from end to start.
+   */
+  private int[] getSegRange(Seg visSeg, boolean ensureDirection)
+  {
+    boolean incl = visSeg.getInclusive();
+    // adjust for inclusive flag.
+    int pol = (visSeg.getStart() <= visSeg.getEnd()) ? 1 : -1; // polarity of
+    // region.
+    int start = visSeg.getStart() + (incl ? 0 : pol);
+    int end = visSeg.getEnd() + (incl ? 0 : -pol);
+    if (ensureDirection && pol == -1)
+    {
+      // jalview doesn't deal with inverted ranges, yet.
+      int t = end;
+      end = start;
+      start = t;
+    }
+    return new int[]
+        {
+        start, end, pol < 0 ? 1 : 0};
+  }
+
+  /**
+   *
+   * @param annotation
+   * @return true if annotation is not to be stored in document
+   */
+  private boolean isJalviewOnly(AlignmentAnnotation annotation)
+  {
+    return annotation.label.equals("Quality")
+        || annotation.label.equals("Conservation")
+        || annotation.label.equals("Consensus");
+  }
+
+  /**
+   * This will return the first AlignFrame viewing AlignViewport av.
+   * It will break if there are more than one AlignFrames viewing a particular av.
+   * This also shouldn't be in the io package.
+   * @param av
+   * @return alignFrame for av
+   */
+  public AlignFrame getAlignFrameFor(AlignViewport av)
+  {
+    if (Desktop.desktop != null)
+    {
+      javax.swing.JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+
+      for (int t = 0; t < frames.length; t++)
+      {
+        if (frames[t] instanceof AlignFrame)
+        {
+          if ( ( (AlignFrame) frames[t]).getViewport() == av)
+          {
+            return (AlignFrame) frames[t];
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  public void updateToJalview()
+  {
+    VAMSAS _roots[] = cdoc.getVamsasRoots();
+
+    for (int _root = 0; _root < _roots.length; _root++)
+    {
+      VAMSAS root = _roots[_root];
+      boolean newds = false;
+      for (int _ds = 0, _nds = root.getDataSetCount(); _ds < _nds; _ds++)
+      {
+        // ///////////////////////////////////
+        // ///LOAD DATASET
+        DataSet dataset = root.getDataSet(_ds);
+        int i, iSize = dataset.getSequenceCount();
+        Vector dsseqs;
+        jalview.datamodel.Alignment jdataset = (jalview.datamodel.Alignment)
+            getvObj2jv(dataset);
+        int jremain = 0;
+        if (jdataset == null)
+        {
+          Cache.log.debug("Initialising new jalview dataset fields");
+          newds = true;
+          dsseqs = new Vector();
+        }
+        else
+        {
+          Cache.log.debug("Update jalview dataset from vamsas.");
+          jremain = jdataset.getHeight();
+          dsseqs = jdataset.getSequences();
+        }
+
+        // TODO: test sequence merging - we preserve existing non vamsas
+        // sequences but add in any new vamsas ones, and don't yet update any
+        // sequence attributes
+        for (i = 0; i < iSize; i++)
+        {
+          Sequence vdseq = dataset.getSequence(i);
+          jalview.datamodel.SequenceI dsseq = (SequenceI) getvObj2jv(vdseq);
+          if (dsseq != null)
+          {
+            if (!dsseq.getSequence().equals(vdseq.getSequence()))
+            {
+              throw new Error(
+                  "Broken! - mismatch of dataset sequence and jalview internal dataset sequence.");
+            }
+            jremain--;
+          }
+          else
+          {
+            dsseq = new jalview.datamodel.Sequence(
+                dataset.getSequence(i).getName(),
+                dataset.getSequence(i).getSequence(),
+                dataset.getSequence(i).getStart(),
+                dataset.getSequence(i).getEnd());
+            dsseq.setDescription(dataset.getSequence(i).getDescription());
+            bindjvvobj(dsseq, dataset.getSequence(i));
+            dsseq.setVamsasId(dataset.getSequence(i).getVorbaId().getId());
+            dsseqs.add(dsseq);
+          }
+          if (vdseq.getDbRefCount() > 0)
+          {
+            DbRef[] dbref = vdseq.getDbRef();
+            for (int db = 0; db < dbref.length; db++)
+            {
+              jalview.datamodel.DBRefEntry dbr = (jalview.datamodel.DBRefEntry)
+                  getvObj2jv(dbref[db]);
+              if (dbr == null)
+              {
+                // add new dbref
+                dsseq.addDBRef(dbr = new jalview.datamodel.DBRefEntry
+                               (
+                                   dbref[db].getSource().toString(),
+                                   dbref[db].getVersion().toString(),
+                                   dbref[db].getAccessionId().toString()));
+                bindjvvobj(dbr, dbref[db]);
+              }
+            }
+          }
+        }
+
+        if (newds)
+        {
+          SequenceI[] seqs = new SequenceI[dsseqs.size()];
+          for (i = 0, iSize = dsseqs.size(); i < iSize; i++)
+          {
+            seqs[i] = (SequenceI) dsseqs.elementAt(i);
+            dsseqs.setElementAt(null, i);
+          }
+          jdataset = new jalview.datamodel.Alignment(seqs);
+          Cache.log.debug("New vamsas dataset imported into jalview.");
+          bindjvvobj(jdataset, dataset);
+        }
+        // ////////
+        // add any new dataset sequence feature annotations
+        if (dataset.getDataSetAnnotations() != null)
+        {
+          for (int dsa = 0; dsa < dataset.getDataSetAnnotationsCount(); dsa++)
+          {
+            DataSetAnnotations dseta = dataset.getDataSetAnnotations(dsa);
+            SequenceI dsSeq = (SequenceI) getvObj2jv( (Vobject) dseta.getSeqRef());
+            if (dsSeq == null)
+            {
+              jalview.bin.Cache.log.warn(
+                  "Couldn't resolve jalview sequenceI for dataset object reference " +
+                  ( (Vobject) dataset.getDataSetAnnotations(dsa).getSeqRef()).
+                  getVorbaId().getId());
+            }
+            else
+            {
+              if (dseta.getAnnotationElementCount() == 0)
+              {
+                jalview.datamodel.SequenceFeature sf = (jalview.datamodel.
+                    SequenceFeature) getvObj2jv(dseta);
+                if (sf == null)
+                {
+                  dsSeq.addSequenceFeature(sf = getJalviewSeqFeature(dseta));
+                  bindjvvobj(sf, dseta);
+                }
+              }
+              else
+              {
+                // TODO: deal with alignmentAnnotation style annotation
+                // appearing on dataset sequences.
+                // JBPNote: we could just add them to all alignments but
+                // that may complicate cross references in the jalview
+                // datamodel
+                Cache.log.warn("Ignoring dataset annotation with annotationElements. Not yet supported in jalview.");
+              }
+            }
+          }
+        }
+
+        if (dataset.getAlignmentCount() > 0)
+        {
+          // LOAD ALIGNMENTS from DATASET
+
+          for (int al = 0, nal = dataset.getAlignmentCount(); al < nal; al++)
+          {
+            org.vamsas.objects.core.Alignment alignment = dataset.getAlignment(
+                al);
+            AlignViewport av = (AlignViewport) getvObj2jv(alignment);
+            jalview.datamodel.AlignmentI jal = null;
+            if (av != null)
+            {
+              jal = av.getAlignment();
+            }
+            iSize = alignment.getAlignmentSequenceCount();
+            boolean newal = (jal == null) ? true : false;
+            Vector newasAnnots = new Vector();
+            char gapChar = ' '; // default for new alignments read in from the document
+            if (jal != null)
+            {
+              dsseqs = jal.getSequences(); // for merge/update
+              gapChar = jal.getGapCharacter();
+            }
+            else
+            {
+              dsseqs = new Vector();
+            }
+            char valGapchar = alignment.getGapChar().charAt(0);
+            for (i = 0; i < iSize; i++)
+            {
+              AlignmentSequence valseq = alignment.getAlignmentSequence(i);
+              jalview.datamodel.SequenceI alseq = (SequenceI) getvObj2jv(valseq);
+              if (alseq != null)
+              {
+                //TODO: upperCase/LowerCase situation here ? do we allow it ?
+                //if (!alseq.getSequence().equals(valseq.getSequence())) {
+                // throw new Error("Broken! - mismatch of dataset sequence and jalview internal dataset sequence.");
+                if (Cache.log.isDebugEnabled())
+                {
+                  Cache.log.debug("Updating apparently edited sequence " +
+                                  alseq.getName());
+                }
+                // this might go *horribly* wrong
+                alseq.setSequence(new String(valseq.getSequence()).replace(
+                    valGapchar, gapChar));
+                jremain--;
+              }
+              else
+              {
+                alseq = new jalview.datamodel.Sequence(
+                    valseq.getName(),
+                    valseq.getSequence().replace(valGapchar, gapChar),
+                    valseq.getStart(),
+                    valseq.getEnd());
+
+                Vobject datsetseq = (Vobject) valseq.getRefid();
+                if (datsetseq != null)
+                {
+                  alseq.setDatasetSequence( (SequenceI) getvObj2jv(datsetseq)); // exceptions if AlignemntSequence reference isn't a simple SequenceI
+                }
+                else
+                {
+                  Cache.log.error(
+                      "Invalid dataset sequence id (null) for alignment sequence " +
+                      valseq.getVorbaId());
+                }
+                bindjvvobj(alseq, valseq);
+                alseq.setVamsasId(valseq.getVorbaId().getId());
+                dsseqs.add(alseq);
+              }
+              if (valseq.getAlignmentSequenceAnnotationCount() > 0)
+              {
+                AlignmentSequenceAnnotation[] vasannot = valseq.
+                    getAlignmentSequenceAnnotation();
+                for (int a = 0; a < vasannot.length; a++)
+                {
+                  jalview.datamodel.AlignmentAnnotation asa = (jalview.
+                      datamodel.AlignmentAnnotation) getvObj2jv(vasannot[a]); // TODO: 1:many jalview alignment sequence annotations
+                  if (asa == null)
+                  {
+                    int se[] = getBounds(vasannot[a]);
+                    asa = getjAlignmentAnnotation(jal, vasannot[a]);
+                    asa.sequenceRef = alseq;
+                    asa.createSequenceMapping(alseq, alseq.getStart() + se[0], false); // TODO: verify that positions in alseqAnnotation correspond to ungapped residue positions.
+                    bindjvvobj(asa, vasannot[a]);
+                    newasAnnots.add(asa);
+                  }
+                  else
+                  {
+                    // update existing annotation - can do this in place
+                    if (vasannot[a].getModifiable())
+                    {
+                      Cache.log.info(
+                          "UNIMPLEMENTED: not recovering user modifiable sequence alignment annotation");
+                      // TODO: should at least replace with new one - otherwise things will break
+                      // basically do this:
+                      // int se[] = getBounds(vasannot[a]);
+                      // asa.update(getjAlignmentAnnotation(jal, vasannot[a])); //  update from another annotation object in place.
+                      // asa.createSequenceMapping(alseq, se[0], false);
+
+                    }
+                  }
+                }
+              }
+            }
+            if (jal == null)
+            {
+              SequenceI[] seqs = new SequenceI[dsseqs.size()];
+              for (i = 0, iSize = dsseqs.size(); i < iSize; i++)
+              {
+                seqs[i] = (SequenceI) dsseqs.elementAt(i);
+                dsseqs.setElementAt(null, i);
+              }
+              jal = new jalview.datamodel.Alignment(seqs);
+              Cache.log.debug("New vamsas alignment imported into jalview " +
+                              alignment.getVorbaId().getId());
+              jal.setDataset(jdataset);
+            }
+            if (newasAnnots != null && newasAnnots.size() > 0)
+            {
+              // Add the new sequence annotations in to the alignment.
+              for (int an = 0, anSize = newasAnnots.size(); an < anSize; an++)
+              {
+                jal.addAnnotation( (AlignmentAnnotation) newasAnnots.elementAt(
+                    an));
+                // TODO: check if anything has to be done - like calling adjustForAlignment or something.
+                newasAnnots.setElementAt(null, an);
+              }
+              newasAnnots = null;
+            }
+            // //////////////////////////////////////////
+            // //LOAD ANNOTATIONS FOR THE ALIGNMENT
+            // ////////////////////////////////////
+            if (alignment.getAlignmentAnnotationCount() > 0)
+            {
+              org.vamsas.objects.core.AlignmentAnnotation[] an = alignment.
+                  getAlignmentAnnotation();
+
+              for (int j = 0; j < an.length; j++)
+              {
+                jalview.datamodel.AlignmentAnnotation jan = (jalview.datamodel.
+                    AlignmentAnnotation) getvObj2jv(an[j]);
+                if (jan != null)
+                {
+                  // update or stay the same.
+                  // TODO: should at least replace with a new one - otherwise things will break
+                  // basically do this:
+                  // jan.update(getjAlignmentAnnotation(jal, an[a])); //  update from another annotation object in place.
+
+                  Cache.log.debug("update from vamsas alignment annotation to existing jalview alignment annotation.");
+                  if (an[j].getModifiable())
+                  {
+                    // TODO: user defined annotation is totally mutable... - so load it up or throw away if locally edited.
+                    Cache.log.info(
+                        "NOT IMPLEMENTED - Recovering user-modifiable annotation - yet...");
+                  }
+                  // TODO: compare annotation element rows
+                  // TODO: compare props.
+                }
+                else
+                {
+                  jan = getjAlignmentAnnotation(jal, an[j]);
+                  jal.addAnnotation(jan);
+                  bindjvvobj(jan, an[j]);
+                }
+              }
+            }
+            AlignFrame alignFrame;
+            if (av == null)
+            {
+              Cache.log.debug("New alignframe for alignment " +
+                              alignment.getVorbaId());
+              // ///////////////////////////////
+              // construct alignment view
+              alignFrame = new AlignFrame(jal, AlignFrame.DEFAULT_WIDTH,
+                                          AlignFrame.DEFAULT_HEIGHT);
+              av = alignFrame.getViewport();
+              String title = alignment.getProvenance().getEntry(alignment.
+                  getProvenance().getEntryCount() - 1).getAction();
+              if (alignment.getPropertyCount() > 0)
+              {
+                for (int p = 0, pe = alignment.getPropertyCount(); p < pe; p++)
+                {
+                  if (alignment.getProperty(p).getName().equals(
+                      "jalview:AlTitle"))
+                  {
+                    title = alignment.getProperty(p).getContent();
+                  }
+                }
+              }
+              // TODO: automatically create meaningful title for a vamsas alignment using its provenance.
+              jalview.gui.Desktop.addInternalFrame(alignFrame,
+                  title + "(" + alignment.getVorbaId() + ")",
+                  AlignFrame.DEFAULT_WIDTH,
+                  AlignFrame.DEFAULT_HEIGHT);
+              bindjvvobj(av, alignment);
+            }
+            else
+            {
+              // find the alignFrame for jal.
+              // TODO: fix this so we retrieve the alignFrame handing av *directly*
+              alignFrame = getAlignFrameFor(av);
+            }
+            // LOAD TREES
+            // /////////////////////////////////////
+            if (alignment.getTreeCount() > 0)
+            {
+
+              for (int t = 0; t < alignment.getTreeCount(); t++)
+              {
+                Tree tree = alignment.getTree(t);
+                TreePanel tp = (TreePanel) getvObj2jv(tree);
+                if (tp != null)
+                {
+                  Cache.log.info(
+                      "Update from vamsas document to alignment associated tree not implemented yet.");
+                }
+                else
+                {
+                  // make a new tree
+                  Object[] idata = this.recoverInputData(tree.getProvenance());
+                  try
+                  {
+                    AlignmentView inputData = null;
+                    if (idata != null && idata[0] != null)
+                    {
+                      inputData = (AlignmentView) idata[0];
+                    }
+                    tp = alignFrame.ShowNewickTree(
+                        new jalview.io.NewickFile(tree.getNewick(0).getContent()),
+                        tree.getNewick(0).getTitle() + " (" + tree.getVorbaId() +
+                        ")", inputData,
+                        600, 500,
+                        t * 20 + 50, t * 20 + 50);
+                    bindjvvobj(tp, tree);
+                  }
+                  catch (Exception e)
+                  {
+                    Cache.log.warn("Problems parsing treefile '" +
+                                   tree.getNewick(0).getContent() + "'", e);
+                  }
+                }
+              }
+            }
+
+          }
+        }
+      }
+    }
+  }
+
+  // bitfields - should be a template in j1.5
+  private static int HASSECSTR = 0;
+  private static int HASVALS = 1;
+  private static int HASHPHOB = 2;
+  private static int HASDC = 3;
+  private static int HASDESCSTR = 4;
+  private static int HASTWOSTATE = 5; // not used yet.
+  /**
+   * parses the AnnotationElements - if they exist - into jalview.datamodel.Annotation[] rows
+   * Two annotation rows are made if there are distinct annotation for both at 'pos' and 'after pos' at any particular site.
+   * @param annotation
+   * @return { boolean[static int constants ], int[ae.length] - map to annotated object frame, jalview.datamodel.Annotation[], jalview.datamodel.Annotation[] (after)}
+   */
+  private Object[] parseRangeAnnotation(org.vamsas.objects.core.RangeAnnotation
+                                        annotation)
+  {
+    // set these attributes by looking in the annotation to decide what kind of alignment annotation rows will be made
+    // TODO: potentially we might make several annotation rows from one vamsas alignment annotation. the jv2Vobj binding mechanism
+    // may not quite cope with this (without binding an array of annotations to a vamsas alignment annotation)
+    // summary flags saying what we found over the set of annotation rows.
+    boolean[] AeContent = new boolean[]
+        {
+        false, false, false, false, false};
+    int[] rangeMap = getMapping(annotation);
+    jalview.datamodel.Annotation[][] anot = new jalview.datamodel.Annotation[][]
+        {
+        new jalview.datamodel.Annotation[rangeMap.length],
+        new jalview.datamodel.Annotation[rangeMap.length]
+    };
+    boolean mergeable = true; //false  if 'after positions cant be placed on same annotation row as positions.
+
+    if (annotation.getAnnotationElementCount() > 0)
+    {
+      AnnotationElement ae[] = annotation.getAnnotationElement();
+      for (int aa = 0; aa < ae.length; aa++)
+      {
+        int pos = ae[aa].getPosition() - 1; // pos counts from 1 to (|seg.start-seg.end|+1)
+        if (pos >= 0 && pos < rangeMap.length)
+        {
+          int row = ae[aa].getAfter() ? 1 : 0;
+          if (anot[row][pos] != null)
+          {
+            // only time this should happen is if the After flag is set.
+            Cache.log.debug("Ignoring duplicate annotation site at " + pos);
+            continue;
+          }
+          if (anot[1 - row][pos] != null)
+          {
+            mergeable = false;
+          }
+          String desc = "";
+          if (ae[aa].getDescription() != null)
+          {
+            desc = ae[aa].getDescription();
+            if (desc.length() > 0)
+            {
+              // have imported valid description string
+              AeContent[HASDESCSTR] = true;
+            }
+          }
+          String dc = null; //ae[aa].getDisplayCharacter()==null ? "dc" : ae[aa].getDisplayCharacter();
+          String ss = null; //ae[aa].getSecondaryStructure()==null ? "ss" : ae[aa].getSecondaryStructure();
+          java.awt.Color colour = null;
+          if (ae[aa].getGlyphCount() > 0)
+          {
+            Glyph[] glyphs = ae[aa].getGlyph();
+            for (int g = 0; g < glyphs.length; g++)
+            {
+              if (glyphs[g].getDict().equals(org.vamsas.objects.utils.
+                                             GlyphDictionary.PROTEIN_SS_3STATE))
+              {
+                ss = glyphs[g].getContent();
+                AeContent[HASSECSTR] = true;
+              }
+              else if (glyphs[g].getDict().equals(org.vamsas.objects.utils.
+                                                  GlyphDictionary.
+                                                  PROTEIN_HD_HYDRO))
+              {
+                Cache.log.debug("ignoring hydrophobicity glyph marker.");
+                AeContent[HASHPHOB] = true;
+                char c = (dc = glyphs[g].getContent()).charAt(0);
+                // dc may get overwritten - but we still set the colour.
+                colour = new java.awt.Color(c == '+' ? 255 : 0,
+                                            c == '.' ? 255 : 0,
+                                            c == '-' ? 255 : 0);
+
+              }
+              else if (glyphs[g].getDict().equals(org.vamsas.objects.utils.
+                                                  GlyphDictionary.DEFAULT))
+              {
+                dc = glyphs[g].getContent();
+                AeContent[HASDC] = true;
+              }
+              else
+              {
+                Cache.log.debug("Ignoring unknown glyph type " +
+                                glyphs[g].getDict());
+              }
+            }
+          }
+          float val = 0;
+          if (ae[aa].getValueCount() > 0)
+          {
+            AeContent[HASVALS] = true;
+            if (ae[aa].getValueCount() > 1)
+            {
+              Cache.log.warn("ignoring additional " +
+                             (ae[aa].getValueCount() - 1) +
+                             "values in annotation element.");
+            }
+            val = ae[aa].getValue(0);
+          }
+          if (colour == null)
+          {
+            anot[row][pos] = new jalview.datamodel.Annotation( (dc != null) ?
+                dc : "", desc, (ss != null) ? ss.charAt(0) : ' ', val);
+          }
+          else
+          {
+            anot[row][pos] = new jalview.datamodel.Annotation( (dc != null) ?
+                dc : "", desc, (ss != null) ? ss.charAt(0) : ' ', val, colour);
+          }
+        }
+        else
+        {
+          Cache.log.warn("Ignoring out of bound annotation element " + aa +
+                         " in " + annotation.getVorbaId().getId());
+        }
+      }
+      // decide on how many annotation rows are needed.
+      if (mergeable)
+      {
+        for (int i = 0; i < anot[0].length; i++)
+        {
+          if (anot[1][i] != null)
+          {
+            anot[0][i] = anot[1][i];
+            anot[0][i].description = anot[0][i].description + " (after)";
+            AeContent[HASDESCSTR] = true; // we have valid description string data
+            anot[1][i] = null;
+          }
+        }
+        anot[1] = null;
+      }
+      else
+      {
+        for (int i = 0; i < anot[0].length; i++)
+        {
+          anot[1][i].description = anot[1][i].description + " (after)";
+        }
+      }
+      return new Object[]
+          {
+          AeContent, rangeMap, anot[0], anot[1]};
+    }
+    else
+    {
+      // no annotations to parse. Just return an empty annotationElement[] array.
+      return new Object[]
+          {
+          AeContent, rangeMap, anot[0], anot[1]};
+    }
+    // return null;
+  }
+
+  /**
+   * @param jal the jalview alignment to which the annotation will be attached (ideally - freshly updated from corresponding vamsas alignment)
+   * @param annotation
+   * @return unbound jalview alignment annotation object.
+   */
+  private jalview.datamodel.AlignmentAnnotation getjAlignmentAnnotation(jalview.
+      datamodel.AlignmentI jal,
+      org.vamsas.objects.core.RangeAnnotation annotation)
+  {
+    jalview.datamodel.AlignmentAnnotation jan = null;
+    if (annotation == null)
+    {
+      return null;
+    }
+    // boolean hasSequenceRef=annotation.getClass().equals(org.vamsas.objects.core.AlignmentSequenceAnnotation.class);
+    //boolean hasProvenance=hasSequenceRef || (annotation.getClass().equals(org.vamsas.objects.core.AlignmentAnnotation.class));
+    /*int se[] = getBounds(annotation);
+         if (se==null)
+      se=new int[] {0,jal.getWidth()-1};
+     */
+    Object[] parsedRangeAnnotation = parseRangeAnnotation(annotation);
+    String a_label = annotation.getLabel();
+    String a_descr = annotation.getDescription();
+    if (a_label == null || a_label.length() == 0)
+    {
+      a_label = annotation.getType();
+      if (a_label.length() == 0)
+      {
+        a_label = "Unamed annotation";
+      }
+    }
+    if (a_descr == null || a_descr.length() == 0)
+    {
+      a_descr = "Annotation of type '" + annotation.getType() + "'";
+    }
+    if (parsedRangeAnnotation == null)
+    {
+      Cache.log.debug(
+          "Inserting empty annotation row elements for a whole-alignment annotation.");
+
+    }
+    else
+    {
+      if (parsedRangeAnnotation[3] != null)
+      {
+        Cache.log.warn("Ignoring 'After' annotation row in " +
+                       annotation.getVorbaId());
+      }
+      jalview.datamodel.Annotation[] arow = (jalview.datamodel.Annotation[])
+          parsedRangeAnnotation[2];
+      boolean[] has = (boolean[]) parsedRangeAnnotation[0];
+      // VAMSAS: getGraph is only on derived annotation for alignments - in this way its 'odd' - there is already an existing TODO about removing this flag as being redundant
+      /*if ((annotation.getClass().equals(org.vamsas.objects.core.AlignmentAnnotation.class) && ((org.vamsas.objects.core.AlignmentAnnotation)annotation).getGraph())
+          || (hasSequenceRef=true && ((org.vamsas.objects.core.AlignmentSequenceAnnotation)annotation).getGraph())) {
+       */
+      if (has[HASVALS])
+      {
+        // make bounds and automatic description strings for jalview user's benefit (these shouldn't be written back to vamsas document)
+        boolean first = true;
+        float min = 0, max = 1;
+        int lastval = 0;
+        for (int i = 0; i < arow.length; i++)
+        {
+          if (arow[i] != null)
+          {
+            if (i - lastval > 1)
+            {
+              // do some interpolation *between* points
+              if (arow[lastval] != null)
+              {
+                float interval = arow[i].value - arow[lastval].value;
+                interval /= i - lastval;
+                float base = arow[lastval].value;
+                for (int ip = lastval + 1, np = 0; ip < i; np++, ip++)
+                {
+                  arow[ip] = new jalview.datamodel.Annotation("", "", ' ',
+                      interval * np + base);
+                  // NB - Interpolated points don't get a tooltip and description.
+                }
+              }
+            }
+            lastval = i;
+            // check range - shouldn't we have a min and max property in the annotation object ?
+            if (first)
+            {
+              min = max = arow[i].value;
+              first = false;
+            }
+            else
+            {
+              if (arow[i].value < min)
+              {
+                min = arow[i].value;
+              }
+              else if (arow[i].value > max)
+              {
+                max = arow[i].value;
+              }
+            }
+            // make tooltip and display char value
+            if (!has[HASDESCSTR])
+            {
+              arow[i].description = arow[i].value + "";
+            }
+            if (!has[HASDC])
+            {
+              arow[i].displayCharacter = arow[i].value + "";
+            }
+          }
+        }
+        int type = jalview.datamodel.AlignmentAnnotation.LINE_GRAPH;
+        if (has[HASHPHOB])
+        {
+          type = jalview.datamodel.AlignmentAnnotation.BAR_GRAPH;
+        }
+        jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr, arow,
+            min, max, type);
+      }
+      else
+      {
+        jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr, arow);
+        jan.setThreshold(null);
+      }
+      if (annotation.getLinkCount() > 0)
+      {
+        Cache.log.warn("Ignoring " + annotation.getLinkCount() +
+                       "links added to AlignmentAnnotation.");
+      }
+      if (annotation.getModifiable())
+      {
+        jan.editable = true;
+      }
+
+      if (annotation.getPropertyCount() > 0)
+      {
+        // look for special jalview properties
+        org.vamsas.objects.core.Property[] props = annotation.getProperty();
+        for (int p = 0; p < props.length; p++)
+        {
+          if (props[p].getName().equalsIgnoreCase("jalview:graphType"))
+          {
+            try
+            {
+              // probably a jalview annotation graph so recover the visualization hints.
+              jan.graph = jalview.datamodel.AlignmentAnnotation.
+                  getGraphValueFromString(props[p].getContent());
+            }
+            catch (Exception e)
+            {
+              Cache.log.debug(
+                  "Invalid graph type value in jalview:graphType property.");
+            }
+            try
+            {
+              if (annotation.getGroup() != null &&
+                  annotation.getGroup().length() > 0)
+              {
+                jan.graphGroup = Integer.parseInt(annotation.getGroup());
+              }
+            }
+            catch (Exception e)
+            {
+              Cache.log.info("UNIMPLEMENTED : Couldn't parse non-integer group value for setting graphGroup correctly.");
+            }
+          }
+        }
+      }
+
+      return jan;
+
+    }
+
+    return null;
+  }
+
+  private SequenceFeature getJalviewSeqFeature(RangeAnnotation dseta)
+  {
+    int[] se = getBounds(dseta);
+    SequenceFeature sf = new jalview.datamodel.SequenceFeature(dseta.getType(),
+        dseta.getDescription(), dseta.getStatus(), se[0], se[1], dseta
+        .getGroup());
+    if (dseta.getLinkCount() > 0)
+    {
+      Link[] links = dseta.getLink();
+      for (int i = 0; i < links.length; i++)
+      {
+        sf.addLink(links[i].getContent() + "|" + links[i].getHref());
+      }
+    }
+    return sf;
+  }
+
+  /**
+   * get real bounds of a RangeType's specification. start and end are an
+   * inclusive range within which all segments and positions lie.
+   * TODO: refactor to vamsas utils
+   * @param dseta
+   * @return int[] { start, end}
+   */
+  private int[] getBounds(RangeType dseta)
+  {
+    if (dseta != null)
+    {
+      int[] se = null;
+      if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0)
+      {
+        throw new Error("Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!");
+      }
+      if (dseta.getSegCount() > 0)
+      {
+        se = getSegRange(dseta.getSeg(0), true);
+        for (int s = 1, sSize = dseta.getSegCount(); s < sSize; s++)
+        {
+          int nse[] = getSegRange(dseta.getSeg(s), true);
+          if (se[0] > nse[0])
+          {
+            se[0] = nse[0];
+          }
+          if (se[1] < nse[1])
+          {
+            se[1] = nse[1];
+          }
+        }
+      }
+      if (dseta.getPosCount() > 0)
+      {
+        // could do a polarity for pos range too. and pass back indication of discontinuities.
+        int pos = dseta.getPos(0).getI();
+        se = new int[]
+            {
+            pos, pos};
+        for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
+        {
+          pos = dseta.getPos(p).getI();
+          if (se[0] > pos)
+          {
+            se[0] = pos;
+          }
+          if (se[1] < pos)
+          {
+            se[1] = pos;
+          }
+        }
+      }
+      return se;
+    }
+    return null;
+  }
+
+  /**
+   * map from a rangeType's internal frame to the referenced object's coordinate frame.
+   * @param dseta
+   * @return int [] { ref(pos)...} for all pos in rangeType's frame.
+   */
+  private int[] getMapping(RangeType dseta)
+  {
+    Vector posList = new Vector();
+    if (dseta != null)
+    {
+      int[] se = null;
+      if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0)
+      {
+        throw new Error("Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!");
+      }
+      if (dseta.getSegCount() > 0)
+      {
+        for (int s = 0, sSize = dseta.getSegCount(); s < sSize; s++)
+        {
+          se = getSegRange(dseta.getSeg(s), false);
+          int se_end = se[1 - se[2]] + (se[2] == 0 ? 1 : -1);
+          for (int p = se[se[2]]; p != se_end; p += se[2] == 0 ? 1 : -1)
+          {
+            posList.add(new Integer(p));
+          }
+        }
+      }
+      else if (dseta.getPosCount() > 0)
+      {
+        int pos = dseta.getPos(0).getI();
+
+        for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
+        {
+          pos = dseta.getPos(p).getI();
+          posList.add(new Integer(pos));
+        }
+      }
+    }
+    if (posList != null && posList.size() > 0)
+    {
+      int[] range = new int[posList.size()];
+      for (int i = 0; i < range.length; i++)
+      {
+        range[i] = ( (Integer) posList.elementAt(i)).intValue();
+      }
+      posList.clear();
+      return range;
+    }
+    return null;
+  }
+
+  /* not needed now.
+   * Provenance getVamsasProvenance(jalview.datamodel.Provenance jprov) {
+    jalview.datamodel.ProvenanceEntry[] entries = null;
+    // TODO: fix App and Action here.
+    Provenance prov = new Provenance();
+    org.exolab.castor.types.Date date = new org.exolab.castor.types.Date(
+        new java.util.Date());
+    Entry provEntry;
+
+    if (jprov != null)
+    {
+      entries = jprov.getEntries();
+      for (int i = 0; i < entries.length; i++)
+      {
+        provEntry = new Entry();
+        try
+        {
+          date = new org.exolab.castor.types.Date(entries[i].getDate());
+        } catch (Exception ex)
+        {
+          ex.printStackTrace();
+
+          date = new org.exolab.castor.types.Date(entries[i].getDate());
+        }
+        provEntry.setDate(date);
+        provEntry.setUser(entries[i].getUser());
+        provEntry.setAction(entries[i].getAction());
+        prov.addEntry(provEntry);
+      }
+    }
+    else
+    {
+      provEntry = new Entry();
+      provEntry.setDate(date);
+      provEntry.setUser(System.getProperty("user.name")); // TODO: ext string
+      provEntry.setApp("JVAPP"); // TODO: ext string
+      provEntry.setAction(action);
+      prov.addEntry(provEntry);
+    }
+
+    return prov;
+     }
+   */
+  jalview.datamodel.Provenance getJalviewProvenance(Provenance prov)
+  {
+    // TODO: fix App and Action entries and check use of provenance in jalview.
+    jalview.datamodel.Provenance jprov = new jalview.datamodel.Provenance();
+    for (int i = 0; i < prov.getEntryCount(); i++)
+    {
+      jprov.addEntry(prov.getEntry(i).getUser(), prov.getEntry(i).getAction(),
+                     prov.getEntry(i).getDate().toDate(),
+                     prov.getEntry(i).getId());
+    }
+
+    return jprov;
+  }
+
+  /**
+   *
+   * @return default initial provenance list for a Jalview created vamsas
+   *         object.
+   */
+  Provenance dummyProvenance()
+  {
+    return dummyProvenance(null);
+  }
+
+  Entry dummyPEntry(String action)
+  {
+    Entry entry = new Entry();
+    entry.setApp(this.provEntry.getApp());
+    if (action != null)
+    {
+      entry.setAction(action);
+    }
+    else
+    {
+      entry.setAction("created.");
+    }
+    entry.setDate(new org.exolab.castor.types.Date(new java.util.Date()));
+    entry.setUser(this.provEntry.getUser());
+    return entry;
+  }
+
+  Provenance dummyProvenance(String action)
+  {
+    Provenance prov = new Provenance();
+    prov.addEntry(dummyPEntry(action));
+    return prov;
+  }
+
+  void addProvenance(Provenance p, String action)
+  {
+    p.addEntry(dummyPEntry(action));
+  }
+
+}
diff --git a/src/jalview/schemes/ScoreMatrix.java b/src/jalview/schemes/ScoreMatrix.java
new file mode 100644 (file)
index 0000000..69a0922
--- /dev/null
@@ -0,0 +1,67 @@
+package jalview.schemes;\r
+\r
+public class ScoreMatrix\r
+{\r
+  String name;\r
+  /**\r
+   * reference to integer score matrix\r
+   */\r
+  int[][] matrix;\r
+  /**\r
+   * 0 for Protein Score matrix. 1 for dna score matrix\r
+   */\r
+  int type;\r
+  ScoreMatrix(String name, int[][] matrix, int type)\r
+  {\r
+    this.matrix = matrix;\r
+    this.type = type;\r
+  }\r
+\r
+  public boolean isDNA()\r
+  {\r
+    return type == 1;\r
+  }\r
+\r
+  public boolean isProtein()\r
+  {\r
+    return type == 0;\r
+  }\r
+\r
+  public int[][] getMatrix()\r
+  {\r
+    return matrix;\r
+  }\r
+\r
+  /**\r
+   *\r
+   * @param A1\r
+   * @param A2\r
+   * @return score for substituting first char in A1 with first char in A2\r
+   */\r
+  public int getPairwiseScore(String A1, String A2)\r
+  {\r
+    return getPairwiseScore(A1.charAt(0), A2.charAt(0));\r
+  }\r
+\r
+  public int getPairwiseScore(char c, char d)\r
+  {\r
+    int pog = 0;\r
+\r
+    try\r
+    {\r
+      int a = (type == 0) ? ResidueProperties.aaIndex[c] :\r
+          ResidueProperties.nucleotideIndex[c];\r
+      int b = (type == 0) ? ResidueProperties.aaIndex[d] :\r
+          ResidueProperties.nucleotideIndex[d];\r
+\r
+      pog = matrix[a][b];\r
+    }\r
+    catch (Exception e)\r
+    {\r
+      //System.out.println("Unknown residue in " + A1 + " " + A2);\r
+    }\r
+\r
+    return pog;\r
+  }\r
+\r
+}\r
diff --git a/src/jalview/util/MapList.java b/src/jalview/util/MapList.java
new file mode 100644 (file)
index 0000000..215ece9
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+package jalview.util;
+
+import java.util.*;
+
+/**
+ * MapList
+ * Simple way of bijectively mapping a non-contiguous linear range to another non-contiguous linear range
+ * Use at your own risk!
+ * TODO: efficient implementation of private posMap method
+ * TODO: test/ensure that sense of from and to ratio start position is conserved (codon start position recovery)
+ * TODO: optimize to use int[][] arrays rather than vectors.
+ */
+public class MapList
+{
+  public Vector fromShifts;
+  public Vector toShifts;
+  int fromRatio; // number of steps in fromShifts to one toRatio unit
+  int toRatio; // number of steps in toShifts to one fromRatio
+  public MapList(int from[], int to[], int fromRatio, int toRatio)
+  {
+    fromShifts = new Vector();
+    for (int i = 0; i < from.length; i += 2)
+    {
+      fromShifts.add(new int[]
+                     {from[i], from[i + 1]});
+    }
+    toShifts = new Vector();
+    for (int i = 0; i < to.length; i += 2)
+    {
+      toShifts.add(new int[]
+                   {to[i], to[i + 1]});
+    }
+    this.fromRatio = fromRatio;
+    this.toRatio = toRatio;
+  }
+
+  /**
+   * get all mapped positions from 'from' to 'to'
+   * @return int[][] { int[] { fromStart, fromFinish, toStart, toFinish }, int [fromFinish-fromStart+2] { toStart..toFinish mappings}}
+   */
+  public int[][] makeFromMap()
+  {
+    return posMap(fromShifts, fromRatio, toShifts, toRatio);
+  }
+
+  /**
+   * get all mapped positions from 'to' to 'from'
+   * @return int[to position]=position mapped in from
+   */
+  public int[][] makeToMap()
+  {
+    return posMap(toShifts, toRatio, fromShifts, fromRatio);
+  }
+
+  /**
+   * construct an int map for intervals in intVals
+   * @param intVals
+   * @return int[] { from, to pos in range }, int[range.to-range.from+1] returning mapped position
+   */
+  private int[][] posMap(Vector intVals, int ratio, Vector toIntVals,
+                         int toRatio)
+  {
+    Iterator iv = intVals.iterator();
+    if (!iv.hasNext())
+    {
+      return null;
+    }
+    int[] intv = (int[]) iv.next();
+    int from = intv[0], to = intv[1];
+    if (from > to)
+    {
+      from = intv[1];
+      to = intv[0];
+    }
+    while (iv.hasNext())
+    {
+      intv = (int[]) iv.next();
+      if (intv[0] < from)
+      {
+        from = intv[0];
+      }
+      if (intv[1] < from)
+      {
+        from = intv[1];
+      }
+      if (intv[0] > to)
+      {
+        to = intv[0];
+      }
+      if (intv[1] > to)
+      {
+        to = intv[1];
+      }
+    }
+    int tF = 0, tT = 0;
+    int mp[][] = new int[to - from + 2][];
+    for (int i = 0; i < mp.length; i++)
+    {
+      int[] m = shift(i + from, intVals, ratio, toIntVals, toRatio);
+      if (m != null)
+      {
+        if (i == 0)
+        {
+          tF = tT = m[0];
+        }
+        else
+        {
+          if (m[0] < tF)
+          {
+            tF = m[0];
+          }
+          if (m[0] > tT)
+          {
+            tT = m[0];
+          }
+        }
+      }
+      mp[i] = m;
+    }
+    int[][] map = new int[][]
+        {
+        new int[]
+        {
+        from, to, tF, tT}, new int[to - from + 2]};
+
+    map[0][2] = tF;
+    map[0][3] = tT;
+
+    for (int i = 0; i < mp.length; i++)
+    {
+      if (mp[i] != null)
+      {
+        map[1][i] = mp[i][0] - tF;
+      }
+      else
+      {
+        map[1][i] = -1; // indicates an out of range mapping
+      }
+    }
+    return map;
+  }
+
+  /**
+   * addShift
+   * @param pos start position for shift (in original reference frame)
+   * @param shift length of shift
+   *
+     public void addShift(int pos, int shift)
+     {
+    int sidx = 0;
+    int[] rshift=null;
+   while (sidx<shifts.size() && (rshift=(int[]) shifts.elementAt(sidx))[0]<pos)
+      sidx++;
+    if (sidx==shifts.size())
+      shifts.insertElementAt(new int[] { pos, shift}, sidx);
+    else
+      rshift[1]+=shift;
+     }
+   */
+  /**
+   * shift from pos to To(pos)
+   *
+   * @param pos int
+   * @return int shifted position in To, frameshift in From
+   */
+  public int[] shiftFrom(int pos)
+  {
+    return shift(pos, fromShifts, fromRatio, toShifts, toRatio);
+  }
+
+  /**
+   * inverse of shiftFrom - maps pos in To to a position in From
+   * @param pos (in To)
+   * @return shifted position in From, frameshift in To
+   */
+  public int[] shiftTo(int pos)
+  {
+    return shift(pos, toShifts, toRatio, fromShifts, fromRatio);
+  }
+
+  /**
+   *
+   * @param fromShifts
+   * @param fromRatio
+   * @param toShifts
+   * @param toRatio
+   * @return
+   */
+  private int[] shift(int pos, Vector fromShifts, int fromRatio,
+                      Vector toShifts, int toRatio)
+  {
+    int[] fromCount = countPos(fromShifts.iterator(), pos);
+    if (fromCount == null)
+    {
+      return null;
+    }
+    int fromRemainder = (fromCount[0] - 1) % fromRatio;
+    int toCount = 1 + ( ( (fromCount[0] - 1) / fromRatio) * toRatio);
+    int[] toPos = countToPos(toShifts.iterator(), toCount);
+    if (toPos == null)
+    {
+      return null; // throw new Error("Bad Mapping!");
+    }
+    //System.out.println(fromCount[0]+" "+fromCount[1]+" "+toCount);
+    return new int[]
+        {
+        toPos[0], fromRemainder};
+  }
+
+  /**
+   * count how many positions pos is along the series of intervals.
+   * @param intVals
+   * @param pos
+   * @return number of positions or null if pos is not within intervals
+   */
+  private int[] countPos(Iterator intVals, int pos)
+  {
+    int count = 0, intv[];
+    while (intVals.hasNext())
+    {
+      intv = (int[]) intVals.next();
+      if (intv[0] <= intv[1])
+      {
+        if (pos >= intv[0] && pos <= intv[1])
+        {
+          return new int[]
+              {
+              count + pos - intv[0] + 1, +1};
+        }
+        else
+        {
+          count += intv[1] - intv[0] + 1;
+        }
+      }
+      else
+      {
+        if (pos >= intv[1] && pos <= intv[0])
+        {
+          return new int[]
+              {
+              count + intv[0] - pos + 1, -1};
+        }
+        else
+        {
+          count += intv[0] - intv[1] + 1;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * count out pos positions into a series of intervals and return the position
+   * @param intVals
+   * @param pos
+   * @return position pos in interval set
+   */
+  private int[] countToPos(Iterator intVals, int pos)
+  {
+    int count = 0, diff = 0, intv[] =
+        {
+        0, 0};
+    while (intVals.hasNext())
+    {
+      intv = (int[]) intVals.next();
+      diff = intv[1] - intv[0];
+      if (diff >= 0)
+      {
+        if (pos <= count + 1 + diff)
+        {
+          return new int[]
+              {
+              pos - count - 1 + intv[0], +1};
+        }
+        else
+        {
+          count += 1 + diff;
+        }
+      }
+      else
+      {
+        if (pos <= count + 1 - diff)
+        {
+          return new int[]
+              {
+              intv[0] - (pos - count - 1), -1};
+        }
+        else
+        {
+          count += 1 - diff;
+        }
+      }
+    }
+    return null; //(diff<0) ? (intv[1]-1) : (intv[0]+1);
+  }
+
+  public static void testMap(MapList ml, int fromS, int fromE)
+  {
+    for (int from = 1; from <= 25; from++)
+    {
+      int[] too = ml.shiftFrom(from);
+      System.out.print("ShiftFrom(" + from + ")==");
+      if (too == null)
+      {
+        System.out.print("NaN\n");
+      }
+      else
+      {
+        System.out.print(too[0] + " % " + too[1]);
+        System.out.print("\t+--+\t");
+        int[] toofrom = ml.shiftTo(too[0]);
+        if (toofrom != null)
+        {
+          if (toofrom[0] != from)
+          {
+            System.err.println("Mapping not reflexive:" + from + " " + too[0] +
+                               "->" + toofrom[0]);
+          }
+          System.out.println("ShiftTo(" + too[0] + ")==" + toofrom[0] + " % " +
+                             toofrom[1]);
+        }
+        else
+        {
+          System.out.println("ShiftTo(" + too[0] + ")==" +
+                             "NaN! - not Bijective Mapping!");
+        }
+      }
+    }
+    int mmap[][] = ml.makeFromMap();
+    System.out.println("FromMap : (" + mmap[0][0] + " " + mmap[0][1] + " " +
+                       mmap[0][2] + " " + mmap[0][3] + " ");
+    for (int i = 1; i <= mmap[1].length; i++)
+    {
+      if (mmap[1][i - 1] == -1)
+      {
+        System.out.print(i + "=XXX");
+
+      }
+      else
+      {
+        System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1]));
+      }
+      if (i % 20 == 0)
+      {
+        System.out.print("\n");
+      }
+      else
+      {
+        System.out.print(",");
+      }
+    }
+    System.out.print("\n");
+  }
+
+  public static void main(String argv[])
+  {
+    MapList ml = new MapList(new int[]
+                             {1, 5, 10, 15, 25, 20},
+                             new int[]
+                             {51, 1}, 1, 3);
+    MapList ml1 = new MapList(new int[]
+                              {1, 3, 17, 4},
+                              new int[]
+                              {51, 1}, 1, 3);
+
+    // test internal consistency
+    int to[] = new int[51];
+    MapList.testMap(ml, 1, 25);
+    /*
+           for (int from=1; from<=51; from++) {
+        int[] too=ml.shiftTo(from);
+        int[] toofrom=ml.shiftFrom(too[0]);
+        System.out.println("ShiftFrom("+from+")=="+too[0]+" % "+too[1]+"\t+-+\tShiftTo("+too[0]+")=="+toofrom[0]+" % "+toofrom[1]);
+           }*/
+    System.out.print("Success?\n"); // if we get here - something must be working!
+  }
+}
diff --git a/src/jalview/ws/JPredThread.java b/src/jalview/ws/JPredThread.java
new file mode 100644 (file)
index 0000000..0cac709
--- /dev/null
@@ -0,0 +1,634 @@
+package jalview.ws;\r
+\r
+import java.util.*;\r
+\r
+import jalview.analysis.*;\r
+import jalview.bin.*;\r
+import jalview.datamodel.*;\r
+import jalview.datamodel.Alignment;\r
+import jalview.gui.*;\r
+import jalview.io.*;\r
+import jalview.util.*;\r
+import vamsas.objects.simple.JpredResult;\r
+\r
+class JPredThread\r
+    extends WSThread implements WSClientI\r
+{\r
+  // TODO: put mapping between JPredJob input and input data here - JNetAnnotation adding is done after result parsing.\r
+  class JPredJob\r
+      extends WSThread.WSJob\r
+  {\r
+    // TODO: make JPredJob deal only with what was sent to and received from a JNet service\r
+    int[] predMap = null; // mapping from sequence(i) to the original sequence(predMap[i]) being predicted on\r
+    vamsas.objects.simple.Sequence sequence;\r
+    vamsas.objects.simple.Msfalignment msa;\r
+    java.util.Hashtable SequenceInfo = null;\r
+    int msaIndex = 0; // the position of the original sequence in the array of Sequences in the input object that this job holds a prediction for\r
+    /**\r
+     *\r
+     * @return true if getResultSet will return a valid alignment and prediction result.\r
+     */\r
+    public boolean hasResults()\r
+    {\r
+      if (subjobComplete && result != null && result.isFinished()\r
+          && ( (JpredResult) result).getPredfile() != null &&\r
+          ( (JpredResult) result).getAligfile() != null)\r
+      {\r
+        return true;\r
+      }\r
+      return false;\r
+    }\r
+\r
+    boolean hasValidInput()\r
+    {\r
+      if (sequence != null)\r
+      {\r
+        return true;\r
+      }\r
+      return false;\r
+    }\r
+\r
+    /**\r
+     *\r
+     * @return null or Object[] { annotated alignment for this prediction, ColumnSelection for this prediction} or null if no results available.\r
+     * @throws Exception\r
+     */\r
+    public Object[] getResultSet()\r
+        throws Exception\r
+    {\r
+      if (result == null || !result.isFinished())\r
+      {\r
+        return null;\r
+      }\r
+      Alignment al = null;\r
+      ColumnSelection alcsel = null;\r
+      int FirstSeq = -1; // the position of the query sequence in Alignment al\r
+\r
+      JpredResult result = (JpredResult)this.result;\r
+\r
+      jalview.bin.Cache.log.debug("Parsing output from JNet job.");\r
+      // JPredFile prediction = new JPredFile("C:/JalviewX/files/jpred.txt", "File");\r
+      jalview.io.JPredFile prediction = new jalview.io.JPredFile(result.\r
+          getPredfile(),\r
+          "Paste");\r
+      SequenceI[] preds = prediction.getSeqsAsArray();\r
+      jalview.bin.Cache.log.debug("Got prediction profile.");\r
+\r
+      if ( (this.msa != null) && (result.getAligfile() != null))\r
+      {\r
+        jalview.bin.Cache.log.debug("Getting associated alignment.");\r
+        // we ignore the returned alignment if we only predicted on a single sequence\r
+        String format = new jalview.io.IdentifyFile().Identify(result.\r
+            getAligfile(),\r
+            "Paste");\r
+\r
+        if (jalview.io.FormatAdapter.isValidFormat(format))\r
+        {\r
+          SequenceI sqs[];\r
+          if (predMap != null)\r
+          {\r
+            Object[] alandcolsel = input.getAlignmentAndColumnSelection(\r
+                alignFrame.getViewport().getGapCharacter());\r
+            sqs = (SequenceI[]) alandcolsel[0];\r
+            al = new Alignment(sqs);\r
+            alcsel = (ColumnSelection) alandcolsel[1];\r
+          }\r
+          else\r
+          {\r
+            al = new FormatAdapter().readFile(result.getAligfile(),\r
+                                              "Paste", format);\r
+            sqs = new SequenceI[al.getHeight()];\r
+\r
+            for (int i = 0, j = al.getHeight(); i < j; i++)\r
+            {\r
+              sqs[i] = al.getSequenceAt(i);\r
+            }\r
+            if (!jalview.analysis.SeqsetUtils.deuniquify( (Hashtable)\r
+                SequenceInfo, sqs))\r
+            {\r
+              throw (new Exception(\r
+                  "Couldn't recover sequence properties for alignment."));\r
+            }\r
+          }\r
+          FirstSeq = 0;\r
+          al.setDataset(null);\r
+\r
+          jalview.io.JnetAnnotationMaker.add_annotation(prediction, al,\r
+              FirstSeq,\r
+              false, predMap);\r
+\r
+        }\r
+        else\r
+        {\r
+          throw (new Exception(\r
+              "Unknown format " + format + " for file : \n" +\r
+              result.getAligfile()));\r
+        }\r
+      }\r
+      else\r
+      {\r
+        al = new Alignment(preds);\r
+        FirstSeq = prediction.getQuerySeqPosition();\r
+        if (predMap != null)\r
+        {\r
+          char gc = alignFrame.getViewport().getGapCharacter();\r
+          SequenceI[] sqs = (SequenceI[]) ( (java.lang.Object[]) input.\r
+                                           getAlignmentAndColumnSelection(gc))[\r
+              0];\r
+          if (this.msaIndex >= sqs.length)\r
+          {\r
+            throw new Error("Implementation Error! Invalid msaIndex for JPredJob on parent MSA input object!");\r
+          }\r
+\r
+          /////\r
+          //Uses RemoveGapsCommand\r
+          /////\r
+          new jalview.commands.RemoveGapsCommand("Remove Gaps",\r
+                                                 new SequenceI[]\r
+                                                 {sqs[msaIndex]},\r
+                                                 alignFrame.getCurrentView().\r
+                                                 getAlignment());\r
+\r
+          SequenceI profileseq = al.getSequenceAt(FirstSeq);\r
+          profileseq.setSequence(sqs[msaIndex].getSequenceAsString());\r
+        }\r
+\r
+        if (!jalview.analysis.SeqsetUtils.SeqCharacterUnhash(\r
+            al.getSequenceAt(FirstSeq), SequenceInfo))\r
+        {\r
+          throw (new Exception(\r
+              "Couldn't recover sequence properties for JNet Query sequence!"));\r
+        }\r
+        else\r
+        {\r
+          al.setDataset(null);\r
+          jalview.io.JnetAnnotationMaker.add_annotation(prediction, al,\r
+              FirstSeq,\r
+              true, predMap);\r
+          SequenceI profileseq = al.getSequenceAt(0); // this includes any gaps.\r
+          alignToProfileSeq(al, profileseq);\r
+          if (predMap != null)\r
+          {\r
+            // Adjust input view for gaps\r
+            // propagate insertions into profile\r
+            alcsel = propagateInsertions(profileseq, al, input);\r
+          }\r
+        }\r
+      }\r
+      return new Object[]\r
+          {\r
+          al, alcsel}; // , FirstSeq, noMsa};\r
+    }\r
+\r
+    /**\r
+     * Given an alignment where all other sequences except profileseq are aligned to the ungapped profileseq, insert gaps in the other sequences to realign them with the residues in profileseq\r
+     * @param al\r
+     * @param profileseq\r
+     */\r
+    private void alignToProfileSeq(Alignment al, SequenceI profileseq)\r
+    {\r
+      char gc = al.getGapCharacter();\r
+      int[] gapMap = profileseq.gapMap();\r
+      // insert gaps into profile\r
+      for (int lp = 0, r = 0; r < gapMap.length; r++)\r
+      {\r
+        if (gapMap[r] - lp > 1)\r
+        {\r
+          StringBuffer sb = new StringBuffer();\r
+          for (int s = 0, ns = gapMap[r] - lp; s < ns; s++)\r
+          {\r
+            sb.append(gc);\r
+          }\r
+          for (int s = 1, ns = al.getHeight(); s < ns; s++)\r
+          {\r
+            String sq = al.getSequenceAt(s).getSequenceAsString();\r
+            int diff = gapMap[r] - sq.length();\r
+            if (diff > 0)\r
+            {\r
+              // pad gaps\r
+              sq = sq + sb;\r
+              while ( (diff = gapMap[r] - sq.length()) > 0)\r
+              {\r
+                sq = sq +\r
+                    ( (diff >= sb.length()) ? sb.toString() :\r
+                     sb.substring(0, diff));\r
+              }\r
+              al.getSequenceAt(s).setSequence(sq);\r
+            }\r
+            else\r
+            {\r
+              al.getSequenceAt(s).setSequence(sq.substring(0, gapMap[r]) +\r
+                                              sb.toString() +\r
+                                              sq.substring(gapMap[r]));\r
+            }\r
+          }\r
+        }\r
+        lp = gapMap[r];\r
+      }\r
+    }\r
+\r
+    /**\r
+     * Add gaps into the sequences aligned to profileseq under the given AlignmentView\r
+     * @param profileseq\r
+     * @param al\r
+     * @param input\r
+     */\r
+    private ColumnSelection propagateInsertions(SequenceI profileseq,\r
+                                                Alignment al,\r
+                                                AlignmentView input)\r
+    {\r
+      char gc = al.getGapCharacter();\r
+      Object[] alandcolsel = input.getAlignmentAndColumnSelection(gc);\r
+      ColumnSelection nview = (ColumnSelection) alandcolsel[1];\r
+      SequenceI origseq;\r
+      nview.pruneDeletions(ShiftList.parseMap( (origseq = ( (SequenceI[])\r
+          alandcolsel[0])[0]).gapMap())); // recover original prediction sequence's mapping to view.\r
+      int[] viscontigs = nview.getVisibleContigs(0, profileseq.getLength());\r
+      int spos = 0;\r
+      int offset = 0;\r
+      //  input.pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap()))\r
+      // add profile to visible contigs\r
+      for (int v = 0; v < viscontigs.length; v += 2)\r
+      {\r
+        if (viscontigs[v] > spos)\r
+        {\r
+          StringBuffer sb = new StringBuffer();\r
+          for (int s = 0, ns = viscontigs[v] - spos; s < ns; s++)\r
+          {\r
+            sb.append(gc);\r
+          }\r
+          for (int s = 0, ns = al.getHeight(); s < ns; s++)\r
+          {\r
+            SequenceI sqobj = al.getSequenceAt(s);\r
+            if (sqobj != profileseq)\r
+            {\r
+              String sq = al.getSequenceAt(s).getSequenceAsString();\r
+              if (sq.length() <= spos + offset)\r
+              {\r
+                // pad sequence\r
+                int diff = spos + offset - sq.length() - 1;\r
+                if (diff > 0)\r
+                {\r
+                  // pad gaps\r
+                  sq = sq + sb;\r
+                  while ( (diff = spos + offset - sq.length() - 1) > 0)\r
+                  {\r
+                    sq = sq +\r
+                        ( (diff >= sb.length()) ? sb.toString() :\r
+                         sb.substring(0, diff));\r
+                  }\r
+                }\r
+                sq += sb.toString();\r
+              }\r
+              else\r
+              {\r
+                al.getSequenceAt(s).setSequence(sq.substring(0, spos + offset) +\r
+                                                sb.toString() +\r
+                                                sq.substring(spos + offset));\r
+              }\r
+            }\r
+          }\r
+          //offset+=sb.length();\r
+        }\r
+        spos = viscontigs[v + 1] + 1;\r
+      }\r
+      if ( (offset + spos) < profileseq.getLength())\r
+      {\r
+        StringBuffer sb = new StringBuffer();\r
+        for (int s = 0, ns = profileseq.getLength() - spos - offset; s < ns; s++)\r
+        {\r
+          sb.append(gc);\r
+        }\r
+        for (int s = 1, ns = al.getHeight(); s < ns; s++)\r
+        {\r
+          String sq = al.getSequenceAt(s).getSequenceAsString();\r
+          // pad sequence\r
+          int diff = origseq.getLength() - sq.length();\r
+          while (diff > 0)\r
+          {\r
+            sq = sq +\r
+                ( (diff >= sb.length()) ? sb.toString() : sb.substring(0, diff));\r
+            diff = origseq.getLength() - sq.length();\r
+          }\r
+        }\r
+      }\r
+      return nview;\r
+    }\r
+\r
+    public JPredJob(Hashtable SequenceInfo, SequenceI seq, int[] delMap)\r
+    {\r
+      super();\r
+      this.predMap = delMap;\r
+      String sq = AlignSeq.extractGaps(Comparison.GapChars,\r
+                                       seq.getSequenceAsString());\r
+      if (sq.length() >= 20)\r
+      {\r
+        this.SequenceInfo = SequenceInfo;\r
+        sequence = new vamsas.objects.simple.Sequence();\r
+        sequence.setId(seq.getName());\r
+        sequence.setSeq(sq);\r
+      }\r
+    }\r
+\r
+    public JPredJob(Hashtable SequenceInfo, SequenceI[] msf, int[] delMap)\r
+    {\r
+      this(SequenceInfo, msf[0], delMap);\r
+      if (sequence != null)\r
+      {\r
+        if (msf.length > 1)\r
+        {\r
+          msa = new vamsas.objects.simple.Msfalignment();\r
+          jalview.io.PileUpfile pileup = new jalview.io.PileUpfile();\r
+          msa.setMsf(pileup.print(msf));\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  ext.vamsas.Jpred server;\r
+  String altitle = "";\r
+  JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server,\r
+              String wsurl, AlignmentView alview, AlignFrame alframe)\r
+  {\r
+    super();\r
+    this.altitle = altitle;\r
+    this.server = server;\r
+    this.wsInfo = wsinfo;\r
+    this.input = alview;\r
+    this.alignFrame = alframe;\r
+    WsUrl = wsurl;\r
+  }\r
+\r
+  JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server,\r
+              String wsurl, Hashtable SequenceInfo, SequenceI seq, int[] delMap,\r
+              AlignmentView alview, AlignFrame alframe)\r
+  {\r
+    this(wsinfo, altitle, server, wsurl, alview, alframe);\r
+    JPredJob job = new JPredJob(SequenceInfo, seq, delMap);\r
+    if (job.hasValidInput())\r
+    {\r
+      OutputHeader = wsInfo.getProgressText();\r
+      jobs = new WSJob[]\r
+          {\r
+          job};\r
+      job.jobnum = 0;\r
+    }\r
+  }\r
+\r
+  JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server,\r
+              Hashtable SequenceInfo, SequenceI[] msf, int[] delMap,\r
+              AlignmentView alview, AlignFrame alframe, String wsurl)\r
+  {\r
+    this(wsinfo, altitle, server, wsurl, alview, alframe);\r
+    JPredJob job = new JPredJob(SequenceInfo, msf, delMap);\r
+    if (job.hasValidInput())\r
+    {\r
+      jobs = new WSJob[]\r
+          {\r
+          job};\r
+      OutputHeader = wsInfo.getProgressText();\r
+      job.jobnum = 0;\r
+    }\r
+  }\r
+\r
+  void StartJob(WSJob j)\r
+  {\r
+    if (! (j instanceof JPredJob))\r
+    {\r
+      throw new Error("Implementation error - StartJob(JpredJob) called on " +\r
+                      j.getClass());\r
+    }\r
+    try\r
+    {\r
+      JPredJob job = (JPredJob) j;\r
+      if (job.msa != null)\r
+      {\r
+        job.jobId = server.predictOnMsa(job.msa);\r
+      }\r
+      else\r
+      if (job.sequence != null)\r
+      {\r
+        job.jobId = server.predict(job.sequence); // debug like : job.jobId = "/jobs/www-jpred/jp_Yatat29";//\r
+      }\r
+\r
+      if (job.jobId != null)\r
+      {\r
+        if (job.jobId.startsWith("Broken"))\r
+        {\r
+          job.result = (vamsas.objects.simple.Result)new JpredResult();\r
+          job.result.setInvalid(true);\r
+          job.result.setStatus("Submission " + job.jobId);\r
+        }\r
+        else\r
+        {\r
+          job.submitted = true;\r
+          job.subjobComplete = false;\r
+          Cache.log.info(WsUrl + " Job Id '" + job.jobId + "'");\r
+        }\r
+      }\r
+      else\r
+      {\r
+        throw new Exception("Server timed out - try again later\n");\r
+      }\r
+    }\r
+    catch (Exception e)\r
+    {\r
+      if (e.getMessage().indexOf("Exception") > -1)\r
+      {\r
+        wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
+        wsInfo.setProgressText(j.jobnum,\r
+                               "Failed to submit the prediction. (Just close the window)\n"\r
+                               +\r
+                               "It is most likely that there is a problem with the server.\n");\r
+        System.err.println(\r
+            "JPredWS Client: Failed to submit the prediction. Quite possibly because of a server error - see below)\n" +\r
+            e.getMessage() + "\n");\r
+\r
+        jalview.bin.Cache.log.warn("Server Exception", e);\r
+      }\r
+      else\r
+      {\r
+        wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
+        // JBPNote - this could be a popup informing the user of the problem.\r
+        wsInfo.appendProgressText(j.jobnum,\r
+                                  "Failed to submit the prediction:\n"\r
+                                  + e.getMessage() +\r
+                                  wsInfo.getProgressText());\r
+\r
+        jalview.bin.Cache.log.debug("Failed Submission of job " + j.jobnum, e);\r
+\r
+      }\r
+      j.allowedServerExceptions = -1;\r
+      j.subjobComplete = true;\r
+    }\r
+  }\r
+\r
+  void parseResult()\r
+  {\r
+    int results = 0; // number of result sets received\r
+    JobStateSummary finalState = new JobStateSummary();\r
+    try\r
+    {\r
+      for (int j = 0; j < jobs.length; j++)\r
+      {\r
+        finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);\r
+        if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())\r
+        {\r
+          results++;\r
+        }\r
+      }\r
+    }\r
+    catch (Exception ex)\r
+    {\r
+\r
+      Cache.log.error("Unexpected exception when processing results for " +\r
+                      altitle, ex);\r
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
+    }\r
+    if (results > 0)\r
+    {\r
+      wsInfo.showResultsNewFrame\r
+          .addActionListener(new java.awt.event.ActionListener()\r
+      {\r
+        public void actionPerformed(\r
+            java.awt.event.ActionEvent evt)\r
+        {\r
+          displayResults(true);\r
+        }\r
+      });\r
+      wsInfo.mergeResults\r
+          .addActionListener(new java.awt.event.ActionListener()\r
+      {\r
+        public void actionPerformed(\r
+            java.awt.event.ActionEvent evt)\r
+        {\r
+          displayResults(false);\r
+        }\r
+      });\r
+      wsInfo.setResultsReady();\r
+    }\r
+    else\r
+    {\r
+      wsInfo.setFinishedNoResults();\r
+    }\r
+  }\r
+\r
+  void displayResults(boolean newWindow)\r
+  {\r
+    // TODO: cope with multiple subjobs.\r
+    if (jobs != null)\r
+    {\r
+      Object[] res = null;\r
+      boolean msa = false;\r
+      for (int jn = 0; jn < jobs.length; jn++)\r
+      {\r
+        Object[] jobres = null;\r
+        JPredJob j = (JPredJob) jobs[jn];\r
+\r
+        if (j.hasResults())\r
+        {\r
+          // hack - we only deal with all single seuqence predictions or all profile predictions\r
+          msa = (j.msa != null) ? true : msa;\r
+          try\r
+          {\r
+            jalview.bin.Cache.log.debug("Parsing output of job " + jn);\r
+            jobres = j.getResultSet();\r
+            jalview.bin.Cache.log.debug("Finished parsing output.");\r
+            if (jobs.length == 1)\r
+            {\r
+              res = jobres;\r
+            }\r
+            else\r
+            {\r
+              // do merge with other job results\r
+              throw new Error(\r
+                  "Multiple JNet subjob merging not yet implemented.");\r
+            }\r
+          }\r
+          catch (Exception e)\r
+          {\r
+            jalview.bin.Cache.log.error(\r
+                "JNet Client: JPred Annotation Parse Error",\r
+                e);\r
+            wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
+            wsInfo.appendProgressText(j.jobnum,\r
+                                      OutputHeader + "\n" +\r
+                                      j.result.getStatus() +\r
+                                      "\nInvalid JNet job result data!\n" +\r
+                                      e.getMessage());\r
+            j.result.setBroken(true);\r
+          }\r
+        }\r
+      }\r
+\r
+      if (res != null)\r
+      {\r
+        if (newWindow)\r
+        {\r
+          AlignFrame af;\r
+          if (input == null)\r
+          {\r
+            if (res[1] != null)\r
+            {\r
+              af = new AlignFrame( (Alignment) res[0], (ColumnSelection) res[1],\r
+                                  AlignFrame.DEFAULT_WIDTH,\r
+                                  AlignFrame.DEFAULT_HEIGHT);\r
+            }\r
+            else\r
+            {\r
+              af = new AlignFrame( (Alignment) res[0], AlignFrame.DEFAULT_WIDTH,\r
+                                  AlignFrame.DEFAULT_HEIGHT);\r
+            }\r
+          }\r
+          else\r
+          {\r
+            /*java.lang.Object[] alandcolsel = input.getAlignmentAndColumnSelection(alignFrame.getViewport().getGapCharacter());\r
+             if (((SequenceI[])alandcolsel[0])[0].getLength()!=res.getWidth()) {\r
+              if (msa) {\r
+                throw new Error("Implementation Error! ColumnSelection from input alignment will not map to result alignment!");\r
+              }\r
+                         }\r
+                         if (!msa) {\r
+              // update hidden regions to account for loss of gaps in profile. - if any\r
+              // gapMap returns insert list, interpreted as delete list by pruneDeletions\r
+              //((ColumnSelection) alandcolsel[1]).pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap()));\r
+                         }*/\r
+\r
+            af = new AlignFrame( (Alignment) res[0], (ColumnSelection) res[1],\r
+                                AlignFrame.DEFAULT_WIDTH,\r
+                                AlignFrame.DEFAULT_HEIGHT);\r
+          }\r
+          Desktop.addInternalFrame(af, altitle,\r
+                                   AlignFrame.DEFAULT_WIDTH,\r
+                                   AlignFrame.DEFAULT_HEIGHT);\r
+        }\r
+        else\r
+        {\r
+          Cache.log.info("Append results onto existing alignment.");\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  void pollJob(WSJob job)\r
+      throws Exception\r
+  {\r
+    job.result = server.getresult(job.jobId);\r
+  }\r
+\r
+  public boolean isCancellable()\r
+  {\r
+    return false;\r
+  }\r
+\r
+  public void cancelJob()\r
+  {\r
+    throw new Error("Implementation error!");\r
+  }\r
+\r
+  public boolean canMergeResults()\r
+  {\r
+    return false;\r
+  }\r
+\r
+}\r