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