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