refactor to abstract jmol/jalview binding and applet and application specific sequenc...
[jalview.git] / src / jalview / appletgui / AppletJmol.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.appletgui;
19
20 import java.util.*;
21 import java.awt.*;
22 import java.awt.event.*;
23
24 import jalview.api.SequenceStructureBinding;
25 import jalview.datamodel.*;
26 import jalview.structure.*;
27 import jalview.io.*;
28
29 import org.jmol.api.*;
30
31 import org.jmol.popup.*;
32 import org.jmol.viewer.JmolConstants;
33
34 import jalview.schemes.*;
35
36 public class AppletJmol extends EmbmenuFrame implements 
37 // StructureListener,
38         KeyListener, ActionListener, ItemListener, SequenceStructureBinding
39
40 {
41   Menu fileMenu = new Menu("File");
42
43   Menu viewMenu = new Menu("View");
44
45   Menu coloursMenu = new Menu("Colours");
46
47   Menu chainMenu = new Menu("Show Chain");
48
49   Menu helpMenu = new Menu("Help");
50
51   MenuItem mappingMenuItem = new MenuItem("View Mapping");
52
53   CheckboxMenuItem seqColour = new CheckboxMenuItem("By Sequence", true);
54
55   MenuItem chain = new MenuItem("By Chain");
56
57   MenuItem charge = new MenuItem("Charge & Cysteine");
58
59   MenuItem zappo = new MenuItem("Zappo");
60
61   MenuItem taylor = new MenuItem("Taylor");
62
63   MenuItem hydro = new MenuItem("Hydrophobicity");
64
65   MenuItem helix = new MenuItem("Helix Propensity");
66
67   MenuItem strand = new MenuItem("Strand Propensity");
68
69   MenuItem turn = new MenuItem("Turn Propensity");
70
71   MenuItem buried = new MenuItem("Buried Index");
72
73   MenuItem user = new MenuItem("User Defined Colours");
74
75   MenuItem jmolHelp = new MenuItem("Jmol Help");
76
77   Panel scriptWindow;
78
79   TextField inputLine;
80
81   TextArea history;
82
83   RenderPanel renderPanel;
84
85   AlignmentPanel ap;
86
87   String fileLoadingError;
88
89   boolean loadedInline;
90
91   // boolean colourBySequence = true;
92
93   FeatureRenderer fr = null;
94
95   AppletJmolBinding jmb;
96
97   /**
98    * datasource protocol for access to PDBEntry
99    */
100   String protocol = null;
101
102   public AppletJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
103           AlignmentPanel ap, String protocol)
104   {
105     this.ap = ap;
106     jmb = new AppletJmolBinding(this, new PDBEntry[]
107     { pdbentry }, seq, chains, protocol);
108     jmb.setColourBySequence(true);
109     if (pdbentry.getId() == null || pdbentry.getId().length() < 1)
110     {
111       if (protocol.equals(AppletFormatAdapter.PASTE))
112       {
113         pdbentry.setId("PASTED PDB"
114                 + (chains == null ? "_" : chains.toString()));
115       }
116       else
117       {
118         pdbentry.setId(pdbentry.getFile());
119       }
120     }
121
122     if (jalview.bin.JalviewLite.debug)
123     {
124       System.err
125               .println("AppletJmol: PDB ID is '" + pdbentry.getId() + "'");
126     }
127
128     String alreadyMapped = StructureSelectionManager
129             .getStructureSelectionManager().alreadyMappedToFile(
130                     pdbentry.getId());
131     MCview.PDBfile reader = null;
132     if (alreadyMapped != null)
133     {
134       reader = StructureSelectionManager.getStructureSelectionManager()
135               .setMapping(seq, chains, pdbentry.getFile(), protocol);
136       // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW?
137       // FOR NOW, LETS JUST OPEN A NEW WINDOW
138     }
139     MenuBar menuBar = new MenuBar();
140     menuBar.add(fileMenu);
141     fileMenu.add(mappingMenuItem);
142     menuBar.add(viewMenu);
143     mappingMenuItem.addActionListener(this);
144     viewMenu.add(chainMenu);
145     menuBar.add(coloursMenu);
146     menuBar.add(helpMenu);
147
148     charge.addActionListener(this);
149     hydro.addActionListener(this);
150     chain.addActionListener(this);
151     seqColour.addItemListener(this);
152     zappo.addActionListener(this);
153     taylor.addActionListener(this);
154     helix.addActionListener(this);
155     strand.addActionListener(this);
156     turn.addActionListener(this);
157     buried.addActionListener(this);
158     user.addActionListener(this);
159
160     jmolHelp.addActionListener(this);
161
162     coloursMenu.add(seqColour);
163     coloursMenu.add(chain);
164     coloursMenu.add(charge);
165     coloursMenu.add(zappo);
166     coloursMenu.add(taylor);
167     coloursMenu.add(hydro);
168     coloursMenu.add(helix);
169     coloursMenu.add(strand);
170     coloursMenu.add(turn);
171     coloursMenu.add(buried);
172     coloursMenu.add(user);
173
174     helpMenu.add(jmolHelp);
175
176     setMenuBar(menuBar);
177
178     renderPanel = new RenderPanel();
179     embedMenuIfNeeded(renderPanel);
180     this.add(renderPanel, BorderLayout.CENTER);
181     jmb.allocateViewer(renderPanel, 
182             "jalviewJmol", ap.av.applet.getDocumentBase(), ap.av.applet
183                     .getCodeBase(), "");
184     jmb.newJmolPopup(true, "Jmol", true);
185
186     this.addWindowListener(new WindowAdapter()
187     {
188       public void windowClosing(WindowEvent evt)
189       {
190         closeViewer();
191       }
192     });
193
194     if (pdbentry.getFile() != null)
195     {
196       // import structure data from pdbentry.getFile based on given protocol
197       if (protocol.equals(AppletFormatAdapter.PASTE))
198       {
199         loadInline(pdbentry.getFile());
200       }
201       else if (protocol.equals(AppletFormatAdapter.FILE)
202               || protocol.equals(AppletFormatAdapter.URL))
203       {
204         jmb.viewer.openFile(pdbentry.getFile());
205       }
206       else
207       {
208         // probably CLASSLOADER based datasource..
209         // Try and get a reader on the datasource, and pass that to Jmol
210         try
211         {
212           java.io.Reader freader = null;
213           if (reader != null)
214           {
215             if (jalview.bin.JalviewLite.debug)
216             {
217               System.err
218                       .println("AppletJmol:Trying to reuse existing PDBfile IO parser.");
219             }
220             // re-use the one we opened earlier
221             freader = reader.getReader();
222           }
223           if (freader == null)
224           {
225             if (jalview.bin.JalviewLite.debug)
226             {
227               System.err
228                       .println("AppletJmol:Creating new PDBfile IO parser.");
229             }
230             FileParse fp = new FileParse(pdbentry.getFile(), protocol);
231             fp.mark();
232             // reader = new MCview.PDBfile(fp);
233             // could set ID, etc.
234             // if (!reader.isValid())
235             // {
236             // throw new Exception("Invalid datasource.
237             // "+reader.getWarningMessage());
238             // }
239             // fp.reset();
240             freader = fp.getReader();
241           }
242           if (freader == null)
243           {
244             throw new Exception(
245                     "Invalid datasource. Could not obtain Reader.");
246           }
247           jmb.viewer.openReader(pdbentry.getFile(), pdbentry.getId(), freader);
248         } catch (Exception e)
249         {
250           // give up!
251           System.err.println("Couldn't access pdbentry id="
252                   + pdbentry.getId() + " and file=" + pdbentry.getFile()
253                   + " using protocol=" + protocol);
254           e.printStackTrace();
255         }
256       }
257     }
258
259     jalview.bin.JalviewLite.addFrame(this, jmb.getViewerTitle(), 400, 400);
260   }
261
262   /**
263    * create a new binding between structures in an existing jmol viewer instance
264    * and an alignpanel with sequences that have existing PDBFile entries. Note,
265    * this does not open a new Jmol window, or modify the display of the
266    * structures in the original jmol window.
267    * 
268    * @param viewer2
269    * @param alignPanel
270    * @param seqs
271    *          - sequences to search for associations
272    */
273   public AppletJmol(JmolViewer viewer2, AlignmentPanel alignPanel,
274           SequenceI[] seqs)
275   {
276
277     // TODO Auto-generated constructor stub
278   }
279
280   public void loadInline(String string)
281   {
282     loadedInline = true;
283     jmb.viewer.openStringInline(string);
284   }
285
286   void setChainMenuItems(Vector chains)
287   {
288     chainMenu.removeAll();
289
290     MenuItem menuItem = new MenuItem("All");
291     menuItem.addActionListener(this);
292
293     chainMenu.add(menuItem);
294
295     CheckboxMenuItem menuItemCB;
296     for (int c = 0; c < chains.size(); c++)
297     {
298       menuItemCB = new CheckboxMenuItem(chains.elementAt(c).toString(),
299               true);
300       menuItemCB.addItemListener(this);
301       chainMenu.add(menuItemCB);
302     }
303   }
304
305   boolean allChainsSelected = false;
306
307   void centerViewer()
308   {
309     Vector toshow = new Vector();
310     String lbl;
311     int mlength, p, mnum;
312     for (int i = 0; i < chainMenu.getItemCount(); i++)
313     {
314       if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
315       {
316         CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i);
317         if (item.getState())
318         {
319           toshow.addElement(item.getLabel());
320         }
321       }
322     }
323     jmb.centerViewer(toshow);
324   }
325
326   void closeViewer()
327   {
328     jmb.closeViewer();
329     jmb = null;
330     this.setVisible(false);
331   }
332
333   public void actionPerformed(ActionEvent evt)
334   {
335     if (evt.getSource() == mappingMenuItem)
336     {
337       jalview.appletgui.CutAndPasteTransfer cap = new jalview.appletgui.CutAndPasteTransfer(
338               false, null);
339       Frame frame = new Frame();
340       frame.add(cap);
341
342       jalview.bin.JalviewLite.addFrame(frame, "PDB - Sequence Mapping",
343               550, 600);
344       StringBuffer sb = new StringBuffer();
345       for (int s = 0; s < jmb.pdbentry.length; s++)
346       {
347         sb.append(StructureSelectionManager.getStructureSelectionManager()
348                 .printMapping(jmb.pdbentry[s].getFile()));
349         sb.append("\n");
350       }
351     }
352     else if (evt.getSource() == charge)
353     {
354       setEnabled(charge);
355       jmb.colourByCharge();
356     }
357
358     else if (evt.getSource() == chain)
359     {
360       setEnabled(chain);
361       jmb.colourByChain();
362     }
363     else if (evt.getSource() == zappo)
364     {
365       setEnabled(zappo);
366       jmb.setJalviewColourScheme(new ZappoColourScheme());
367     }
368     else if (evt.getSource() == taylor)
369     {
370       setEnabled(taylor);
371       jmb.setJalviewColourScheme(new TaylorColourScheme());
372     }
373     else if (evt.getSource() == hydro)
374     {
375       setEnabled(hydro);
376       jmb.setJalviewColourScheme(new HydrophobicColourScheme());
377     }
378     else if (evt.getSource() == helix)
379     {
380       setEnabled(helix);
381       jmb.setJalviewColourScheme(new HelixColourScheme());
382     }
383     else if (evt.getSource() == strand)
384     {
385       setEnabled(strand);
386       jmb.setJalviewColourScheme(new StrandColourScheme());
387     }
388     else if (evt.getSource() == turn)
389     {
390       setEnabled(turn);
391       jmb.setJalviewColourScheme(new TurnColourScheme());
392     }
393     else if (evt.getSource() == buried)
394     {
395       setEnabled(buried);
396       jmb.setJalviewColourScheme(new BuriedColourScheme());
397     }
398     else if (evt.getSource() == user)
399     {
400       setEnabled(user);
401       new UserDefinedColours(this);
402     }
403     else if (evt.getSource() == jmolHelp)
404     {
405       try
406       {
407         ap.av.applet.getAppletContext().showDocument(
408                 new java.net.URL(
409                         "http://jmol.sourceforge.net/docs/JmolUserGuide/"),
410                 "jmolHelp");
411       } catch (java.net.MalformedURLException ex)
412       {
413       }
414     }
415     else
416     {
417       allChainsSelected = true;
418       for (int i = 0; i < chainMenu.getItemCount(); i++)
419       {
420         if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
421           ((CheckboxMenuItem) chainMenu.getItem(i)).setState(true);
422       }
423
424       centerViewer();
425       allChainsSelected = false;
426     }
427   }
428
429   /**
430    * tick or untick the seqColour menu entry depending upon if it was selected or not.
431    * @param itm
432    */
433   private void setEnabled(MenuItem itm)
434   {
435     seqColour.setState(itm==seqColour);
436     jmb.setColourBySequence(itm==seqColour);
437   }
438
439   public void itemStateChanged(ItemEvent evt)
440   {
441     if (evt.getSource() == seqColour)
442     {
443       setEnabled(seqColour);
444       jmb
445               .colourBySequence(ap.av.getShowSequenceFeatures(),
446                       ap.av.alignment);
447     }
448     else if (!allChainsSelected)
449       centerViewer();
450   }
451
452   public void keyPressed(KeyEvent evt)
453   {
454     if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible())
455     {
456       jmb.eval(inputLine.getText());
457       history.append("\n$ " + inputLine.getText());
458       inputLine.setText("");
459     }
460
461   }
462
463   public void keyTyped(KeyEvent evt)
464   {
465   }
466
467   public void keyReleased(KeyEvent evt)
468   {
469   }
470
471   public void updateColours(Object source)
472   {
473     AlignmentPanel ap = (AlignmentPanel) source;
474     jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap.av.alignment);
475   }
476
477   public void updateTitleAndMenus()
478   {
479     if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
480     {
481       repaint();
482       return;
483     }
484     setChainMenuItems(jmb.chainNames);
485     jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap.av.alignment);
486
487     setTitle(jmb.getViewerTitle());
488   }
489
490   public void showUrl(String url)
491   {
492     try
493     {
494       ap.av.applet.getAppletContext().showDocument(new java.net.URL(url),
495               "jmolOutput");
496     } catch (java.net.MalformedURLException ex)
497     {
498     }
499   }
500
501   public void showConsole(boolean showConsole)
502   {
503     if (scriptWindow == null)
504     {
505       scriptWindow = new Panel(new BorderLayout());
506       inputLine = new TextField();
507       history = new TextArea(5, 40);
508       scriptWindow.add(history, BorderLayout.CENTER);
509       scriptWindow.add(inputLine, BorderLayout.SOUTH);
510       add(scriptWindow, BorderLayout.SOUTH);
511       scriptWindow.setVisible(false);
512       history.setEditable(false);
513       inputLine.addKeyListener(this);
514     }
515
516     scriptWindow.setVisible(!scriptWindow.isVisible());
517     validate();
518   }
519
520   public float[][] functionXY(String functionName, int x, int y)
521   {
522     return null;
523   }
524
525   // /End JmolStatusListener
526   // /////////////////////////////
527
528   class RenderPanel extends Panel
529   {
530     Dimension currentSize = new Dimension();
531
532     Rectangle rectClip = new Rectangle();
533
534     public void update(Graphics g)
535     {
536       paint(g);
537     }
538
539     public void paint(Graphics g)
540     {
541       currentSize = this.getSize();
542       rectClip = g.getClipBounds();
543
544       if (jmb.viewer == null)
545       {
546         g.setColor(Color.black);
547         g.fillRect(0, 0, currentSize.width, currentSize.height);
548         g.setColor(Color.white);
549         g.setFont(new Font("Verdana", Font.BOLD, 14));
550         g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);
551       }
552       else
553       {
554         jmb.viewer.renderScreenImage(g, currentSize, rectClip);
555       }
556     }
557   }
558 /*
559   @Override
560   public Color getColour(int atomIndex, int pdbResNum, String chain,
561           String pdbId)
562   {
563     return jmb.getColour(atomIndex, pdbResNum, chain, pdbId);
564   }
565
566   @Override
567   public String[] getPdbFile()
568   {
569     return jmb.getPdbFile();
570   }
571
572   @Override
573   public void highlightAtom(int atomIndex, int pdbResNum, String chain,
574           String pdbId)
575   {
576     jmb.highlightAtom(atomIndex, pdbResNum, chain, pdbId);
577
578   }
579
580   @Override
581   public void mouseOverStructure(int atomIndex, String strInfo)
582   {
583     jmb.mouseOverStructure(atomIndex, strInfo);
584
585   }
586 */
587   public void setJalviewColourScheme(UserColourScheme ucs)
588   {
589     jmb.setJalviewColourScheme(ucs);
590   }
591 }