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