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