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
5 * This file is part of Jalview.
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.
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.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
18 package jalview.appletgui;
22 import java.awt.event.*;
24 import jalview.datamodel.*;
25 import jalview.structure.*;
28 import org.jmol.api.*;
29 import org.jmol.adapter.smarter.SmarterJmolAdapter;
31 import org.jmol.popup.*;
32 import org.jmol.viewer.JmolConstants;
34 import jalview.schemes.*;
36 public class AppletJmol extends EmbmenuFrame implements StructureListener,
37 JmolStatusListener, KeyListener, ActionListener, ItemListener
40 Menu fileMenu = new Menu("File");
42 Menu viewMenu = new Menu("View");
44 Menu coloursMenu = new Menu("Colours");
46 Menu chainMenu = new Menu("Show Chain");
48 Menu helpMenu = new Menu("Help");
50 MenuItem mappingMenuItem = new MenuItem("View Mapping");
52 CheckboxMenuItem seqColour = new CheckboxMenuItem("By Sequence", true);
54 MenuItem chain = new MenuItem("By Chain");
56 MenuItem charge = new MenuItem("Charge & Cysteine");
58 MenuItem zappo = new MenuItem("Zappo");
60 MenuItem taylor = new MenuItem("Taylor");
62 MenuItem hydro = new MenuItem("Hydrophobicity");
64 MenuItem helix = new MenuItem("Helix Propensity");
66 MenuItem strand = new MenuItem("Strand Propensity");
68 MenuItem turn = new MenuItem("Turn Propensity");
70 MenuItem buried = new MenuItem("Buried Index");
72 MenuItem user = new MenuItem("User Defined Colours");
74 MenuItem jmolHelp = new MenuItem("Jmol Help");
90 StructureSelectionManager ssm;
92 RenderPanel renderPanel;
96 String fileLoadingError;
102 boolean colourBySequence = true;
104 Vector atomsPicked = new Vector();
107 * datasource protocol for access to PDBEntry
109 String protocol = null;
111 public AppletJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
112 AlignmentPanel ap, String protocol)
116 this.chains = chains;
117 this.pdbentry = pdbentry;
118 this.protocol = protocol;
119 if (pdbentry.getId() == null || pdbentry.getId().length() < 1)
121 if (protocol.equals(AppletFormatAdapter.PASTE))
123 pdbentry.setId("PASTED PDB"
124 + (chains == null ? "_" : chains.toString()));
128 pdbentry.setId(pdbentry.getFile());
132 if (jalview.bin.JalviewLite.debug)
135 .println("AppletJmol: PDB ID is '" + pdbentry.getId() + "'");
138 String alreadyMapped = StructureSelectionManager
139 .getStructureSelectionManager().alreadyMappedToFile(
141 MCview.PDBfile reader = null;
142 if (alreadyMapped != null)
144 reader = StructureSelectionManager.getStructureSelectionManager()
145 .setMapping(seq, chains, pdbentry.getFile(), protocol);
146 // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW?
147 // FOR NOW, LETS JUST OPEN A NEW WINDOW
149 MenuBar menuBar = new MenuBar();
150 menuBar.add(fileMenu);
151 fileMenu.add(mappingMenuItem);
152 menuBar.add(viewMenu);
153 mappingMenuItem.addActionListener(this);
154 viewMenu.add(chainMenu);
155 menuBar.add(coloursMenu);
156 menuBar.add(helpMenu);
158 charge.addActionListener(this);
159 hydro.addActionListener(this);
160 chain.addActionListener(this);
161 seqColour.addItemListener(this);
162 zappo.addActionListener(this);
163 taylor.addActionListener(this);
164 helix.addActionListener(this);
165 strand.addActionListener(this);
166 turn.addActionListener(this);
167 buried.addActionListener(this);
168 user.addActionListener(this);
170 jmolHelp.addActionListener(this);
172 coloursMenu.add(seqColour);
173 coloursMenu.add(chain);
174 coloursMenu.add(charge);
175 coloursMenu.add(zappo);
176 coloursMenu.add(taylor);
177 coloursMenu.add(hydro);
178 coloursMenu.add(helix);
179 coloursMenu.add(strand);
180 coloursMenu.add(turn);
181 coloursMenu.add(buried);
182 coloursMenu.add(user);
184 helpMenu.add(jmolHelp);
188 renderPanel = new RenderPanel();
189 embedMenuIfNeeded(renderPanel);
190 this.add(renderPanel, BorderLayout.CENTER);
191 viewer = JmolViewer.allocateViewer(renderPanel,
192 new SmarterJmolAdapter(), "jalviewJmol", ap.av.applet.getDocumentBase(),
193 ap.av.applet.getCodeBase(), "", this);
195 jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
197 this.addWindowListener(new WindowAdapter()
199 public void windowClosing(WindowEvent evt)
205 if (pdbentry.getFile() != null)
207 // import structure data from pdbentry.getFile based on given protocol
208 if (protocol.equals(AppletFormatAdapter.PASTE))
210 loadInline(pdbentry.getFile());
212 else if (protocol.equals(AppletFormatAdapter.FILE)
213 || protocol.equals(AppletFormatAdapter.URL))
215 viewer.openFile(pdbentry.getFile());
219 // probably CLASSLOADER based datasource..
220 // Try and get a reader on the datasource, and pass that to Jmol
223 java.io.Reader freader = null;
226 if (jalview.bin.JalviewLite.debug)
229 .println("AppletJmol:Trying to reuse existing PDBfile IO parser.");
231 // re-use the one we opened earlier
232 freader = reader.getReader();
236 if (jalview.bin.JalviewLite.debug)
239 .println("AppletJmol:Creating new PDBfile IO parser.");
241 FileParse fp = new FileParse(pdbentry.getFile(), protocol);
243 // reader = new MCview.PDBfile(fp);
244 // could set ID, etc.
245 // if (!reader.isValid())
247 // throw new Exception("Invalid datasource.
248 // "+reader.getWarningMessage());
251 freader = fp.getReader();
256 "Invalid datasource. Could not obtain Reader.");
258 viewer.openReader(pdbentry.getFile(), pdbentry.getId(), freader);
259 } catch (Exception e)
262 System.err.println("Couldn't access pdbentry id="
263 + pdbentry.getId() + " and file=" + pdbentry.getFile()
264 + " using protocol=" + protocol);
270 jalview.bin.JalviewLite.addFrame(this, "Jmol", 400, 400);
273 public void loadInline(String string)
276 viewer.openStringInline(string);
279 void setChainMenuItems(Vector chains)
281 chainMenu.removeAll();
283 MenuItem menuItem = new MenuItem("All");
284 menuItem.addActionListener(this);
286 chainMenu.add(menuItem);
288 CheckboxMenuItem menuItemCB;
289 for (int c = 0; c < chains.size(); c++)
291 menuItemCB = new CheckboxMenuItem(chains.elementAt(c).toString(),
293 menuItemCB.addItemListener(this);
294 chainMenu.add(menuItemCB);
298 boolean allChainsSelected = false;
302 StringBuffer cmd = new StringBuffer();
303 for (int i = 0; i < chainMenu.getItemCount(); i++)
305 if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
307 CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i);
309 cmd.append(":" + item.getLabel() + " or ");
313 if (cmd.length() > 0)
314 cmd.setLength(cmd.length() - 4);
317 .evalString("select *;restrict " + cmd + ";cartoon;center "
323 viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
324 viewer.evalStringQuiet("zap");
325 viewer.setJmolStatusListener(null);
328 // We'll need to find out what other
329 // listeners need to be shut down in Jmol
330 StructureSelectionManager.getStructureSelectionManager()
331 .removeStructureViewerListener(this, pdbentry.getId());
333 this.setVisible(false);
336 public void actionPerformed(ActionEvent evt)
338 if (evt.getSource() == mappingMenuItem)
340 jalview.appletgui.CutAndPasteTransfer cap = new jalview.appletgui.CutAndPasteTransfer(
342 Frame frame = new Frame();
345 jalview.bin.JalviewLite.addFrame(frame, "PDB - Sequence Mapping",
347 cap.setText(StructureSelectionManager.getStructureSelectionManager()
348 .printMapping(pdbentry.getFile()));
350 else if (evt.getSource() == charge)
352 colourBySequence = false;
353 seqColour.setState(false);
355 .evalStringQuiet("select *;color white;select ASP,GLU;color red;"
356 + "select LYS,ARG;color blue;select CYS;color yellow");
359 else if (evt.getSource() == chain)
361 colourBySequence = false;
362 seqColour.setState(false);
363 viewer.evalStringQuiet("select *;color chain");
365 else if (evt.getSource() == zappo)
367 setJalviewColourScheme(new ZappoColourScheme());
369 else if (evt.getSource() == taylor)
371 setJalviewColourScheme(new TaylorColourScheme());
373 else if (evt.getSource() == hydro)
375 setJalviewColourScheme(new HydrophobicColourScheme());
377 else if (evt.getSource() == helix)
379 setJalviewColourScheme(new HelixColourScheme());
381 else if (evt.getSource() == strand)
383 setJalviewColourScheme(new StrandColourScheme());
385 else if (evt.getSource() == turn)
387 setJalviewColourScheme(new TurnColourScheme());
389 else if (evt.getSource() == buried)
391 setJalviewColourScheme(new BuriedColourScheme());
393 else if (evt.getSource() == user)
395 new UserDefinedColours(this);
397 else if (evt.getSource() == jmolHelp)
401 ap.av.applet.getAppletContext().showDocument(
403 "http://jmol.sourceforge.net/docs/JmolUserGuide/"),
405 } catch (java.net.MalformedURLException ex)
411 allChainsSelected = true;
412 for (int i = 0; i < chainMenu.getItemCount(); i++)
414 if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
415 ((CheckboxMenuItem) chainMenu.getItem(i)).setState(true);
418 allChainsSelected = false;
422 public void setJalviewColourScheme(ColourSchemeI cs)
424 colourBySequence = false;
425 seqColour.setState(false);
434 Enumeration en = ResidueProperties.aa3Hash.keys();
435 StringBuffer command = new StringBuffer("select *;color white;");
436 while (en.hasMoreElements())
438 res = en.nextElement().toString();
439 index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
443 col = cs.findColour(ResidueProperties.aa[index].charAt(0));
445 command.append("select " + res + ";color[" + col.getRed() + ","
446 + col.getGreen() + "," + col.getBlue() + "];");
449 viewer.evalStringQuiet(command.toString());
452 public void itemStateChanged(ItemEvent evt)
454 if (evt.getSource() == seqColour)
457 colourBySequence = seqColour.getState();
458 colourBySequence(ap);
460 else if (!allChainsSelected)
464 public void keyPressed(KeyEvent evt)
466 if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible())
468 viewer.evalString(inputLine.getText());
469 history.append("\n$ " + inputLine.getText());
470 inputLine.setText("");
475 public void keyTyped(KeyEvent evt)
479 public void keyReleased(KeyEvent evt)
483 // ////////////////////////////////
484 // /StructureListener
485 public String getPdbFile()
492 public void mouseOverStructure(int atomIndex, String strInfo)
496 int chainSeparator = strInfo.indexOf(":");
498 if (chainSeparator == -1)
499 chainSeparator = strInfo.indexOf(".");
501 pdbResNum = Integer.parseInt(strInfo.substring(
502 strInfo.indexOf("]") + 1, chainSeparator));
506 if (strInfo.indexOf(":") > -1)
507 chainId = strInfo.substring(strInfo.indexOf(":") + 1, strInfo
514 if (lastMessage == null || !lastMessage.equals(strInfo))
515 ssm.mouseOverStructure(pdbResNum, chainId, pdbentry.getFile());
517 lastMessage = strInfo;
520 StringBuffer resetLastRes = new StringBuffer();
522 StringBuffer eval = new StringBuffer();
524 public void highlightAtom(int atomIndex, int pdbResNum, String chain,
527 if (!pdbfile.equals(pdbentry.getFile()))
530 if (resetLastRes.length() > 0)
532 viewer.evalStringQuiet(resetLastRes.toString());
536 eval.append("select " + pdbResNum);
538 resetLastRes.setLength(0);
539 resetLastRes.append("select " + pdbResNum);
542 resetLastRes.append(":");
543 if (!chain.equals(" "))
546 resetLastRes.append(chain);
549 eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
551 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
552 + " and not hetero; spacefill 0;");
554 eval.append("spacefill 200;select none");
556 viewer.evalStringQuiet(eval.toString());
560 public void updateColours(Object source)
562 colourBySequence((AlignmentPanel) source);
565 // End StructureListener
566 // //////////////////////////
568 public Color getColour(int atomIndex, int pdbResNum, String chain,
571 if (!pdbfile.equals(pdbentry.getFile()))
574 return new Color(viewer.getAtomArgb(atomIndex));
579 FeatureRenderer fr = null;
581 public void colourBySequence(AlignmentPanel sourceap)
585 if (!colourBySequence)
588 StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
590 if (mapping.length < 1)
593 SequenceRenderer sr = new SequenceRenderer(ap.av);
595 boolean showFeatures = false;
597 if (ap.av.showSequenceFeatures)
602 fr = new jalview.appletgui.FeatureRenderer(ap.av);
605 fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
608 StringBuffer command = new StringBuffer();
611 for (int s = 0; s < sequence.length; s++)
613 for (int sp, m = 0; m < mapping.length; m++)
615 if (mapping[m].getSequence() == sequence[s]
616 && (sp = ap.av.alignment.findIndex(sequence[s])) > -1)
618 SequenceI asp = ap.av.alignment.getSequenceAt(sp);
619 for (int r = 0; r < asp.getLength(); r++)
621 // no mapping to gaps in sequence
622 if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
626 int pos = mapping[m].getPDBResNum(asp.findPosition(r));
628 if (pos < 1 || pos == lastPos)
633 Color col = sr.getResidueBoxColour(sequence[s], r);
636 col = fr.findFeatureColour(col, sequence[s], r);
638 if (command.toString().endsWith(
639 ":" + mapping[m].getChain() + ";color[" + col.getRed()
640 + "," + col.getGreen() + "," + col.getBlue()
643 command = condenseCommand(command.toString(), pos);
647 command.append(";select " + pos);
649 if (!mapping[m].getChain().equals(" "))
651 command.append(":" + mapping[m].getChain());
654 command.append(";color[" + col.getRed() + "," + col.getGreen()
655 + "," + col.getBlue() + "]");
662 if (lastCommand == null || !lastCommand.equals(command.toString()))
664 viewer.evalStringQuiet(command.toString());
666 lastCommand = command.toString();
669 StringBuffer condenseCommand(String command, int pos)
672 StringBuffer sb = new StringBuffer(command.substring(0, command
673 .lastIndexOf("select") + 7));
675 command = command.substring(sb.length());
679 if (command.indexOf("-") > -1)
681 start = command.substring(0, command.indexOf("-"));
685 start = command.substring(0, command.indexOf(":"));
688 sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
693 // ///////////////////////////////
694 // JmolStatusListener
696 public String eval(String strEval)
698 // System.out.println(strEval);
699 // "# 'eval' is implemented only for the applet.";
703 public void createImage(String file, String type, int quality)
707 public void notifyFileLoaded(String fullPathName, String fileName,
708 String modelName, String errorMsg, int modelParts)
710 if (errorMsg != null)
712 fileLoadingError = errorMsg;
717 fileLoadingError = null;
719 if (fileName != null)
721 // TODO: do some checking using the modelPts number of parts against our own estimate of the number of chains
723 jmolpopup.updateComputedMenus();
725 .evalStringQuiet("select backbone;restrict;cartoon;wireframe off;spacefill off");
727 ssm = StructureSelectionManager.getStructureSelectionManager();
731 pdb = ssm.setMapping(sequence, chains, pdbentry.getFile(),
732 AppletFormatAdapter.PASTE);
733 pdbentry.setFile("INLINE" + pdb.id);
737 // TODO: Jmol can in principle retrieve from CLASSLOADER but this needs
738 // to be tested. See mantis bug
739 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
741 pdb = ssm.setMapping(sequence, chains, pdbentry.getFile(),
742 AppletFormatAdapter.URL);
746 pdbentry.setId(pdb.id);
748 ssm.addStructureViewerListener(this);
750 Vector chains = new Vector();
751 for (int i = 0; i < pdb.chains.size(); i++)
753 chains.addElement(((MCview.PDBChain) pdb.chains.elementAt(i)).id);
755 setChainMenuItems(chains);
757 colourBySequence(ap);
759 StringBuffer title = new StringBuffer(sequence[0].getName() + ":"
762 if (pdbentry.getProperty() != null)
764 if (pdbentry.getProperty().get("method") != null)
766 title.append(" Method: ");
767 title.append(pdbentry.getProperty().get("method"));
769 if (pdbentry.getProperty().get("chains") != null)
771 title.append(" Chain:");
772 title.append(pdbentry.getProperty().get("chains"));
776 this.setTitle(title.toString());
783 public void sendConsoleEcho(String strEcho)
785 if (scriptWindow == null)
788 history.append("\n" + strEcho);
791 public void sendConsoleMessage(String strStatus)
793 if (history != null && strStatus != null
794 && !strStatus.equals("Script completed"))
796 history.append("\n" + strStatus);
800 public void notifyScriptTermination(String strStatus, int msWalltime)
804 public void handlePopupMenu(int x, int y)
806 jmolpopup.show(x, y);
809 public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
811 notifyAtomPicked(iatom, strMeasure, null);
814 public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
818 System.err.println("Ignoring additional pick data string "+strData);
820 int chainSeparator = strInfo.indexOf(":");
822 if (chainSeparator == -1)
823 chainSeparator = strInfo.indexOf(".");
825 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
828 if (strInfo.indexOf(":") > -1)
829 picked += strInfo.substring(strInfo.indexOf(":") + 1, strInfo
832 picked = "(("+picked+".CA" + ")|("+picked+".P"+"))";
834 if (!atomsPicked.contains(picked))
836 viewer.evalString("select " + picked + ";label %n %r:%c");
837 atomsPicked.addElement(picked);
841 viewer.evalString("select " + picked + ";label off");
842 atomsPicked.removeElement(picked);
846 public void notifyAtomHovered(int atomIndex, String strInfo, String data)
850 System.err.println("Ignoring additional hover info: "+data);
852 mouseOverStructure(atomIndex, strInfo);
856 public void showUrl(String url)
860 ap.av.applet.getAppletContext().showDocument(new java.net.URL(url),
862 } catch (java.net.MalformedURLException ex)
867 public void showConsole(boolean showConsole)
869 if (scriptWindow == null)
871 scriptWindow = new Panel(new BorderLayout());
872 inputLine = new TextField();
873 history = new TextArea(5, 40);
874 scriptWindow.add(history, BorderLayout.CENTER);
875 scriptWindow.add(inputLine, BorderLayout.SOUTH);
876 add(scriptWindow, BorderLayout.SOUTH);
877 scriptWindow.setVisible(false);
878 history.setEditable(false);
879 inputLine.addKeyListener(this);
882 scriptWindow.setVisible(!scriptWindow.isVisible());
886 public float[][] functionXY(String functionName, int x, int y)
891 // /End JmolStatusListener
892 // /////////////////////////////
894 class RenderPanel extends Panel
896 Dimension currentSize = new Dimension();
898 Rectangle rectClip = new Rectangle();
900 public void update(Graphics g)
905 public void paint(Graphics g)
907 currentSize = this.getSize();
908 rectClip = g.getClipBounds();
912 g.setColor(Color.black);
913 g.fillRect(0, 0, currentSize.width, currentSize.height);
914 g.setColor(Color.white);
915 g.setFont(new Font("Verdana", Font.BOLD, 14));
916 g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);
920 viewer.renderScreenImage(g, currentSize, rectClip);
926 public String createImage(String fileName, String type,
927 Object textOrBytes, int quality)
929 // TODO Auto-generated method stub
934 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
936 // TODO Auto-generated method stub
941 public Hashtable getRegistryInfo()
943 // TODO Auto-generated method stub
948 public void notifyCallback(int type, Object[] data)
953 case JmolConstants.CALLBACK_LOADSTRUCT:
954 notifyFileLoaded((String) data[1], (String) data[2],
955 (String) data[3], (String) data[4], ((Integer) data[5]).intValue());
958 case JmolConstants.CALLBACK_PICK:
959 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1], (String) data[0]);
960 // also highlight in alignment
961 case JmolConstants.CALLBACK_HOVER:
962 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1], (String) data[0]);
964 case JmolConstants.CALLBACK_SCRIPT:
965 notifyScriptTermination((String)data[2], ((Integer)data[3]).intValue());
967 case JmolConstants.CALLBACK_ECHO:
968 sendConsoleEcho((String)data[1]);
970 case JmolConstants.CALLBACK_MESSAGE:
971 sendConsoleMessage((data==null) ? ((String) null) : (String)data[1]);
973 case JmolConstants.CALLBACK_MEASURE:
974 case JmolConstants.CALLBACK_CLICK:
976 System.err.println("Unhandled callback "+type+" "+data);
982 System.err.println("Squashed Jmol callback handler error:");
988 public boolean notifyEnabled(int callbackPick)
990 switch (callbackPick)
992 case JmolConstants.CALLBACK_ECHO:
993 case JmolConstants.CALLBACK_LOADSTRUCT:
994 case JmolConstants.CALLBACK_MEASURE:
995 case JmolConstants.CALLBACK_MESSAGE:
996 case JmolConstants.CALLBACK_PICK:
997 case JmolConstants.CALLBACK_SCRIPT:
998 case JmolConstants.CALLBACK_HOVER:
999 case JmolConstants.CALLBACK_ERROR:
1001 case JmolConstants.CALLBACK_CLICK:
1002 case JmolConstants.CALLBACK_ANIMFRAME:
1003 case JmolConstants.CALLBACK_MINIMIZATION:
1004 case JmolConstants.CALLBACK_RESIZE:
1005 case JmolConstants.CALLBACK_SYNC:
1011 public void setCallbackFunction(String callbackType,
1012 String callbackFunction)
1014 System.err.println("Ignoring set-callback request to associate "+callbackType+" with function "+callbackFunction);