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