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