2.5.1 release branding
[jalview.git] / src / jalview / appletgui / AppletJmol.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5.1)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.appletgui;
19
20 import java.util.*;
21 import java.awt.*;
22 import java.awt.event.*;
23
24 import jalview.datamodel.*;
25 import jalview.structure.*;
26 import jalview.io.*;
27
28 import org.jmol.api.*;
29 import org.jmol.adapter.smarter.SmarterJmolAdapter;
30
31 import org.jmol.popup.*;
32 import jalview.schemes.*;
33
34 public class AppletJmol extends EmbmenuFrame implements StructureListener,
35         JmolStatusListener, KeyListener, ActionListener, ItemListener
36
37 {
38   Menu fileMenu = new Menu("File");
39
40   Menu viewMenu = new Menu("View");
41
42   Menu coloursMenu = new Menu("Colours");
43
44   Menu chainMenu = new Menu("Show Chain");
45
46   Menu helpMenu = new Menu("Help");
47
48   MenuItem mappingMenuItem = new MenuItem("View Mapping");
49
50   CheckboxMenuItem seqColour = new CheckboxMenuItem("By Sequence", true);
51
52   MenuItem chain = new MenuItem("By Chain");
53
54   MenuItem charge = new MenuItem("Charge & Cysteine");
55
56   MenuItem zappo = new MenuItem("Zappo");
57
58   MenuItem taylor = new MenuItem("Taylor");
59
60   MenuItem hydro = new MenuItem("Hydrophobicity");
61
62   MenuItem helix = new MenuItem("Helix Propensity");
63
64   MenuItem strand = new MenuItem("Strand Propensity");
65
66   MenuItem turn = new MenuItem("Turn Propensity");
67
68   MenuItem buried = new MenuItem("Buried Index");
69
70   MenuItem user = new MenuItem("User Defined Colours");
71
72   MenuItem jmolHelp = new MenuItem("Jmol Help");
73
74   JmolViewer viewer;
75
76   JmolPopup jmolpopup;
77
78   Panel scriptWindow;
79
80   TextField inputLine;
81
82   TextArea history;
83
84   SequenceI[] sequence;
85
86   String[] chains;
87
88   StructureSelectionManager ssm;
89
90   RenderPanel renderPanel;
91
92   AlignmentPanel ap;
93
94   String fileLoadingError;
95
96   boolean loadedInline;
97
98   PDBEntry pdbentry;
99
100   boolean colourBySequence = true;
101
102   Vector atomsPicked = new Vector();
103
104   /**
105    * datasource protocol for access to PDBEntry
106    */
107   String protocol = null;
108
109   public AppletJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
110           AlignmentPanel ap, String protocol)
111   {
112     this.ap = ap;
113     this.sequence = seq;
114     this.chains = chains;
115     this.pdbentry = pdbentry;
116     this.protocol = protocol;
117     if (pdbentry.getId() == null || pdbentry.getId().length() < 1)
118     {
119       if (protocol.equals(AppletFormatAdapter.PASTE))
120       {
121         pdbentry.setId("PASTED PDB"
122                 + (chains == null ? "_" : chains.toString()));
123       }
124       else
125       {
126         pdbentry.setId(pdbentry.getFile());
127       }
128     }
129
130     if (jalview.bin.JalviewLite.debug)
131     {
132       System.err
133               .println("AppletJmol: PDB ID is '" + pdbentry.getId() + "'");
134     }
135
136     String alreadyMapped = StructureSelectionManager
137             .getStructureSelectionManager().alreadyMappedToFile(
138                     pdbentry.getId());
139     MCview.PDBfile reader = null;
140     if (alreadyMapped != null)
141     {
142       reader = StructureSelectionManager.getStructureSelectionManager()
143               .setMapping(seq, chains, pdbentry.getFile(), protocol);
144       // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW?
145       // FOR NOW, LETS JUST OPEN A NEW WINDOW
146     }
147     MenuBar menuBar = new MenuBar();
148     menuBar.add(fileMenu);
149     fileMenu.add(mappingMenuItem);
150     menuBar.add(viewMenu);
151     mappingMenuItem.addActionListener(this);
152     viewMenu.add(chainMenu);
153     menuBar.add(coloursMenu);
154     menuBar.add(helpMenu);
155
156     charge.addActionListener(this);
157     hydro.addActionListener(this);
158     chain.addActionListener(this);
159     seqColour.addItemListener(this);
160     zappo.addActionListener(this);
161     taylor.addActionListener(this);
162     helix.addActionListener(this);
163     strand.addActionListener(this);
164     turn.addActionListener(this);
165     buried.addActionListener(this);
166     user.addActionListener(this);
167
168     jmolHelp.addActionListener(this);
169
170     coloursMenu.add(seqColour);
171     coloursMenu.add(chain);
172     coloursMenu.add(charge);
173     coloursMenu.add(zappo);
174     coloursMenu.add(taylor);
175     coloursMenu.add(hydro);
176     coloursMenu.add(helix);
177     coloursMenu.add(strand);
178     coloursMenu.add(turn);
179     coloursMenu.add(buried);
180     coloursMenu.add(user);
181
182     helpMenu.add(jmolHelp);
183
184     setMenuBar(menuBar);
185
186     renderPanel = new RenderPanel();
187     embedMenuIfNeeded(renderPanel);
188     this.add(renderPanel, BorderLayout.CENTER);
189     viewer = JmolViewer.allocateViewer(renderPanel,
190             new SmarterJmolAdapter());
191
192     viewer.setAppletContext("jalview", ap.av.applet.getDocumentBase(),
193             ap.av.applet.getCodeBase(), null);
194
195     viewer.setJmolStatusListener(this);
196
197     jmolpopup = JmolPopup.newJmolPopup(viewer);
198
199     this.addWindowListener(new WindowAdapter()
200     {
201       public void windowClosing(WindowEvent evt)
202       {
203         closeViewer();
204       }
205     });
206
207     if (pdbentry.getFile() != null)
208     {
209       // import structure data from pdbentry.getFile based on given protocol
210       if (protocol.equals(AppletFormatAdapter.PASTE))
211       {
212         loadInline(pdbentry.getFile());
213       }
214       else if (protocol.equals(AppletFormatAdapter.FILE)
215               || protocol.equals(AppletFormatAdapter.URL))
216       {
217         viewer.openFile(pdbentry.getFile());
218       }
219       else
220       {
221         // probably CLASSLOADER based datasource..
222         // Try and get a reader on the datasource, and pass that to Jmol
223         try
224         {
225           java.io.Reader freader = null;
226           if (reader != null)
227           {
228             if (jalview.bin.JalviewLite.debug)
229             {
230               System.err
231                       .println("AppletJmol:Trying to reuse existing PDBfile IO parser.");
232             }
233             // re-use the one we opened earlier
234             freader = reader.getReader();
235           }
236           if (freader == null)
237           {
238             if (jalview.bin.JalviewLite.debug)
239             {
240               System.err
241                       .println("AppletJmol:Creating new PDBfile IO parser.");
242             }
243             FileParse fp = new FileParse(pdbentry.getFile(), protocol);
244             fp.mark();
245             // reader = new MCview.PDBfile(fp);
246             // could set ID, etc.
247             // if (!reader.isValid())
248             // {
249             // throw new Exception("Invalid datasource.
250             // "+reader.getWarningMessage());
251             // }
252             // fp.reset();
253             freader = fp.getReader();
254           }
255           if (freader == null)
256           {
257             throw new Exception(
258                     "Invalid datasource. Could not obtain Reader.");
259           }
260           viewer.openReader(pdbentry.getFile(), pdbentry.getId(), freader);
261         } catch (Exception e)
262         {
263           // give up!
264           System.err.println("Couldn't access pdbentry id="
265                   + pdbentry.getId() + " and file=" + pdbentry.getFile()
266                   + " using protocol=" + protocol);
267           e.printStackTrace();
268         }
269       }
270     }
271
272     jalview.bin.JalviewLite.addFrame(this, "Jmol", 400, 400);
273   }
274
275   public void loadInline(String string)
276   {
277     loadedInline = true;
278     viewer.openStringInline(string);
279   }
280
281   void setChainMenuItems(Vector chains)
282   {
283     chainMenu.removeAll();
284
285     MenuItem menuItem = new MenuItem("All");
286     menuItem.addActionListener(this);
287
288     chainMenu.add(menuItem);
289
290     CheckboxMenuItem menuItemCB;
291     for (int c = 0; c < chains.size(); c++)
292     {
293       menuItemCB = new CheckboxMenuItem(chains.elementAt(c).toString(),
294               true);
295       menuItemCB.addItemListener(this);
296       chainMenu.add(menuItemCB);
297     }
298   }
299
300   boolean allChainsSelected = false;
301
302   void centerViewer()
303   {
304     StringBuffer cmd = new StringBuffer();
305     for (int i = 0; i < chainMenu.getItemCount(); i++)
306     {
307       if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
308       {
309         CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i);
310         if (item.getState())
311           cmd.append(":" + item.getLabel() + " or ");
312       }
313     }
314
315     if (cmd.length() > 0)
316       cmd.setLength(cmd.length() - 4);
317
318     viewer
319             .evalString("select *;restrict " + cmd + ";cartoon;center "
320                     + cmd);
321   }
322
323   void closeViewer()
324   {
325     viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
326     viewer.evalStringQuiet("zap");
327     viewer.setJmolStatusListener(null);
328     viewer = null;
329
330     // We'll need to find out what other
331     // listeners need to be shut down in Jmol
332     StructureSelectionManager.getStructureSelectionManager()
333             .removeStructureViewerListener(this, pdbentry.getId());
334
335     this.setVisible(false);
336   }
337
338   public void actionPerformed(ActionEvent evt)
339   {
340     if (evt.getSource() == mappingMenuItem)
341     {
342       jalview.appletgui.CutAndPasteTransfer cap = new jalview.appletgui.CutAndPasteTransfer(
343               false, null);
344       Frame frame = new Frame();
345       frame.add(cap);
346
347       jalview.bin.JalviewLite.addFrame(frame, "PDB - Sequence Mapping",
348               550, 600);
349       cap.setText(StructureSelectionManager.getStructureSelectionManager()
350               .printMapping(pdbentry.getFile()));
351     }
352     else if (evt.getSource() == charge)
353     {
354       colourBySequence = false;
355       seqColour.setState(false);
356       viewer
357               .evalStringQuiet("select *;color white;select ASP,GLU;color red;"
358                       + "select LYS,ARG;color blue;select CYS;color yellow");
359     }
360
361     else if (evt.getSource() == chain)
362     {
363       colourBySequence = false;
364       seqColour.setState(false);
365       viewer.evalStringQuiet("select *;color chain");
366     }
367     else if (evt.getSource() == zappo)
368     {
369       setJalviewColourScheme(new ZappoColourScheme());
370     }
371     else if (evt.getSource() == taylor)
372     {
373       setJalviewColourScheme(new TaylorColourScheme());
374     }
375     else if (evt.getSource() == hydro)
376     {
377       setJalviewColourScheme(new HydrophobicColourScheme());
378     }
379     else if (evt.getSource() == helix)
380     {
381       setJalviewColourScheme(new HelixColourScheme());
382     }
383     else if (evt.getSource() == strand)
384     {
385       setJalviewColourScheme(new StrandColourScheme());
386     }
387     else if (evt.getSource() == turn)
388     {
389       setJalviewColourScheme(new TurnColourScheme());
390     }
391     else if (evt.getSource() == buried)
392     {
393       setJalviewColourScheme(new BuriedColourScheme());
394     }
395     else if (evt.getSource() == user)
396     {
397       new UserDefinedColours(this);
398     }
399     else if (evt.getSource() == jmolHelp)
400     {
401       try
402       {
403         ap.av.applet.getAppletContext().showDocument(
404                 new java.net.URL(
405                         "http://jmol.sourceforge.net/docs/JmolUserGuide/"),
406                 "jmolHelp");
407       } catch (java.net.MalformedURLException ex)
408       {
409       }
410     }
411     else
412     {
413       allChainsSelected = true;
414       for (int i = 0; i < chainMenu.getItemCount(); i++)
415       {
416         if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
417           ((CheckboxMenuItem) chainMenu.getItem(i)).setState(true);
418       }
419       centerViewer();
420       allChainsSelected = false;
421     }
422   }
423
424   public void setJalviewColourScheme(ColourSchemeI cs)
425   {
426     colourBySequence = false;
427     seqColour.setState(false);
428
429     if (cs == null)
430       return;
431
432     String res;
433     int index;
434     Color col;
435
436     Enumeration en = ResidueProperties.aa3Hash.keys();
437     StringBuffer command = new StringBuffer("select *;color white;");
438     while (en.hasMoreElements())
439     {
440       res = en.nextElement().toString();
441       index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
442       if (index > 20)
443         continue;
444
445       col = cs.findColour(ResidueProperties.aa[index].charAt(0));
446
447       command.append("select " + res + ";color[" + col.getRed() + ","
448               + col.getGreen() + "," + col.getBlue() + "];");
449     }
450
451     viewer.evalStringQuiet(command.toString());
452   }
453
454   public void itemStateChanged(ItemEvent evt)
455   {
456     if (evt.getSource() == seqColour)
457     {
458       lastCommand = null;
459       colourBySequence = seqColour.getState();
460       colourBySequence(ap);
461     }
462     else if (!allChainsSelected)
463       centerViewer();
464   }
465
466   public void keyPressed(KeyEvent evt)
467   {
468     if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible())
469     {
470       viewer.evalString(inputLine.getText());
471       history.append("\n$ " + inputLine.getText());
472       inputLine.setText("");
473     }
474
475   }
476
477   public void keyTyped(KeyEvent evt)
478   {
479   }
480
481   public void keyReleased(KeyEvent evt)
482   {
483   }
484
485   // ////////////////////////////////
486   // /StructureListener
487   public String getPdbFile()
488   {
489     return "???";
490   }
491
492   String lastMessage;
493
494   public void mouseOverStructure(int atomIndex, String strInfo)
495   {
496     int pdbResNum;
497
498     int chainSeparator = strInfo.indexOf(":");
499
500     if (chainSeparator == -1)
501       chainSeparator = strInfo.indexOf(".");
502
503     pdbResNum = Integer.parseInt(strInfo.substring(
504             strInfo.indexOf("]") + 1, chainSeparator));
505
506     String chainId;
507
508     if (strInfo.indexOf(":") > -1)
509       chainId = strInfo.substring(strInfo.indexOf(":") + 1, strInfo
510               .indexOf("."));
511     else
512     {
513       chainId = " ";
514     }
515
516     if (lastMessage == null || !lastMessage.equals(strInfo))
517       ssm.mouseOverStructure(pdbResNum, chainId, pdbentry.getFile());
518
519     lastMessage = strInfo;
520   }
521
522   StringBuffer resetLastRes = new StringBuffer();
523
524   StringBuffer eval = new StringBuffer();
525
526   public void highlightAtom(int atomIndex, int pdbResNum, String chain,
527           String pdbfile)
528   {
529     if (!pdbfile.equals(pdbentry.getFile()))
530       return;
531
532     if (resetLastRes.length() > 0)
533     {
534       viewer.evalStringQuiet(resetLastRes.toString());
535     }
536
537     eval.setLength(0);
538     eval.append("select " + pdbResNum);
539
540     resetLastRes.setLength(0);
541     resetLastRes.append("select " + pdbResNum);
542
543     eval.append(":");
544     resetLastRes.append(":");
545     if (!chain.equals(" "))
546     {
547       eval.append(chain);
548       resetLastRes.append(chain);
549     }
550
551     eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
552
553     resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
554             + " and not hetero; spacefill 0;");
555
556     eval.append("spacefill 200;select none");
557
558     viewer.evalStringQuiet(eval.toString());
559
560   }
561
562   public void updateColours(Object source)
563   {
564     colourBySequence((AlignmentPanel) source);
565   }
566
567   // End StructureListener
568   // //////////////////////////
569
570   public Color getColour(int atomIndex, int pdbResNum, String chain,
571           String pdbfile)
572   {
573     if (!pdbfile.equals(pdbentry.getFile()))
574       return null;
575
576     return new Color(viewer.getAtomArgb(atomIndex));
577   }
578
579   String lastCommand;
580
581   FeatureRenderer fr = null;
582
583   public void colourBySequence(AlignmentPanel sourceap)
584   {
585     this.ap = sourceap;
586
587     if (!colourBySequence)
588       return;
589
590     StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
591
592     if (mapping.length < 1)
593       return;
594
595     SequenceRenderer sr = new SequenceRenderer(ap.av);
596
597     boolean showFeatures = false;
598
599     if (ap.av.showSequenceFeatures)
600     {
601       showFeatures = true;
602       if (fr == null)
603       {
604         fr = new jalview.appletgui.FeatureRenderer(ap.av);
605       }
606
607       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
608     }
609
610     StringBuffer command = new StringBuffer();
611
612     int lastPos = -1;
613     for (int s = 0; s < sequence.length; s++)
614     {
615       for (int sp, m = 0; m < mapping.length; m++)
616       {
617         if (mapping[m].getSequence() == sequence[s]
618                 && (sp = ap.av.alignment.findIndex(sequence[s])) > -1)
619         {
620           SequenceI asp = ap.av.alignment.getSequenceAt(sp);
621           for (int r = 0; r < asp.getLength(); r++)
622           {
623             // no mapping to gaps in sequence
624             if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
625             {
626               continue;
627             }
628             int pos = mapping[m].getPDBResNum(asp.findPosition(r));
629
630             if (pos < 1 || pos == lastPos)
631               continue;
632
633             lastPos = pos;
634
635             Color col = sr.getResidueBoxColour(sequence[s], r);
636
637             if (showFeatures)
638               col = fr.findFeatureColour(col, sequence[s], r);
639
640             if (command.toString().endsWith(
641                     ":" + mapping[m].getChain() + ";color[" + col.getRed()
642                             + "," + col.getGreen() + "," + col.getBlue()
643                             + "]"))
644             {
645               command = condenseCommand(command.toString(), pos);
646               continue;
647             }
648
649             command.append(";select " + pos);
650
651             if (!mapping[m].getChain().equals(" "))
652             {
653               command.append(":" + mapping[m].getChain());
654             }
655
656             command.append(";color[" + col.getRed() + "," + col.getGreen()
657                     + "," + col.getBlue() + "]");
658           }
659           break;
660         }
661       }
662     }
663
664     if (lastCommand == null || !lastCommand.equals(command.toString()))
665     {
666       viewer.evalStringQuiet(command.toString());
667     }
668     lastCommand = command.toString();
669   }
670
671   StringBuffer condenseCommand(String command, int pos)
672   {
673
674     StringBuffer sb = new StringBuffer(command.substring(0, command
675             .lastIndexOf("select") + 7));
676
677     command = command.substring(sb.length());
678
679     String start;
680
681     if (command.indexOf("-") > -1)
682     {
683       start = command.substring(0, command.indexOf("-"));
684     }
685     else
686     {
687       start = command.substring(0, command.indexOf(":"));
688     }
689
690     sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
691
692     return sb;
693   }
694
695   // ///////////////////////////////
696   // JmolStatusListener
697
698   public String eval(String strEval)
699   {
700     // System.out.println(strEval);
701     // "# 'eval' is implemented only for the applet.";
702     return null;
703   }
704
705   public void createImage(String file, String type, int quality)
706   {
707   }
708
709   public void setCallbackFunction(String callbackType,
710           String callbackFunction)
711   {
712   }
713
714   public void notifyFileLoaded(String fullPathName, String fileName,
715           String modelName, Object clientFile, String errorMsg)
716   {
717     if (errorMsg != null)
718     {
719       fileLoadingError = errorMsg;
720       repaint();
721       return;
722     }
723
724     fileLoadingError = null;
725
726     if (fileName != null)
727     {
728       // FILE LOADED OK
729       jmolpopup.updateComputedMenus();
730       viewer
731               .evalStringQuiet("select backbone;restrict;cartoon;wireframe off;spacefill off");
732
733       ssm = StructureSelectionManager.getStructureSelectionManager();
734       MCview.PDBfile pdb;
735       if (loadedInline)
736       {
737         pdb = ssm.setMapping(sequence, chains, pdbentry.getFile(),
738                 AppletFormatAdapter.PASTE);
739         pdbentry.setFile("INLINE" + pdb.id);
740       }
741       else
742       {
743         // TODO: Jmol can in principle retrieve from CLASSLOADER but this needs
744         // to be tested. See mantis bug
745         // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
746
747         pdb = ssm.setMapping(sequence, chains, pdbentry.getFile(),
748                 AppletFormatAdapter.URL);
749
750       }
751
752       pdbentry.setId(pdb.id);
753
754       ssm.addStructureViewerListener(this);
755
756       Vector chains = new Vector();
757       for (int i = 0; i < pdb.chains.size(); i++)
758       {
759         chains.addElement(((MCview.PDBChain) pdb.chains.elementAt(i)).id);
760       }
761       setChainMenuItems(chains);
762
763       colourBySequence(ap);
764
765       StringBuffer title = new StringBuffer(sequence[0].getName() + ":"
766               + pdbentry.getId());
767
768       if (pdbentry.getProperty() != null)
769       {
770         if (pdbentry.getProperty().get("method") != null)
771         {
772           title.append(" Method: ");
773           title.append(pdbentry.getProperty().get("method"));
774         }
775         if (pdbentry.getProperty().get("chains") != null)
776         {
777           title.append(" Chain:");
778           title.append(pdbentry.getProperty().get("chains"));
779         }
780       }
781
782       this.setTitle(title.toString());
783
784     }
785     else
786       return;
787   }
788
789   public void notifyFrameChanged(int frameNo)
790   {
791     boolean isAnimationRunning = (frameNo <= -2);
792   }
793
794   public void notifyScriptStart(String statusMessage, String additionalInfo)
795   {
796   }
797
798   public void sendConsoleEcho(String strEcho)
799   {
800     if (scriptWindow == null)
801       showConsole(true);
802
803     history.append("\n" + strEcho);
804   }
805
806   public void sendConsoleMessage(String strStatus)
807   {
808     if (history != null && strStatus != null
809             && !strStatus.equals("Script completed"))
810     {
811       history.append("\n" + strStatus);
812     }
813   }
814
815   public void notifyScriptTermination(String strStatus, int msWalltime)
816   {
817   }
818
819   public void handlePopupMenu(int x, int y)
820   {
821     jmolpopup.show(x, y);
822   }
823
824   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
825   {
826     notifyAtomPicked(iatom, strMeasure);
827   }
828
829   public void notifyNewDefaultModeMeasurement(int count, String strInfo)
830   {
831   }
832
833   public void notifyAtomPicked(int atomIndex, String strInfo)
834   {
835
836     int chainSeparator = strInfo.indexOf(":");
837
838     if (chainSeparator == -1)
839       chainSeparator = strInfo.indexOf(".");
840
841     String picked = strInfo.substring(strInfo.indexOf("]") + 1,
842             chainSeparator);
843
844     if (strInfo.indexOf(":") > -1)
845       picked += strInfo.substring(strInfo.indexOf(":") + 1, strInfo
846               .indexOf("."));
847
848     picked += ".CA";
849
850     if (!atomsPicked.contains(picked))
851     {
852       viewer.evalString("select " + picked + ";label %n %r:%c");
853       atomsPicked.addElement(picked);
854     }
855     else
856     {
857       viewer.evalString("select " + picked + ";label off");
858       atomsPicked.removeElement(picked);
859     }
860   }
861
862   public void notifyAtomHovered(int atomIndex, String strInfo)
863   {
864     mouseOverStructure(atomIndex, strInfo);
865   }
866
867   public void sendSyncScript(String script, String appletName)
868   {
869   }
870
871   public void showUrl(String url)
872   {
873     try
874     {
875       ap.av.applet.getAppletContext().showDocument(new java.net.URL(url),
876               "jmolOutput");
877     } catch (java.net.MalformedURLException ex)
878     {
879     }
880   }
881
882   public void showConsole(boolean showConsole)
883   {
884     if (scriptWindow == null)
885     {
886       scriptWindow = new Panel(new BorderLayout());
887       inputLine = new TextField();
888       history = new TextArea(5, 40);
889       scriptWindow.add(history, BorderLayout.CENTER);
890       scriptWindow.add(inputLine, BorderLayout.SOUTH);
891       add(scriptWindow, BorderLayout.SOUTH);
892       scriptWindow.setVisible(false);
893       history.setEditable(false);
894       inputLine.addKeyListener(this);
895     }
896
897     scriptWindow.setVisible(!scriptWindow.isVisible());
898     validate();
899   }
900
901   public float functionXY(String functionName, int x, int y)
902   {
903     return 0;
904   }
905
906   // /End JmolStatusListener
907   // /////////////////////////////
908
909   class RenderPanel extends Panel
910   {
911     Dimension currentSize = new Dimension();
912
913     Rectangle rectClip = new Rectangle();
914
915     public void update(Graphics g)
916     {
917       paint(g);
918     }
919
920     public void paint(Graphics g)
921     {
922       currentSize = this.getSize();
923       rectClip = g.getClipBounds();
924
925       if (viewer == null)
926       {
927         g.setColor(Color.black);
928         g.fillRect(0, 0, currentSize.width, currentSize.height);
929         g.setColor(Color.white);
930         g.setFont(new Font("Verdana", Font.BOLD, 14));
931         g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);
932       }
933       else
934       {
935         viewer.renderScreenImage(g, currentSize, rectClip);
936       }
937     }
938   }
939
940 }