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