na pdb: highlight any part of a residue, not just the CA atom
[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     // TODO: rna: remove CA dependency in select string
600     if (!pdbfile.equals(pdbentry.getFile()))
601       return;
602
603     if (resetLastRes.length() > 0)
604     {
605       viewer.evalStringQuiet(resetLastRes.toString());
606     }
607
608     eval.setLength(0);
609     eval.append("select " + pdbResNum);
610
611     resetLastRes.setLength(0);
612     resetLastRes.append("select " + pdbResNum);
613
614     if (!chain.equals(" "))
615     {
616       eval.append(":" + chain);
617       resetLastRes.append(":" + chain);
618     }
619
620     eval.append(";wireframe 100;" + eval.toString() + ".*;");
621
622     resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
623             + ".*;spacefill 0;");
624
625     eval.append("spacefill 200;select none");
626
627     viewer.evalStringQuiet(eval.toString());
628   }
629
630   public Color getColour(int atomIndex, int pdbResNum, String chain,
631           String pdbfile)
632   {
633     if (!pdbfile.equals(pdbentry.getFile()))
634       return null;
635
636     return new Color(viewer.getAtomArgb(atomIndex));
637   }
638
639   public void updateColours(Object source)
640   {
641     colourBySequence((AlignmentPanel) source);
642   }
643
644   // End StructureListener
645   // //////////////////////////
646
647   String lastCommand;
648
649   FeatureRenderer fr = null;
650
651   public void colourBySequence(AlignmentPanel sourceap)
652   {
653     this.ap = sourceap;
654
655     if (!colourBySequence || ap.alignFrame.getCurrentView() != ap.av)
656       return;
657
658     StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
659
660     if (mapping.length < 1)
661       return;
662
663     SequenceRenderer sr = new SequenceRenderer(ap.av);
664
665     boolean showFeatures = false;
666
667     if (ap.av.showSequenceFeatures)
668     {
669       showFeatures = true;
670       if (fr == null)
671       {
672         fr = new jalview.gui.FeatureRenderer(ap);
673       }
674
675       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
676     }
677
678     StringBuffer command = new StringBuffer();
679
680     int lastPos = -1;
681     for (int sp, s = 0; s < sequence.length; s++)
682     {
683       for (int m = 0; m < mapping.length; m++)
684       {
685         if (mapping[m].getSequence() == sequence[s]
686                 && (sp = ap.av.alignment.findIndex(sequence[s])) > -1)
687         {
688           SequenceI asp = ap.av.alignment.getSequenceAt(sp);
689           for (int r = 0; r < asp.getLength(); r++)
690           {
691             // No mapping to gaps in sequence.
692             if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
693             {
694               continue;
695             }
696             int pos = mapping[m].getPDBResNum(asp.findPosition(r));
697
698             if (pos < 1 || pos == lastPos)
699               continue;
700
701             lastPos = pos;
702
703             Color col = sr.getResidueBoxColour(asp, r);
704
705             if (showFeatures)
706               col = fr.findFeatureColour(col, asp, r);
707
708             if (command.toString().endsWith(
709                     ":" + mapping[m].getChain() + ";color[" + col.getRed()
710                             + "," + col.getGreen() + "," + col.getBlue()
711                             + "]"))
712             {
713               command = condenseCommand(command, pos);
714               continue;
715             }
716
717             command.append(";select " + pos);
718
719             if (!mapping[m].getChain().equals(" "))
720             {
721               command.append(":" + mapping[m].getChain());
722             }
723
724             command.append(";color[" + col.getRed() + "," + col.getGreen()
725                     + "," + col.getBlue() + "]");
726
727           }
728           break;
729         }
730       }
731     }
732
733     if (lastCommand == null || !lastCommand.equals(command.toString()))
734     {
735       viewer.evalStringQuiet(command.toString());
736     }
737     lastCommand = command.toString();
738   }
739
740   StringBuffer condenseCommand(StringBuffer command, int pos)
741   {
742     StringBuffer sb = new StringBuffer(command.substring(0, command
743             .lastIndexOf("select") + 7));
744
745     command.delete(0, sb.length());
746
747     String start;
748
749     if (command.indexOf("-") > -1)
750     {
751       start = command.substring(0, command.indexOf("-"));
752     }
753     else
754     {
755       start = command.substring(0, command.indexOf(":"));
756     }
757
758     sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
759
760     return sb;
761   }
762
763   // ///////////////////////////////
764   // JmolStatusListener
765
766   public String eval(String strEval)
767   {
768     // System.out.println(strEval);
769     // "# 'eval' is implemented only for the applet.";
770     return null;
771   }
772
773   public void createImage(String file, String type, int quality)
774   {
775     System.out.println("JMOL CREATE IMAGE");
776   }
777
778   public void setCallbackFunction(String callbackType,
779           String callbackFunction)
780   {
781   }
782
783   public void notifyFileLoaded(String fullPathName, String fileName,
784           String modelName, Object clientFile, String errorMsg)
785   {
786     if (errorMsg != null)
787     {
788       fileLoadingError = errorMsg;
789       repaint();
790       return;
791     }
792
793     fileLoadingError = null;
794
795     if (fileName != null)
796     {
797
798       // FILE LOADED OK
799       ssm = StructureSelectionManager.getStructureSelectionManager();
800       MCview.PDBfile pdbFile = ssm.setMapping(sequence, chains, pdbentry
801               .getFile(), AppletFormatAdapter.FILE);
802       ssm.addStructureViewerListener(this);
803       Vector chains = new Vector();
804       for (int i = 0; i < pdbFile.chains.size(); i++)
805       {
806         chains
807                 .addElement(((MCview.PDBChain) pdbFile.chains.elementAt(i)).id);
808       }
809       setChainMenuItems(chains);
810
811       jmolpopup.updateComputedMenus();
812
813       if (!loadingFromArchive)
814       {
815         viewer
816                 .evalStringQuiet("select backbone;restrict;cartoon;wireframe off;spacefill off");
817
818         colourBySequence(ap);
819       }
820       if (fr != null)
821         fr.featuresAdded();
822
823       loadingFromArchive = false;
824     }
825     else
826       return;
827   }
828
829   public void notifyFrameChanged(int frameNo)
830   {
831     boolean isAnimationRunning = (frameNo <= -2);
832   }
833
834   public void notifyScriptStart(String statusMessage, String additionalInfo)
835   {
836   }
837
838   public void sendConsoleEcho(String strEcho)
839   {
840     if (scriptWindow != null)
841       scriptWindow.sendConsoleEcho(strEcho);
842   }
843
844   public void sendConsoleMessage(String strStatus)
845   {
846     if (scriptWindow != null)
847       scriptWindow.sendConsoleMessage(strStatus);
848   }
849
850   public void notifyScriptTermination(String strStatus, int msWalltime)
851   {
852     if (scriptWindow != null)
853       scriptWindow.notifyScriptTermination(strStatus, msWalltime);
854   }
855
856   public void handlePopupMenu(int x, int y)
857   {
858     jmolpopup.show(x, y);
859   }
860
861   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
862   {
863     notifyAtomPicked(iatom, strMeasure);
864   }
865
866   public void notifyNewDefaultModeMeasurement(int count, String strInfo)
867   {
868   }
869
870   public void notifyAtomPicked(int atomIndex, String strInfo)
871   {
872     Matcher matcher = pattern.matcher(strInfo);
873     matcher.find();
874
875     matcher.group(1);
876     String resnum = new String(matcher.group(2));
877     String chainId = matcher.group(3);
878
879     String picked = resnum;
880
881     if (chainId != null)
882       picked += (":" + chainId.substring(1, chainId.length()));
883
884     picked += ".CA";
885
886     if (!atomsPicked.contains(picked))
887     {
888       if (chainId != null)
889         viewer.evalString("select " + picked + ";label %n %r:%c");
890       else
891         viewer.evalString("select " + picked + ";label %n %r");
892       atomsPicked.addElement(picked);
893     }
894     else
895     {
896       viewer.evalString("select " + picked + ";label off");
897       atomsPicked.removeElement(picked);
898     }
899
900     if (scriptWindow != null)
901     {
902       scriptWindow.sendConsoleMessage(strInfo);
903       scriptWindow.sendConsoleMessage("\n");
904     }
905   }
906
907   public void notifyAtomHovered(int atomIndex, String strInfo)
908   {
909     mouseOverStructure(atomIndex, strInfo);
910   }
911
912   public void sendSyncScript(String script, String appletName)
913   {
914   }
915
916   public void showUrl(String url)
917   {
918   }
919
920   public void showConsole(boolean showConsole)
921   {
922     if (scriptWindow == null)
923       scriptWindow = new ScriptWindow(this);
924
925     if (showConsole)
926     {
927       if (splitPane == null)
928       {
929         splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
930         splitPane.setTopComponent(renderPanel);
931         splitPane.setBottomComponent(scriptWindow);
932         this.getContentPane().add(splitPane, BorderLayout.CENTER);
933       }
934
935       splitPane.setDividerLocation(getHeight() - 200);
936       splitPane.validate();
937     }
938     else
939     {
940       if (splitPane != null)
941         splitPane.setVisible(false);
942
943       splitPane = null;
944
945       this.getContentPane().add(renderPanel, BorderLayout.CENTER);
946     }
947
948     validate();
949   }
950
951   public float functionXY(String functionName, int x, int y)
952   {
953     return 0;
954   }
955
956   // /End JmolStatusListener
957   // /////////////////////////////
958
959   class RenderPanel extends JPanel
960   {
961     final Dimension currentSize = new Dimension();
962
963     final Rectangle rectClip = new Rectangle();
964
965     public void paintComponent(Graphics g)
966     {
967       getSize(currentSize);
968       g.getClipBounds(rectClip);
969
970       if (viewer == null)
971       {
972         g.setColor(Color.black);
973         g.fillRect(0, 0, currentSize.width, currentSize.height);
974         g.setColor(Color.white);
975         g.setFont(new Font("Verdana", Font.BOLD, 14));
976         g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);
977       }
978       else if (fileLoadingError != null)
979       {
980         g.setColor(Color.black);
981         g.fillRect(0, 0, currentSize.width, currentSize.height);
982         g.setColor(Color.white);
983         g.setFont(new Font("Verdana", Font.BOLD, 14));
984         g.drawString("Error loading file..." + pdbentry.getId(), 20,
985                 currentSize.height / 2);
986       }
987       else
988       {
989         viewer.renderScreenImage(g, currentSize, rectClip);
990       }
991     }
992   }
993
994   String viewId = null;
995
996   public String getViewId()
997   {
998     if (viewId == null)
999     {
1000       viewId = System.currentTimeMillis() + "." + this.hashCode();
1001     }
1002     return viewId;
1003   }
1004
1005 }