42fbd70ffc954bfb4b81c3d2a2d7eaee80c3876a
[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
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(MessageManager.getString("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(MessageManager.getString("exception.invalid_datasource_couldnt_obtain_reader"));
319           }
320           jmb.viewer.openReader(pdbentry.getFile(), pdbentry.getId(),
321                   freader);
322         } catch (Exception e)
323         {
324           // give up!
325           System.err.println("Couldn't access pdbentry id="
326                   + pdbentry.getId() + " and file=" + pdbentry.getFile()
327                   + " using protocol=" + protocol);
328           e.printStackTrace();
329         }
330       }
331     }
332
333     jalview.bin.JalviewLite.addFrame(this, jmb.getViewerTitle(), 400, 400);
334   }
335
336   public void loadInline(String string)
337   {
338     loadedInline = true;
339     jmb.loadInline(string);
340   }
341
342   void setChainMenuItems(Vector chains)
343   {
344     chainMenu.removeAll();
345
346     MenuItem menuItem = new MenuItem(MessageManager.getString("label.all"));
347     menuItem.addActionListener(this);
348
349     chainMenu.add(menuItem);
350
351     CheckboxMenuItem menuItemCB;
352     for (int c = 0; c < chains.size(); c++)
353     {
354       menuItemCB = new CheckboxMenuItem(chains.elementAt(c).toString(),
355               true);
356       menuItemCB.addItemListener(this);
357       chainMenu.add(menuItemCB);
358     }
359   }
360
361   boolean allChainsSelected = false;
362
363   void centerViewer()
364   {
365     Vector toshow = new Vector();
366     String lbl;
367     int mlength, p, mnum;
368     for (int i = 0; i < chainMenu.getItemCount(); i++)
369     {
370       if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
371       {
372         CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i);
373         if (item.getState())
374         {
375           toshow.addElement(item.getLabel());
376         }
377       }
378     }
379     jmb.centerViewer(toshow);
380   }
381
382   void closeViewer()
383   {
384     jmb.closeViewer();
385     jmb = null;
386     this.setVisible(false);
387   }
388
389   public void actionPerformed(ActionEvent evt)
390   {
391     if (evt.getSource() == mappingMenuItem)
392     {
393       jalview.appletgui.CutAndPasteTransfer cap = new jalview.appletgui.CutAndPasteTransfer(
394               false, null);
395       Frame frame = new Frame();
396       frame.add(cap);
397
398       StringBuffer sb = new StringBuffer();
399       try
400       {
401         for (int s = 0; s < jmb.pdbentry.length; s++)
402         {
403           sb.append(jmb.printMapping(jmb.pdbentry[s].getFile()));
404           sb.append("\n");
405         }
406         cap.setText(sb.toString());
407       } catch (OutOfMemoryError ex)
408       {
409         frame.dispose();
410         System.err
411                 .println("Out of memory when trying to create dialog box with sequence-structure mapping.");
412         return;
413       }
414       jalview.bin.JalviewLite.addFrame(frame,
415               MessageManager.getString("label.pdb_sequence_mapping"), 550,
416               600);
417     }
418     else if (evt.getSource() == charge)
419     {
420       setEnabled(charge);
421       jmb.colourByCharge();
422     }
423
424     else if (evt.getSource() == chain)
425     {
426       setEnabled(chain);
427       jmb.colourByChain();
428     }
429     else if (evt.getSource() == zappo)
430     {
431       setEnabled(zappo);
432       jmb.setJalviewColourScheme(new ZappoColourScheme());
433     }
434     else if (evt.getSource() == taylor)
435     {
436       setEnabled(taylor);
437       jmb.setJalviewColourScheme(new TaylorColourScheme());
438     }
439     else if (evt.getSource() == hydro)
440     {
441       setEnabled(hydro);
442       jmb.setJalviewColourScheme(new HydrophobicColourScheme());
443     }
444     else if (evt.getSource() == helix)
445     {
446       setEnabled(helix);
447       jmb.setJalviewColourScheme(new HelixColourScheme());
448     }
449     else if (evt.getSource() == strand)
450     {
451       setEnabled(strand);
452       jmb.setJalviewColourScheme(new StrandColourScheme());
453     }
454     else if (evt.getSource() == turn)
455     {
456       setEnabled(turn);
457       jmb.setJalviewColourScheme(new TurnColourScheme());
458     }
459     else if (evt.getSource() == buried)
460     {
461       setEnabled(buried);
462       jmb.setJalviewColourScheme(new BuriedColourScheme());
463     }
464     else if (evt.getSource() == purinepyrimidine)
465     {
466       jmb.setJalviewColourScheme(new PurinePyrimidineColourScheme());
467     }
468     else if (evt.getSource() == user)
469     {
470       setEnabled(user);
471       new UserDefinedColours(this);
472     }
473     else if (evt.getSource() == jmolHelp)
474     {
475       try
476       {
477         ap.av.applet.getAppletContext().showDocument(
478                 new java.net.URL(
479                         "http://jmol.sourceforge.net/docs/JmolUserGuide/"),
480                 "jmolHelp");
481       } catch (java.net.MalformedURLException ex)
482       {
483       }
484     }
485     else
486     {
487       allChainsSelected = true;
488       for (int i = 0; i < chainMenu.getItemCount(); i++)
489       {
490         if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
491           ((CheckboxMenuItem) chainMenu.getItem(i)).setState(true);
492       }
493
494       centerViewer();
495       allChainsSelected = false;
496     }
497   }
498
499   /**
500    * tick or untick the seqColour menu entry or jmoColour entry depending upon
501    * if it was selected or not.
502    * 
503    * @param itm
504    */
505   private void setEnabled(MenuItem itm)
506   {
507     jmolColour.setState(itm == jmolColour);
508     seqColour.setState(itm == seqColour);
509     jmb.setColourBySequence(itm == seqColour);
510   }
511
512   public void itemStateChanged(ItemEvent evt)
513   {
514     if (evt.getSource() == jmolColour)
515     {
516       setEnabled(jmolColour);
517       jmb.setColourBySequence(false);
518     }
519     else if (evt.getSource() == seqColour)
520     {
521       setEnabled(seqColour);
522       jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
523     }
524     else if (!allChainsSelected)
525       centerViewer();
526   }
527
528   public void keyPressed(KeyEvent evt)
529   {
530     if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible())
531     {
532       jmb.eval(inputLine.getText());
533       history.append("\n$ " + inputLine.getText());
534       inputLine.setText("");
535     }
536
537   }
538
539   public void keyTyped(KeyEvent evt)
540   {
541   }
542
543   public void keyReleased(KeyEvent evt)
544   {
545   }
546
547   public void updateColours(Object source)
548   {
549     AlignmentPanel ap = (AlignmentPanel) source;
550     jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
551   }
552
553   public void updateTitleAndMenus()
554   {
555     if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
556     {
557       repaint();
558       return;
559     }
560     setChainMenuItems(jmb.chainNames);
561     jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
562
563     setTitle(jmb.getViewerTitle());
564   }
565
566   public void showUrl(String url)
567   {
568     try
569     {
570       ap.av.applet.getAppletContext().showDocument(new java.net.URL(url),
571               "jmolOutput");
572     } catch (java.net.MalformedURLException ex)
573     {
574     }
575   }
576
577   Panel splitPane = null;
578
579   public void showConsole(boolean showConsole)
580   {
581     if (showConsole)
582     {
583       remove(renderPanel);
584       splitPane = new Panel();
585
586       splitPane.setLayout(new java.awt.GridLayout(2, 1));
587       splitPane.add(renderPanel);
588       splitPane.add(scriptWindow);
589       scriptWindow.setVisible(true);
590       this.add(splitPane, BorderLayout.CENTER);
591       splitPane.setVisible(true);
592       splitPane.validate();
593     }
594     else
595     {
596       scriptWindow.setVisible(false);
597       remove(splitPane);
598       add(renderPanel, BorderLayout.CENTER);
599       splitPane = null;
600     }
601     validate();
602   }
603
604   public float[][] functionXY(String functionName, int x, int y)
605   {
606     return null;
607   }
608
609   // /End JmolStatusListener
610   // /////////////////////////////
611
612   class RenderPanel extends Panel
613   {
614     Dimension currentSize = new Dimension();
615
616     Rectangle rectClip = new Rectangle();
617
618     public void update(Graphics g)
619     {
620       paint(g);
621     }
622
623     public void paint(Graphics g)
624     {
625       currentSize = this.getSize();
626       rectClip = g.getClipBounds();
627
628       if (jmb.viewer == null)
629       {
630         g.setColor(Color.black);
631         g.fillRect(0, 0, currentSize.width, currentSize.height);
632         g.setColor(Color.white);
633         g.setFont(new Font("Verdana", Font.BOLD, 14));
634         g.drawString(MessageManager.getString("label.retrieving_pdb_data"),
635                 20, currentSize.height / 2);
636       }
637       else
638       {
639         jmb.viewer.renderScreenImage(g, currentSize, rectClip);
640       }
641     }
642   }
643
644   /*
645    * @Override public Color getColour(int atomIndex, int pdbResNum, String
646    * chain, String pdbId) { return jmb.getColour(atomIndex, pdbResNum, chain,
647    * pdbId); }
648    * 
649    * @Override public String[] getPdbFile() { return jmb.getPdbFile(); }
650    * 
651    * @Override public void highlightAtom(int atomIndex, int pdbResNum, String
652    * chain, String pdbId) { jmb.highlightAtom(atomIndex, pdbResNum, chain,
653    * pdbId);
654    * 
655    * }
656    * 
657    * @Override public void mouseOverStructure(int atomIndex, String strInfo) {
658    * jmb.mouseOverStructure(atomIndex, strInfo);
659    * 
660    * }
661    */
662   public void setJalviewColourScheme(UserColourScheme ucs)
663   {
664     jmb.setJalviewColourScheme(ucs);
665   }
666
667   public AlignmentPanel getAlignmentPanelFor(AlignmentI alignment)
668   {
669     for (int i = 0; i < _aps.size(); i++)
670     {
671       if (((AlignmentPanel) _aps.get(i)).av.getAlignment() == alignment)
672       {
673         return ((AlignmentPanel) _aps.get(i));
674       }
675     }
676     return ap;
677   }
678 }