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