improved debug output when Jmol viewer creation fails
[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     try
182     {
183       jmb.allocateViewer(renderPanel, "jalviewJmol",
184               ap.av.applet.getDocumentBase(), ap.av.applet.getCodeBase(),
185               "");
186     } catch (Exception e)
187     {
188       System.err
189               .println("Couldn't create a jmol viewer. Args to allocate viewer were:\nDocumentBase="
190                       + ap.av.applet.getDocumentBase()
191                       + "\nCodebase="
192                       + ap.av.applet.getCodeBase());
193       e.printStackTrace();
194       dispose();
195       return;
196     }
197     jmb.newJmolPopup(true, "Jmol", true);
198
199     this.addWindowListener(new WindowAdapter()
200     {
201       public void windowClosing(WindowEvent evt)
202       {
203         closeViewer();
204       }
205     });
206
207     if (pdbentry.getFile() != null)
208     {
209       // import structure data from pdbentry.getFile based on given protocol
210       if (protocol.equals(AppletFormatAdapter.PASTE))
211       {
212         loadInline(pdbentry.getFile());
213       }
214       else if (protocol.equals(AppletFormatAdapter.FILE)
215               || protocol.equals(AppletFormatAdapter.URL))
216       {
217         jmb.viewer.openFile(pdbentry.getFile());
218       }
219       else
220       {
221         // probably CLASSLOADER based datasource..
222         // Try and get a reader on the datasource, and pass that to Jmol
223         try
224         {
225           java.io.Reader freader = null;
226           if (reader != null)
227           {
228             if (jalview.bin.JalviewLite.debug)
229             {
230               System.err
231                       .println("AppletJmol:Trying to reuse existing PDBfile IO parser.");
232             }
233             // re-use the one we opened earlier
234             freader = reader.getReader();
235           }
236           if (freader == null)
237           {
238             if (jalview.bin.JalviewLite.debug)
239             {
240               System.err
241                       .println("AppletJmol:Creating new PDBfile IO parser.");
242             }
243             FileParse fp = new FileParse(pdbentry.getFile(), protocol);
244             fp.mark();
245             // reader = new MCview.PDBfile(fp);
246             // could set ID, etc.
247             // if (!reader.isValid())
248             // {
249             // throw new Exception("Invalid datasource.
250             // "+reader.getWarningMessage());
251             // }
252             // fp.reset();
253             freader = fp.getReader();
254           }
255           if (freader == null)
256           {
257             throw new Exception(
258                     "Invalid datasource. Could not obtain Reader.");
259           }
260           jmb.viewer.openReader(pdbentry.getFile(), pdbentry.getId(),
261                   freader);
262         } catch (Exception e)
263         {
264           // give up!
265           System.err.println("Couldn't access pdbentry id="
266                   + pdbentry.getId() + " and file=" + pdbentry.getFile()
267                   + " using protocol=" + protocol);
268           e.printStackTrace();
269         }
270       }
271     }
272
273     jalview.bin.JalviewLite.addFrame(this, jmb.getViewerTitle(), 400, 400);
274   }
275
276   public void loadInline(String string)
277   {
278     loadedInline = true;
279     jmb.viewer.openStringInline(string);
280   }
281
282   void setChainMenuItems(Vector chains)
283   {
284     chainMenu.removeAll();
285
286     MenuItem menuItem = new MenuItem("All");
287     menuItem.addActionListener(this);
288
289     chainMenu.add(menuItem);
290
291     CheckboxMenuItem menuItemCB;
292     for (int c = 0; c < chains.size(); c++)
293     {
294       menuItemCB = new CheckboxMenuItem(chains.elementAt(c).toString(),
295               true);
296       menuItemCB.addItemListener(this);
297       chainMenu.add(menuItemCB);
298     }
299   }
300
301   boolean allChainsSelected = false;
302
303   void centerViewer()
304   {
305     Vector toshow = new Vector();
306     String lbl;
307     int mlength, p, mnum;
308     for (int i = 0; i < chainMenu.getItemCount(); i++)
309     {
310       if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
311       {
312         CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i);
313         if (item.getState())
314         {
315           toshow.addElement(item.getLabel());
316         }
317       }
318     }
319     jmb.centerViewer(toshow);
320   }
321
322   void closeViewer()
323   {
324     jmb.closeViewer();
325     jmb = null;
326     this.setVisible(false);
327   }
328
329   public void actionPerformed(ActionEvent evt)
330   {
331     if (evt.getSource() == mappingMenuItem)
332     {
333       jalview.appletgui.CutAndPasteTransfer cap = new jalview.appletgui.CutAndPasteTransfer(
334               false, null);
335       Frame frame = new Frame();
336       frame.add(cap);
337
338       jalview.bin.JalviewLite.addFrame(frame, "PDB - Sequence Mapping",
339               550, 600);
340       StringBuffer sb = new StringBuffer();
341       for (int s = 0; s < jmb.pdbentry.length; s++)
342       {
343         sb.append(StructureSelectionManager.getStructureSelectionManager()
344                 .printMapping(jmb.pdbentry[s].getFile()));
345         sb.append("\n");
346       }
347     }
348     else if (evt.getSource() == charge)
349     {
350       setEnabled(charge);
351       jmb.colourByCharge();
352     }
353
354     else if (evt.getSource() == chain)
355     {
356       setEnabled(chain);
357       jmb.colourByChain();
358     }
359     else if (evt.getSource() == zappo)
360     {
361       setEnabled(zappo);
362       jmb.setJalviewColourScheme(new ZappoColourScheme());
363     }
364     else if (evt.getSource() == taylor)
365     {
366       setEnabled(taylor);
367       jmb.setJalviewColourScheme(new TaylorColourScheme());
368     }
369     else if (evt.getSource() == hydro)
370     {
371       setEnabled(hydro);
372       jmb.setJalviewColourScheme(new HydrophobicColourScheme());
373     }
374     else if (evt.getSource() == helix)
375     {
376       setEnabled(helix);
377       jmb.setJalviewColourScheme(new HelixColourScheme());
378     }
379     else if (evt.getSource() == strand)
380     {
381       setEnabled(strand);
382       jmb.setJalviewColourScheme(new StrandColourScheme());
383     }
384     else if (evt.getSource() == turn)
385     {
386       setEnabled(turn);
387       jmb.setJalviewColourScheme(new TurnColourScheme());
388     }
389     else if (evt.getSource() == buried)
390     {
391       setEnabled(buried);
392       jmb.setJalviewColourScheme(new BuriedColourScheme());
393     }
394     else if (evt.getSource() == user)
395     {
396       setEnabled(user);
397       new UserDefinedColours(this);
398     }
399     else if (evt.getSource() == jmolHelp)
400     {
401       try
402       {
403         ap.av.applet.getAppletContext().showDocument(
404                 new java.net.URL(
405                         "http://jmol.sourceforge.net/docs/JmolUserGuide/"),
406                 "jmolHelp");
407       } catch (java.net.MalformedURLException ex)
408       {
409       }
410     }
411     else
412     {
413       allChainsSelected = true;
414       for (int i = 0; i < chainMenu.getItemCount(); i++)
415       {
416         if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
417           ((CheckboxMenuItem) chainMenu.getItem(i)).setState(true);
418       }
419
420       centerViewer();
421       allChainsSelected = false;
422     }
423   }
424
425   /**
426    * tick or untick the seqColour menu entry depending upon if it was selected
427    * or not.
428    * 
429    * @param itm
430    */
431   private void setEnabled(MenuItem itm)
432   {
433     seqColour.setState(itm == seqColour);
434     jmb.setColourBySequence(itm == seqColour);
435   }
436
437   public void itemStateChanged(ItemEvent evt)
438   {
439     if (evt.getSource() == seqColour)
440     {
441       setEnabled(seqColour);
442       jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap.av.alignment);
443     }
444     else if (!allChainsSelected)
445       centerViewer();
446   }
447
448   public void keyPressed(KeyEvent evt)
449   {
450     if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible())
451     {
452       jmb.eval(inputLine.getText());
453       history.append("\n$ " + inputLine.getText());
454       inputLine.setText("");
455     }
456
457   }
458
459   public void keyTyped(KeyEvent evt)
460   {
461   }
462
463   public void keyReleased(KeyEvent evt)
464   {
465   }
466
467   public void updateColours(Object source)
468   {
469     AlignmentPanel ap = (AlignmentPanel) source;
470     jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap.av.alignment);
471   }
472
473   public void updateTitleAndMenus()
474   {
475     if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
476     {
477       repaint();
478       return;
479     }
480     setChainMenuItems(jmb.chainNames);
481     jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap.av.alignment);
482
483     setTitle(jmb.getViewerTitle());
484   }
485
486   public void showUrl(String url)
487   {
488     try
489     {
490       ap.av.applet.getAppletContext().showDocument(new java.net.URL(url),
491               "jmolOutput");
492     } catch (java.net.MalformedURLException ex)
493     {
494     }
495   }
496
497   public void showConsole(boolean showConsole)
498   {
499     if (scriptWindow == null)
500     {
501       scriptWindow = new Panel(new BorderLayout());
502       inputLine = new TextField();
503       history = new TextArea(5, 40);
504       scriptWindow.add(history, BorderLayout.CENTER);
505       scriptWindow.add(inputLine, BorderLayout.SOUTH);
506       add(scriptWindow, BorderLayout.SOUTH);
507       scriptWindow.setVisible(false);
508       history.setEditable(false);
509       inputLine.addKeyListener(this);
510     }
511
512     scriptWindow.setVisible(!scriptWindow.isVisible());
513     validate();
514   }
515
516   public float[][] functionXY(String functionName, int x, int y)
517   {
518     return null;
519   }
520
521   // /End JmolStatusListener
522   // /////////////////////////////
523
524   class RenderPanel extends Panel
525   {
526     Dimension currentSize = new Dimension();
527
528     Rectangle rectClip = new Rectangle();
529
530     public void update(Graphics g)
531     {
532       paint(g);
533     }
534
535     public void paint(Graphics g)
536     {
537       currentSize = this.getSize();
538       rectClip = g.getClipBounds();
539
540       if (jmb.viewer == null)
541       {
542         g.setColor(Color.black);
543         g.fillRect(0, 0, currentSize.width, currentSize.height);
544         g.setColor(Color.white);
545         g.setFont(new Font("Verdana", Font.BOLD, 14));
546         g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);
547       }
548       else
549       {
550         jmb.viewer.renderScreenImage(g, currentSize, rectClip);
551       }
552     }
553   }
554
555   /*
556    * @Override public Color getColour(int atomIndex, int pdbResNum, String
557    * chain, String pdbId) { return jmb.getColour(atomIndex, pdbResNum, chain,
558    * pdbId); }
559    * 
560    * @Override public String[] getPdbFile() { return jmb.getPdbFile(); }
561    * 
562    * @Override public void highlightAtom(int atomIndex, int pdbResNum, String
563    * chain, String pdbId) { jmb.highlightAtom(atomIndex, pdbResNum, chain,
564    * pdbId);
565    * 
566    * }
567    * 
568    * @Override public void mouseOverStructure(int atomIndex, String strInfo) {
569    * jmb.mouseOverStructure(atomIndex, strInfo);
570    * 
571    * }
572    */
573   public void setJalviewColourScheme(UserColourScheme ucs)
574   {
575     jmb.setJalviewColourScheme(ucs);
576   }
577 }