JAL-3210 Improvements to eclipse detection. New src tree and SwingJS updated from...
[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.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.JalviewApp;
26 import jalview.api.SequenceRenderer;
27 import jalview.appletgui.AlignFrame;
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
74         implements JsCallBack, StructureListener
75 {
76
77   String _listenerfn;
78
79   public String[] modelSet;
80
81   public MouseOverStructureListener(JalviewApp app, String listener,
82           String[] modelList, boolean debug)
83   {
84     super(app, debug);
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[] getStructureFiles()
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[]
156                 { "mouseover", "" + atom.getPdbFile(), "" + atom.getChain(),
157                     "" + (atom.getPdbResNum()), "" + 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 source)
169   {
170     StructureSelectionManager ssm = StructureSelectionManager
171             .getStructureSelectionManager(
172                     jvlite.getStructureSelectionManagerProvider());
173
174     if (debug)
175     {
176       System.err.println(
177               this.getClass().getName() + " modelSet[0]: " + modelSet[0]);
178       ssm.reportMapping();
179     }
180
181     if (source instanceof AlignmentViewPanel)
182     {
183       AlignmentViewPanel panel = (AlignmentViewPanel) source;
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 = panel.getSequenceRenderer();
208       AlignViewportI vp = panel.getAlignViewport();
209       jalview.renderer.seqfeatures.FeatureRenderer fr = vp
210               .isShowSequenceFeatures() ? jvlite.getNewFeatureRenderer(vp)
211               : null;
212       if (fr != null)
213       {
214         fr.transferSettings(panel.getFeatureRenderer());
215       }
216
217       // Form a colour command from the given alignment panel for each distinct
218       // structure
219       ArrayList<String[]> ccomands = new ArrayList<>();
220       ArrayList<String> pdbfn = new ArrayList<>();
221       StructureMappingcommandSet[] colcommands = JmolCommands
222               .getColourBySequenceCommand(ssm, modelSet, sequence, sr,
223                       (AlignmentViewPanel) source);
224       if (colcommands == null)
225       {
226         return;
227       }
228       int sz = 0;
229       for (jalview.structure.StructureMappingcommandSet ccset : colcommands)
230       {
231         sz += ccset.commands.length;
232         ccomands.add(ccset.commands);
233         pdbfn.add(ccset.mapping);
234       }
235
236       String ccomandset[] = new String[sz];
237       sz = 0;
238       for (String[] ccset : ccomands)
239       {
240         System.arraycopy(ccset, 0, ccomandset, sz, ccset.length);
241         sz += ccset.length;
242       }
243       String mclass = "colourstruct";
244       String mhandle = vp.getViewId();
245       if (isJsMessageSetChanged(mclass, mhandle, ccomandset, jvlite))
246       {
247         setJsMessageSet(mclass, mhandle, ccomandset, jvlite);
248         // and notify javascript handler
249         String st[] = new String[] { mclass, mhandle,
250             "" + ccomandset.length, jvlite.arrayToSeparatorList(
251                     pdbfn.toArray(new String[pdbfn.size()])) };
252         executeJavascriptFunction(true, st);
253       }
254       /*
255        * new Thread(new Runnable() { public void run() { // and send to
256        * javascript handler String st[] = new String[0]; int i = 0; for (String
257        * colcommand : colcommands) { // do sync execution for each chunk try {
258        * executeJavascriptFunction( false, _listenerfn, st = new String[] {
259        * "colourstruct", "" + ((jalview.appletgui.AlignmentPanel) source).av
260        * .getViewId(), handle, "" }); } catch (Exception ex) {
261        * System.err.println("Couldn't execute callback with " + _listenerfn +
262        * " using args { " + st[0] + ", " + st[1] + ", " + st[2] + "," + st[3] +
263        * "\n"); ex.printStackTrace();
264        * 
265        * } } } }).start();
266        */
267     }
268
269     jvlite.updateColoursFromMouseOver(source, this);
270   }
271
272   @Override
273   public AlignFrame getAlignFrame()
274   {
275     // associated with all alignframes, always.
276     return null;
277   }
278
279   @Override
280   public String getListenerFunction()
281   {
282     return _listenerfn;
283   }
284
285   @Override
286   public void releaseReferences(Object svl)
287   {
288
289     // TODO Auto-generated method stub
290
291   }
292
293   @Override
294   public boolean isListeningFor(SequenceI seq)
295   {
296     return true;
297   }
298
299   public void executeJavascriptFunction(boolean b, String[] st)
300   {
301     try
302     {
303       executeJavascriptFunction(true, _listenerfn, st);
304     } catch (Exception ex)
305     {
306       System.err.println("Couldn't execute callback with " + _listenerfn
307               + " using args { " + st[0] + ", " + st[1] + ", " + st[2] + ","
308               + st[3] + "}"); // + ","+st[4]+"\n");
309       ex.printStackTrace();
310
311     }
312   }
313
314 }