Merge branch 'Jalview-JS/develop.JAL-3446.ctrlDown' into
[jalview.git] / src / jalview / appletgui / js / MouseOverStructureListener.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.appletgui.js;
22
23 import jalview.api.AlignmentViewPanel;
24 import jalview.api.FeatureRenderer;
25 import jalview.api.SequenceRenderer;
26 import jalview.appletgui.AlignFrame;
27 import jalview.appletgui.js.JsCallBack;
28 import jalview.bin.JalviewLite;
29 import jalview.datamodel.SequenceI;
30 import jalview.ext.jmol.JmolCommands;
31 import jalview.structure.AtomSpec;
32 import jalview.structure.StructureListener;
33 import jalview.structure.StructureMapping;
34 import jalview.structure.StructureMappingcommandSet;
35 import jalview.structure.StructureSelectionManager;
36 import jalview.util.HttpUtils;
37
38 import java.util.ArrayList;
39 import java.util.List;
40
41 /**
42  * Propagate events involving PDB structures associated with sequences to a
43  * javascript function. Generally, the javascript handler is called with a
44  * series of arguments like (eventname, ... ). As of Jalview 2.7, the following
45  * different types of events are supported:
46  * <ul>
47  * <li>mouseover: javascript function called with arguments
48  * 
49  * <pre>
50  * ['mouseover',String(pdb file URI), String(pdb file chain ID), String(residue
51  * number moused over), String(atom index corresponding to residue)]
52  * </pre>
53  * 
54  * </li>
55  * <li>colourstruct: javascript function called with arguments
56  * 
57  * <pre>
58  * ['colourstruct',String(alignment view id),String(number of javascript message
59  * chunks to collect),String(length of first chunk in set of messages - or zero
60  * for null message)]
61  * </pre>
62  * 
63  * <br>
64  * The message contains a series of Jmol script commands that will colour
65  * structures according to their associated sequences in the current view. Use
66  * jalview
67  * .javascript.JalviewLiteJsApi.getJsMessage('colourstruct',String(alignment
68  * view id)) to retrieve successive chunks of the message.</li>
69  * </ul>
70  * 
71  * @author Jim Procter (jprocter)
72  * 
73  */
74 public class MouseOverStructureListener extends JSFunctionExec
75         implements JsCallBack, StructureListener
76 {
77
78   String _listenerfn;
79
80   String[] modelSet;
81
82   public MouseOverStructureListener(JalviewLite jalviewLite,
83           String listener, String[] modelList)
84   {
85     super(jalviewLite);
86     _listenerfn = listener;
87     modelSet = modelList;
88     if (modelSet != null)
89     {
90       for (int i = 0; i < modelSet.length; i++)
91       {
92         modelSet[i] = resolveModelFile(modelSet[i]);
93       }
94     }
95   }
96
97   /**
98    * Returns the first out of: file, file prefixed by document base, or file
99    * prefixed by codebase which can be resolved to a valid URL. If none can,
100    * returns the input parameter value.
101    * 
102    * @param file
103    */
104   public String resolveModelFile(String file)
105   {
106     // TODO reuse JalviewLite.LoadingThread.addProtocol instead
107     if (HttpUtils.isValidUrl(file))
108     {
109       return file;
110     }
111
112     String db = jvlite.getDocumentBase().toString();
113     db = db.substring(0, db.lastIndexOf("/"));
114     String docBaseFile = db + "/" + file;
115     if (HttpUtils.isValidUrl(docBaseFile))
116     {
117       return docBaseFile;
118     }
119
120     String cb = jvlite.getCodeBase() + file;
121     if (HttpUtils.isValidUrl(cb))
122     {
123       return cb;
124     }
125
126     return file;
127   }
128
129   @Override
130   public String[] getStructureFiles()
131   {
132     return modelSet;
133   }
134
135   public void mouseOverStructure(int atomIndex, String strInfo)
136   {
137
138     // StructureSelectionManager.getStructureSelectionManager().mouseOverStructure(atomIndex,
139     // chain, pdbfile)
140     // TODO Auto-generated method stub
141
142   }
143
144   @Override
145   public void highlightAtoms(List<AtomSpec> atoms)
146   {
147     for (AtomSpec atom : atoms)
148     {
149       try
150       {
151         // TODO is this right? StructureSelectionManager passes pdbFile as the
152         // field that is interpreted (in 2.8.2) as pdbId?
153         // JBPComment: yep - this is right! the Javascript harness uses the
154         // absolute pdbFile URI to locate the PDB file in the external viewer
155         executeJavascriptFunction(_listenerfn,
156                 new String[]
157                 { "mouseover", "" + atom.getPdbFile(), "" + atom.getChain(),
158                     "" + (atom.getPdbResNum()), "" + atom.getAtomIndex() });
159       } catch (Exception ex)
160       {
161         System.err.println("Couldn't execute callback with " + _listenerfn
162                 + " for atomSpec: " + atom);
163         ex.printStackTrace();
164       }
165     }
166   }
167
168   @Override
169   public synchronized void updateColours(Object srce)
170   {
171     final Object source = srce;
172     StructureSelectionManager ssm = StructureSelectionManager
173             .getStructureSelectionManager(jvlite);
174
175     if (JalviewLite.debug)
176     {
177       System.err.println(
178               this.getClass().getName() + " modelSet[0]: " + modelSet[0]);
179       ssm.reportMapping();
180     }
181
182     if (source instanceof jalview.api.AlignmentViewPanel)
183     {
184       SequenceI[][] sequence = new SequenceI[modelSet.length][];
185       for (int m = 0; m < modelSet.length; m++)
186       {
187         StructureMapping[] sm = ssm.getMapping(modelSet[m]);
188         if (sm != null && sm.length > 0)
189         {
190           sequence[m] = new SequenceI[sm.length];
191           for (int i = 0; i < sm.length; i++)
192           {
193             sequence[m][i] = sm[i].getSequence();
194           }
195         }
196         else
197         {
198           sequence[m] = new SequenceI[0];
199         }
200         // if (jvlite.debug)
201         // {
202         // System.err.println("Mapped '" + modelSet[m] + "' to "
203         // + sequence[m].length + " sequences.");
204         // }
205       }
206
207       SequenceRenderer sr = ((jalview.appletgui.AlignmentPanel) source)
208               .getSequenceRenderer();
209       FeatureRenderer fr = ((jalview.appletgui.AlignmentPanel) source).av
210               .isShowSequenceFeatures()
211                       ? new jalview.appletgui.FeatureRenderer(
212                               ((jalview.appletgui.AlignmentPanel) source).av)
213                       : null;
214       if (fr != null)
215       {
216         ((jalview.appletgui.FeatureRenderer) fr).transferSettings(
217                 ((jalview.appletgui.AlignmentPanel) source)
218                         .getFeatureRenderer());
219       }
220       ;
221
222       // Form a colour command from the given alignment panel for each distinct
223       // structure
224       ArrayList<String[]> ccomands = new ArrayList<String[]>();
225       ArrayList<String> pdbfn = new ArrayList<String>();
226       StructureMappingcommandSet[] colcommands = JmolCommands
227               .getColourBySequenceCommand(ssm, modelSet, sequence, sr,
228                       (AlignmentViewPanel) source);
229       if (colcommands == null)
230       {
231         return;
232       }
233       int sz = 0;
234       for (jalview.structure.StructureMappingcommandSet ccset : colcommands)
235       {
236         sz += ccset.commands.length;
237         ccomands.add(ccset.commands);
238         pdbfn.add(ccset.mapping);
239       }
240
241       String mclass, mhandle;
242       String ccomandset[] = new String[sz];
243       sz = 0;
244       for (String[] ccset : ccomands)
245       {
246         System.arraycopy(ccset, 0, ccomandset, sz, ccset.length);
247         sz += ccset.length;
248       }
249       if (jvlite.isJsMessageSetChanged(mclass = "colourstruct",
250               mhandle = ((jalview.appletgui.AlignmentPanel) source).av
251                       .getViewId(),
252               ccomandset))
253       {
254         jvlite.setJsMessageSet(mclass, mhandle, ccomandset);
255         // and notify javascript handler
256         String st[] = new String[] { "colourstruct",
257             "" + ((jalview.appletgui.AlignmentPanel) source).av.getViewId(),
258             "" + ccomandset.length, jvlite.arrayToSeparatorList(
259                     pdbfn.toArray(new String[pdbfn.size()])) };
260         try
261         {
262           executeJavascriptFunction(true, _listenerfn, st);
263         } catch (Exception ex)
264         {
265           System.err.println("Couldn't execute callback with " + _listenerfn
266                   + " using args { " + st[0] + ", " + st[1] + ", " + st[2]
267                   + "," + st[3] + "}"); // + ","+st[4]+"\n");
268           ex.printStackTrace();
269
270         }
271       }
272       /*
273        * new Thread(new Runnable() { public void run() { // and send to
274        * javascript handler String st[] = new String[0]; int i = 0; for (String
275        * colcommand : colcommands) { // do sync execution for each chunk try {
276        * executeJavascriptFunction( false, _listenerfn, st = new String[] {
277        * "colourstruct", "" + ((jalview.appletgui.AlignmentPanel) source).av
278        * .getViewId(), handle, "" }); } catch (Exception ex) {
279        * System.err.println("Couldn't execute callback with " + _listenerfn +
280        * " using args { " + st[0] + ", " + st[1] + ", " + st[2] + "," + st[3] +
281        * "\n"); ex.printStackTrace();
282        * 
283        * } } } }).start();
284        */
285     }
286
287   }
288
289   @Override
290   public AlignFrame getAlignFrame()
291   {
292     // associated with all alignframes, always.
293     return null;
294   }
295
296   @Override
297   public String getListenerFunction()
298   {
299     return _listenerfn;
300   }
301
302   @Override
303   public void releaseReferences(Object svl)
304   {
305
306     // TODO Auto-generated method stub
307
308   }
309
310   @Override
311   public boolean isListeningFor(SequenceI seq)
312   {
313     return true;
314   }
315
316 }