selection sending and receiving, autoscrolling to highlighted position, new viewport...
[jalview.git] / src / jalview / gui / VamsasApplication.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import jalview.bin.Cache;
22 import jalview.datamodel.AlignmentI;
23 import jalview.datamodel.ColumnSelection;
24 import jalview.datamodel.SequenceGroup;
25 import jalview.datamodel.SequenceI;
26 import jalview.io.VamsasAppDatastore;
27 import jalview.structure.SelectionListener;
28 import jalview.structure.SelectionSource;
29 import jalview.structure.StructureSelectionManager;
30 import jalview.structure.VamsasListener;
31
32 import java.beans.PropertyChangeEvent;
33 import java.beans.PropertyChangeListener;
34 import java.io.File;
35 import java.io.IOException;
36 import java.util.Enumeration;
37 import java.util.Hashtable;
38 import java.util.IdentityHashMap;
39 import java.util.Iterator;
40
41 import javax.swing.JInternalFrame;
42 import javax.swing.JOptionPane;
43
44 import uk.ac.vamsas.client.ClientHandle;
45 import uk.ac.vamsas.client.IClient;
46 import uk.ac.vamsas.client.IClientDocument;
47 import uk.ac.vamsas.client.InvalidSessionDocumentException;
48 import uk.ac.vamsas.client.NoDefaultSessionException;
49 import uk.ac.vamsas.client.UserHandle;
50 import uk.ac.vamsas.client.VorbaId;
51 import uk.ac.vamsas.client.picking.IMessageHandler;
52 import uk.ac.vamsas.client.picking.IPickManager;
53 import uk.ac.vamsas.client.picking.Message;
54 import uk.ac.vamsas.client.picking.MouseOverMessage;
55 import uk.ac.vamsas.client.picking.SelectionMessage;
56 import uk.ac.vamsas.objects.core.Entry;
57 import uk.ac.vamsas.objects.core.Input;
58 import uk.ac.vamsas.objects.core.Pos;
59 import uk.ac.vamsas.objects.core.Seg;
60
61 /**
62  * @author jimp
63  * 
64  */
65 public class VamsasApplication implements SelectionSource
66 {
67   IClient vclient = null;
68
69   ClientHandle app = null;
70
71   UserHandle user = null;
72
73   Desktop jdesktop = null; // our jalview desktop reference
74
75   // Cache.preferences for vamsas client session arena
76   // preferences for check for default session at startup.
77   // user and organisation stuff.
78   public VamsasApplication(Desktop jdesktop, File sessionPath)
79   {
80     // JBPNote:
81     // we should create a session URI from the sessionPath and pass it to
82     // the clientFactory - but the vamsas api doesn't cope with that yet.
83     this.jdesktop = jdesktop;
84     initClientSession(null, sessionPath);
85   }
86
87   private static uk.ac.vamsas.client.IClientFactory getClientFactory()
88           throws IOException
89   {
90     return new uk.ac.vamsas.client.simpleclient.SimpleClientFactory();
91   }
92
93   /**
94    * Start a new vamsas session
95    * 
96    * @param jdesktop
97    */
98   public VamsasApplication(Desktop jdesktop)
99   {
100     this.jdesktop = jdesktop;
101     initClientSession(null, null);
102   }
103
104   /**
105    * init a connection to the session at the given url
106    * 
107    * @param jdesktop
108    * @param sessionUrl
109    */
110   public VamsasApplication(Desktop jdesktop, String sessionUrl)
111   {
112     this.jdesktop = jdesktop;
113     initClientSession(sessionUrl, null);
114   }
115
116   /**
117    * @throws IOException
118    *                 or other if clientfactory instantiation failed.
119    * @return list of current sessions or null if no session exists.
120    */
121   public static String[] getSessionList() throws Exception
122   {
123     return getClientFactory().getCurrentSessions();
124   }
125
126   /**
127    * initialise, possibly with either a valid session url or a file for a new
128    * session
129    * 
130    * @param sess
131    *                null or a valid session url
132    * @param vamsasDocument
133    *                null or a valid vamsas document file
134    * @return false if no vamsas connection was made
135    */
136   private boolean initClientSession(String sess, File vamsasDocument)
137   {
138     try
139     {
140       // Only need to tell the library what the application is here
141       app = getJalviewHandle();
142       uk.ac.vamsas.client.IClientFactory clientfactory = getClientFactory();
143       if (vamsasDocument != null)
144       {
145         if (sess != null)
146         {
147           throw new Error(
148                   "Implementation Error - cannot import existing vamsas document into an existing session, Yet!");
149         }
150         try
151         {
152           vclient = clientfactory.openAsNewSessionIClient(app,
153                   vamsasDocument);
154         } catch (InvalidSessionDocumentException e)
155         {
156           JOptionPane
157                   .showInternalMessageDialog(
158                           Desktop.desktop,
159
160                           "VAMSAS Document could not be opened as a new session - please choose another",
161                           "VAMSAS Document Import Failed",
162                           JOptionPane.ERROR_MESSAGE);
163
164         }
165       }
166       else
167       {
168         // join existing or create a new session
169         if (sess == null)
170         {
171           vclient = clientfactory.getNewSessionIClient(app);
172         }
173         else
174         {
175           vclient = clientfactory.getIClient(app, sess);
176         }
177       }
178       // set some properties for our VAMSAS interaction
179       setVclientConfig();
180       user = vclient.getUserHandle();
181
182     } catch (Exception e)
183     {
184       jalview.bin.Cache.log
185               .error("Couldn't instantiate vamsas client !", e);
186       return false;
187     }
188     return true;
189   }
190
191   private void setVclientConfig()
192   {
193     if (vclient == null)
194     {
195       return;
196     }
197     try
198     {
199       if (vclient instanceof uk.ac.vamsas.client.simpleclient.SimpleClient)
200       {
201         uk.ac.vamsas.client.simpleclient.SimpleClientConfig cfg = ((uk.ac.vamsas.client.simpleclient.SimpleClient) vclient)
202                 .getSimpleClientConfig();
203         cfg._validatemergedroots = false;
204         cfg._validateupdatedroots = true; // we may write rubbish otherwise.
205       }
206     } catch (Error e)
207     {
208       Cache.log
209               .warn(
210                       "Probable SERIOUS VAMSAS client incompatibility - carrying on regardless",
211                       e);
212     } catch (Exception e)
213     {
214       Cache.log
215               .warn(
216                       "Probable VAMSAS client incompatibility - carrying on regardless",
217                       e);
218     }
219   }
220
221   /**
222    * make the appHandle for Jalview
223    * 
224    * @return
225    */
226   private ClientHandle getJalviewHandle()
227   {
228     return new ClientHandle("jalview.bin.Jalview", jalview.bin.Cache
229             .getProperty("VERSION"));
230   }
231
232   /**
233    * 
234    * @return true if we are registered in a vamsas session
235    */
236   public boolean inSession()
237   {
238     return (vclient != null);
239   }
240
241   /**
242    * called to connect to session inits handlers, does an initial document
243    * update.
244    */
245   public void initial_update()
246   {
247     if (!inSession())
248     {
249       throw new Error(
250               "Impementation error! Vamsas Operations when client not initialised and connected.");
251     }
252     addDocumentUpdateHandler();
253     addStoreDocumentHandler();
254     startSession();
255     Cache.log
256             .debug("Jalview loading the Vamsas Session for the first time.");
257     dealWithDocumentUpdate(false); // we don't push an update out to the
258     // document yet.
259     Cache.log.debug("... finished update for the first time.");
260   }
261
262   /**
263    * Update all windows after a vamsas datamodel change. this could go on the
264    * desktop object!
265    * 
266    */
267   protected void updateJalviewGui()
268   {
269     JInternalFrame[] frames = jdesktop.getAllFrames();
270
271     if (frames == null)
272     {
273       return;
274     }
275
276     try
277     {
278       // REVERSE ORDER
279       for (int i = frames.length - 1; i > -1; i--)
280       {
281         if (frames[i] instanceof AlignFrame)
282         {
283           AlignFrame af = (AlignFrame) frames[i];
284           af.alignPanel.alignmentChanged();
285         }
286       }
287     } catch (Exception e)
288     {
289       Cache.log
290               .warn(
291                       "Exception whilst refreshing jalview windows after a vamsas document update.",
292                       e);
293     }
294   }
295
296   public void push_update()
297   {
298     Thread udthread = new Thread(new Runnable()
299     {
300
301       public void run()
302       {
303         Cache.log.info("Jalview updating to the Vamsas Session.");
304
305         dealWithDocumentUpdate(true);
306         /*
307          * IClientDocument cdoc=null; try { cdoc = vclient.getClientDocument(); }
308          * catch (Exception e) { Cache.log.error("Failed to get client document
309          * for update."); // RAISE A WARNING DIALOG disableGui(false); return; }
310          * updateVamsasDocument(cdoc); updateJalviewGui();
311          * cdoc.setVamsasRoots(cdoc.getVamsasRoots()); // propagate update flags
312          * back vclient.updateDocument(cdoc);
313          */
314         Cache.log.info("Jalview finished updating to the Vamsas Session.");
315         // TODO Auto-generated method stub
316       }
317
318     });
319     udthread.start();
320   }
321
322   public void end_session()
323   {
324     if (!inSession())
325       throw new Error("Jalview not connected to Vamsas session.");
326     Cache.log.info("Jalview disconnecting from the Vamsas Session.");
327     try
328     {
329       if (joinedSession)
330       {
331         vclient.finalizeClient();
332         Cache.log.info("Jalview has left the session.");
333       }
334       else
335       {
336         Cache.log
337                 .warn("JV Client leaving a session that's its not joined yet.");
338       }
339       joinedSession = false;
340       vclient = null;
341       app = null;
342       user = null;
343       jv2vobj = null;
344       vobj2jv = null;
345     } catch (Exception e)
346     {
347       Cache.log.error("Vamsas Session finalization threw exceptions!", e);
348     }
349   }
350
351   public void updateJalview(IClientDocument cdoc)
352   {
353     Cache.log.debug("Jalview updating from sesion document ..");
354     ensureJvVamsas();
355     VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj,
356             baseProvEntry(), alRedoState);
357     try
358     {
359       vds.updateToJalview();
360     } catch (Exception e)
361     {
362       Cache.log.error("Failed to update Jalview from vamsas document.", e);
363     }
364     try
365     {
366       if (firstUpdate)
367       {
368         vds.updateJalviewFromAppdata();
369         // Comment this out to repeatedly read in data from JalviewAppData
370         // firstUpdate=false;
371       }
372     } catch (Exception e)
373     {
374       Cache.log.error(
375               "Exception when updating Jalview settings from Appdata.", e);
376     }
377     Cache.log.debug(".. finished updating from sesion document.");
378
379   }
380
381   boolean firstUpdate = false;
382
383   private void ensureJvVamsas()
384   {
385     if (jv2vobj == null)
386     {
387       jv2vobj = new IdentityHashMap();
388       vobj2jv = new Hashtable();
389       alRedoState = new Hashtable();
390       firstUpdate = true;
391     }
392   }
393
394   /**
395    * jalview object binding to VorbaIds
396    */
397   IdentityHashMap jv2vobj = null;
398
399   Hashtable vobj2jv = null;
400
401   Hashtable alRedoState = null;
402
403   boolean errorsDuringUpdate = false;
404
405   boolean errorsDuringAppUpdate = false;
406
407   /**
408    * update the document accessed through doc. A backup of the current object
409    * bindings is made.
410    * 
411    * @param doc
412    */
413   public void updateVamsasDocument(IClientDocument doc)
414   {
415     ensureJvVamsas();
416     errorsDuringUpdate = false;
417     errorsDuringAppUpdate = false;
418     backup_objectMapping();
419     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
420             baseProvEntry(), alRedoState);
421     // wander through frames
422     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
423
424     if (frames == null)
425     {
426       return;
427     }
428     Hashtable skipList = new Hashtable();
429     Hashtable viewset = new Hashtable();
430
431     try
432     {
433       // REVERSE ORDER
434       for (int i = frames.length - 1; i > -1; i--)
435       {
436         if (frames[i] instanceof AlignFrame)
437         {
438           AlignFrame af = (AlignFrame) frames[i];
439           if (!viewset.containsKey(af.getViewport().getSequenceSetId()))
440           {
441             // update alignment and root from frame.
442             boolean stored = false;
443             try
444             {
445               stored = vds.storeVAMSAS(af.getViewport(), af.getTitle());
446             } catch (Exception e)
447             {
448               errorsDuringUpdate = true;
449               Cache.log.error("Exception synchronizing "
450                       + af.getTitle()
451                       + " "
452                       + (af.getViewport().viewName == null ? "" : " view "
453                               + af.getViewport().viewName)
454                       + " to document.", e);
455               stored = false;
456             }
457             if (!stored)
458             { // record skip in skipList
459               skipList.put(af.getViewport().getSequenceSetId(), af);
460             }
461             else
462             {
463               // could try to eliminate sequenceSetId from skiplist ..
464               // (skipList.containsKey(af.getViewport().getSequenceSetId()))
465               // remember sequenceSetId so we can skip all the other views on
466               // same alignment
467               viewset.put(af.getViewport().getSequenceSetId(), af);
468             }
469           }
470         }
471       }
472       // REVERSE ORDER
473       // for (int i = frames.length - 1; i > -1; i--)
474       // {
475       // if (frames[i] instanceof AlignFrame)
476       // {
477       // AlignFrame af = (AlignFrame) frames[i];
478       Iterator aframes = viewset.values().iterator();
479       while (aframes.hasNext())
480       {
481         AlignFrame af = (AlignFrame) aframes.next();
482         // add any AlignedCodonFrame mappings on this alignment to any other.
483         vds.storeSequenceMappings(af.getViewport(), af.getTitle());
484       }
485     } catch (Exception e)
486     {
487       Cache.log.error("Exception synchronizing Views to Document :", e);
488       errorsDuringUpdate = true;
489     }
490
491     try
492     {
493       if (viewset.size() > 0)
494       {
495         // Alignment views were synchronized, so store their state in the
496         // appData, too.
497         // The skipList ensures we don't write out any alignments not actually
498         // in the document.
499         vds.setSkipList(skipList);
500         vds.updateJalviewClientAppdata();
501       }
502     } catch (Exception e)
503     {
504       Cache.log.error("Client Appdata Write exception", e);
505       errorsDuringAppUpdate = true;
506     }
507     vds.clearSkipList();
508   }
509
510   private Entry baseProvEntry()
511   {
512     uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
513     pentry.setUser(user.getFullName());
514     pentry.setApp(app.getClientUrn());
515     pentry.setDate(new java.util.Date());
516     pentry.setAction("created");
517     return pentry;
518   }
519
520   /**
521    * do a vamsas document update or update jalview from the vamsas document
522    * 
523    * @param fromJalview
524    *                true to update from jalview to the vamsas document
525    */
526   protected void dealWithDocumentUpdate(boolean fromJalview)
527   {
528     // called by update handler for document update.
529     Cache.log.debug("Updating jalview from changed vamsas document.");
530     disableGui(true);
531     try
532     {
533       long time = System.currentTimeMillis();
534       IClientDocument cdoc = vclient.getClientDocument();
535       if (Cache.log.isDebugEnabled())
536       {
537         Cache.log.debug("Time taken to get ClientDocument = "
538                 + (System.currentTimeMillis() - time));
539         time = System.currentTimeMillis();
540       }
541       if (fromJalview)
542       {
543         this.updateVamsasDocument(cdoc);
544         if (Cache.log.isDebugEnabled())
545         {
546           Cache.log
547                   .debug("Time taken to update Vamsas Document from jalview\t= "
548                           + (System.currentTimeMillis() - time));
549           time = System.currentTimeMillis();
550         }
551         cdoc.setVamsasRoots(cdoc.getVamsasRoots());
552         if (Cache.log.isDebugEnabled())
553         {
554           Cache.log.debug("Time taken to set Document Roots\t\t= "
555                   + (System.currentTimeMillis() - time));
556           time = System.currentTimeMillis();
557         }
558       }
559       else
560       {
561         updateJalview(cdoc);
562         if (Cache.log.isDebugEnabled())
563         {
564           Cache.log
565                   .debug("Time taken to update Jalview from vamsas document Roots\t= "
566                           + (System.currentTimeMillis() - time));
567           time = System.currentTimeMillis();
568         }
569
570       }
571       vclient.updateDocument(cdoc);
572       if (Cache.log.isDebugEnabled())
573       {
574         Cache.log.debug("Time taken to update Session Document\t= "
575                 + (System.currentTimeMillis() - time));
576         time = System.currentTimeMillis();
577       }
578       cdoc = null;
579     } catch (Exception ee)
580     {
581       System.err.println("Exception whilst updating :");
582       ee.printStackTrace(System.err);
583       // recover object map backup, since its probably corrupted with references
584       // to Vobjects that don't exist anymore.
585       this.recover_objectMappingBackup();
586     }
587     Cache.log.debug("Finished updating from document change.");
588     disableGui(false);
589   }
590
591   private void addDocumentUpdateHandler()
592   {
593     final VamsasApplication client = this;
594     vclient.addDocumentUpdateHandler(new PropertyChangeListener()
595     {
596       public void propertyChange(PropertyChangeEvent evt)
597       {
598         Cache.log.debug("Dealing with document update event.");
599         client.dealWithDocumentUpdate(false);
600         Cache.log.debug("finished dealing with event.");
601       }
602     });
603     Cache.log.debug("Added Jalview handler for vamsas document updates.");
604   }
605
606   private void addStoreDocumentHandler()
607   {
608     final VamsasApplication client = this;
609     vclient.addVorbaEventHandler(
610             uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE,
611             new PropertyChangeListener()
612             {
613               public void propertyChange(PropertyChangeEvent evt)
614               {
615                 Cache.log
616                         .debug("Asking user if the vamsas session should be stored.");
617                 int reply = JOptionPane
618                         .showInternalConfirmDialog(
619                                 Desktop.desktop,
620                                 "The current VAMSAS session has unsaved data - do you want to save it ?",
621                                 "VAMSAS Session Shutdown",
622                                 JOptionPane.YES_NO_OPTION,
623                                 JOptionPane.QUESTION_MESSAGE);
624
625                 if (reply == JOptionPane.YES_OPTION)
626                 {
627                   Cache.log.debug("Prompting for vamsas store filename.");
628                   Desktop.instance.vamsasSave_actionPerformed(null);
629                   Cache.log.debug("Finished attempt at storing document.");
630                 }
631                 Cache.log
632                         .debug("finished dealing with REQUESTTOCLOSE event.");
633               }
634             });
635     Cache.log.debug("Added Jalview handler for vamsas document updates.");
636   }
637
638   public void disableGui(boolean b)
639   {
640     Desktop.instance.setVamsasUpdate(b);
641   }
642
643   Hashtable _backup_vobj2jv;
644
645   IdentityHashMap _backup_jv2vobj;
646
647   /**
648    * make a backup of the object mappings (vobj2jv and jv2vobj)
649    */
650   public void backup_objectMapping()
651   {
652     _backup_vobj2jv = new Hashtable(vobj2jv);
653     _backup_jv2vobj = new IdentityHashMap(jv2vobj);
654   }
655
656   /**
657    * recover original object mappings from the object mapping backup if document
658    * IO failed
659    * 
660    * @throws Error
661    *                 if backup_objectMapping was not called.
662    */
663   public void recover_objectMappingBackup()
664   {
665     if (_backup_vobj2jv == null)
666     {
667       throw new Error(
668               "IMPLEMENTATION ERROR: Cannot recover vamsas object mappings - no backup was made.");
669     }
670     jv2vobj.clear();
671     Iterator el = _backup_jv2vobj.entrySet().iterator();
672     while (el.hasNext())
673     {
674       java.util.Map.Entry mp = (java.util.Map.Entry) el.next();
675       jv2vobj.put(mp.getKey(), mp.getValue());
676     }
677     el = _backup_vobj2jv.entrySet().iterator();
678     while (el.hasNext())
679     {
680       java.util.Map.Entry mp = (java.util.Map.Entry) el.next();
681       vobj2jv.put(mp.getKey(), mp.getValue());
682     }
683   }
684
685   private boolean joinedSession = false;
686
687   private VamsasListener picker = null;
688
689   private SelectionListener selecter;
690
691   private void startSession()
692   {
693     if (inSession())
694     {
695       try
696       {
697         vclient.joinSession();
698         joinedSession = true;
699       } catch (Exception e)
700       {
701         // Complain to GUI
702         Cache.log.error("Failed to join vamsas session.", e);
703         vclient = null;
704       }
705       try
706       {
707         final IPickManager pm = vclient.getPickManager();
708         final StructureSelectionManager ssm = StructureSelectionManager
709                 .getStructureSelectionManager();
710         final SelectionSource me = this;
711         pm.registerMessageHandler(new IMessageHandler()
712         {
713           String last = null;
714
715           public void handleMessage(Message message)
716           {
717             if (vobj2jv == null)
718             {
719               // we are not in a session yet.
720               return;
721             }
722             if (message instanceof MouseOverMessage)
723             {
724               MouseOverMessage mm = (MouseOverMessage) message;
725               String mstring = mm.getVorbaID() + " " + mm.getPosition();
726               if (last != null && mstring.equals(last))
727               {
728                 return;
729               }
730               // if (Cache.log.isDebugEnabled())
731               // {
732               // Cache.log.debug("Received MouseOverMessage "+mm.getVorbaID()+"
733               // "+mm.getPosition());
734               // }
735               Object jvobj = vobj2jv.get(mm.getVorbaID());
736               if (jvobj != null && jvobj instanceof SequenceI)
737               {
738                 last = mstring;
739                 // Cache.log.debug("Handling Mouse over "+mm.getVorbaID()+"
740                 // bound to "+jvobj+" at "+mm.getPosition());
741                 // position is character position in aligned sequence
742                 ssm.mouseOverVamsasSequence((SequenceI) jvobj, mm
743                         .getPosition());
744               }
745             }
746             if (message instanceof uk.ac.vamsas.client.picking.SelectionMessage)
747             {
748               // we only care about AlignmentSequence selections
749               SelectionMessage sm = (SelectionMessage) message;
750               sm.validate();
751               System.err.println("Received\n"+sm.getRawMessage());
752               Object[] jvobjs = sm.getVorbaIDs()==null ? null : new Object[sm.getVorbaIDs().length];
753               if (jvobjs==null)
754               {
755                 // TODO: rationalise : can only clear a selection over a referred to object
756                 ssm.sendSelection(null,null,me);
757                 return;
758               }
759               Class type = null;
760               boolean send = true;
761               for (int o = 0; o < jvobjs.length; o++)
762               {
763                 jvobjs[o] = vobj2jv.get(sm.getVorbaIDs()[o]);
764                 if (jvobjs[o] == null)
765                 {
766                   // can't cope with selections for unmapped objects
767                   continue;
768                 }
769                 if (type == null)
770                 {
771                   type = jvobjs[o].getClass();
772                 }
773                 ;
774                 if (type != jvobjs[o].getClass())
775                 {
776                   send=false;
777                   // discard - can't cope with selections over mixed objects
778                   continue;
779                 }
780               }
781               SequenceGroup jselection = null;
782               ColumnSelection colsel = null;
783               if (type == jalview.datamodel.Alignment.class)
784               {
785                 if (jvobjs.length == 1)
786                 {
787                   // TODO if (sm.isNone())// send a message to select the specified columns over the
788                   // given
789                   // alignment
790
791                   send = true;
792                 }
793               }
794               if (type == jalview.datamodel.Sequence.class)
795               {
796
797                 SequenceI seq;
798                 boolean aligned = ((jalview.datamodel.Sequence) jvobjs[0])
799                         .getDatasetSequence() != null;
800                 int maxWidth=0;
801                 if (aligned)
802                 {
803                   jselection = new SequenceGroup();
804                   jselection.addSequence(seq =
805                         (jalview.datamodel.Sequence) jvobjs[0], false);
806                   maxWidth = seq.getLength();
807                 }
808                 for (int c = 1; aligned && jvobjs.length > 1 && c < jvobjs.length; c++)
809                 {
810                   if (((jalview.datamodel.Sequence) jvobjs[c])
811                           .getDatasetSequence() == null)
812                   {
813                     aligned = false;
814                     continue;
815                   }
816                   else
817                   {
818                     jselection.addSequence(
819                             seq = (jalview.datamodel.Sequence) jvobjs[c], false);
820                     if (maxWidth<seq.getLength())
821                     {
822                       maxWidth = seq.getLength();
823                     }
824                     
825                   }
826                 }
827                 if (!aligned)
828                 {
829                   jselection = null;
830                   // if cardinality is greater than one then verify all
831                   // sequences are alignment sequences.
832                   if (jvobjs.length == 1)
833                   {
834                     // find all instances of this dataset sequence in the
835                     // displayed alignments containing the associated range and
836                     // select them.
837                   }
838                 }
839                 else
840                 {
841                   jselection.setStartRes(0);
842                   jselection.setEndRes(maxWidth);
843                   // locate the alignment containing the given sequences and
844                   // select the associated ranges on them.
845                   if (sm.getRanges() != null)
846                   {
847                     int[] prange = uk.ac.vamsas.objects.utils.Range.getBounds(sm.getRanges());
848                     boolean rangeset=false;
849                     colsel = new ColumnSelection();
850                     prange = uk.ac.vamsas.objects.utils.Range.getIntervals(sm.getRanges());
851                     for (int p=0;p<prange.length;p+=2)
852                     {
853                       int d = (prange[p]<=prange[p+1]) ? 1 : -1;
854                       if (!rangeset)
855                       {
856                         if (jselection!=null)
857                         {
858                           // set the bounds of the selected area using the first interval.
859                           jselection.setStartRes(prange[p]-1);
860                           jselection.setEndRes(prange[p+1]-1);
861                           rangeset=true;
862                         }
863                       } else {
864                         // try to join up adjacent columns to make a larger selection
865                         // lower and upper bounds
866                         int l=(d<0) ? 1 : 0;
867                         int u=(d>0) ? 1 : 0;
868                         
869                         if (jselection.getStartRes()>0 && prange[p+l]==jselection.getStartRes())
870                         {
871                           jselection.setStartRes(prange[p+l]-1);
872                         }
873                         if (jselection.getEndRes()<=maxWidth && prange[p+u]==(jselection.getEndRes()+2))
874                         {
875                           jselection.setEndRes(prange[p+u]-1);
876                         }
877                       }
878                       // mark all the columns in the range.
879                       for (int sr=prange[p],er=prange[p+1],de=er+d; sr!=de; sr+=d)
880                       {
881                         colsel.addElement(sr-1);
882                       }
883                     }
884                   }
885                   send = true;
886                 }
887               }
888               if (send)
889               {
890                 ssm.sendSelection(jselection, colsel, me);
891               }
892               // discard message.
893               for (int c = 0; c < jvobjs.length; c++)
894               {
895                 jvobjs[c] = null;
896               }
897               ;
898               jvobjs = null;
899               return;
900             }
901           }
902         });
903         picker = new VamsasListener()
904         {
905           SequenceI last = null;
906
907           int i = -1;
908
909           public void mouseOver(SequenceI seq, int index)
910           {
911             if (jv2vobj == null)
912               return;
913             if (seq != last || i != index)
914             {
915               VorbaId v = (VorbaId) jv2vobj.get(seq);
916               if (v != null)
917               {
918                 Cache.log.debug("Mouse over " + v.getId() + " bound to "
919                         + seq + " at " + index);
920                 last = seq;
921                 i = index;
922                 MouseOverMessage message = new MouseOverMessage(v.getId(),
923                         index);
924                 pm.sendMessage(message);
925               }
926             }
927           }
928         };
929         selecter = new SelectionListener()
930         {
931
932           public void selection(SequenceGroup seqsel,
933                   ColumnSelection colsel, SelectionSource source)
934           {
935             if (source != me)
936             {
937               AlignmentI visal=null;
938               if (source instanceof AlignViewport)
939               {
940                 visal = ((AlignViewport) source).getAlignment();
941               }
942               SelectionMessage sm = null;
943               if ((seqsel == null || seqsel.getSize() == 0)
944                       && (colsel == null || colsel.getSelected() == null || colsel
945                               .getSelected().size() == 0))
946               {
947                 if (source instanceof AlignViewport)
948                 {
949                   // the empty selection.
950                   sm = new SelectionMessage("jalview", new String[] { ((AlignViewport)source).getSequenceSetId()}, null, true);
951                 } else {
952                   // the empty selection.
953                   sm = new SelectionMessage("jalview", null, null, true);
954                 }
955               }
956               else
957               {
958                 String[] vobj = new String[seqsel.getSize()];
959                 int o = 0;
960                 Enumeration sels = seqsel.getSequences(null).elements();
961                 while (sels.hasMoreElements())
962                 {
963                   SequenceI sel = (SequenceI) sels.nextElement();
964                   VorbaId v = (VorbaId) jv2vobj.get(sel);
965                   if (v != null)
966                   {
967                     vobj[o++] = v.toString();
968                   }
969                 }
970                 if (o < vobj.length)
971                 {
972                   String t[] = vobj;
973                   vobj = new String[o];
974                   System.arraycopy(t, 0, vobj, 0, o);
975                   t = null;
976                 }
977                 Input range = null;
978                 if (seqsel!=null && colsel != null)
979                 {
980                   // deparse the colsel into positions on the vamsas alignment
981                   // sequences
982                   range = new Input();
983                   if (colsel.getSelected()!=null && colsel.getSelected().size()>0 && visal!=null && seqsel.getSize()==visal.getHeight())
984                   {
985                     // gather selected columns outwith the sequence positions too
986                     Enumeration cols = colsel.getSelected().elements();
987                     while (cols.hasMoreElements())
988                     {
989                       int ival = ((Integer) cols.nextElement()).intValue();
990                       Pos p = new Pos();
991                       p.setI(ival+1);
992                       range.addPos(p);
993                     }
994                   } else {
995                     int[] intervals = colsel.getVisibleContigs(seqsel.getStartRes(), seqsel.getEndRes()+1);
996                     for (int iv=0;iv<intervals.length; iv+=2)
997                     {
998                       Seg s = new Seg();
999                       s.setStart(intervals[iv]+1); // vamsas indices begin at 1, not zero.
1000                       s.setEnd(intervals[iv+1]+1);
1001                       s.setInclusive(true);
1002                       range.addSeg(s);
1003                     }
1004                   }
1005                 }
1006                 sm = new SelectionMessage("jalview", vobj, range);
1007               }
1008               sm.validate(); // debug
1009               Cache.log.debug("Pick Message\n"+sm.getRawMessage());
1010               pm.sendMessage(sm);
1011             }
1012           }
1013
1014         };
1015         ssm.addStructureViewerListener(picker); // better method here
1016         ssm.addSelectionListener(selecter);
1017       } catch (Exception e)
1018       {
1019         Cache.log.error("Failed to init Vamsas Picking", e);
1020       }
1021     }
1022   }
1023 }