javascript callback for selection, mousover, and functions for selection and highligh...
authorjprocter <Jim Procter>
Wed, 12 Jan 2011 17:05:05 +0000 (17:05 +0000)
committerjprocter <Jim Procter>
Wed, 12 Jan 2011 17:05:05 +0000 (17:05 +0000)
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/AnnotationLabels.java
src/jalview/appletgui/IdPanel.java
src/jalview/appletgui/PaintRefresher.java
src/jalview/appletgui/RotatableCanvas.java
src/jalview/appletgui/ScalePanel.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/TreeCanvas.java
src/jalview/bin/JalviewLite.java

index 5288fe4..a56f76d 100755 (executable)
@@ -112,6 +112,11 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
   MenuItem revealAll = new MenuItem();
 
+  MenuItem revealSeq = new MenuItem();
+  /**
+   * index of sequence to be revealed
+   */
+  int revealSeq_index=-1;
   Menu menu1 = new Menu();
 
   public APopupMenu(AlignmentPanel apanel, final Sequence seq, Vector links)
@@ -298,6 +303,17 @@ public class APopupMenu extends java.awt.PopupMenu implements
     if (!ap.av.hasHiddenRows)
     {
       remove(revealAll);
+      remove(revealSeq);
+    } else {
+      final int index = ap.av.alignment.findIndex(seq);
+
+      if (ap.av.adjustForHiddenSeqs(index)
+              - ap.av.adjustForHiddenSeqs(index - 1) > 1)
+      {
+        revealSeq_index=index;
+      } else {
+        remove(revealSeq);
+      }
     }
   }
 
@@ -446,6 +462,10 @@ public class APopupMenu extends java.awt.PopupMenu implements
     {
       hideSequences(true);
     }
+    else if (source == revealSeq)
+    {
+      ap.av.showSequence(revealSeq_index);
+    }
     else if (source == revealAll)
     {
       ap.av.showAllHiddenSeqs();
@@ -712,10 +732,12 @@ public class APopupMenu extends java.awt.PopupMenu implements
     hideSeqs.setLabel("Hide Sequences");
     repGroup.setLabel("Represent Group with");
     revealAll.setLabel("Reveal All");
+    revealSeq.setLabel("Reveal Sequences");
     menu1.setLabel("Group");
     add(groupMenu);
     this.add(seqMenu);
     this.add(hideSeqs);
+    this.add(revealSeq);
     this.add(revealAll);
     groupMenu.add(editGroupName);
     groupMenu.add(editMenu);
@@ -796,6 +818,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
     hideSeqs.addActionListener(this);
     repGroup.addActionListener(this);
     revealAll.addActionListener(this);
+    revealSeq.addActionListener(this);
   }
 
   void refresh()
@@ -1034,6 +1057,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
     }
 
     ap.av.hideSequence(hseqs);
+    ap.av.sendSelection();
   }
 
 }
index ffe445b..2fde9ad 100755 (executable)
@@ -21,12 +21,9 @@ import java.io.*;
 import java.net.*;
 import java.util.*;
 
-import java.applet.Applet;
 import java.awt.*;
 import java.awt.event.*;
 
-import org.jmol.api.JmolViewer;
-
 import jalview.analysis.*;
 import jalview.api.SequenceStructureBinding;
 import jalview.bin.JalviewLite;
@@ -796,7 +793,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
     else if (source == alProperties)
     {
-      StringBuffer contents = new jalview.io.AlignmentProperties(viewport.alignment).formatAsString();
+      StringBuffer contents = new jalview.io.AlignmentProperties(
+              viewport.alignment).formatAsString();
       CutAndPasteTransfer cap = new CutAndPasteTransfer(false, this);
       cap.setText(contents.toString());
       Frame frame = new Frame();
@@ -1640,6 +1638,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     {
       this.setVisible(false);
     }
+    viewport.sendSelection();
   }
 
   protected void makeGrpsFromSelection_actionPerformed()
@@ -1694,6 +1693,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.setSelectionGroup(sg);
     alignPanel.paintAlignment(true);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+    viewport.sendSelection();
   }
 
   public void deselectAllSequenceMenuItem_actionPerformed()
@@ -1710,6 +1710,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
     alignPanel.paintAlignment(true);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+    viewport.sendSelection();
   }
 
   public void invertSequenceMenuItem_actionPerformed()
@@ -1721,6 +1722,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
 
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+    viewport.sendSelection();
   }
 
   public void invertColSel_actionPerformed()
@@ -1728,6 +1730,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.invertColumnSelection();
     alignPanel.paintAlignment(true);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+    viewport.sendSelection();
   }
 
   void trimAlignment(boolean trimLeft)
@@ -3164,36 +3167,73 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
    * create a new binding between structures in an existing jmol viewer instance
    * and an alignpanel with sequences that have existing PDBFile entries. Note,
    * this does not open a new Jmol window, or modify the display of the
-   * structures in the original jmol window. Note
+   * structures in the original jmol window. Note This method doesn't work
+   * without an additional javascript library to exchange messages between the
+   * distinct applets. See http://issues.jalview.org/browse/JAL-621
    * 
    * @param viewer
    *          JmolViewer instance
    * @param sequenceIds
-   *          - sequence Ids to search for associations This method doesn't
-   *          work. See http://issues.jalview.org/browse/JAL-621
-   * 
-   *          public SequenceStructureBinding addStructureViewInstance(Object
-   *          jmolviewer, String[] sequenceIds) { org.jmol.api.JmolViewer
-   *          viewer=null; try { viewer = (org.jmol.api.JmolViewer) jmolviewer;
-   *          } catch (ClassCastException ex) {
-   *          System.err.println("Unsupported viewer object :"
-   *          +jmolviewer.getClass()); } if (viewer==null) {
-   *          System.err.println("Can't use this object as a structure viewer:"
-   *          +jmolviewer.getClass()); return null; } SequenceI[] seqs=null; if
-   *          (sequenceIds==null || sequenceIds.length==0) { seqs =
-   *          viewport.getAlignment().getSequencesArray(); } else { Vector
-   *          sqi=new Vector(); AlignmentI al = viewport.getAlignment(); for
-   *          (int sid=0;sid<sequenceIds.length;sid++) { SequenceI sq =
-   *          al.findName(sequenceIds[sid]); if (sq!=null) { sqi.addElement(sq);
-   *          } } if (sqi.size()>0) { seqs = new SequenceI[sqi.size()]; for (int
-   *          sid=0,sSize=sqi.size();sid<sSize;sid++) { seqs[sid] = (SequenceI)
-   *          sqi.elementAt(sid); } } else { return null; } } ExtJmol jmv=null;
-   *          // TODO: search for a jmv that involves viewer if (jmv==null){ //
-   *          create a new viewer/jalview binding. jmv = new ExtJmol(viewer,
-   *          alignPanel, seqs); } return jmv;
-   * 
-   *          }
-   **/
+   *          - sequence Ids to search for associations
+   */
+  public SequenceStructureBinding addStructureViewInstance(
+          Object jmolviewer, String[] sequenceIds)
+  {
+    org.jmol.api.JmolViewer viewer = null;
+    try
+    {
+      viewer = (org.jmol.api.JmolViewer) jmolviewer;
+    } catch (ClassCastException ex)
+    {
+      System.err.println("Unsupported viewer object :"
+              + jmolviewer.getClass());
+    }
+    if (viewer == null)
+    {
+      System.err.println("Can't use this object as a structure viewer:"
+              + jmolviewer.getClass());
+      return null;
+    }
+    SequenceI[] seqs = null;
+    if (sequenceIds == null || sequenceIds.length == 0)
+    {
+      seqs = viewport.getAlignment().getSequencesArray();
+    }
+    else
+    {
+      Vector sqi = new Vector();
+      AlignmentI al = viewport.getAlignment();
+      for (int sid = 0; sid < sequenceIds.length; sid++)
+      {
+        SequenceI sq = al.findName(sequenceIds[sid]);
+        if (sq != null)
+        {
+          sqi.addElement(sq);
+        }
+      }
+      if (sqi.size() > 0)
+      {
+        seqs = new SequenceI[sqi.size()];
+        for (int sid = 0, sSize = sqi.size(); sid < sSize; sid++)
+        {
+          seqs[sid] = (SequenceI) sqi.elementAt(sid);
+        }
+      }
+      else
+      {
+        return null;
+      }
+    }
+    ExtJmol jmv = null;
+    // TODO: search for a jmv that involves viewer
+    if (jmv == null)
+    { // create a new viewer/jalview binding.
+      jmv = new ExtJmol(viewer, alignPanel, new SequenceI[][] {seqs});
+    }
+    return jmv;
+
+  }
+
   public boolean addPdbFile(String sequenceId, String pdbEntryString,
           String pdbFile)
   {
@@ -3344,4 +3384,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     // TODO Auto-generated method stub
     System.err.println("Aligned Structure View: Not yet implemented.");
   }
+
+  /**
+   * modify the current selection, providing the user has not made a selection already.
+   * @param sel - sequences from this alignment 
+   * @param csel - columns to be selected on the alignment
+   */
+  public void select(SequenceGroup sel, ColumnSelection csel)
+  {
+    alignPanel.seqPanel.selection(sel, csel, null);
+  }
 }
index 62c313f..ed61bc5 100755 (executable)
@@ -25,8 +25,9 @@ import jalview.analysis.*;
 import jalview.bin.*;
 import jalview.datamodel.*;
 import jalview.schemes.*;
+import jalview.structure.SelectionSource;
 
-public class AlignViewport
+public class AlignViewport implements SelectionSource
 {
   int startRes;
 
@@ -1122,7 +1123,31 @@ public class AlignViewport
       firePropertyChange("alignment", null, alignment.getSequences());
     }
   }
+  public void showSequence(int index)
+  {
+    Vector tmp = alignment.getHiddenSequences().showSequence(index,
+            hiddenRepSequences);
+    if (tmp.size() > 0)
+    {
+      if (selectionGroup == null)
+      {
+        selectionGroup = new SequenceGroup();
+        selectionGroup.setEndRes(alignment.getWidth() - 1);
+      }
+
+      for (int t = 0; t < tmp.size(); t++)
+      {
+        selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
+      }
+      firePropertyChange("alignment", null, alignment.getSequences());
+      sendSelection();
+    }
 
+    if (alignment.getHiddenSequences().getSize() < 1)
+    {
+      hasHiddenRows = false;
+    }
+  }
   public void showColumn(int col)
   {
     colSel.revealHiddenColumns(col);
@@ -1156,6 +1181,7 @@ public class AlignViewport
       firePropertyChange("alignment", null, alignment.getSequences());
       hasHiddenRows = false;
       hiddenRepSequences = null;
+      sendSelection();
     }
   }
 
@@ -1378,6 +1404,20 @@ public class AlignViewport
 
     return sequenceSetID;
   }
+  /**
+   * unique viewId for synchronizing state (e.g. with stored Jalview Project)
+   * 
+   */
+  private String viewId = null;
+
+  public String getViewId()
+  {
+    if (viewId == null)
+    {
+      viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
+    }
+    return viewId;
+  }
 
   public void alignmentChanged(AlignmentPanel ap)
   {
@@ -1483,6 +1523,53 @@ public class AlignViewport
     return followHighlight;
   }
 
+  public boolean followSelection = true;
+
+  /**
+   * @return true if view selection should always follow the selections
+   *         broadcast by other selection sources
+   */
+  public boolean getFollowSelection()
+  {
+    return followSelection;
+  }
+
+  private long sgrouphash = -1, colselhash = -1;
+
+  /**
+   * checks current SelectionGroup against record of last hash value, and
+   * updates record.
+   * 
+   * @return true if SelectionGroup changed since last call
+   */
+  boolean isSelectionGroupChanged()
+  {
+    int hc = (selectionGroup == null) ? -1 : selectionGroup.hashCode();
+    if (hc != sgrouphash)
+    {
+      sgrouphash = hc;
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * checks current colsel against record of last hash value, and updates
+   * record.
+   * 
+   * @return true if colsel changed since last call
+   */
+  boolean isColSelChanged()
+  {
+    int hc = (colSel == null) ? -1 : colSel.hashCode();
+    if (hc != colselhash)
+    {
+      colselhash = hc;
+      return true;
+    }
+    return false;
+  }
+
   /**
    * show non-conserved residues only
    */
@@ -1612,5 +1699,13 @@ public class AlignViewport
       }
     }
   }
+  public void sendSelection()
+  {
+    jalview.structure.StructureSelectionManager
+            .getStructureSelectionManager().sendSelection(
+                    new SequenceGroup(getSelectionGroup()),
+                    new ColumnSelection(getColumnSelection()), this);
+  }
+
 
 }
index ebc4ebb..2184851 100755 (executable)
@@ -249,9 +249,13 @@ public class AnnotationLabels extends Panel implements ActionListener,
   public void mousePressed(MouseEvent evt)
   {
     selectedRow = getSelectedRow(evt.getY() - scrollOffset);
-
     AlignmentAnnotation[] aa = ap.av.alignment.getAlignmentAnnotation();
 
+    // DETECT RIGHT MOUSE BUTTON IN AWT
+    if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
+    {
+
+
     PopupMenu popup = new PopupMenu("Annotations");
 
     MenuItem item = new MenuItem(ADDNEW);
@@ -309,7 +313,53 @@ public class AnnotationLabels extends Panel implements ActionListener,
     }
 
     popup.show(this, evt.getX(), evt.getY());
+    } else {
+      // selection action.
+      if (selectedRow > -1 && selectedRow < aa.length)
+      {
+        if (aa[selectedRow].groupRef != null)
+        {
+          if (evt.getClickCount() >= 2)
+          {
+            // todo: make the ap scroll to the selection - not necessary, first click highlights/scrolls, second selects
+            ap.seqPanel.ap.idPanel.highlightSearchResults(null);
+            ap.av.setSelectionGroup(// new SequenceGroup(
+            aa[selectedRow].groupRef); // );
+            ap.av.sendSelection();
+            ap.paintAlignment(false);
+            PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
+          }
+          else
+          {
+            ap.seqPanel.ap.idPanel
+                    .highlightSearchResults(aa[selectedRow].groupRef
+                            .getSequences(null));
+          }
+          return;
+        }
+        else if (aa[selectedRow].sequenceRef != null)
+        {
+          Vector sr = new Vector();
+          sr.addElement(aa[selectedRow].sequenceRef);
+          if (evt.getClickCount() == 1)
+          {
+            ap.seqPanel.ap.idPanel.highlightSearchResults(sr);
+          }
+          else if (evt.getClickCount() >= 2)
+          {
+            ap.seqPanel.ap.idPanel.highlightSearchResults(null);
+            SequenceGroup sg = new SequenceGroup();
+            sg.addSequence(aa[selectedRow].sequenceRef, false);
+            ap.av.setSelectionGroup(sg);
+            ap.paintAlignment(false);
+            PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
+            ap.av.sendSelection();
+          }
+
+        }
+      }
 
+    }
   }
 
   /**
index a7a20ed..43da907 100755 (executable)
@@ -386,6 +386,8 @@ public class IdPanel extends Panel implements MouseListener,
 
     mouseDragging = false;
     PaintRefresher.Refresh(this, av.getSequenceSetId());
+    // always send selection message when mouse is released
+    av.sendSelection();
   }
 
   public void highlightSearchResults(java.util.Vector found)
index ab3325b..f08bd66 100755 (executable)
@@ -225,4 +225,23 @@ public class PaintRefresher
       }
     }
   }
+
+  public static AlignmentPanel[] getAssociatedPanels(String id)
+  {
+    Vector comps = (Vector) components.get(id);
+    Vector tmp = new Vector();
+    int i, iSize = comps.size();
+    for (i = 0; i < iSize; i++)
+    {
+      if (comps.elementAt(i) instanceof AlignmentPanel)
+      {
+        tmp.addElement(((AlignmentPanel) comps.elementAt(i)));
+      }
+    }
+    AlignmentPanel[] result = new AlignmentPanel[tmp.size()];
+    tmp.toArray(result);
+
+    return result;
+  }
+
 }
index 4872aad..34db801 100755 (executable)
@@ -514,11 +514,11 @@ public class RotatableCanvas extends Panel implements MouseListener,
 
     if (found != null)
     {
+      // TODO: applet PCA is not associatable with multi-panels - only parent view 
       if (av.getSelectionGroup() != null)
       {
         av.getSelectionGroup().addOrRemove(found, true);
         av.getSelectionGroup().setEndRes(av.alignment.getWidth() - 1);
-        PaintRefresher.Refresh(this, av.getSequenceSetId());
       }
       else
       {
@@ -527,6 +527,8 @@ public class RotatableCanvas extends Panel implements MouseListener,
         av.getSelectionGroup().setEndRes(av.alignment.getWidth() - 1);
 
       }
+      PaintRefresher.Refresh(this, av.getSequenceSetId());
+      av.sendSelection();
     }
     repaint();
   }
index 0971882..28139fb 100755 (executable)
@@ -173,6 +173,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     }
 
     ap.paintAlignment(true);
+    av.sendSelection();
   }
 
   public void mouseReleased(MouseEvent evt)
@@ -211,6 +212,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
 
     stretchingGroup = false;
     ap.paintAlignment(false);
+    av.sendSelection();
   }
 
   public void mouseDragged(MouseEvent evt)
index 24f4786..98dcc07 100755 (executable)
@@ -25,6 +25,7 @@ import java.awt.event.*;
 import jalview.commands.*;
 import jalview.datamodel.*;
 import jalview.schemes.*;
+import jalview.structure.SelectionSource;
 import jalview.structure.SequenceListener;
 import jalview.structure.StructureSelectionManager;
 
@@ -302,8 +303,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       sg.addSequence(sequence, false);
       av.setSelectionGroup(sg);
     }
-
     ap.paintAlignment(false);
+    av.sendSelection();
   }
 
   void insertGapAtCursor(boolean group)
@@ -1448,6 +1449,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     stretchGroup = null;
     PaintRefresher.Refresh(ap, av.getSequenceSetId());
     ap.paintAlignment(true);
+    av.sendSelection();
   }
 
   public void doMouseDraggedDefineMode(MouseEvent evt)
@@ -1681,5 +1683,95 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       }
     }
   }
+  /**
+   * modify current selection according to a received message.
+   */
+  public void selection(SequenceGroup seqsel, ColumnSelection colsel,
+          SelectionSource source)
+  {
+    // TODO: fix this hack - source of messages is align viewport, but SeqPanel
+    // handles selection messages...
+    // TODO: extend config options to allow user to control if selections may be
+    // shared between viewports.
+    if (av!=null && (av == source
+            || !av.followSelection
+            || (source instanceof AlignViewport && ((AlignViewport) source)
+                    .getSequenceSetId().equals(av.getSequenceSetId()))))
+    {
+      return;
+    }
+    // do we want to thread this ? (contention with seqsel and colsel locks, I
+    // suspect)
+    // rules are: colsel is copied if there is a real intersection between
+    // sequence selection
+    boolean repaint = false, copycolsel = true;
+    if (av.selectionGroup == null || !av.isSelectionGroupChanged())
+    {
+      SequenceGroup sgroup = null;
+      if (seqsel != null)
+      {
+        if (av.alignment == null)
+        {
+          System.out.println("Selection message: alignviewport av SeqSetId="
+                  + av.getSequenceSetId() + " ViewId=" + av.getViewId()
+                  + " 's alignment is NULL! returning immediatly.");
+          return;
+        }
+        sgroup = seqsel.intersect(av.alignment,
+                (av.hasHiddenRows) ? av.hiddenRepSequences : null);
+        if ((sgroup == null || sgroup.getSize() == 0)
+                && (colsel == null || colsel.size() == 0))
+        {
+          // don't copy columns if the region didn't intersect.
+          copycolsel = false;
+        }
+      }
+      if (sgroup != null && sgroup.getSize() > 0)
+      {
+        av.setSelectionGroup(sgroup);
+      }
+      else
+      {
+        av.setSelectionGroup(null);
+      }
+      repaint = av.isSelectionGroupChanged();
+    }
+    if (copycolsel && (av.colSel == null || !av.isColSelChanged()))
+    {
+      // the current selection is unset or from a previous message
+      // so import the new colsel.
+      if (colsel == null || colsel.size() == 0)
+      {
+        if (av.colSel != null)
+        {
+          av.colSel.clear();
+        }
+      }
+      else
+      {
+        // TODO: shift colSel according to the intersecting sequences
+        if (av.colSel == null)
+        {
+          av.colSel = new ColumnSelection(colsel);
+        }
+        else
+        {
+          av.colSel.setElementsFrom(colsel);
+        }
+      }
+      repaint |= av.isColSelChanged();
+    }
+    if (copycolsel && av.hasHiddenColumns
+            && (av.colSel == null || av.colSel.getHiddenColumns() == null))
+    {
+      System.err.println("Bad things");
+    }
+    if (repaint)
+    {
+      // probably finessing with multiple redraws here
+      PaintRefresher.Refresh(this, av.getSequenceSetId());
+      // ap.paintAlignment(false);
+    }
+  }
 
 }
index d40ab73..4a38767 100755 (executable)
@@ -508,6 +508,7 @@ public class TreeCanvas extends Panel implements MouseListener,
 
       PaintRefresher.Refresh(this, av.getSequenceSetId());
       repaint();
+      av.sendSelection();
     }
   }
 
@@ -550,6 +551,7 @@ public class TreeCanvas extends Panel implements MouseListener,
       treeSelectionChanged((Sequence) ob);
       PaintRefresher.Refresh(this, av.getSequenceSetId());
       repaint();
+      av.sendSelection();
       return;
     }
     else if (!(ob instanceof SequenceNode))
index 6a29ecd..5c5e3df 100755 (executable)
  */
 package jalview.bin;
 
-import jalview.api.SequenceStructureBinding;
 import jalview.appletgui.AlignFrame;
-import jalview.appletgui.AppletJmol;
 import jalview.appletgui.EmbmenuFrame;
 import jalview.appletgui.FeatureSettings;
 import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.io.AnnotationFile;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.FileParse;
 import jalview.io.IdentifyFile;
 import jalview.io.JnetAnnotationMaker;
+import jalview.javascript.JsCallBack;
+import jalview.structure.SelectionListener;
+import jalview.structure.StructureSelectionManager;
 
 import java.applet.Applet;
 import java.awt.Button;
@@ -44,8 +48,6 @@ import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
-import java.lang.reflect.Method;
-import java.util.Enumeration;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
@@ -125,6 +127,259 @@ public class JalviewLite extends Applet
   }
 
   /**
+   * 
+   * @param sequenceId id of sequence to highlight
+   * @param position integer position [ tobe implemented or range ] on sequence
+   * @param alignedPosition true/false/empty string - indicate if position is an alignment column or unaligned sequence position
+   */
+  public void highlight(String sequenceId, String position, String alignedPosition)
+  {
+    highlight(currentAlignFrame, sequenceId, position, alignedPosition);
+  }
+  /**
+   * 
+   * @param sequenceId id of sequence to highlight
+   * @param position integer position [ tobe implemented or range ] on sequence
+   * @param alignedPosition false, blank or something else - indicate if position is an alignment column or unaligned sequence position
+   */
+  public void highlight(AlignFrame alf, String sequenceId, String position, String alignedPosition)
+  {
+    SequenceI sq = alf.getAlignViewport().getAlignment().findName(sequenceId);
+    if (sq!=null)
+    {
+      int pos, apos=-1;
+      try {
+        apos = new Integer(position).intValue();
+        apos--;
+      } catch (NumberFormatException ex)
+      {
+        return;
+      }
+      // use vamsas listener to broadcast to all listeners in scope
+      if (alignedPosition!=null && (alignedPosition.trim().length()==0 || alignedPosition.toLowerCase().indexOf("false")>-1))
+      {
+        StructureSelectionManager.getStructureSelectionManager().mouseOverVamsasSequence(sq,sq.findIndex(apos));
+      } else {
+        StructureSelectionManager.getStructureSelectionManager().mouseOverVamsasSequence(sq,apos); 
+      }
+              
+    }
+  }
+  /**
+   * select regions of the currrent alignment frame
+   * 
+   * @param sequenceIds String separated list of sequence ids or empty string
+   * @param columns 
+   *          String separated list { column range or column, ..} or empty string
+   */
+  public void select(String sequenceIds, String columns)
+  {
+    select(currentAlignFrame, sequenceIds, columns, "¬");
+  }
+
+  /**
+   * select regions of the currrent alignment frame
+   * 
+   * @param toselect
+   *          String separated list { column range, seq1...seqn sequence ids }
+   * @param sep
+   *          separator between toselect fields
+   */
+  public void select(String sequenceIds, String columns, String sep)
+  {
+    select(currentAlignFrame, sequenceIds, columns, sep);
+  }
+
+  /**
+   * select regions of the given alignment frame
+   * 
+   * @param alf
+   * @param toselect
+   *          String separated list { column range, seq1...seqn sequence ids }
+   * @param sep
+   *          separator between toselect fields
+   */
+  public void select(AlignFrame alf, String sequenceIds, String columns)
+  {
+    select(alf, sequenceIds, columns, separator);
+  }
+
+  /**
+   * select regions of the given alignment frame
+   * 
+   * @param alf
+   * @param toselect
+   *          String separated list { column range, seq1...seqn sequence ids }
+   * @param sep
+   *          separator between toselect fields
+   */
+  public void select(AlignFrame alf, String sequenceIds, String columns,
+          String sep)
+  {
+    if (sep == null || sep.length() == 0)
+    {
+      sep = separator;
+    }
+    // deparse fields
+    String[] ids = separatorListToArray(sequenceIds, sep);
+    String[] cols = separatorListToArray(columns, sep);
+    SequenceGroup sel = new SequenceGroup();
+    ColumnSelection csel = new ColumnSelection();
+    AlignmentI al = alf.viewport.getAlignment();
+    int start = 0, end = al.getWidth(), alw = al.getWidth();
+    if (ids != null && ids.length > 0)
+    {
+      for (int i = 0; i < ids.length; i++)
+      {
+        if (ids[i].trim().length() == 0)
+        {
+          continue;
+        }
+        SequenceI sq = al.findName(ids[i]);
+        if (sq != null)
+        {
+          sel.addSequence(sq, false);
+        }
+      }
+    }
+    if (cols != null && cols.length > 0)
+    {
+      boolean seset = false;
+      for (int i = 0; i < cols.length; i++)
+      {
+        String cl = cols[i].trim();
+        if (cl.length() == 0)
+        {
+          continue;
+        }
+        int p;
+        if ((p = cl.indexOf("-")) > -1)
+        {
+          int from = -1, to = -1;
+          try
+          {
+            from = new Integer(cl.substring(0, p)).intValue();
+            from--;
+          } catch (NumberFormatException ex)
+          {
+            System.err
+                    .println("ERROR: Couldn't parse first integer in range element column selection string '"
+                            + cl + "' - format is 'from-to'");
+            return;
+          }
+          try
+          {
+            to = new Integer(cl.substring(p + 1)).intValue();
+            to--;
+          } catch (NumberFormatException ex)
+          {
+            System.err
+                    .println("ERROR: Couldn't parse second integer in range element column selection string '"
+                            + cl + "' - format is 'from-to'");
+            return;
+          }
+          if (from >= 0 && to >= 0)
+          {
+            // valid range
+            if (from < to)
+            {
+              int t = to;
+              to = from;
+              to = t;
+            }
+            if (!seset)
+            {
+              start = from;
+              end = to;
+              seset = true;
+            }
+            else
+            {
+              // comment to prevent range extension
+              if (start > from)
+              {
+                start = from;
+              }
+              if (end < to)
+              {
+                end = to;
+              }
+            }
+            for (int r = from; r <= to; r++)
+            {
+              if (r >= 0 && r < alw)
+              {
+                csel.addElement(r);
+              }
+            }
+            if (debug)
+            {
+              System.err.println("Range '" + cl + "' deparsed as [" + from
+                      + "," + to + "]");
+            }
+          }
+          else
+          {
+            System.err.println("ERROR: Invalid Range '" + cl
+                    + "' deparsed as [" + from + "," + to + "]");
+          }
+        }
+        else
+        {
+          int r = -1;
+          try
+          {
+            r = new Integer(cl).intValue();
+            r--;
+          } catch (NumberFormatException ex)
+          {
+            System.err
+                    .println("ERROR: Couldn't parse integer from point selection element of column selection string '"
+                            + cl + "'");
+            return;
+          }
+          if (r >= 0 && r <= alw)
+          {
+            if (!seset)
+            {
+              start = r;
+              end = r;
+              seset = true;
+            }
+            else
+            {
+              // comment to prevent range extension
+              if (start > r)
+              {
+                start = r;
+              }
+              if (end < r)
+              {
+                end = r;
+              }
+            }
+            csel.addElement(r);
+            if (debug)
+            {
+              System.err.println("Point selection '" + cl
+                      + "' deparsed as [" + r + "]");
+            }
+          }
+          else
+          {
+            System.err.println("ERROR: Invalid Point selection '" + cl
+                    + "' deparsed as [" + r + "]");
+          }
+        }
+      }
+    }
+    sel.setStartRes(start);
+    sel.setEndRes(end);
+    alf.select(sel, csel);
+
+  }
+
+  /**
    * get sequences selected in current alignFrame and return their alignment in
    * format 'format' either with or without suffix
    * 
@@ -297,6 +552,155 @@ public class JalviewLite extends Applet
     return null;
   }
 
+  public void setMouseoverListener(String listener)
+  {
+    setMouseoverListener(currentAlignFrame, listener);
+  }
+
+  private Vector mouseoverListeners = new Vector();
+
+  public void setMouseoverListener(AlignFrame af, String listener)
+  {
+    if (listener != null)
+    {
+      listener = listener.trim();
+      if (listener.length() == 0)
+      {
+        System.err
+                .println("jalview Javascript error: Ignoring empty function for mouseover listener.");
+        return;
+      }
+    }
+    jalview.javascript.MouseOverListener mol = new jalview.javascript.MouseOverListener(
+            this, af, listener);
+    mouseoverListeners.add(mol);
+    StructureSelectionManager.getStructureSelectionManager()
+            .addStructureViewerListener(mol);
+    if (debug)
+    {
+      System.err.println("Added a mouseover listener for "
+              + ((af == null) ? "All frames" : "Just views for "
+                      + af.getAlignViewport().getSequenceSetId()));
+      System.err.println("There are now " + mouseoverListeners.size()
+              + " listeners in total.");
+    }
+  }
+
+  public void setSelectionListener(String listener)
+  {
+    setSelectionListener(currentAlignFrame, listener);
+  }
+
+  public void setSelectionListener(AlignFrame af, String listener)
+  {
+    if (listener != null)
+    {
+      listener = listener.trim();
+      if (listener.length() == 0)
+      {
+        System.err
+                .println("jalview Javascript error: Ignoring empty function for selection listener.");
+        return;
+      }
+    }
+    jalview.javascript.JsSelectionSender mol = new jalview.javascript.JsSelectionSender(
+            this, af, listener);
+    mouseoverListeners.add(mol);
+    StructureSelectionManager.getStructureSelectionManager()
+            .addSelectionListener(mol);
+    if (debug)
+    {
+      System.err.println("Added a selection listener for "
+              + ((af == null) ? "All frames" : "Just views for "
+                      + af.getAlignViewport().getSequenceSetId()));
+      System.err.println("There are now " + mouseoverListeners.size()
+              + " listeners in total.");
+    }
+  }
+
+  /**
+   * remove any callback using the given listener function and associated with
+   * the given alignFrame (or null for all callbacks)
+   * 
+   * @param af
+   *          (may be null)
+   * @param listener
+   *          (may be null)
+   */
+  public void removeJavascriptListener(AlignFrame af, String listener)
+  {
+    if (listener != null)
+    {
+      listener = listener.trim();
+      if (listener.length() == 0)
+      {
+        listener = null;
+      }
+    }
+    boolean rprt = false;
+    for (Object lstn : mouseoverListeners)
+    {
+      JsCallBack lstner = (JsCallBack) lstn;
+      if ((af == null || lstner.getAlignFrame() == af)
+              && (listener == null || lstner.getListenerFunction().equals(
+                      listener)))
+      {
+        mouseoverListeners.remove(lstner);
+        if (lstner instanceof SelectionListener)
+        {
+          StructureSelectionManager.getStructureSelectionManager()
+                  .removeSelectionListener((SelectionListener) lstner);
+        }
+        else
+        {
+          StructureSelectionManager.getStructureSelectionManager()
+                  .removeStructureViewerListener(lstner, null);
+        }
+        rprt = debug;
+        if (debug)
+        {
+          System.err.println("Removed listener '" + listener + "'");
+        }
+      }
+    }
+    if (rprt)
+    {
+      System.err.println("There are now " + mouseoverListeners.size()
+              + " listeners in total.");
+    }
+  }
+
+  public void stop()
+  {
+    if (mouseoverListeners!=null) 
+    {
+      while (mouseoverListeners.size()>0)
+      {
+        Object mol =         mouseoverListeners.remove(0);
+// mouseoverListeners.elementAt(0);
+        if (mol instanceof SelectionListener)
+        {
+          StructureSelectionManager.getStructureSelectionManager().removeSelectionListener((SelectionListener)mol);          
+        } else {
+          StructureSelectionManager.getStructureSelectionManager().removeStructureViewerListener(mol, null);
+        }
+      }
+    }
+  }
+  /**
+   * send a mouseover message to all the alignment windows associated with the
+   * given residue in the pdbfile
+   * 
+   * @param pdbResNum
+   * @param chain
+   * @param pdbfile
+   */
+  public void mouseOverStructure(int pdbResNum, String chain, String pdbfile)
+  {
+    StructureSelectionManager.getStructureSelectionManager()
+            .mouseOverStructure(pdbResNum, chain, pdbfile);
+  }
+
   // //////////////////////////////////////////////
   // //////////////////////////////////////////////
 
@@ -391,7 +795,8 @@ public class JalviewLite extends Applet
    */
   public void init()
   {
-
+    // remove any handlers that might be hanging around from an earlier instance
+    
     /**
      * turn on extra applet debugging
      */
@@ -1147,8 +1552,21 @@ public class JalviewLite extends Applet
    */
   public String[] separatorListToArray(String list)
   {
+    return separatorListToArray(list, separator);
+  }
+
+  /**
+   * parse the string into a list
+   * 
+   * @param list
+   * @param separator
+   * @return elements separated by separator
+   */
+  public String[] separatorListToArray(String list, String separator)
+  {
+    // note separator local variable intentionally masks object field
     int seplen = separator.length();
-    if (list == null || list.equals(""))
+    if (list == null || list.equals("") || list.equals(separator))
       return null;
     java.util.Vector jv = new Vector();
     int cp = 0, pos;
@@ -1196,6 +1614,18 @@ public class JalviewLite extends Applet
    */
   public String arrayToSeparatorList(String[] list)
   {
+    return arrayToSeparatorList(list, separator);
+  }
+
+  /**
+   * concatenate the list with separator
+   * 
+   * @param list
+   * @param separator
+   * @return concatenated string
+   */
+  public String arrayToSeparatorList(String[] list, String separator)
+  {
     StringBuffer v = new StringBuffer();
     if (list != null && list.length > 0)
     {
@@ -1224,7 +1654,7 @@ public class JalviewLite extends Applet
       System.err.println("Returning empty '" + separator
               + "' separated List\n");
     }
-    return "";
+    return "" + separator;
   }
 
   /**