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