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