merge from 2_4_Release branch
[jalview.git] / src / jalview / gui / AppJmol.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import java.util.regex.*;
22 import java.util.*;
23 import java.awt.*;
24 import javax.swing.*;
25 import javax.swing.event.*;
26 import java.awt.event.*;
27 import java.io.*;
28
29 import jalview.jbgui.GStructureViewer;
30 import jalview.datamodel.*;
31 import jalview.gui.*;
32 import jalview.structure.*;
33 import jalview.datamodel.PDBEntry;
34 import jalview.io.*;
35 import jalview.schemes.*;
36 import jalview.ws.ebi.EBIFetchClient;
37
38 import org.jmol.api.*;
39 import org.jmol.adapter.smarter.SmarterJmolAdapter;
40 import org.jmol.popup.*;
41
42 public class AppJmol extends GStructureViewer implements StructureListener,
43         JmolStatusListener, Runnable
44
45 {
46   JmolViewer viewer;
47
48   JmolPopup jmolpopup;
49
50   ScriptWindow scriptWindow;
51
52   PDBEntry pdbentry;
53
54   SequenceI[] sequence;
55
56   String[] chains;
57
58   StructureSelectionManager ssm;
59
60   JSplitPane splitPane;
61
62   RenderPanel renderPanel;
63
64   AlignmentPanel ap;
65
66   String fileLoadingError;
67
68   boolean colourBySequence = true;
69
70   boolean loadingFromArchive = false;
71
72   Vector atomsPicked = new Vector();
73
74   public AppJmol(String file, String id, SequenceI[] seq,
75           AlignmentPanel ap, String loadStatus, Rectangle bounds)
76   {
77     loadingFromArchive = true;
78     pdbentry = new PDBEntry();
79     pdbentry.setFile(file);
80     pdbentry.setId(id);
81     this.chains = chains;
82     this.sequence = seq;
83     this.ap = ap;
84     this.setBounds(bounds);
85     colourBySequence = false;
86     seqColour.setSelected(false);
87
88     // jalview.gui.Desktop.addInternalFrame(this, "Loading File",
89     // bounds.width,bounds.height);
90
91     initJmol(loadStatus);
92
93     this.addInternalFrameListener(new InternalFrameAdapter()
94     {
95       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
96       {
97         closeViewer();
98       }
99     });
100   }
101
102   public synchronized void addSequence(SequenceI[] seq)
103   {
104     Vector v = new Vector();
105     for (int i = 0; i < sequence.length; i++)
106       v.addElement(sequence[i]);
107
108     for (int i = 0; i < seq.length; i++)
109       if (!v.contains(seq[i]))
110         v.addElement(seq[i]);
111
112     SequenceI[] tmp = new SequenceI[v.size()];
113     v.copyInto(tmp);
114     sequence = tmp;
115   }
116
117   public AppJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
118           AlignmentPanel ap)
119   {
120     // ////////////////////////////////
121     // Is the pdb file already loaded?
122     String alreadyMapped = StructureSelectionManager
123             .getStructureSelectionManager().alreadyMappedToFile(
124                     pdbentry.getId());
125
126     if (alreadyMapped != null)
127     {
128       int option = JOptionPane
129               .showInternalConfirmDialog(
130                       Desktop.desktop,
131                       pdbentry.getId()
132                               + " is already displayed."
133                               + "\nDo you want to map sequences to the visible structure?",
134                       "Map Sequences to Visible Window: "
135                               + pdbentry.getId(), JOptionPane.YES_NO_OPTION);
136
137       if (option == JOptionPane.YES_OPTION)
138       {
139         StructureSelectionManager.getStructureSelectionManager()
140                 .setMapping(seq, chains, alreadyMapped,
141                         AppletFormatAdapter.FILE);
142         if (ap.seqPanel.seqCanvas.fr != null)
143         {
144           ap.seqPanel.seqCanvas.fr.featuresAdded();
145           ap.paintAlignment(true);
146         }
147
148         // Now this AppJmol is mapped to new sequences. We must add them to
149         // the exisiting array
150         JInternalFrame[] frames = Desktop.instance.getAllFrames();
151
152         for (int i = 0; i < frames.length; i++)
153         {
154           if (frames[i] instanceof AppJmol)
155           {
156             AppJmol topJmol = ((AppJmol) frames[i]);
157             if (topJmol.pdbentry.getFile().equals(alreadyMapped))
158             {
159               topJmol.addSequence(seq);
160               break;
161             }
162           }
163         }
164
165         return;
166       }
167     }
168     // /////////////////////////////////
169
170     this.ap = ap;
171     this.pdbentry = pdbentry;
172     this.sequence = seq;
173     this.setSize(400, 400);
174     // jalview.gui.Desktop.addInternalFrame(this, "Jmol
175     // View"+(pdbentry.getId()!=null ? "for "+pdbentry.getId()
176     // : ""), 400, 400);
177
178     if (pdbentry.getFile() != null)
179     {
180       initJmol("load \"" + pdbentry.getFile() + "\"");
181     }
182     else
183     {
184       Thread worker = new Thread(this);
185       worker.start();
186     }
187
188     this.addInternalFrameListener(new InternalFrameAdapter()
189     {
190       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
191       {
192         closeViewer();
193       }
194     });
195   }
196
197   void initJmol(String command)
198   {
199     renderPanel = new RenderPanel();
200
201     this.getContentPane().add(renderPanel, java.awt.BorderLayout.CENTER);
202
203     StringBuffer title = new StringBuffer(sequence[0].getName() + ":"
204             + pdbentry.getId());
205
206     if (pdbentry.getProperty() != null)
207     {
208       if (pdbentry.getProperty().get("method") != null)
209       {
210         title.append(" Method: ");
211         title.append(pdbentry.getProperty().get("method"));
212       }
213       if (pdbentry.getProperty().get("chains") != null)
214       {
215         title.append(" Chain:");
216         title.append(pdbentry.getProperty().get("chains"));
217       }
218     }
219
220     this.setTitle(title.toString());
221     jalview.gui.Desktop.addInternalFrame(this, title.toString(),
222             getBounds().width, getBounds().height);
223
224     viewer = org.jmol.api.JmolViewer.allocateViewer(renderPanel,
225             new SmarterJmolAdapter());
226
227     viewer.setAppletContext("", null, null, "");
228
229     viewer.setJmolStatusListener(this);
230
231     jmolpopup = JmolPopup.newJmolPopup(viewer);
232
233     viewer.evalStringQuiet(command);
234   }
235
236   void setChainMenuItems(Vector chains)
237   {
238     chainMenu.removeAll();
239
240     JMenuItem menuItem = new JMenuItem("All");
241     menuItem.addActionListener(new ActionListener()
242     {
243       public void actionPerformed(ActionEvent evt)
244       {
245         allChainsSelected = true;
246         for (int i = 0; i < chainMenu.getItemCount(); i++)
247         {
248           if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
249             ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);
250         }
251         centerViewer();
252         allChainsSelected = false;
253       }
254     });
255
256     chainMenu.add(menuItem);
257
258     for (int c = 0; c < chains.size(); c++)
259     {
260       menuItem = new JCheckBoxMenuItem(chains.elementAt(c).toString(), true);
261       menuItem.addItemListener(new ItemListener()
262       {
263         public void itemStateChanged(ItemEvent evt)
264         {
265           if (!allChainsSelected)
266             centerViewer();
267         }
268       });
269
270       chainMenu.add(menuItem);
271     }
272   }
273
274   boolean allChainsSelected = false;
275
276   void centerViewer()
277   {
278     StringBuffer cmd = new StringBuffer();
279     for (int i = 0; i < chainMenu.getItemCount(); i++)
280     {
281       if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
282       {
283         JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i);
284         if (item.isSelected())
285           cmd.append(":" + item.getText() + " or ");
286       }
287     }
288
289     if (cmd.length() > 0)
290       cmd.setLength(cmd.length() - 4);
291
292     viewer.evalStringQuiet("select *;restrict " + cmd + ";cartoon;center "
293             + cmd);
294   }
295
296   void closeViewer()
297   {
298     viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
299     viewer.evalStringQuiet("zap");
300     viewer.setJmolStatusListener(null);
301     viewer = null;
302
303     // We'll need to find out what other
304     // listeners need to be shut down in Jmol
305     StructureSelectionManager.getStructureSelectionManager()
306             .removeStructureViewerListener(this, pdbentry.getFile());
307   }
308
309   public void run()
310   {
311     try
312     {
313       EBIFetchClient ebi = new EBIFetchClient();
314       String query = "pdb:" + pdbentry.getId();
315       pdbentry.setFile(ebi.fetchDataAsFile(query, "default", "raw")
316               .getAbsolutePath());
317       initJmol("load " + pdbentry.getFile());
318     } catch (OutOfMemoryError oomerror)
319     {
320       new OOMWarning("Retrieving PDB id " + pdbentry.getId() + " from MSD",
321               oomerror);
322     } catch (Exception ex)
323     {
324       ex.printStackTrace();
325     }
326   }
327
328   public void pdbFile_actionPerformed(ActionEvent actionEvent)
329   {
330     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
331             .getProperty("LAST_DIRECTORY"));
332
333     chooser.setFileView(new JalviewFileView());
334     chooser.setDialogTitle("Save PDB File");
335     chooser.setToolTipText("Save");
336
337     int value = chooser.showSaveDialog(this);
338
339     if (value == JalviewFileChooser.APPROVE_OPTION)
340     {
341       try
342       {
343         BufferedReader in = new BufferedReader(new FileReader(pdbentry
344                 .getFile()));
345         File outFile = chooser.getSelectedFile();
346
347         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
348         String data;
349         while ((data = in.readLine()) != null)
350         {
351           if (!(data.indexOf("<PRE>") > -1 || data.indexOf("</PRE>") > -1))
352           {
353             out.println(data);
354           }
355         }
356         out.close();
357       } catch (Exception ex)
358       {
359         ex.printStackTrace();
360       }
361     }
362   }
363
364   public void viewMapping_actionPerformed(ActionEvent actionEvent)
365   {
366     jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer();
367     jalview.gui.Desktop.addInternalFrame(cap, "PDB - Sequence Mapping",
368             550, 600);
369     cap.setText(StructureSelectionManager.getStructureSelectionManager()
370             .printMapping(pdbentry.getFile()));
371   }
372
373   /**
374    * DOCUMENT ME!
375    * 
376    * @param e
377    *                DOCUMENT ME!
378    */
379   public void eps_actionPerformed(ActionEvent e)
380   {
381     makePDBImage(jalview.util.ImageMaker.EPS);
382   }
383
384   /**
385    * DOCUMENT ME!
386    * 
387    * @param e
388    *                DOCUMENT ME!
389    */
390   public void png_actionPerformed(ActionEvent e)
391   {
392     makePDBImage(jalview.util.ImageMaker.PNG);
393   }
394
395   void makePDBImage(int type)
396   {
397     int width = getWidth();
398     int height = getHeight();
399
400     jalview.util.ImageMaker im;
401
402     if (type == jalview.util.ImageMaker.PNG)
403     {
404       im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.PNG,
405               "Make PNG image from view", width, height, null, null);
406     }
407     else
408     {
409       im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.EPS,
410               "Make EPS file from view", width, height, null, this
411                       .getTitle());
412     }
413
414     if (im.getGraphics() != null)
415     {
416       Rectangle rect = new Rectangle(width, height);
417       viewer.renderScreenImage(im.getGraphics(), rect.getSize(), rect);
418       im.writeImage();
419     }
420   }
421
422   public void seqColour_actionPerformed(ActionEvent actionEvent)
423   {
424     lastCommand = null;
425     colourBySequence = seqColour.isSelected();
426     colourBySequence(ap.alignFrame.alignPanel);
427   }
428
429   public void chainColour_actionPerformed(ActionEvent actionEvent)
430   {
431     colourBySequence = false;
432     seqColour.setSelected(false);
433     viewer.evalStringQuiet("select *;color chain");
434   }
435
436   public void chargeColour_actionPerformed(ActionEvent actionEvent)
437   {
438     colourBySequence = false;
439     seqColour.setSelected(false);
440     viewer.evalStringQuiet("select *;color white;select ASP,GLU;color red;"
441             + "select LYS,ARG;color blue;select CYS;color yellow");
442   }
443
444   public void zappoColour_actionPerformed(ActionEvent actionEvent)
445   {
446     setJalviewColourScheme(new ZappoColourScheme());
447   }
448
449   public void taylorColour_actionPerformed(ActionEvent actionEvent)
450   {
451     setJalviewColourScheme(new TaylorColourScheme());
452   }
453
454   public void hydroColour_actionPerformed(ActionEvent actionEvent)
455   {
456     setJalviewColourScheme(new HydrophobicColourScheme());
457   }
458
459   public void helixColour_actionPerformed(ActionEvent actionEvent)
460   {
461     setJalviewColourScheme(new HelixColourScheme());
462   }
463
464   public void strandColour_actionPerformed(ActionEvent actionEvent)
465   {
466     setJalviewColourScheme(new StrandColourScheme());
467   }
468
469   public void turnColour_actionPerformed(ActionEvent actionEvent)
470   {
471     setJalviewColourScheme(new TurnColourScheme());
472   }
473
474   public void buriedColour_actionPerformed(ActionEvent actionEvent)
475   {
476     setJalviewColourScheme(new BuriedColourScheme());
477   }
478
479   public void setJalviewColourScheme(ColourSchemeI cs)
480   {
481     colourBySequence = false;
482     seqColour.setSelected(false);
483
484     if (cs == null)
485       return;
486
487     String res;
488     int index;
489     Color col;
490
491     Enumeration en = ResidueProperties.aa3Hash.keys();
492     StringBuffer command = new StringBuffer("select *;color white;");
493     while (en.hasMoreElements())
494     {
495       res = en.nextElement().toString();
496       index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
497       if (index > 20)
498         continue;
499
500       col = cs.findColour(ResidueProperties.aa[index].charAt(0));
501
502       command.append("select " + res + ";color[" + col.getRed() + ","
503               + col.getGreen() + "," + col.getBlue() + "];");
504     }
505
506     viewer.evalStringQuiet(command.toString());
507   }
508
509   public void userColour_actionPerformed(ActionEvent actionEvent)
510   {
511     new UserDefinedColours(this, null);
512   }
513
514   public void backGround_actionPerformed(ActionEvent actionEvent)
515   {
516     java.awt.Color col = JColorChooser.showDialog(this,
517             "Select Background Colour", null);
518
519     if (col != null)
520     {
521       viewer.evalStringQuiet("background [" + col.getRed() + ","
522               + col.getGreen() + "," + col.getBlue() + "];");
523     }
524   }
525
526   public void jmolHelp_actionPerformed(ActionEvent actionEvent)
527   {
528     try
529     {
530       jalview.util.BrowserLauncher
531               .openURL("http://jmol.sourceforge.net/docs/JmolUserGuide/");
532     } catch (Exception ex)
533     {
534     }
535   }
536
537   // ////////////////////////////////
538   // /StructureListener
539   public String getPdbFile()
540   {
541     return pdbentry.getFile();
542   }
543
544   Pattern pattern = Pattern
545           .compile("\\[(.*)\\]([0-9]+)(:[a-zA-Z]*)?\\.([a-zA-Z]+)(/[0-9]*)?");
546
547   String lastMessage;
548
549   public void mouseOverStructure(int atomIndex, String strInfo)
550   {
551     Matcher matcher = pattern.matcher(strInfo);
552     matcher.find();
553     matcher.group(1);
554     int pdbResNum = Integer.parseInt(matcher.group(2));
555     String chainId = matcher.group(3);
556
557     if (chainId != null)
558       chainId = chainId.substring(1, chainId.length());
559     else
560     {
561       chainId = " ";
562     }
563
564     if (lastMessage == null || !lastMessage.equals(strInfo))
565     {
566       ssm.mouseOverStructure(pdbResNum, chainId, pdbentry.getFile());
567     }
568     lastMessage = strInfo;
569   }
570
571   StringBuffer resetLastRes = new StringBuffer();
572
573   StringBuffer eval = new StringBuffer();
574
575   public void highlightAtom(int atomIndex, int pdbResNum, String chain,
576           String pdbfile)
577   {
578     if (!pdbfile.equals(pdbentry.getFile()))
579       return;
580
581     if (resetLastRes.length() > 0)
582     {
583       viewer.evalStringQuiet(resetLastRes.toString());
584     }
585
586     eval.setLength(0);
587     eval.append("select " + pdbResNum);
588
589     resetLastRes.setLength(0);
590     resetLastRes.append("select " + pdbResNum);
591
592     if (!chain.equals(" "))
593     {
594       eval.append(":" + chain);
595       resetLastRes.append(":" + chain);
596     }
597
598     eval.append(";wireframe 100;" + eval.toString() + ".CA;");
599
600     resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
601             + ".CA;spacefill 0;");
602
603     eval.append("spacefill 200;select none");
604
605     viewer.evalStringQuiet(eval.toString());
606   }
607
608   public Color getColour(int atomIndex, int pdbResNum, String chain,
609           String pdbfile)
610   {
611     if (!pdbfile.equals(pdbentry.getFile()))
612       return null;
613
614     return new Color(viewer.getAtomArgb(atomIndex));
615   }
616
617   public void updateColours(Object source)
618   {
619     colourBySequence((AlignmentPanel) source);
620   }
621
622   // End StructureListener
623   // //////////////////////////
624
625   String lastCommand;
626
627   FeatureRenderer fr = null;
628
629   public void colourBySequence(AlignmentPanel sourceap)
630   {
631     this.ap = sourceap;
632
633     if (!colourBySequence || ap.alignFrame.getCurrentView() != ap.av)
634       return;
635
636     StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
637
638     if (mapping.length < 1)
639       return;
640
641     SequenceRenderer sr = new SequenceRenderer(ap.av);
642
643     boolean showFeatures = false;
644
645     if (ap.av.showSequenceFeatures)
646     {
647       showFeatures = true;
648       if (fr == null)
649       {
650         fr = new jalview.gui.FeatureRenderer(ap);
651       }
652
653       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
654     }
655
656     StringBuffer command = new StringBuffer();
657
658     int lastPos = -1;
659     for (int sp, s = 0; s < sequence.length; s++)
660     {
661       for (int m = 0; m < mapping.length; m++)
662       {
663         if (mapping[m].getSequence() == sequence[s]
664                 && (sp = ap.av.alignment.findIndex(sequence[s])) > -1)
665         {
666           SequenceI asp = ap.av.alignment.getSequenceAt(sp);
667           for (int r = 0; r < asp.getLength(); r++)
668           {
669             // No mapping to gaps in sequence.
670             if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
671             {
672               continue;
673             }
674             int pos = mapping[m].getPDBResNum(asp.findPosition(r));
675
676             if (pos < 1 || pos == lastPos)
677               continue;
678
679             lastPos = pos;
680
681             Color col = sr.getResidueBoxColour(asp, r);
682
683             if (showFeatures)
684               col = fr.findFeatureColour(col, asp, r);
685
686             if (command.toString().endsWith(
687                     ":" + mapping[m].getChain() + ";color[" + col.getRed()
688                             + "," + col.getGreen() + "," + col.getBlue()
689                             + "]"))
690             {
691               command = condenseCommand(command, pos);
692               continue;
693             }
694
695             command.append(";select " + pos);
696
697             if (!mapping[m].getChain().equals(" "))
698             {
699               command.append(":" + mapping[m].getChain());
700             }
701
702             command.append(";color[" + col.getRed() + "," + col.getGreen()
703                     + "," + col.getBlue() + "]");
704
705           }
706           break;
707         }
708       }
709     }
710
711     if (lastCommand == null || !lastCommand.equals(command.toString()))
712     {
713       viewer.evalStringQuiet(command.toString());
714     }
715     lastCommand = command.toString();
716   }
717
718   StringBuffer condenseCommand(StringBuffer command, int pos)
719   {
720     StringBuffer sb = new StringBuffer(command.substring(0, command
721             .lastIndexOf("select") + 7));
722
723     command.delete(0, sb.length());
724
725     String start;
726
727     if (command.indexOf("-") > -1)
728     {
729       start = command.substring(0, command.indexOf("-"));
730     }
731     else
732     {
733       start = command.substring(0, command.indexOf(":"));
734     }
735
736     sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
737
738     return sb;
739   }
740
741   // ///////////////////////////////
742   // JmolStatusListener
743
744   public String eval(String strEval)
745   {
746     // System.out.println(strEval);
747     // "# 'eval' is implemented only for the applet.";
748     return null;
749   }
750
751   public void createImage(String file, String type, int quality)
752   {
753     System.out.println("JMOL CREATE IMAGE");
754   }
755
756   public void setCallbackFunction(String callbackType,
757           String callbackFunction)
758   {
759   }
760
761   public void notifyFileLoaded(String fullPathName, String fileName,
762           String modelName, Object clientFile, String errorMsg)
763   {
764     if (errorMsg != null)
765     {
766       fileLoadingError = errorMsg;
767       repaint();
768       return;
769     }
770
771     fileLoadingError = null;
772
773     if (fileName != null)
774     {
775
776       // FILE LOADED OK
777       ssm = StructureSelectionManager.getStructureSelectionManager();
778       MCview.PDBfile pdbFile = ssm.setMapping(sequence, chains, pdbentry
779               .getFile(), AppletFormatAdapter.FILE);
780       ssm.addStructureViewerListener(this);
781       Vector chains = new Vector();
782       for (int i = 0; i < pdbFile.chains.size(); i++)
783       {
784         chains
785                 .addElement(((MCview.PDBChain) pdbFile.chains.elementAt(i)).id);
786       }
787       setChainMenuItems(chains);
788
789       jmolpopup.updateComputedMenus();
790
791       if (!loadingFromArchive)
792       {
793         viewer
794                 .evalStringQuiet("select backbone;restrict;cartoon;wireframe off;spacefill off");
795
796         colourBySequence(ap);
797       }
798       if (fr != null)
799         fr.featuresAdded();
800
801       loadingFromArchive = false;
802     }
803     else
804       return;
805   }
806
807   public void notifyFrameChanged(int frameNo)
808   {
809     boolean isAnimationRunning = (frameNo <= -2);
810   }
811
812   public void notifyScriptStart(String statusMessage, String additionalInfo)
813   {
814   }
815
816   public void sendConsoleEcho(String strEcho)
817   {
818     if (scriptWindow != null)
819       scriptWindow.sendConsoleEcho(strEcho);
820   }
821
822   public void sendConsoleMessage(String strStatus)
823   {
824     if (scriptWindow != null)
825       scriptWindow.sendConsoleMessage(strStatus);
826   }
827
828   public void notifyScriptTermination(String strStatus, int msWalltime)
829   {
830     if (scriptWindow != null)
831       scriptWindow.notifyScriptTermination(strStatus, msWalltime);
832   }
833
834   public void handlePopupMenu(int x, int y)
835   {
836     jmolpopup.show(x, y);
837   }
838
839   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
840   {
841     notifyAtomPicked(iatom, strMeasure);
842   }
843
844   public void notifyNewDefaultModeMeasurement(int count, String strInfo)
845   {
846   }
847
848   public void notifyAtomPicked(int atomIndex, String strInfo)
849   {
850     Matcher matcher = pattern.matcher(strInfo);
851     matcher.find();
852
853     matcher.group(1);
854     String resnum = new String(matcher.group(2));
855     String chainId = matcher.group(3);
856
857     String picked = resnum;
858
859     if (chainId != null)
860       picked += (":" + chainId.substring(1, chainId.length()));
861
862     picked += ".CA";
863
864     if (!atomsPicked.contains(picked))
865     {
866       if (chainId != null)
867         viewer.evalString("select " + picked + ";label %n %r:%c");
868       else
869         viewer.evalString("select " + picked + ";label %n %r");
870       atomsPicked.addElement(picked);
871     }
872     else
873     {
874       viewer.evalString("select " + picked + ";label off");
875       atomsPicked.removeElement(picked);
876     }
877
878     if (scriptWindow != null)
879     {
880       scriptWindow.sendConsoleMessage(strInfo);
881       scriptWindow.sendConsoleMessage("\n");
882     }
883   }
884
885   public void notifyAtomHovered(int atomIndex, String strInfo)
886   {
887     mouseOverStructure(atomIndex, strInfo);
888   }
889
890   public void sendSyncScript(String script, String appletName)
891   {
892   }
893
894   public void showUrl(String url)
895   {
896   }
897
898   public void showConsole(boolean showConsole)
899   {
900     if (scriptWindow == null)
901       scriptWindow = new ScriptWindow(this);
902
903     if (showConsole)
904     {
905       if (splitPane == null)
906       {
907         splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
908         splitPane.setTopComponent(renderPanel);
909         splitPane.setBottomComponent(scriptWindow);
910         this.getContentPane().add(splitPane, BorderLayout.CENTER);
911       }
912
913       splitPane.setDividerLocation(getHeight() - 200);
914       splitPane.validate();
915     }
916     else
917     {
918       if (splitPane != null)
919         splitPane.setVisible(false);
920
921       splitPane = null;
922
923       this.getContentPane().add(renderPanel, BorderLayout.CENTER);
924     }
925
926     validate();
927   }
928
929   public float functionXY(String functionName, int x, int y)
930   {
931     return 0;
932   }
933
934   // /End JmolStatusListener
935   // /////////////////////////////
936
937   class RenderPanel extends JPanel
938   {
939     final Dimension currentSize = new Dimension();
940
941     final Rectangle rectClip = new Rectangle();
942
943     public void paintComponent(Graphics g)
944     {
945       getSize(currentSize);
946       g.getClipBounds(rectClip);
947
948       if (viewer == null)
949       {
950         g.setColor(Color.black);
951         g.fillRect(0, 0, currentSize.width, currentSize.height);
952         g.setColor(Color.white);
953         g.setFont(new Font("Verdana", Font.BOLD, 14));
954         g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);
955       }
956       else if (fileLoadingError != null)
957       {
958         g.setColor(Color.black);
959         g.fillRect(0, 0, currentSize.width, currentSize.height);
960         g.setColor(Color.white);
961         g.setFont(new Font("Verdana", Font.BOLD, 14));
962         g.drawString("Error loading file..." + pdbentry.getId(), 20,
963                 currentSize.height / 2);
964       }
965       else
966       {
967         viewer.renderScreenImage(g, currentSize, rectClip);
968       }
969     }
970   }
971
972 }