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