2c2a97928d0d6819d651198b937c20209a02fa03
[jalview.git] / src / jalview / appletgui / AppletJmol.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
20 package jalview.appletgui;
21
22 import java.util.*;
23 import java.awt.*;
24 import java.awt.event.*;
25
26 import jalview.datamodel.*;
27 import jalview.structure.*;
28 import jalview.io.*;
29
30 import org.jmol.api.*;
31 import org.jmol.adapter.smarter.SmarterJmolAdapter;
32
33 import org.jmol.popup.*;
34 import jalview.schemes.*;
35
36
37 public class AppletJmol extends Frame
38     implements  StructureListener, JmolStatusListener,
39     KeyListener, ActionListener, ItemListener
40
41 {
42   Menu fileMenu = new Menu("File");
43   Menu viewMenu = new Menu("View");
44   Menu coloursMenu = new Menu("Colours");
45   Menu chainMenu = new Menu("Show Chain");
46   Menu helpMenu = new Menu("Help");
47   MenuItem mappingMenuItem = new MenuItem("View Mapping");
48
49   CheckboxMenuItem seqColour = new CheckboxMenuItem("By Sequence", true);
50   MenuItem chain = new MenuItem("By Chain");
51   MenuItem charge = new MenuItem("Charge & Cysteine");
52   MenuItem zappo = new MenuItem("Zappo");
53   MenuItem taylor = new MenuItem("Taylor");
54   MenuItem hydro = new MenuItem("Hydrophobicity");
55   MenuItem helix = new MenuItem("Helix Propensity");
56   MenuItem strand = new MenuItem("Strand Propensity");
57   MenuItem turn = new MenuItem("Turn Propensity");
58   MenuItem buried = new MenuItem("Buried Index");
59   MenuItem user = new MenuItem("User Defined Colours");
60
61   MenuItem jmolHelp = new MenuItem("Jmol Help");
62
63   JmolViewer viewer;
64   JmolPopup jmolpopup;
65
66   Panel scriptWindow;
67   TextField inputLine;
68   TextArea history;
69   SequenceI[] sequence;
70   StructureSelectionManager ssm;
71   RenderPanel renderPanel;
72   AlignmentPanel ap;
73   String fileLoadingError;
74   boolean loadedInline;
75   PDBEntry pdbentry;
76   boolean colourBySequence = true;
77   Vector atomsPicked = new Vector();
78
79   public AppletJmol(PDBEntry pdbentry,
80                     SequenceI[] seq,
81                     AlignmentPanel ap,
82                     String protocol)
83   {
84     this.ap = ap;
85     this.sequence = seq;
86     this.pdbentry = pdbentry;
87
88    String alreadyMapped = StructureSelectionManager
89         .getStructureSelectionManager()
90         .alreadyMappedToFile(pdbentry.getId());
91
92     if (alreadyMapped != null)
93     {
94        StructureSelectionManager.getStructureSelectionManager()
95             .setMapping(seq, pdbentry.getFile(), protocol);
96         return;
97     }
98
99     renderPanel = new RenderPanel();
100
101     this.add(renderPanel, BorderLayout.CENTER);
102     viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter());
103
104     viewer.setAppletContext("jalview",
105                        ap.av.applet.getDocumentBase(),
106                             ap.av.applet.getCodeBase(),
107                             null);
108
109     viewer.setJmolStatusListener(this);
110
111     jmolpopup = JmolPopup.newJmolPopup(viewer);
112
113     this.addWindowListener(new WindowAdapter()
114         {
115           public void windowClosing(WindowEvent evt)
116           {
117             closeViewer();
118           }
119         });
120
121     MenuBar menuBar = new MenuBar();
122     menuBar.add(fileMenu);
123     fileMenu.add(mappingMenuItem);
124     menuBar.add(viewMenu);
125     mappingMenuItem.addActionListener(this);
126     viewMenu.add(chainMenu);
127     menuBar.add(coloursMenu);
128     menuBar.add(helpMenu);
129
130     charge.addActionListener(this);
131     hydro.addActionListener(this);
132     chain.addActionListener(this);
133     seqColour.addItemListener(this);
134     zappo.addActionListener(this);
135     taylor.addActionListener(this);
136     helix.addActionListener(this);
137     strand.addActionListener(this);
138     turn.addActionListener(this);
139     buried.addActionListener(this);
140     user.addActionListener(this);
141
142     jmolHelp.addActionListener(this);
143
144     coloursMenu.add(seqColour);
145     coloursMenu.add(chain);
146     coloursMenu.add(charge);
147     coloursMenu.add(zappo);
148     coloursMenu.add(taylor);
149     coloursMenu.add(hydro);
150     coloursMenu.add(helix);
151     coloursMenu.add(strand);
152     coloursMenu.add(turn);
153     coloursMenu.add(buried);
154     coloursMenu.add(user);
155
156     helpMenu.add(jmolHelp);
157
158     this.setMenuBar(menuBar);
159
160     if(pdbentry.getFile()!=null)
161     {
162       if (protocol.equals(AppletFormatAdapter.PASTE))
163         loadInline(pdbentry.getFile());
164       else
165           viewer.openFile(pdbentry.getFile());
166     }
167
168     this.setBounds(400, 400, 400, 400);
169
170     this.setVisible(true);
171   }
172
173   public void loadInline(String string)
174   {
175     loadedInline = true;
176     viewer.openStringInline(string);
177   }
178
179
180   void setChainMenuItems(Vector chains)
181   {
182     chainMenu.removeAll();
183
184     MenuItem menuItem = new MenuItem("All");
185     menuItem.addActionListener(this);
186
187     chainMenu.add(menuItem);
188
189     CheckboxMenuItem menuItemCB;
190     for (int c = 0; c < chains.size(); c++)
191     {
192       menuItemCB = new CheckboxMenuItem(chains.elementAt(c).toString(), true);
193       menuItemCB.addItemListener(this);
194       chainMenu.add(menuItemCB);
195     }
196   }
197
198   boolean allChainsSelected = false;
199   void centerViewer()
200   {
201     StringBuffer cmd = new StringBuffer();
202     for (int i = 0; i < chainMenu.getItemCount(); i++)
203     {
204       if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
205       {
206         CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i);
207         if (item.getState())
208           cmd.append(":" + item.getLabel() + " or ");
209       }
210     }
211
212     if (cmd.length() > 0)
213       cmd.setLength(cmd.length() - 4);
214
215     viewer.evalString("select *;restrict "
216                       + cmd + ";cartoon;center " + cmd);
217   }
218
219
220   void closeViewer()
221   {
222     viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
223     viewer.evalStringQuiet("zap");
224     viewer.setJmolStatusListener(null);
225     viewer = null;
226
227     //We'll need to find out what other
228     // listeners need to be shut down in Jmol
229     StructureSelectionManager
230         .getStructureSelectionManager()
231         .removeStructureViewerListener(this, pdbentry.getId());
232
233     this.setVisible(false);
234   }
235
236   public void actionPerformed(ActionEvent evt)
237   {
238     if(evt.getSource()==mappingMenuItem)
239     {
240       jalview.appletgui.CutAndPasteTransfer cap
241           = new jalview.appletgui.CutAndPasteTransfer(false, null);
242       Frame frame = new Frame();
243       frame.add(cap);
244
245       jalview.bin.JalviewLite.addFrame(frame, "PDB - Sequence Mapping", 550,
246                                        600);
247       cap.setText(
248           StructureSelectionManager.getStructureSelectionManager().printMapping(
249               pdbentry.getFile())
250           );
251     }
252     else if (evt.getSource() == charge)
253     {
254       colourBySequence = false;
255       seqColour.setState(false);
256       viewer.evalStringQuiet("select *;color white;select ASP,GLU;color red;"
257                       +"select LYS,ARG;color blue;select CYS;color yellow");
258     }
259
260     else if (evt.getSource() == chain)
261     {
262       colourBySequence = false;
263       seqColour.setState(false);
264       viewer.evalStringQuiet("select *;color chain");
265     }
266     else if (evt.getSource() == zappo)
267     {
268       setJalviewColourScheme(new ZappoColourScheme());
269     }
270     else if (evt.getSource() == taylor)
271     {
272      setJalviewColourScheme(new TaylorColourScheme());
273     }
274     else if (evt.getSource() == hydro)
275     {
276       setJalviewColourScheme(new HydrophobicColourScheme());
277     }
278     else if (evt.getSource() == helix)
279     {
280       setJalviewColourScheme(new HelixColourScheme());
281     }
282     else if (evt.getSource() == strand)
283     {
284       setJalviewColourScheme(new StrandColourScheme());
285     }
286     else if (evt.getSource() == turn)
287     {
288       setJalviewColourScheme(new TurnColourScheme());
289     }
290     else if (evt.getSource() == buried)
291     {
292       setJalviewColourScheme(new BuriedColourScheme());
293     }
294     else if (evt.getSource() == user)
295     {
296       new UserDefinedColours(this);
297     }
298     else if(evt.getSource() == jmolHelp)
299     {
300       try{
301         ap.av.applet.getAppletContext().showDocument(
302             new java.net.URL("http://jmol.sourceforge.net/docs/JmolUserGuide/"),
303             "jmolHelp");
304       }catch(java.net.MalformedURLException ex){}
305     }
306     else
307     {
308       allChainsSelected = true;
309       for (int i = 0; i < chainMenu.getItemCount(); i++)
310       {
311         if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
312           ( (CheckboxMenuItem) chainMenu.getItem(i)).setState(true);
313       }
314       centerViewer();
315       allChainsSelected = false;
316     }
317   }
318
319   public void setJalviewColourScheme(ColourSchemeI cs)
320   {
321     colourBySequence = false;
322     seqColour.setState(false);
323
324     if(cs==null)
325       return;
326
327     String res;
328     int index;
329     Color col;
330
331     Enumeration en = ResidueProperties.aa3Hash.keys();
332     StringBuffer command = new StringBuffer("select *;color white;");
333     while(en.hasMoreElements())
334     {
335       res = en.nextElement().toString();
336       index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
337       if(index>20)
338         continue;
339
340       col = cs.findColour(ResidueProperties.aa[index].charAt(0));
341
342       command.append("select "+res+";color["
343                         + col.getRed() + ","
344                         + col.getGreen() + ","
345                         + col.getBlue() + "];");
346     }
347
348     viewer.evalStringQuiet(command.toString());
349   }
350
351   public void itemStateChanged(ItemEvent evt)
352   {
353     if (evt.getSource() == seqColour)
354     {
355       colourBySequence = seqColour.getState();
356       colourBySequence(ap);
357     }
358     else if (!allChainsSelected)
359       centerViewer();
360   }
361
362   public void keyPressed(KeyEvent evt)
363   {
364     if (evt.getKeyCode() == KeyEvent.VK_ENTER
365         && scriptWindow.isVisible())
366     {
367       viewer.evalString(inputLine.getText());
368       history.append("\n$ "+inputLine.getText());
369       inputLine.setText("");
370     }
371
372   }
373
374   public void keyTyped(KeyEvent evt)
375   {  }
376
377   public void keyReleased(KeyEvent evt){}
378
379   //////////////////////////////////
380   ///StructureListener
381   public String getPdbFile()
382   {
383     return "???";
384   }
385
386
387
388   String lastMessage;
389   public void mouseOverStructure(int atomIndex, String strInfo)
390   {
391       int pdbResNum;
392
393       int chainSeparator = strInfo.indexOf(":");
394
395       if(chainSeparator==-1)
396         chainSeparator = strInfo.indexOf(".");
397
398       pdbResNum = Integer.parseInt(
399           strInfo.substring(strInfo.indexOf("]")+ 1, chainSeparator));
400
401       String chainId;
402
403       if (strInfo.indexOf(":") > -1)
404         chainId = strInfo.substring
405             (strInfo.indexOf(":")+1, strInfo.indexOf("."));
406       else
407       {
408         chainId = " ";
409       }
410
411       if (lastMessage == null || !lastMessage.equals(strInfo))
412         ssm.mouseOverStructure(pdbResNum, chainId, pdbentry.getFile());
413
414       lastMessage = strInfo;
415   }
416
417   StringBuffer resetLastRes = new StringBuffer();
418   StringBuffer eval = new StringBuffer();
419
420   public void highlightAtom(int atomIndex, int pdbResNum, String chain, String pdbfile)
421   {
422     if (!pdbfile.equals(pdbentry.getFile()))
423       return;
424
425     if (resetLastRes.length() > 0)
426     {
427       viewer.evalStringQuiet(resetLastRes.toString());
428     }
429
430     eval.setLength(0);
431     eval.append("select " + pdbResNum);
432
433     resetLastRes.setLength(0);
434     resetLastRes.append("select " + pdbResNum);
435
436     if (!chain.equals(" "))
437     {
438       eval.append(":" + chain);
439       resetLastRes.append(":" + chain);
440     }
441
442     eval.append(";color gold;wireframe 100");
443
444     Color col = new Color(viewer.getAtomArgb(atomIndex));
445
446     resetLastRes.append(";color["
447                         + col.getRed() + ","
448                         + col.getGreen() + ","
449                         + col.getBlue() + "];wireframe 0");
450
451     viewer.evalStringQuiet(eval.toString());
452
453   }
454
455   public void updateColours(Object source)
456   {
457     colourBySequence( (AlignmentPanel) source);
458   }
459
460 //End StructureListener
461 ////////////////////////////
462
463   FeatureRenderer fr;
464   public void colourBySequence(AlignmentPanel ap)
465   {
466     if(!colourBySequence)
467       return;
468
469
470     StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
471
472     if (mapping.length < 1)
473       return;
474
475     SequenceRenderer sr = ap.seqPanel.seqCanvas.getSequenceRenderer();
476
477     boolean showFeatures = false;
478     if (ap.av.showSequenceFeatures)
479     {
480       showFeatures = true;
481       if (fr == null)
482       {
483         fr = new jalview.appletgui.FeatureRenderer(ap.av);
484       }
485
486       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
487     }
488
489     StringBuffer command = new StringBuffer();
490
491     int lastPos = -1;
492     for (int s = 0; s < sequence.length; s++)
493     {
494       for (int m = 0; m < mapping.length; m++)
495       {
496         if (mapping[m].getSequence() == sequence[s])
497         {
498           for (int r = 0; r < sequence[s].getLength(); r++)
499           {
500             int pos = mapping[m].getPDBResNum(
501                 sequence[s].findPosition(r));
502
503             if (pos < 1 || pos==lastPos)
504               continue;
505
506             lastPos = pos;
507
508             Color col = sr.getResidueBoxColour(sequence[s], r);
509
510             if (showFeatures)
511               col = fr.findFeatureColour(col, sequence[s], r);
512
513             if (command.toString().endsWith(":" + mapping[m].getChain()+
514                                             ";color["
515                                             + col.getRed() + ","
516                                             + col.getGreen() + ","
517                                             + col.getBlue() + "]"))
518             {
519               command = condenseCommand(command.toString(), pos);
520               continue;
521             }
522
523             command.append(";select " + pos);
524
525             if (!mapping[m].getChain().equals(" "))
526             {
527               command.append(":" + mapping[m].getChain());
528             }
529
530             command.append(";color["
531                              + col.getRed() + ","
532                              + col.getGreen() + ","
533                              + col.getBlue() + "]");
534
535           }
536           break;
537         }
538       }
539     }
540
541     viewer.evalStringQuiet(command.toString());
542   }
543
544   StringBuffer condenseCommand(String command, int pos)
545   {
546
547     StringBuffer sb = new StringBuffer(command.substring(0, command.lastIndexOf("select")+7));
548
549     command = command.substring(sb.length());
550
551     String start;
552
553     if (command.indexOf("-") > -1)
554     {
555       start = command.substring(0,command.indexOf("-"));
556     }
557     else
558     {
559       start = command.substring(0, command.indexOf(":"));
560     }
561
562     sb.append(start+"-"+pos+command.substring(command.indexOf(":")));
563
564     return sb;
565   }
566
567   /////////////////////////////////
568   //JmolStatusListener
569
570   public String eval(String strEval)
571   {
572    // System.out.println(strEval);
573    //"# 'eval' is implemented only for the applet.";
574     return null;
575   }
576
577   public void createImage(String file, String type, int quality)
578   {}
579
580   public void setCallbackFunction(String callbackType,
581                                   String callbackFunction)
582   {}
583
584   public void notifyFileLoaded(String fullPathName, String fileName,
585                                String modelName, Object clientFile,
586                                String errorMsg)
587   {
588     if(errorMsg!=null)
589     {
590       fileLoadingError = errorMsg;
591       repaint();
592       return;
593     }
594
595     fileLoadingError = null;
596
597     if (fileName != null)
598     {
599       //FILE LOADED OK
600       jmolpopup.updateComputedMenus();
601             viewer.evalStringQuiet(
602           "select backbone;restrict;cartoon;wireframe off;spacefill off");
603
604       ssm = StructureSelectionManager.getStructureSelectionManager();
605
606       MCview.PDBfile pdb;
607       if (loadedInline)
608       {
609         pdb = ssm.setMapping(sequence,
610                                 pdbentry.getFile(),
611                                 AppletFormatAdapter.PASTE);
612         pdbentry.setFile("INLINE"+pdb.id);
613       }
614       else
615       {
616          pdb = ssm.setMapping(sequence,
617                               pdbentry.getFile(),
618                               AppletFormatAdapter.URL);
619       }
620
621       pdbentry.setId(pdb.id);
622
623       ssm.addStructureViewerListener(this);
624
625       Vector chains = new Vector();
626       for (int i = 0; i < pdb.chains.size(); i++)
627       {
628         chains.addElement( ( (MCview.PDBChain) pdb.chains.elementAt(i)).id);
629       }
630       setChainMenuItems(chains);
631
632       colourBySequence(ap);
633
634       StringBuffer title = new StringBuffer(sequence[0].getName() + ":" +
635                                             pdbentry.getId());
636
637       if (pdbentry.getProperty() != null)
638       {
639         if (pdbentry.getProperty().get("method") != null)
640         {
641           title.append(" Method: ");
642           title.append(pdbentry.getProperty().get("method"));
643         }
644         if (pdbentry.getProperty().get("chains") != null)
645         {
646           title.append(" Chain:");
647           title.append(pdbentry.getProperty().get("chains"));
648         }
649       }
650
651       this.setTitle(title.toString());
652
653     }
654     else
655       return;
656   }
657
658   public void notifyFrameChanged(int frameNo)
659   {
660     boolean isAnimationRunning = (frameNo <= -2);
661   }
662
663   public void notifyScriptStart(String statusMessage, String additionalInfo)
664   {}
665
666   public void sendConsoleEcho(String strEcho)
667   {
668     if (scriptWindow == null)
669       showConsole(true);
670
671     history.append("\n"+strEcho);
672   }
673
674   public void sendConsoleMessage(String strStatus)
675   {
676     if(history!=null && strStatus!=null
677        && !strStatus.equals("Script completed"))
678     {
679       history.append("\n"+strStatus);
680     }
681   }
682
683   public void notifyScriptTermination(String strStatus, int msWalltime)
684   {  }
685
686   public void handlePopupMenu(int x, int y)
687   {
688     jmolpopup.show(x, y);
689   }
690
691   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
692   {
693     notifyAtomPicked(iatom, strMeasure);
694   }
695
696   public void notifyNewDefaultModeMeasurement(int count, String strInfo)
697   {}
698
699   public void notifyAtomPicked(int atomIndex, String strInfo)
700   {
701
702     int chainSeparator = strInfo.indexOf(":");
703
704     if(chainSeparator==-1)
705       chainSeparator = strInfo.indexOf(".");
706
707     String picked =
708         strInfo.substring(strInfo.indexOf("]")+ 1, chainSeparator);
709
710
711     if (strInfo.indexOf(":") > -1)
712       picked+=strInfo.substring(strInfo.indexOf(":")+1,
713                                strInfo.indexOf("."));
714
715     picked+=".C";
716
717     if (!atomsPicked.contains(picked))
718     {
719       viewer.evalString("select "+picked+";label %n %r:%c");
720       atomsPicked.addElement(picked);
721     }
722     else
723     {
724       viewer.evalString("select "+picked+";label off");
725       atomsPicked.removeElement(picked);
726     }
727   }
728
729   public void notifyAtomHovered(int atomIndex, String strInfo)
730   {
731     mouseOverStructure(atomIndex, strInfo);
732   }
733
734   public void sendSyncScript(String script, String appletName)
735   {}
736
737   public void showUrl(String url)
738   {
739     try{
740       ap.av.applet.getAppletContext().showDocument(new java.net.URL(url),
741           "jmolOutput");
742     }catch(java.net.MalformedURLException ex)
743     {}
744   }
745
746   public void showConsole(boolean showConsole)
747   {
748     if (scriptWindow == null)
749     {
750       scriptWindow = new Panel(new BorderLayout());
751       inputLine = new TextField();
752       history = new TextArea(5, 40);
753       scriptWindow.add(history, BorderLayout.CENTER);
754       scriptWindow.add(inputLine, BorderLayout.SOUTH);
755       add(scriptWindow, BorderLayout.SOUTH);
756       scriptWindow.setVisible(false);
757       history.setEditable(false);
758       inputLine.addKeyListener(this);
759     }
760
761     scriptWindow.setVisible(!scriptWindow.isVisible());
762     validate();
763   }
764
765   public float functionXY(String functionName, int x, int y)
766   {
767     return 0;
768   }
769
770   ///End JmolStatusListener
771   ///////////////////////////////
772
773
774   class RenderPanel
775       extends Panel
776   {
777     Dimension currentSize = new Dimension();
778     Rectangle rectClip = new Rectangle();
779
780     public void update(Graphics g) {
781       paint(g);
782     }
783     public void paint(Graphics g)
784     {
785       currentSize = this.getSize();
786       rectClip = g.getClipBounds();
787
788       if (viewer == null)
789       {
790         g.setColor(Color.black);
791         g.fillRect(0, 0, currentSize.width, currentSize.height);
792         g.setColor(Color.white);
793         g.setFont(new Font("Verdana", Font.BOLD, 14));
794         g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);
795       }
796       else
797       {
798         viewer.renderScreenImage(g, currentSize, rectClip);
799       }
800     }
801   }
802
803 }