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