3d1d0fbfc5d139dfa655a2a545c46f266292a722
[jalview.git] / src / jalview / appletgui / AppletJmol.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.appletgui;
22
23 import java.util.*;
24 import java.awt.*;
25 import java.awt.event.*;
26
27 import jalview.api.SequenceStructureBinding;
28 import jalview.datamodel.*;
29 import jalview.structure.*;
30 import jalview.io.*;
31
32 import jalview.schemes.*;
33 import jalview.util.MessageManager;
34
35 public class AppletJmol extends EmbmenuFrame implements
36 // StructureListener,
37         KeyListener, ActionListener, ItemListener, SequenceStructureBinding
38
39 {
40   Menu fileMenu = new Menu(MessageManager.getString("action.file"));
41
42   Menu viewMenu = new Menu(MessageManager.getString("action.view"));
43
44   Menu coloursMenu = new Menu(MessageManager.getString("action.colour"));
45
46   Menu chainMenu = new Menu(MessageManager.getString("action.show_chain"));
47
48   Menu helpMenu = new Menu(MessageManager.getString("action.help"));
49
50   MenuItem mappingMenuItem = new MenuItem(
51           MessageManager.getString("label.view_mapping"));
52
53   CheckboxMenuItem seqColour = new CheckboxMenuItem(
54           MessageManager.getString("action.by_sequence"), true);
55
56   CheckboxMenuItem jmolColour = new CheckboxMenuItem(
57           MessageManager.getString("action.using_jmol"), false);
58
59   MenuItem chain = new MenuItem(MessageManager.getString("action.by_chain"));
60
61   MenuItem charge = new MenuItem(
62           MessageManager.getString("label.charge_cysteine"));
63
64   MenuItem zappo = new MenuItem(MessageManager.getString("label.zappo"));
65
66   MenuItem taylor = new MenuItem(MessageManager.getString("label.taylor"));
67
68   MenuItem hydro = new MenuItem(
69           MessageManager.getString("label.hydrophobicity"));
70
71   MenuItem helix = new MenuItem(
72           MessageManager.getString("label.helix_propensity"));
73
74   MenuItem strand = new MenuItem(
75           MessageManager.getString("label.strand_propensity"));
76
77   MenuItem turn = new MenuItem(
78           MessageManager.getString("label.turn_propensity"));
79
80   MenuItem buried = new MenuItem(
81           MessageManager.getString("label.buried_index"));
82
83   MenuItem purinepyrimidine = new MenuItem(
84           MessageManager.getString("label.purine_pyrimidine"));
85
86   MenuItem user = new MenuItem(
87           MessageManager.getString("label.user_defined_colours"));
88
89   MenuItem jmolHelp = new MenuItem(
90           MessageManager.getString("label.jmol_help"));
91
92   Panel scriptWindow;
93
94   TextField inputLine;
95
96   TextArea history;
97
98   RenderPanel renderPanel;
99
100   AlignmentPanel ap;
101
102   ArrayList _aps = new ArrayList();
103
104   String fileLoadingError;
105
106   boolean loadedInline;
107
108   // boolean colourBySequence = true;
109
110   FeatureRenderer fr = null;
111
112   AppletJmolBinding jmb;
113
114   /**
115    * datasource protocol for access to PDBEntry
116    */
117   String protocol = null;
118
119   /**
120    * Load a bunch of pdb entries associated with sequences in the alignment and
121    * display them - aligning them if necessary.
122    * 
123    * @param pdbentries
124    *          each pdb file (at least one needed)
125    * @param boundseqs
126    *          each set of sequences for each pdb file (must match number of pdb
127    *          files)
128    * @param boundchains
129    *          the target pdb chain corresponding with each sequence associated
130    *          with each pdb file (may be null at any level)
131    * @param align
132    *          true/false
133    * @param ap
134    *          associated alignment
135    * @param protocol
136    *          how to get pdb data
137    */
138   public AppletJmol(PDBEntry[] pdbentries, SequenceI[][] boundseqs,
139           String[][] boundchains, boolean align, AlignmentPanel ap,
140           String protocol)
141   {
142     throw new Error("Not yet implemented.");
143   }
144
145   public AppletJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
146           AlignmentPanel ap, String protocol)
147   {
148     this.ap = ap;
149     jmb = new AppletJmolBinding(this, ap.getStructureSelectionManager(),
150             new PDBEntry[]
151             { pdbentry }, new SequenceI[][]
152             { seq }, new String[][]
153             { chains }, protocol);
154     jmb.setColourBySequence(true);
155     if (pdbentry.getId() == null || pdbentry.getId().length() < 1)
156     {
157       if (protocol.equals(AppletFormatAdapter.PASTE))
158       {
159         pdbentry.setId("PASTED PDB"
160                 + (chains == null ? "_" : chains.toString()));
161       }
162       else
163       {
164         pdbentry.setId(pdbentry.getFile());
165       }
166     }
167
168     if (jalview.bin.JalviewLite.debug)
169     {
170       System.err
171               .println("AppletJmol: PDB ID is '" + pdbentry.getId() + "'");
172     }
173
174     String alreadyMapped = StructureSelectionManager
175             .getStructureSelectionManager(ap.av.applet)
176             .alreadyMappedToFile(pdbentry.getId());
177     MCview.PDBfile reader = null;
178     if (alreadyMapped != null)
179     {
180       reader = StructureSelectionManager.getStructureSelectionManager(
181               ap.av.applet).setMapping(seq, chains, pdbentry.getFile(),
182               protocol);
183       // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW?
184       // FOR NOW, LETS JUST OPEN A NEW WINDOW
185     }
186     MenuBar menuBar = new MenuBar();
187     menuBar.add(fileMenu);
188     fileMenu.add(mappingMenuItem);
189     menuBar.add(viewMenu);
190     mappingMenuItem.addActionListener(this);
191     viewMenu.add(chainMenu);
192     menuBar.add(coloursMenu);
193     menuBar.add(helpMenu);
194
195     charge.addActionListener(this);
196     hydro.addActionListener(this);
197     chain.addActionListener(this);
198     seqColour.addItemListener(this);
199     jmolColour.addItemListener(this);
200     zappo.addActionListener(this);
201     taylor.addActionListener(this);
202     helix.addActionListener(this);
203     strand.addActionListener(this);
204     turn.addActionListener(this);
205     buried.addActionListener(this);
206     purinepyrimidine.addActionListener(this);
207     user.addActionListener(this);
208
209     jmolHelp.addActionListener(this);
210
211     coloursMenu.add(seqColour);
212     coloursMenu.add(chain);
213     coloursMenu.add(charge);
214     coloursMenu.add(zappo);
215     coloursMenu.add(taylor);
216     coloursMenu.add(hydro);
217     coloursMenu.add(helix);
218     coloursMenu.add(strand);
219     coloursMenu.add(turn);
220     coloursMenu.add(buried);
221     coloursMenu.add(purinepyrimidine);
222     coloursMenu.add(user);
223     coloursMenu.add(jmolColour);
224     helpMenu.add(jmolHelp);
225     this.setLayout(new BorderLayout());
226
227     setMenuBar(menuBar);
228
229     renderPanel = new RenderPanel();
230     embedMenuIfNeeded(renderPanel);
231     this.add(renderPanel, BorderLayout.CENTER);
232     scriptWindow = new Panel();
233     scriptWindow.setVisible(false);
234     // this.add(scriptWindow, BorderLayout.SOUTH);
235
236     try
237     {
238       jmb.allocateViewer(renderPanel, true, ap.av.applet.getName()
239               + "_jmol_", ap.av.applet.getDocumentBase(),
240               ap.av.applet.getCodeBase(), "-applet", scriptWindow, null);
241     } catch (Exception e)
242     {
243       System.err
244               .println("Couldn't create a jmol viewer. Args to allocate viewer were:\nDocumentBase="
245                       + ap.av.applet.getDocumentBase()
246                       + "\nCodebase="
247                       + ap.av.applet.getCodeBase());
248       e.printStackTrace();
249       dispose();
250       return;
251     }
252     jmb.newJmolPopup(true, "Jmol", true);
253
254     this.addWindowListener(new WindowAdapter()
255     {
256       public void windowClosing(WindowEvent evt)
257       {
258         closeViewer();
259       }
260     });
261     if (pdbentry.getProperty() == null)
262     {
263       pdbentry.setProperty(new Hashtable());
264       pdbentry.getProperty().put("protocol", protocol);
265     }
266     if (pdbentry.getFile() != null)
267     {
268       // import structure data from pdbentry.getFile based on given protocol
269       if (protocol.equals(AppletFormatAdapter.PASTE))
270       {
271         // TODO: JAL-623 : correctly record file contents for matching up later
272         // pdbentry.getProperty().put("pdbfilehash",""+pdbentry.getFile().hashCode());
273         loadInline(pdbentry.getFile());
274       }
275       else if (protocol.equals(AppletFormatAdapter.FILE)
276               || protocol.equals(AppletFormatAdapter.URL))
277       {
278         jmb.viewer.openFile(pdbentry.getFile());
279       }
280       else
281       {
282         // probably CLASSLOADER based datasource..
283         // Try and get a reader on the datasource, and pass that to Jmol
284         try
285         {
286           java.io.Reader freader = null;
287           if (reader != null)
288           {
289             if (jalview.bin.JalviewLite.debug)
290             {
291               System.err
292                       .println("AppletJmol:Trying to reuse existing PDBfile IO parser.");
293             }
294             // re-use the one we opened earlier
295             freader = reader.getReader();
296           }
297           if (freader == null)
298           {
299             if (jalview.bin.JalviewLite.debug)
300             {
301               System.err
302                       .println("AppletJmol:Creating new PDBfile IO parser.");
303             }
304             FileParse fp = new FileParse(pdbentry.getFile(), protocol);
305             fp.mark();
306             // reader = new MCview.PDBfile(fp);
307             // could set ID, etc.
308             // if (!reader.isValid())
309             // {
310             // throw new Exception("Invalid datasource.
311             // "+reader.getWarningMessage());
312             // }
313             // fp.reset();
314             freader = fp.getReader();
315           }
316           if (freader == null)
317           {
318             throw new Exception(
319                     "Invalid datasource. Could not obtain Reader.");
320           }
321           jmb.viewer.openReader(pdbentry.getFile(), pdbentry.getId(),
322                   freader);
323         } catch (Exception e)
324         {
325           // give up!
326           System.err.println("Couldn't access pdbentry id="
327                   + pdbentry.getId() + " and file=" + pdbentry.getFile()
328                   + " using protocol=" + protocol);
329           e.printStackTrace();
330         }
331       }
332     }
333
334     jalview.bin.JalviewLite.addFrame(this, jmb.getViewerTitle(), 400, 400);
335   }
336
337   public void loadInline(String string)
338   {
339     loadedInline = true;
340     jmb.loadInline(string);
341   }
342
343   void setChainMenuItems(Vector chains)
344   {
345     chainMenu.removeAll();
346
347     MenuItem menuItem = new MenuItem(MessageManager.getString("label.all"));
348     menuItem.addActionListener(this);
349
350     chainMenu.add(menuItem);
351
352     CheckboxMenuItem menuItemCB;
353     for (int c = 0; c < chains.size(); c++)
354     {
355       menuItemCB = new CheckboxMenuItem(chains.elementAt(c).toString(),
356               true);
357       menuItemCB.addItemListener(this);
358       chainMenu.add(menuItemCB);
359     }
360   }
361
362   boolean allChainsSelected = false;
363
364   void centerViewer()
365   {
366     Vector toshow = new Vector();
367     String lbl;
368     int mlength, p, mnum;
369     for (int i = 0; i < chainMenu.getItemCount(); i++)
370     {
371       if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
372       {
373         CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i);
374         if (item.getState())
375         {
376           toshow.addElement(item.getLabel());
377         }
378       }
379     }
380     jmb.centerViewer(toshow);
381   }
382
383   void closeViewer()
384   {
385     jmb.closeViewer();
386     jmb = null;
387     this.setVisible(false);
388   }
389
390   public void actionPerformed(ActionEvent evt)
391   {
392     if (evt.getSource() == mappingMenuItem)
393     {
394       jalview.appletgui.CutAndPasteTransfer cap = new jalview.appletgui.CutAndPasteTransfer(
395               false, null);
396       Frame frame = new Frame();
397       frame.add(cap);
398
399       StringBuffer sb = new StringBuffer();
400       try
401       {
402         for (int s = 0; s < jmb.pdbentry.length; s++)
403         {
404           sb.append(jmb.printMapping(jmb.pdbentry[s].getFile()));
405           sb.append("\n");
406         }
407         cap.setText(sb.toString());
408       } catch (OutOfMemoryError ex)
409       {
410         frame.dispose();
411         System.err
412                 .println("Out of memory when trying to create dialog box with sequence-structure mapping.");
413         return;
414       }
415       jalview.bin.JalviewLite.addFrame(frame,
416               MessageManager.getString("label.pdb_sequence_mapping"), 550,
417               600);
418     }
419     else if (evt.getSource() == charge)
420     {
421       setEnabled(charge);
422       jmb.colourByCharge();
423     }
424
425     else if (evt.getSource() == chain)
426     {
427       setEnabled(chain);
428       jmb.colourByChain();
429     }
430     else if (evt.getSource() == zappo)
431     {
432       setEnabled(zappo);
433       jmb.setJalviewColourScheme(new ZappoColourScheme());
434     }
435     else if (evt.getSource() == taylor)
436     {
437       setEnabled(taylor);
438       jmb.setJalviewColourScheme(new TaylorColourScheme());
439     }
440     else if (evt.getSource() == hydro)
441     {
442       setEnabled(hydro);
443       jmb.setJalviewColourScheme(new HydrophobicColourScheme());
444     }
445     else if (evt.getSource() == helix)
446     {
447       setEnabled(helix);
448       jmb.setJalviewColourScheme(new HelixColourScheme());
449     }
450     else if (evt.getSource() == strand)
451     {
452       setEnabled(strand);
453       jmb.setJalviewColourScheme(new StrandColourScheme());
454     }
455     else if (evt.getSource() == turn)
456     {
457       setEnabled(turn);
458       jmb.setJalviewColourScheme(new TurnColourScheme());
459     }
460     else if (evt.getSource() == buried)
461     {
462       setEnabled(buried);
463       jmb.setJalviewColourScheme(new BuriedColourScheme());
464     }
465     else if (evt.getSource() == purinepyrimidine)
466     {
467       jmb.setJalviewColourScheme(new PurinePyrimidineColourScheme());
468     }
469     else if (evt.getSource() == user)
470     {
471       setEnabled(user);
472       new UserDefinedColours(this);
473     }
474     else if (evt.getSource() == jmolHelp)
475     {
476       try
477       {
478         ap.av.applet.getAppletContext().showDocument(
479                 new java.net.URL(
480                         "http://jmol.sourceforge.net/docs/JmolUserGuide/"),
481                 "jmolHelp");
482       } catch (java.net.MalformedURLException ex)
483       {
484       }
485     }
486     else
487     {
488       allChainsSelected = true;
489       for (int i = 0; i < chainMenu.getItemCount(); i++)
490       {
491         if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
492           ((CheckboxMenuItem) chainMenu.getItem(i)).setState(true);
493       }
494
495       centerViewer();
496       allChainsSelected = false;
497     }
498   }
499
500   /**
501    * tick or untick the seqColour menu entry or jmoColour entry depending upon
502    * if it was selected or not.
503    * 
504    * @param itm
505    */
506   private void setEnabled(MenuItem itm)
507   {
508     jmolColour.setState(itm == jmolColour);
509     seqColour.setState(itm == seqColour);
510     jmb.setColourBySequence(itm == seqColour);
511   }
512
513   public void itemStateChanged(ItemEvent evt)
514   {
515     if (evt.getSource() == jmolColour)
516     {
517       setEnabled(jmolColour);
518       jmb.setColourBySequence(false);
519     }
520     else if (evt.getSource() == seqColour)
521     {
522       setEnabled(seqColour);
523       jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
524     }
525     else if (!allChainsSelected)
526       centerViewer();
527   }
528
529   public void keyPressed(KeyEvent evt)
530   {
531     if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible())
532     {
533       jmb.eval(inputLine.getText());
534       history.append("\n$ " + inputLine.getText());
535       inputLine.setText("");
536     }
537
538   }
539
540   public void keyTyped(KeyEvent evt)
541   {
542   }
543
544   public void keyReleased(KeyEvent evt)
545   {
546   }
547
548   public void updateColours(Object source)
549   {
550     AlignmentPanel ap = (AlignmentPanel) source;
551     jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
552   }
553
554   public void updateTitleAndMenus()
555   {
556     if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
557     {
558       repaint();
559       return;
560     }
561     setChainMenuItems(jmb.chainNames);
562     jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
563
564     setTitle(jmb.getViewerTitle());
565   }
566
567   public void showUrl(String url)
568   {
569     try
570     {
571       ap.av.applet.getAppletContext().showDocument(new java.net.URL(url),
572               "jmolOutput");
573     } catch (java.net.MalformedURLException ex)
574     {
575     }
576   }
577
578   Panel splitPane = null;
579
580   public void showConsole(boolean showConsole)
581   {
582     if (showConsole)
583     {
584       remove(renderPanel);
585       splitPane = new Panel();
586
587       splitPane.setLayout(new java.awt.GridLayout(2, 1));
588       splitPane.add(renderPanel);
589       splitPane.add(scriptWindow);
590       scriptWindow.setVisible(true);
591       this.add(splitPane, BorderLayout.CENTER);
592       splitPane.setVisible(true);
593       splitPane.validate();
594     }
595     else
596     {
597       scriptWindow.setVisible(false);
598       remove(splitPane);
599       add(renderPanel, BorderLayout.CENTER);
600       splitPane = null;
601     }
602     validate();
603   }
604
605   public float[][] functionXY(String functionName, int x, int y)
606   {
607     return null;
608   }
609
610   // /End JmolStatusListener
611   // /////////////////////////////
612
613   class RenderPanel extends Panel
614   {
615     Dimension currentSize = new Dimension();
616
617     Rectangle rectClip = new Rectangle();
618
619     public void update(Graphics g)
620     {
621       paint(g);
622     }
623
624     public void paint(Graphics g)
625     {
626       currentSize = this.getSize();
627       rectClip = g.getClipBounds();
628
629       if (jmb.viewer == null)
630       {
631         g.setColor(Color.black);
632         g.fillRect(0, 0, currentSize.width, currentSize.height);
633         g.setColor(Color.white);
634         g.setFont(new Font("Verdana", Font.BOLD, 14));
635         g.drawString(MessageManager.getString("label.retrieving_pdb_data"),
636                 20, currentSize.height / 2);
637       }
638       else
639       {
640         jmb.viewer.renderScreenImage(g, currentSize, rectClip);
641       }
642     }
643   }
644
645   /*
646    * @Override public Color getColour(int atomIndex, int pdbResNum, String
647    * chain, String pdbId) { return jmb.getColour(atomIndex, pdbResNum, chain,
648    * pdbId); }
649    * 
650    * @Override public String[] getPdbFile() { return jmb.getPdbFile(); }
651    * 
652    * @Override public void highlightAtom(int atomIndex, int pdbResNum, String
653    * chain, String pdbId) { jmb.highlightAtom(atomIndex, pdbResNum, chain,
654    * pdbId);
655    * 
656    * }
657    * 
658    * @Override public void mouseOverStructure(int atomIndex, String strInfo) {
659    * jmb.mouseOverStructure(atomIndex, strInfo);
660    * 
661    * }
662    */
663   public void setJalviewColourScheme(UserColourScheme ucs)
664   {
665     jmb.setJalviewColourScheme(ucs);
666   }
667
668   public AlignmentPanel getAlignmentPanelFor(AlignmentI alignment)
669   {
670     for (int i = 0; i < _aps.size(); i++)
671     {
672       if (((AlignmentPanel) _aps.get(i)).av.getAlignment() == alignment)
673       {
674         return ((AlignmentPanel) _aps.get(i));
675       }
676     }
677     return ap;
678   }
679 }