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