Use paintAlignment
[jalview.git] / src / jalview / gui / AppJMol.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.gui;\r
20 \r
21 import java.util.regex.*;\r
22 import java.util.*;\r
23 import java.awt.*;\r
24 import javax.swing.*;\r
25 import javax.swing.event.*;\r
26 import java.awt.event.*;\r
27 import java.io.*;\r
28 \r
29 import jalview.jbgui.GStructureViewer;\r
30 import jalview.datamodel.*;\r
31 import jalview.gui.*;\r
32 import jalview.structure.*;\r
33 import jalview.datamodel.PDBEntry;\r
34 import jalview.io.*;\r
35 \r
36 import org.jmol.api.*;\r
37 import org.jmol.adapter.smarter.SmarterJmolAdapter;\r
38 import org.jmol.popup.*;\r
39 \r
40 \r
41 public class AppJMol\r
42     extends GStructureViewer\r
43     implements StructureListener, JmolStatusListener, Runnable\r
44 \r
45 {\r
46   JmolViewer viewer;\r
47   JmolPopup jmolpopup;\r
48   ScriptWindow scriptWindow;\r
49   PDBEntry pdbentry;\r
50   SequenceI[] sequence;\r
51   StructureSelectionManager ssm;\r
52   RenderPanel renderPanel;\r
53   AlignmentPanel ap;\r
54   String fileLoadingError;\r
55 \r
56   public AppJMol(PDBEntry pdbentry, SequenceI[] seq, AlignmentPanel ap)\r
57   {\r
58     //////////////////////////////////\r
59     //Is the pdb file already loaded?\r
60     String alreadyMapped = StructureSelectionManager\r
61         .getStructureSelectionManager()\r
62         .alreadyMappedToFile(pdbentry.getId());\r
63     if (alreadyMapped != null)\r
64     {\r
65       int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,\r
66           pdbentry.getId() + " is already displayed."\r
67           + "\nDo you want to map sequences to the visible structure?",\r
68           "Map Sequences to Visible Window: " + pdbentry.getId(),\r
69           JOptionPane.YES_NO_OPTION);\r
70 \r
71       if (option == JOptionPane.YES_OPTION)\r
72       {\r
73         StructureSelectionManager.getStructureSelectionManager()\r
74             .setMapping(seq, alreadyMapped, AppletFormatAdapter.FILE);\r
75         return;\r
76       }\r
77     }\r
78     ///////////////////////////////////\r
79 \r
80     this.ap = ap;\r
81     this.pdbentry = pdbentry;\r
82     this.sequence = seq;\r
83 \r
84     renderPanel = new RenderPanel();\r
85     this.getContentPane().add(renderPanel, java.awt.BorderLayout.CENTER);\r
86 \r
87     jalview.gui.Desktop.addInternalFrame(this, "Loading File", 400, 400);\r
88 \r
89     if (pdbentry.getFile() != null)\r
90     {\r
91       initJmol();\r
92     }\r
93     else\r
94     {\r
95       Thread worker = new Thread(this);\r
96       worker.start();\r
97     }\r
98 \r
99     this.addInternalFrameListener(new InternalFrameAdapter()\r
100     {\r
101       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)\r
102       {\r
103         closeViewer();\r
104       }\r
105     });\r
106   }\r
107 \r
108   void initJmol()\r
109   {\r
110     StringBuffer title = new StringBuffer(sequence[0].getName() + ":" +\r
111                                           pdbentry.getId());\r
112 \r
113     if (pdbentry.getProperty() != null)\r
114     {\r
115       if (pdbentry.getProperty().get("method") != null)\r
116       {\r
117         title.append(" Method: ");\r
118         title.append(pdbentry.getProperty().get("method"));\r
119       }\r
120       if (pdbentry.getProperty().get("chains") != null)\r
121       {\r
122         title.append(" Chain:");\r
123         title.append(pdbentry.getProperty().get("chains"));\r
124       }\r
125     }\r
126 \r
127     this.setTitle(title.toString());\r
128 \r
129     viewer = org.jmol.api.JmolViewer.allocateViewer(renderPanel,\r
130         new SmarterJmolAdapter());\r
131 \r
132     viewer.setAppletContext("", null, null, "");\r
133 \r
134     viewer.setJmolStatusListener(this);\r
135 \r
136     scriptWindow = new ScriptWindow(viewer);\r
137 \r
138     jmolpopup = JmolPopup.newJmolPopup(viewer);\r
139 \r
140     viewer.openFile(pdbentry.getFile());\r
141 \r
142   }\r
143 \r
144 \r
145   void setChainMenuItems(Vector chains)\r
146   {\r
147     chainMenu.removeAll();\r
148 \r
149     JMenuItem menuItem = new JMenuItem("All");\r
150     menuItem.addActionListener(new ActionListener()\r
151         {\r
152           public void actionPerformed(ActionEvent evt)\r
153           {\r
154             allChainsSelected = true;\r
155             for(int i=0; i<chainMenu.getItemCount(); i++)\r
156             {\r
157               if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)\r
158                 ( (JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);\r
159             }\r
160             centerViewer();\r
161             allChainsSelected = false;\r
162           }\r
163         });\r
164 \r
165     chainMenu.add(menuItem);\r
166 \r
167     for (int c = 0; c < chains.size(); c++)\r
168     {\r
169       menuItem = new JCheckBoxMenuItem(chains.elementAt(c).toString(), true);\r
170       menuItem.addItemListener(new ItemListener()\r
171       {\r
172         public void itemStateChanged(ItemEvent evt)\r
173         {\r
174           if (!allChainsSelected)\r
175             centerViewer();\r
176         }\r
177       });\r
178 \r
179       chainMenu.add(menuItem);\r
180     }\r
181   }\r
182 \r
183   boolean allChainsSelected = false;\r
184   void centerViewer()\r
185   {\r
186     StringBuffer cmd = new StringBuffer();\r
187     for(int i=0; i<chainMenu.getItemCount(); i++)\r
188     {\r
189       if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)\r
190       {\r
191        JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i);\r
192        if(item.isSelected())\r
193          cmd.append(":"+item.getText()+" or ");\r
194       }\r
195     }\r
196 \r
197     if (cmd.length() > 0)\r
198       cmd.setLength(cmd.length() - 4);\r
199 \r
200     viewer.evalStringQuiet("select *;restrict "\r
201                       +cmd+";cartoon;center "+cmd);\r
202   }\r
203 \r
204   void closeViewer()\r
205   {\r
206     viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);\r
207     viewer.evalStringQuiet("zap");\r
208     viewer.setJmolStatusListener(null);\r
209     viewer = null;\r
210 \r
211     //We'll need to find out what other\r
212     // listeners need to be shut down in Jmol\r
213     StructureSelectionManager\r
214         .getStructureSelectionManager()\r
215         .removeStructureViewerListener(this, pdbentry.getFile());\r
216   }\r
217 \r
218   public void run()\r
219   {\r
220     try\r
221     {\r
222       EBIFetchClient ebi = new EBIFetchClient();\r
223       String query = "pdb:" + pdbentry.getId();\r
224       pdbentry.setFile(ebi.fetchDataAsFile(query, "default", "raw")\r
225                        .getAbsolutePath());\r
226       initJmol();\r
227     }\r
228     catch (Exception ex)\r
229     {\r
230       ex.printStackTrace();\r
231     }\r
232   }\r
233 \r
234   public void pdbFile_actionPerformed(ActionEvent actionEvent)\r
235   {\r
236     JalviewFileChooser chooser = new JalviewFileChooser(\r
237         jalview.bin.Cache.getProperty(\r
238             "LAST_DIRECTORY"));\r
239 \r
240     chooser.setFileView(new JalviewFileView());\r
241     chooser.setDialogTitle("Save PDB File");\r
242     chooser.setToolTipText("Save");\r
243 \r
244     int value = chooser.showSaveDialog(this);\r
245 \r
246     if (value == JalviewFileChooser.APPROVE_OPTION)\r
247     {\r
248       try\r
249       {\r
250         BufferedReader in = new BufferedReader(new FileReader(pdbentry.getFile()));\r
251         File outFile = chooser.getSelectedFile();\r
252 \r
253         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));\r
254         String data;\r
255         while ( (data = in.readLine()) != null)\r
256         {\r
257           if (\r
258               ! (data.indexOf("<PRE>") > -1 || data.indexOf("</PRE>") > -1)\r
259               )\r
260           {\r
261             out.println(data);\r
262           }\r
263         }\r
264         out.close();\r
265       }\r
266       catch (Exception ex)\r
267       {\r
268         ex.printStackTrace();\r
269       }\r
270     }\r
271   }\r
272 \r
273   public void viewMapping_actionPerformed(ActionEvent actionEvent)\r
274   {\r
275     jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer();\r
276     jalview.gui.Desktop.addInternalFrame(cap, "PDB - Sequence Mapping", 550,\r
277                                          600);\r
278     cap.setText(\r
279         StructureSelectionManager.getStructureSelectionManager().printMapping(\r
280             pdbentry.getFile())\r
281         );\r
282   }\r
283 \r
284   /**\r
285    * DOCUMENT ME!\r
286    *\r
287    * @param e DOCUMENT ME!\r
288    */\r
289   public void eps_actionPerformed(ActionEvent e)\r
290   {\r
291     makePDBImage(jalview.util.ImageMaker.EPS);\r
292   }\r
293 \r
294   /**\r
295    * DOCUMENT ME!\r
296    *\r
297    * @param e DOCUMENT ME!\r
298    */\r
299   public void png_actionPerformed(ActionEvent e)\r
300   {\r
301     makePDBImage(jalview.util.ImageMaker.PNG);\r
302   }\r
303 \r
304   void makePDBImage(int type)\r
305   {\r
306     int width = getWidth();\r
307     int height = getHeight();\r
308 \r
309     jalview.util.ImageMaker im;\r
310 \r
311     if (type == jalview.util.ImageMaker.PNG)\r
312     {\r
313       im = new jalview.util.ImageMaker(this,\r
314                                        jalview.util.ImageMaker.PNG,\r
315                                        "Make PNG image from view",\r
316                                        width, height,\r
317                                        null, null);\r
318     }\r
319     else\r
320     {\r
321       im = new jalview.util.ImageMaker(this,\r
322                                        jalview.util.ImageMaker.EPS,\r
323                                        "Make EPS file from view",\r
324                                        width, height,\r
325                                        null, this.getTitle());\r
326     }\r
327 \r
328     if (im.getGraphics() != null)\r
329     {\r
330       Rectangle rect = new Rectangle(width, height);\r
331       viewer.renderScreenImage(im.getGraphics(),\r
332                                rect.getSize(), rect);\r
333       im.writeImage();\r
334     }\r
335   }\r
336 \r
337   //////////////////////////////////\r
338   ///StructureListener\r
339   public String getPdbFile()\r
340   {\r
341     return pdbentry.getFile();\r
342   }\r
343 \r
344   Pattern pattern = Pattern.compile(\r
345       "\\[(.*)\\]([0-9]+)(:[a-zA-Z]*)?\\.([a-zA-Z]+)(/[0-9]*)?"\r
346       );\r
347 \r
348   String lastMessage;\r
349   public void mouseOverStructure(int atomIndex, String strInfo)\r
350   {\r
351     Matcher matcher = pattern.matcher(strInfo);\r
352     matcher.find();\r
353     matcher.group(1);\r
354     int pdbResNum = Integer.parseInt(matcher.group(2));\r
355     String chainId = matcher.group(3);\r
356 \r
357     if (chainId != null)\r
358       chainId = chainId.substring(1, chainId.length());\r
359     else\r
360     {\r
361       chainId = " ";\r
362     }\r
363 \r
364     if (lastMessage == null || !lastMessage.equals(strInfo))\r
365       ssm.mouseOverStructure(pdbResNum, chainId, pdbentry.getFile());\r
366 \r
367     lastMessage = strInfo;\r
368   }\r
369 \r
370   StringBuffer resetLastRes = new StringBuffer();\r
371   StringBuffer eval = new StringBuffer();\r
372 \r
373   public void highlightAtom(int atomIndex, int pdbResNum, String chain, String pdbfile)\r
374   {\r
375     if (!pdbfile.equals(pdbentry.getFile()))\r
376       return;\r
377 \r
378     if (resetLastRes.length() > 0)\r
379     {\r
380       viewer.evalStringQuiet(resetLastRes.toString());\r
381     }\r
382 \r
383     eval.setLength(0);\r
384     eval.append("select " + pdbResNum);\r
385 \r
386     resetLastRes.setLength(0);\r
387     resetLastRes.append("select " + pdbResNum);\r
388 \r
389     if (!chain.equals(" "))\r
390     {\r
391       eval.append(":" + chain);\r
392       resetLastRes.append(":" + chain);\r
393     }\r
394 \r
395     eval.append(";color gold;wireframe 100");\r
396 \r
397     Color col = new Color(viewer.getAtomArgb(atomIndex));\r
398 \r
399     resetLastRes.append(";color["\r
400                         + col.getRed() + ","\r
401                         + col.getGreen() + ","\r
402                         + col.getBlue() + "];wireframe 0");\r
403 \r
404     viewer.evalStringQuiet(eval.toString());\r
405 \r
406   }\r
407 \r
408   public void updateColours(Object source)\r
409   {\r
410     colourBySequence( (AlignmentPanel) source);\r
411   }\r
412 \r
413 //End StructureListener\r
414 ////////////////////////////\r
415 \r
416   FeatureRenderer fr;\r
417   public void colourBySequence(AlignmentPanel ap)\r
418   {\r
419     StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());\r
420 \r
421     if (mapping.length < 1)\r
422       return;\r
423 \r
424     SequenceRenderer sr = ap.seqPanel.seqCanvas.getSequenceRenderer();\r
425 \r
426     boolean showFeatures = false;\r
427     if (ap.av.showSequenceFeatures)\r
428     {\r
429       showFeatures = true;\r
430       if (fr == null)\r
431       {\r
432         fr = new jalview.gui.FeatureRenderer(ap.av);\r
433       }\r
434 \r
435       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());\r
436     }\r
437 \r
438     StringBuffer command = new StringBuffer();\r
439 \r
440     int lastPos = -1;\r
441     for (int s = 0; s < sequence.length; s++)\r
442     {\r
443       for (int m = 0; m < mapping.length; m++)\r
444       {\r
445         if (mapping[m].getSequence() == sequence[s])\r
446         {\r
447           for (int r = 0; r < sequence[s].getLength(); r++)\r
448           {\r
449             int pos = mapping[m].getPDBResNum(\r
450                 sequence[s].findPosition(r));\r
451 \r
452             if (pos < 1 || pos==lastPos)\r
453               continue;\r
454 \r
455             lastPos = pos;\r
456 \r
457             command.append(";select " + pos);\r
458 \r
459             if (!mapping[m].getChain().equals(" "))\r
460             {\r
461               command.append(":" + mapping[m].getChain());\r
462             }\r
463 \r
464             Color col = sr.getResidueBoxColour(sequence[s], r);\r
465 \r
466             if (showFeatures)\r
467               col = fr.findFeatureColour(col, sequence[s], r);\r
468 \r
469             command.append("; color ["\r
470                            + col.getRed() + ","\r
471                            + col.getGreen() + ","\r
472                            + col.getBlue() + "]");\r
473 \r
474           }\r
475         }\r
476       }\r
477     }\r
478 \r
479     viewer.evalStringQuiet(command.toString());\r
480   }\r
481 \r
482   /////////////////////////////////\r
483   //JmolStatusListener\r
484 \r
485   public String eval(String strEval)\r
486   {\r
487    // System.out.println(strEval);\r
488    //"# 'eval' is implemented only for the applet.";\r
489     return null;\r
490   }\r
491 \r
492   public void createImage(String file, String type, int quality)\r
493   {\r
494     System.out.println("JMOL CREATE IMAGE");\r
495   }\r
496 \r
497   public void setCallbackFunction(String callbackType,\r
498                                   String callbackFunction)\r
499   {}\r
500 \r
501   public void notifyFileLoaded(String fullPathName, String fileName,\r
502                                String modelName, Object clientFile,\r
503                                String errorMsg)\r
504   {\r
505     if(errorMsg!=null)\r
506     {\r
507       fileLoadingError = errorMsg;\r
508       repaint();\r
509       return;\r
510     }\r
511 \r
512     fileLoadingError = null;\r
513 \r
514     if (fileName != null)\r
515     {\r
516 \r
517       //FILE LOADED OK\r
518       ssm = StructureSelectionManager.getStructureSelectionManager();\r
519       MCview.PDBfile pdbFile = ssm.setMapping(sequence, pdbentry.getFile(), AppletFormatAdapter.FILE);\r
520       ssm.addStructureViewerListener(this);\r
521 \r
522       Vector chains = new Vector();\r
523       for(int i=0; i<pdbFile.chains.size(); i++)\r
524       {\r
525         chains.addElement(((MCview.PDBChain)pdbFile.chains.elementAt(i)).id);\r
526       }\r
527       setChainMenuItems(chains);\r
528 \r
529       jmolpopup.updateComputedMenus();\r
530       viewer.evalStringQuiet(\r
531           "select backbone;restrict;cartoon;wireframe off;spacefill off");\r
532 \r
533       colourBySequence(ap);\r
534     }\r
535     else\r
536       return;\r
537   }\r
538 \r
539   public void notifyFrameChanged(int frameNo)\r
540   {\r
541     boolean isAnimationRunning = (frameNo <= -2);\r
542   }\r
543 \r
544   public void notifyScriptStart(String statusMessage, String additionalInfo)\r
545   {}\r
546 \r
547   public void sendConsoleEcho(String strEcho)\r
548   {\r
549     if (scriptWindow != null)\r
550       scriptWindow.sendConsoleEcho(strEcho);\r
551   }\r
552 \r
553   public void sendConsoleMessage(String strStatus)\r
554   {\r
555     if (scriptWindow != null)\r
556       scriptWindow.sendConsoleMessage(strStatus);\r
557   }\r
558 \r
559   public void notifyScriptTermination(String strStatus, int msWalltime)\r
560   {\r
561     if (scriptWindow != null)\r
562       scriptWindow.notifyScriptTermination(strStatus, msWalltime);\r
563   }\r
564 \r
565   public void handlePopupMenu(int x, int y)\r
566   {\r
567     jmolpopup.show(x, y);\r
568   }\r
569 \r
570   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)\r
571   {\r
572     notifyAtomPicked(iatom, strMeasure);\r
573   }\r
574 \r
575   public void notifyNewDefaultModeMeasurement(int count, String strInfo)\r
576   {}\r
577 \r
578   public void notifyAtomPicked(int atomIndex, String strInfo)\r
579   {\r
580     if (scriptWindow != null)\r
581     {\r
582       scriptWindow.sendConsoleMessage(strInfo);\r
583       scriptWindow.sendConsoleMessage("\n");\r
584     }\r
585   }\r
586 \r
587   public void notifyAtomHovered(int atomIndex, String strInfo)\r
588   {\r
589     mouseOverStructure(atomIndex, strInfo);\r
590   }\r
591 \r
592   public void sendSyncScript(String script, String appletName)\r
593   {}\r
594 \r
595   public void showUrl(String url)\r
596   {}\r
597 \r
598   public void showConsole(boolean showConsole)\r
599   {\r
600     final String title = "Jmol Script Window for: "+this.getTitle();\r
601     if (scriptWindow == null)\r
602       return;\r
603     if (showConsole)\r
604     {\r
605       javax.swing.SwingUtilities.invokeLater(new Runnable()\r
606       {\r
607         public void run()\r
608         {\r
609           jalview.gui.Desktop.addInternalFrame(scriptWindow,\r
610                                                title,\r
611                                                400,\r
612                                                300);\r
613         }\r
614       });\r
615 \r
616     }\r
617     else\r
618       scriptWindow.setVisible(false);\r
619   }\r
620 \r
621   public float functionXY(String functionName, int x, int y)\r
622   {\r
623     return 0;\r
624   }\r
625 \r
626   ///End JmolStatusListener\r
627   ///////////////////////////////\r
628 \r
629 \r
630   class RenderPanel\r
631       extends JPanel\r
632   {\r
633     final Dimension currentSize = new Dimension();\r
634     final Rectangle rectClip = new Rectangle();\r
635 \r
636     public void paintComponent(Graphics g)\r
637     {\r
638       getSize(currentSize);\r
639       g.getClipBounds(rectClip);\r
640 \r
641       if (viewer == null)\r
642       {\r
643         g.setColor(Color.black);\r
644         g.fillRect(0, 0, currentSize.width, currentSize.height);\r
645         g.setColor(Color.white);\r
646         g.setFont(new Font("Verdana", Font.BOLD, 14));\r
647         g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);\r
648       }\r
649       else if(fileLoadingError!=null)\r
650       {\r
651         g.setColor(Color.black);\r
652         g.fillRect(0, 0, currentSize.width, currentSize.height);\r
653         g.setColor(Color.white);\r
654         g.setFont(new Font("Verdana", Font.BOLD, 14));\r
655         g.drawString("Error loading file..." + pdbentry.getId(), 20,\r
656                      currentSize.height / 2);\r
657       }\r
658       else\r
659       {\r
660         viewer.renderScreenImage(g, currentSize, rectClip);\r
661       }\r
662     }\r
663   }\r
664 \r
665 }\r