JAL-4083 only set globalUseScriptWait if needed
[jalview.git] / src / jalview / ext / jmol / JalviewJmolBinding.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.ext.jmol;
22
23 import java.awt.Container;
24 import java.awt.event.ComponentEvent;
25 import java.awt.event.ComponentListener;
26 import java.io.File;
27 import java.net.URL;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.StringTokenizer;
32 import java.util.Vector;
33
34 import javax.swing.SwingUtilities;
35
36 import org.jmol.adapter.smarter.SmarterJmolAdapter;
37 import org.jmol.api.JmolAppConsoleInterface;
38 import org.jmol.api.JmolSelectionListener;
39 import org.jmol.api.JmolStatusListener;
40 import org.jmol.api.JmolViewer;
41 import org.jmol.c.CBK;
42 import org.jmol.viewer.Viewer;
43
44 import jalview.api.AlignmentViewPanel;
45 import jalview.api.FeatureRenderer;
46 import jalview.api.FeatureSettingsModelI;
47 import jalview.api.SequenceRenderer;
48 import jalview.bin.Console;
49 import jalview.datamodel.PDBEntry;
50 import jalview.datamodel.SequenceI;
51 import jalview.gui.AppJmol;
52 import jalview.gui.IProgressIndicator;
53 import jalview.gui.StructureViewer.ViewerType;
54 import jalview.io.DataSourceType;
55 import jalview.io.StructureFile;
56 import jalview.structure.AtomSpec;
57 import jalview.structure.StructureCommand;
58 import jalview.structure.StructureCommandI;
59 import jalview.structure.StructureSelectionManager;
60 import jalview.structures.models.AAStructureBindingModel;
61 import jalview.ws.dbsources.Pdb;
62 import javajs.util.BS;
63
64 public abstract class JalviewJmolBinding extends AAStructureBindingModel
65         implements JmolStatusListener, JmolSelectionListener,
66         ComponentListener
67 {
68   private String lastMessage;
69
70   /*
71    * when true, try to search the associated datamodel for sequences that are
72    * associated with any unknown structures in the Jmol view.
73    */
74   private boolean associateNewStructs = false;
75
76   private Vector<String> atomsPicked = new Vector<>();
77
78   private String lastCommand;
79
80   private boolean loadedInline;
81
82   private StringBuffer resetLastRes = new StringBuffer();
83
84   public Viewer jmolViewer;
85
86   private boolean globalUseScriptWait = false;
87
88   public JalviewJmolBinding(StructureSelectionManager ssm,
89           PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
90           DataSourceType protocol)
91   {
92     super(ssm, pdbentry, sequenceIs, protocol);
93     setStructureCommands(new JmolCommands());
94     /*
95      * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
96      * "jalviewJmol", ap.av.applet .getDocumentBase(), ap.av.applet.getCodeBase(),
97      * "", this);
98      * 
99      * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
100      */
101   }
102
103   public JalviewJmolBinding(StructureSelectionManager ssm,
104           SequenceI[][] seqs, Viewer theViewer)
105   {
106     super(ssm, seqs);
107
108     jmolViewer = theViewer;
109     jmolViewer.setJmolStatusListener(this);
110     jmolViewer.addSelectionListener(this);
111     setStructureCommands(new JmolCommands());
112   }
113
114   /**
115    * construct a title string for the viewer window based on the data jalview
116    * knows about
117    * 
118    * @return
119    */
120   public String getViewerTitle()
121   {
122     return getViewerTitle("Jmol", true);
123   }
124
125   private String jmolScript(String script)
126   {
127     return jmolScript(script, false);
128   }
129
130   private String jmolScript(String script, boolean useScriptWait)
131   {
132     Console.debug(">>Jmol>> " + script);
133     String s;
134     if (globalUseScriptWait || useScriptWait)
135     {
136       s = jmolViewer.scriptWait(script);
137     }
138     else
139     {
140       s = jmolViewer.evalStringQuiet(script); // scriptWait(script); BH
141     }
142     Console.debug("<<Jmol<< " + s);
143
144     return s;
145   }
146
147   @Override
148   public List<String> executeCommand(StructureCommandI command,
149           boolean getReply)
150   {
151     if (command == null)
152     {
153       return null;
154     }
155     String cmd = command.getCommand();
156     jmolHistory(false);
157     if (lastCommand == null || !lastCommand.equals(cmd))
158     {
159       jmolScript(cmd + "\n");
160     }
161     jmolHistory(true);
162     lastCommand = cmd;
163     return null;
164   }
165
166   public void createImage(String file, String type, int quality)
167   {
168     System.out.println("JMOL CREATE IMAGE");
169   }
170
171   @Override
172   public String createImage(String fileName, String type,
173           Object textOrBytes, int quality)
174   {
175     System.out.println("JMOL CREATE IMAGE");
176     return null;
177   }
178
179   @Override
180   public String eval(String strEval)
181   {
182     // System.out.println(strEval);
183     // "# 'eval' is implemented only for the applet.";
184     return null;
185   }
186
187   // End StructureListener
188   // //////////////////////////
189
190   @Override
191   public float[][] functionXY(String functionName, int x, int y)
192   {
193     return null;
194   }
195
196   @Override
197   public float[][][] functionXYZ(String functionName, int nx, int ny,
198           int nz)
199   {
200     // TODO Auto-generated method stub
201     return null;
202   }
203
204   /**
205    * map between index of model filename returned from getPdbFile and the first
206    * index of models from this file in the viewer. Note - this is not trimmed -
207    * use getPdbFile to get number of unique models.
208    */
209   private int _modelFileNameMap[];
210
211   @Override
212   public synchronized String[] getStructureFiles()
213   {
214     if (jmolViewer == null)
215     {
216       return new String[0];
217     }
218
219     if (modelFileNames == null)
220     {
221       int modelCount = jmolViewer.ms.mc;
222       String filePath = null;
223       List<String> mset = new ArrayList<>();
224       for (int i = 0; i < modelCount; ++i)
225       {
226         /*
227          * defensive check for null as getModelFileName can return null even when model
228          * count ms.mc is > 0
229          */
230         filePath = jmolViewer.ms.getModelFileName(i);
231         if (filePath != null && !mset.contains(filePath))
232         {
233           mset.add(filePath);
234         }
235       }
236       if (!mset.isEmpty())
237       {
238         modelFileNames = mset.toArray(new String[mset.size()]);
239       }
240     }
241
242     return modelFileNames;
243   }
244
245   /**
246    * map from string to applet
247    */
248   @Override
249   public Map<String, Object> getRegistryInfo()
250   {
251     // TODO Auto-generated method stub
252     return null;
253   }
254
255   // ///////////////////////////////
256   // JmolStatusListener
257
258   public void handlePopupMenu(int x, int y)
259   {
260     // jmolpopup.show(x, y);
261     // jmolpopup.jpiShow(x, y);
262   }
263
264   /**
265    * Highlight zero, one or more atoms on the structure
266    */
267   @Override
268   public void highlightAtoms(List<AtomSpec> atoms)
269   {
270     if (atoms != null)
271     {
272       // we set the globalUseScriptWait (and reset to previous value) only if
273       // we're going to use it here
274       boolean useScriptWait = atoms.size() > 1;
275       boolean prevUseScriptWait = false;
276       if (useScriptWait)
277         prevUseScriptWait = setGlobalUseScriptWait(useScriptWait);
278       if (resetLastRes.length() > 0)
279       {
280         jmolScript(resetLastRes.toString(), useScriptWait);
281         resetLastRes.setLength(0);
282       }
283       for (AtomSpec atom : atoms)
284       {
285         highlightAtom(atom.getAtomIndex(), atom.getPdbResNum(),
286                 atom.getChain(), atom.getPdbFile(), useScriptWait);
287       }
288       if (useScriptWait)
289         setGlobalUseScriptWait(prevUseScriptWait);
290     }
291   }
292
293   // jmol/ssm only
294   public void highlightAtom(int atomIndex, int pdbResNum, String chain,
295           String pdbfile, boolean useScriptWait)
296   {
297     String modelId = getModelIdForFile(pdbfile);
298     if (modelId.isEmpty())
299     {
300       return;
301     }
302
303     jmolHistory(false);
304
305     StringBuilder selection = new StringBuilder(32);
306     StringBuilder cmd = new StringBuilder(64);
307     selection.append("select ").append(String.valueOf(pdbResNum));
308     selection.append(":");
309     if (!chain.equals(" "))
310     {
311       selection.append(chain);
312     }
313     selection.append(" /").append(modelId);
314
315     cmd.append(selection).append(";wireframe 100;").append(selection)
316             .append(" and not hetero;").append("spacefill 200;select none");
317
318     resetLastRes.append(selection).append(";wireframe 0;").append(selection)
319             .append(" and not hetero; spacefill 0;");
320
321     jmolScript(cmd.toString(), useScriptWait);
322     jmolHistory(true);
323   }
324
325   private boolean debug = true;
326
327   private void jmolHistory(boolean enable)
328   {
329     jmolScript("History " + ((debug || enable) ? "on" : "off"));
330   }
331
332   public void loadInline(String string)
333   {
334     loadedInline = true;
335     // TODO: re JAL-623
336     // viewer.loadInline(strModel, isAppend);
337     // could do this:
338     // construct fake fullPathName and fileName so we can identify the file
339     // later.
340     // Then, construct pass a reader for the string to Jmol.
341     // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
342     // fileName, null, reader, false, null, null, 0);
343     jmolViewer.openStringInline(string);
344   }
345
346   protected void mouseOverStructure(int atomIndex, final String strInfo)
347   {
348     int pdbResNum;
349     int alocsep = strInfo.indexOf("^");
350     int mdlSep = strInfo.indexOf("/");
351     int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
352
353     if (chainSeparator == -1)
354     {
355       chainSeparator = strInfo.indexOf(".");
356       if (mdlSep > -1 && mdlSep < chainSeparator)
357       {
358         chainSeparator1 = chainSeparator;
359         chainSeparator = mdlSep;
360       }
361     }
362     // handle insertion codes
363     if (alocsep != -1)
364     {
365       pdbResNum = Integer.parseInt(
366               strInfo.substring(strInfo.indexOf("]") + 1, alocsep));
367
368     }
369     else
370     {
371       pdbResNum = Integer.parseInt(
372               strInfo.substring(strInfo.indexOf("]") + 1, chainSeparator));
373     }
374     String chainId;
375
376     if (strInfo.indexOf(":") > -1)
377     {
378       chainId = strInfo.substring(strInfo.indexOf(":") + 1,
379               strInfo.indexOf("."));
380     }
381     else
382     {
383       chainId = " ";
384     }
385
386     String pdbfilename = modelFileNames[0]; // default is first model
387     if (mdlSep > -1)
388     {
389       if (chainSeparator1 == -1)
390       {
391         chainSeparator1 = strInfo.indexOf(".", mdlSep);
392       }
393       String mdlId = (chainSeparator1 > -1)
394               ? strInfo.substring(mdlSep + 1, chainSeparator1)
395               : strInfo.substring(mdlSep + 1);
396       try
397       {
398         // recover PDB filename for the model hovered over.
399         int mnumber = Integer.valueOf(mdlId).intValue() - 1;
400         if (_modelFileNameMap != null)
401         {
402           int _mp = _modelFileNameMap.length - 1;
403
404           while (mnumber < _modelFileNameMap[_mp])
405           {
406             _mp--;
407           }
408           pdbfilename = modelFileNames[_mp];
409         }
410         else
411         {
412           if (mnumber >= 0 && mnumber < modelFileNames.length)
413           {
414             pdbfilename = modelFileNames[mnumber];
415           }
416
417           if (pdbfilename == null)
418           {
419             pdbfilename = new File(jmolViewer.ms.getModelFileName(mnumber))
420                     .getAbsolutePath();
421           }
422         }
423       } catch (Exception e)
424       {
425       }
426     }
427
428     /*
429      * highlight position on alignment(s); if some text is returned, show this as a
430      * second line on the structure hover tooltip
431      */
432     String label = getSsm().mouseOverStructure(pdbResNum, chainId,
433             pdbfilename);
434     if (label != null)
435     {
436       // change comma to pipe separator (newline token for Jmol)
437       label = label.replace(',', '|');
438       StringTokenizer toks = new StringTokenizer(strInfo, " ");
439       StringBuilder sb = new StringBuilder();
440       sb.append("select ").append(String.valueOf(pdbResNum)).append(":")
441               .append(chainId).append("/1");
442       sb.append(";set hoverLabel \"").append(toks.nextToken()).append(" ")
443               .append(toks.nextToken());
444       sb.append("|").append(label).append("\"");
445       executeCommand(new StructureCommand(sb.toString()), false);
446     }
447   }
448
449   public void notifyAtomHovered(int atomIndex, String strInfo, String data)
450   {
451     if (strInfo.equals(lastMessage))
452     {
453       return;
454     }
455     lastMessage = strInfo;
456     if (data != null)
457     {
458       System.err.println("Ignoring additional hover info: " + data
459               + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
460     }
461     mouseOverStructure(atomIndex, strInfo);
462   }
463
464   /*
465    * { if (history != null && strStatus != null &&
466    * !strStatus.equals("Script completed")) { history.append("\n" + strStatus); }
467    * }
468    */
469
470   public void notifyAtomPicked(int atomIndex, String strInfo,
471           String strData)
472   {
473     /**
474      * this implements the toggle label behaviour copied from the original
475      * structure viewer, mc_view
476      */
477     if (strData != null)
478     {
479       System.err.println("Ignoring additional pick data string " + strData);
480     }
481     int chainSeparator = strInfo.indexOf(":");
482     int p = 0;
483     if (chainSeparator == -1)
484     {
485       chainSeparator = strInfo.indexOf(".");
486     }
487
488     String picked = strInfo.substring(strInfo.indexOf("]") + 1,
489             chainSeparator);
490     String mdlString = "";
491     if ((p = strInfo.indexOf(":")) > -1)
492     {
493       picked += strInfo.substring(p, strInfo.indexOf("."));
494     }
495
496     if ((p = strInfo.indexOf("/")) > -1)
497     {
498       mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
499     }
500     picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
501             + mdlString + "))";
502     jmolHistory(false);
503
504     if (!atomsPicked.contains(picked))
505     {
506       jmolScript("select " + picked + ";label %n %r:%c");
507       atomsPicked.addElement(picked);
508     }
509     else
510     {
511       jmolViewer.evalString("select " + picked + ";label off");
512       atomsPicked.removeElement(picked);
513     }
514     jmolHistory(true);
515     // TODO: in application this happens
516     //
517     // if (scriptWindow != null)
518     // {
519     // scriptWindow.sendConsoleMessage(strInfo);
520     // scriptWindow.sendConsoleMessage("\n");
521     // }
522
523   }
524
525   @Override
526   public void notifyCallback(CBK type, Object[] data)
527   {
528     /*
529      * ensure processed in AWT thread to avoid risk of deadlocks
530      */
531     SwingUtilities.invokeLater(new Runnable()
532     {
533
534       @Override
535       public void run()
536       {
537         processCallback(type, data);
538       }
539     });
540   }
541
542   /**
543    * Processes one callback notification from Jmol
544    * 
545    * @param type
546    * @param data
547    */
548   protected void processCallback(CBK type, Object[] data)
549   {
550     try
551     {
552       switch (type)
553       {
554       case LOADSTRUCT:
555         notifyFileLoaded((String) data[1], (String) data[2],
556                 (String) data[3], (String) data[4],
557                 ((Integer) data[5]).intValue());
558
559         break;
560       case PICK:
561         notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
562                 (String) data[0]);
563         // also highlight in alignment
564         // deliberate fall through
565       case HOVER:
566         notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
567                 (String) data[0]);
568         break;
569       case SCRIPT:
570         notifyScriptTermination((String) data[2],
571                 ((Integer) data[3]).intValue());
572         break;
573       case ECHO:
574         sendConsoleEcho((String) data[1]);
575         break;
576       case MESSAGE:
577         sendConsoleMessage(
578                 (data == null) ? ((String) null) : (String) data[1]);
579         break;
580       case ERROR:
581         // System.err.println("Ignoring error callback.");
582         break;
583       case SYNC:
584       case RESIZE:
585         refreshGUI();
586         break;
587       case MEASURE:
588
589       case CLICK:
590       default:
591         System.err.println(
592                 "Unhandled callback " + type + " " + data[1].toString());
593         break;
594       }
595     } catch (Exception e)
596     {
597       System.err.println("Squashed Jmol callback handler error:");
598       e.printStackTrace();
599     }
600   }
601
602   @Override
603   public boolean notifyEnabled(CBK callbackPick)
604   {
605     switch (callbackPick)
606     {
607     case ECHO:
608     case LOADSTRUCT:
609     case MEASURE:
610     case MESSAGE:
611     case PICK:
612     case SCRIPT:
613     case HOVER:
614     case ERROR:
615       return true;
616     default:
617       return false;
618     }
619   }
620
621   // incremented every time a load notification is successfully handled -
622   // lightweight mechanism for other threads to detect when they can start
623   // referrring to new structures.
624   private long loadNotifiesHandled = 0;
625
626   public long getLoadNotifiesHandled()
627   {
628     return loadNotifiesHandled;
629   }
630
631   public void notifyFileLoaded(String fullPathName, String fileName2,
632           String modelName, String errorMsg, int modelParts)
633   {
634     if (errorMsg != null)
635     {
636       fileLoadingError = errorMsg;
637       refreshGUI();
638       return;
639     }
640     // TODO: deal sensibly with models loaded inLine:
641     // modelName will be null, as will fullPathName.
642
643     // the rest of this routine ignores the arguments, and simply interrogates
644     // the Jmol view to find out what structures it contains, and adds them to
645     // the structure selection manager.
646     fileLoadingError = null;
647     String[] oldmodels = modelFileNames;
648     modelFileNames = null;
649     boolean notifyLoaded = false;
650     String[] modelfilenames = getStructureFiles();
651     if (modelfilenames == null)
652     {
653       // Jmol is still loading files!
654       return;
655     }
656     // first check if we've lost any structures
657     if (oldmodels != null && oldmodels.length > 0)
658     {
659       int oldm = 0;
660       for (int i = 0; i < oldmodels.length; i++)
661       {
662         for (int n = 0; n < modelfilenames.length; n++)
663         {
664           if (modelfilenames[n] == oldmodels[i])
665           {
666             oldmodels[i] = null;
667             break;
668           }
669         }
670         if (oldmodels[i] != null)
671         {
672           oldm++;
673         }
674       }
675       if (oldm > 0)
676       {
677         String[] oldmfn = new String[oldm];
678         oldm = 0;
679         for (int i = 0; i < oldmodels.length; i++)
680         {
681           if (oldmodels[i] != null)
682           {
683             oldmfn[oldm++] = oldmodels[i];
684           }
685         }
686         // deregister the Jmol instance for these structures - we'll add
687         // ourselves again at the end for the current structure set.
688         getSsm().removeStructureViewerListener(this, oldmfn);
689       }
690     }
691     refreshPdbEntries();
692     for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
693     {
694       String fileName = modelfilenames[modelnum];
695       boolean foundEntry = false;
696       StructureFile pdb = null;
697       String pdbfile = null;
698       // model was probably loaded inline - so check the pdb file hashcode
699       if (loadedInline)
700       {
701         // calculate essential attributes for the pdb data imported inline.
702         // prolly need to resolve modelnumber properly - for now just use our
703         // 'best guess'
704         pdbfile = jmolViewer.getData(
705                 "" + (1 + _modelFileNameMap[modelnum]) + ".0", "PDB");
706       }
707       // search pdbentries and sequences to find correct pdbentry for this
708       // model
709       for (int pe = 0; pe < getPdbCount(); pe++)
710       {
711         boolean matches = false;
712         addSequence(pe, getSequence()[pe]);
713         if (fileName == null)
714         {
715           if (false)
716           // see JAL-623 - need method of matching pasted data up
717           {
718             pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
719                     pdbfile, DataSourceType.PASTE, getIProgressIndicator());
720             getPdbEntry(modelnum).setFile("INLINE" + pdb.getId());
721             matches = true;
722             foundEntry = true;
723           }
724         }
725         else
726         {
727           File fl = new File(getPdbEntry(pe).getFile());
728           matches = fl.equals(new File(fileName));
729           if (matches)
730           {
731             foundEntry = true;
732             // TODO: Jmol can in principle retrieve from CLASSLOADER but
733             // this
734             // needs
735             // to be tested. See mantis bug
736             // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
737             DataSourceType protocol = DataSourceType.URL;
738             try
739             {
740               if (fl.exists())
741               {
742                 protocol = DataSourceType.FILE;
743               }
744             } catch (Exception e)
745             {
746             } catch (Error e)
747             {
748             }
749             // Explicitly map to the filename used by Jmol ;
750             pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
751                     fileName, protocol, getIProgressIndicator());
752             // pdbentry[pe].getFile(), protocol);
753
754           }
755         }
756         if (matches)
757         {
758           stashFoundChains(pdb, fileName);
759           notifyLoaded = true;
760         }
761       }
762
763       if (!foundEntry && associateNewStructs)
764       {
765         // this is a foreign pdb file that jalview doesn't know about - add
766         // it to the dataset and try to find a home - either on a matching
767         // sequence or as a new sequence.
768         String pdbcontent = jmolViewer.getData("/" + (modelnum + 1) + ".1",
769                 "PDB");
770         // parse pdb file into a chain, etc.
771         // locate best match for pdb in associated views and add mapping to
772         // ssm
773         // if properly registered then
774         notifyLoaded = true;
775
776       }
777     }
778     // FILE LOADED OK
779     // so finally, update the jmol bits and pieces
780     // if (jmolpopup != null)
781     // {
782     // // potential for deadlock here:
783     // // jmolpopup.updateComputedMenus();
784     // }
785     if (!isLoadingFromArchive())
786     {
787       jmolScript(
788               "model *; select backbone;restrict;cartoon;wireframe off;spacefill off");
789     }
790     // register ourselves as a listener and notify the gui that it needs to
791     // update itself.
792     getSsm().addStructureViewerListener(this);
793     if (notifyLoaded)
794     {
795       FeatureRenderer fr = getFeatureRenderer(null);
796       if (fr != null)
797       {
798         FeatureSettingsModelI colours = new Pdb().getFeatureColourScheme();
799         ((AppJmol) getViewer()).getAlignmentPanel().av
800                 .applyFeaturesStyle(colours);
801       }
802       refreshGUI();
803       loadNotifiesHandled++;
804     }
805     setLoadingFromArchive(false);
806   }
807
808   protected IProgressIndicator getIProgressIndicator()
809   {
810     return null;
811   }
812
813   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
814   {
815     notifyAtomPicked(iatom, strMeasure, null);
816   }
817
818   public abstract void notifyScriptTermination(String strStatus,
819           int msWalltime);
820
821   /**
822    * display a message echoed from the jmol viewer
823    * 
824    * @param strEcho
825    */
826   public abstract void sendConsoleEcho(String strEcho); /*
827                                                          * { showConsole(true);
828                                                          * 
829                                                          * history.append("\n" + strEcho); }
830                                                          */
831
832   // /End JmolStatusListener
833   // /////////////////////////////
834
835   /**
836    * @param strStatus
837    *          status message - usually the response received after a script
838    *          executed
839    */
840   public abstract void sendConsoleMessage(String strStatus);
841
842   @Override
843   public void setCallbackFunction(String callbackType,
844           String callbackFunction)
845   {
846     System.err.println("Ignoring set-callback request to associate "
847             + callbackType + " with function " + callbackFunction);
848
849   }
850
851   public void showHelp()
852   {
853     showUrl("http://wiki.jmol.org"
854     // BH 2018 "http://jmol.sourceforge.net/docs/JmolUserGuide/"
855             , "jmolHelp");
856   }
857
858   /**
859    * open the URL somehow
860    * 
861    * @param target
862    */
863   public abstract void showUrl(String url, String target);
864
865   /**
866    * called to show or hide the associated console window container.
867    * 
868    * @param show
869    */
870   public abstract void showConsole(boolean show);
871
872   public static Viewer getJmolData(JmolParser jmolParser)
873   {
874     return (Viewer) JmolViewer.allocateViewer(null, null, null, null, null,
875             "-x -o -n", jmolParser);
876   }
877
878   /**
879    * 
880    * 
881    * 
882    * @param renderPanel
883    * @param jmolfileio
884    *          - when true will initialise jmol's file IO system (should be false
885    *          in applet context)
886    * @param htmlName
887    * @param documentBase
888    * @param codeBase
889    * @param commandOptions
890    */
891   public void allocateViewer(Container renderPanel, boolean jmolfileio,
892           String htmlName, URL documentBase, URL codeBase,
893           String commandOptions)
894   {
895     allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
896             codeBase, commandOptions, null, null);
897   }
898
899   /**
900    * 
901    * @param renderPanel
902    * @param jmolfileio
903    *          - when true will initialise jmol's file IO system (should be false
904    *          in applet context)
905    * @param htmlName
906    * @param documentBase
907    * @param codeBase
908    * @param commandOptions
909    * @param consolePanel
910    *          - panel to contain Jmol console
911    * @param buttonsToShow
912    *          - buttons to show on the console, in order
913    */
914   public void allocateViewer(Container renderPanel, boolean jmolfileio,
915           String htmlName, URL documentBase, URL codeBase,
916           String commandOptions, final Container consolePanel,
917           String buttonsToShow)
918   {
919
920     System.err.println("Allocating Jmol Viewer: " + commandOptions);
921
922     if (commandOptions == null)
923     {
924       commandOptions = "";
925     }
926     jmolViewer = (Viewer) JmolViewer.allocateViewer(renderPanel,
927             (jmolfileio ? new SmarterJmolAdapter() : null),
928             htmlName + ((Object) this).toString(), documentBase, codeBase,
929             commandOptions, this);
930
931     jmolViewer.setJmolStatusListener(this); // extends JmolCallbackListener
932
933     try
934     {
935       console = createJmolConsole(consolePanel, buttonsToShow);
936     } catch (Throwable e)
937     {
938       System.err.println("Could not create Jmol application console. "
939               + e.getMessage());
940       e.printStackTrace();
941     }
942     if (consolePanel != null)
943     {
944       consolePanel.addComponentListener(this);
945
946     }
947
948   }
949
950   protected abstract JmolAppConsoleInterface createJmolConsole(
951           Container consolePanel, String buttonsToShow);
952
953   // BH 2018 -- Jmol console is not working due to problems with styled
954   // documents.
955
956   protected org.jmol.api.JmolAppConsoleInterface console = null;
957
958   @Override
959   public int[] resizeInnerPanel(String data)
960   {
961     // Jalview doesn't honour resize panel requests
962     return null;
963   }
964
965   /**
966    * 
967    */
968   protected void closeConsole()
969   {
970     if (console != null)
971     {
972       try
973       {
974         console.setVisible(false);
975       } catch (Error e)
976       {
977       } catch (Exception x)
978       {
979       }
980       ;
981       console = null;
982     }
983   }
984
985   /**
986    * ComponentListener method
987    */
988   @Override
989   public void componentMoved(ComponentEvent e)
990   {
991   }
992
993   /**
994    * ComponentListener method
995    */
996   @Override
997   public void componentResized(ComponentEvent e)
998   {
999   }
1000
1001   /**
1002    * ComponentListener method
1003    */
1004   @Override
1005   public void componentShown(ComponentEvent e)
1006   {
1007     showConsole(true);
1008   }
1009
1010   /**
1011    * ComponentListener method
1012    */
1013   @Override
1014   public void componentHidden(ComponentEvent e)
1015   {
1016     showConsole(false);
1017   }
1018
1019   @Override
1020   protected String getModelIdForFile(String pdbFile)
1021   {
1022     if (modelFileNames == null)
1023     {
1024       return "";
1025     }
1026     for (int i = 0; i < modelFileNames.length; i++)
1027     {
1028       if (modelFileNames[i].equalsIgnoreCase(pdbFile))
1029       {
1030         return String.valueOf(i + 1);
1031       }
1032     }
1033     return "";
1034   }
1035
1036   @Override
1037   protected ViewerType getViewerType()
1038   {
1039     return ViewerType.JMOL;
1040   }
1041
1042   @Override
1043   protected String getModelId(int pdbfnum, String file)
1044   {
1045     return String.valueOf(pdbfnum + 1);
1046   }
1047
1048   /**
1049    * Returns ".spt" - the Jmol session file extension
1050    * 
1051    * @return
1052    * @see https://chemapps.stolaf.edu/jmol/docs/#writemodel
1053    */
1054   @Override
1055   public String getSessionFileExtension()
1056   {
1057     return ".spt";
1058   }
1059
1060   @Override
1061   public void selectionChanged(BS arg0)
1062   {
1063     // TODO Auto-generated method stub
1064
1065   }
1066
1067   @Override
1068   public SequenceRenderer getSequenceRenderer(AlignmentViewPanel avp)
1069   {
1070     return new jalview.gui.SequenceRenderer(avp.getAlignViewport());
1071   }
1072
1073   @Override
1074   public String getHelpURL()
1075   {
1076     return "http://wiki.jmol.org"; // BH 2018
1077   }
1078
1079   private boolean setGlobalUseScriptWait(boolean b)
1080   {
1081     boolean prev = globalUseScriptWait;
1082     globalUseScriptWait = b;
1083     return prev;
1084   }
1085 }