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