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