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