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