3b6ea99087b972b43bffcb456c790b466299f26e
[jalview.git] / src / jalview / gui / PymolViewer.java
1 package jalview.gui;
2
3 import java.awt.event.ActionEvent;
4 import java.awt.event.ActionListener;
5 import java.io.File;
6 import java.util.ArrayList;
7 import java.util.List;
8 import java.util.Map;
9
10 import javax.swing.JInternalFrame;
11 import javax.swing.JMenuItem;
12 import javax.swing.event.InternalFrameAdapter;
13 import javax.swing.event.InternalFrameEvent;
14
15 import jalview.api.AlignmentViewPanel;
16 import jalview.api.FeatureRenderer;
17 import jalview.bin.Console;
18 import jalview.datamodel.PDBEntry;
19 import jalview.datamodel.SequenceI;
20 import jalview.datamodel.StructureViewerModel;
21 import jalview.datamodel.StructureViewerModel.StructureData;
22 import jalview.gui.StructureViewer.ViewerType;
23 import jalview.io.DataSourceType;
24 import jalview.io.StructureFile;
25 import jalview.structures.models.AAStructureBindingModel;
26 import jalview.util.MessageManager;
27
28 public class PymolViewer extends StructureViewerBase
29 {
30   private static final int myWidth = 500;
31
32   private static final int myHeight = 150;
33
34   private PymolBindingModel binding;
35
36   private String pymolSessionFile;
37
38   public PymolViewer()
39   {
40     super();
41
42     /*
43      * closeViewer will decide whether or not to close this frame
44      * depending on whether user chooses to Cancel or not
45      */
46     setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
47   }
48
49   public PymolViewer(PDBEntry pdb, SequenceI[] seqs, Object object,
50           AlignmentPanel ap)
51   {
52     this();
53     openNewPymol(ap, new PDBEntry[] { pdb },
54             new SequenceI[][]
55             { seqs });
56   }
57
58   public PymolViewer(PDBEntry[] pe, boolean alignAdded, SequenceI[][] seqs,
59           AlignmentPanel ap)
60   {
61     this();
62     setAlignAddedStructures(alignAdded);
63     openNewPymol(ap, pe, seqs);
64   }
65
66   /**
67    * Constructor given a session file to be restored
68    * 
69    * @param sessionFile
70    * @param alignPanel
71    * @param pdbArray
72    * @param seqsArray
73    * @param colourByPymol
74    * @param colourBySequence
75    * @param newViewId
76    */
77   public PymolViewer(StructureViewerModel viewerModel,
78           AlignmentPanel alignPanel, String sessionFile, String vid)
79   {
80     // TODO convert to base/factory class method
81     this();
82     setViewId(vid);
83     this.pymolSessionFile = sessionFile;
84     Map<File, StructureData> pdbData = viewerModel.getFileData();
85     PDBEntry[] pdbArray = new PDBEntry[pdbData.size()];
86     SequenceI[][] seqsArray = new SequenceI[pdbData.size()][];
87     int i = 0;
88     for (StructureData data : pdbData.values())
89     {
90       PDBEntry pdbentry = new PDBEntry(data.getPdbId(), null,
91               PDBEntry.Type.PDB, data.getFilePath());
92       pdbArray[i] = pdbentry;
93       List<SequenceI> sequencesForPdb = data.getSeqList();
94       seqsArray[i] = sequencesForPdb
95               .toArray(new SequenceI[sequencesForPdb.size()]);
96       i++;
97     }
98
99     openNewPymol(alignPanel, pdbArray, seqsArray);
100     if (viewerModel.isColourByViewer())
101     {
102       binding.setColourBySequence(false);
103       seqColour.setSelected(false);
104       viewerColour.setSelected(true);
105     }
106     else if (viewerModel.isColourWithAlignPanel())
107     {
108       binding.setColourBySequence(true);
109       seqColour.setSelected(true);
110       viewerColour.setSelected(false);
111     }
112   }
113
114   private void openNewPymol(AlignmentPanel ap, PDBEntry[] pe,
115           SequenceI[][] seqs)
116   {
117     createProgressBar();
118     binding = new PymolBindingModel(this, ap.getStructureSelectionManager(),
119             pe, seqs);
120     addAlignmentPanel(ap);
121     useAlignmentPanelForColourbyseq(ap);
122
123     if (pe.length > 1)
124     {
125       useAlignmentPanelForSuperposition(ap);
126     }
127     binding.setColourBySequence(true);
128     setSize(myWidth, myHeight);
129     initMenus();
130     viewerActionMenu.setText("PyMOL");
131     updateTitleAndMenus();
132
133     addingStructures = false;
134     worker = new Thread(this);
135     worker.start();
136
137     this.addInternalFrameListener(new InternalFrameAdapter()
138     {
139       @Override
140       public void internalFrameClosing(
141               InternalFrameEvent internalFrameEvent)
142       {
143         closeViewer(false);
144       }
145     });
146
147   }
148
149   /**
150    * Create a helper to manage progress bar display
151    */
152   protected void createProgressBar()
153   {
154     if (getProgressIndicator() == null)
155     {
156       setProgressIndicator(new ProgressBar(statusPanel, statusBar));
157     }
158   }
159
160   @Override
161   public void run()
162   {
163     // todo pull up much of this
164   
165     StringBuilder errormsgs = new StringBuilder(128);
166     List<PDBEntry> filePDB = new ArrayList<>();
167     List<Integer> filePDBpos = new ArrayList<>();
168     String[] curfiles = binding.getStructureFiles(); // files currently in viewer
169     for (int pi = 0; pi < binding.getPdbCount(); pi++)
170     {
171       String file = null;
172       PDBEntry thePdbEntry = binding.getPdbEntry(pi);
173       if (thePdbEntry.getFile() == null)
174       {
175         /*
176          * Retrieve PDB data, save to file, attach to PDBEntry
177          */
178         file = fetchPdbFile(thePdbEntry);
179         if (file == null)
180         {
181           errormsgs.append("'" + thePdbEntry.getId() + "' ");
182         }
183       }
184       else
185       {
186         /*
187          * got file already
188          */
189         file = new File(thePdbEntry.getFile()).getAbsoluteFile()
190                 .getPath();
191         // todo - skip if already loaded in PyMOL
192       }
193       if (file != null)
194       {
195         filePDB.add(thePdbEntry);
196         filePDBpos.add(Integer.valueOf(pi));
197       }
198     }
199         
200     if (!filePDB.isEmpty())
201     {
202       /*
203        * at least one structure to add to viewer
204        */
205       binding.setFinishedInit(false);
206       if (!addingStructures)
207       {
208         try
209         {
210           initPymol();
211         } catch (Exception ex)
212         {
213           Console.error("Couldn't open PyMOL viewer!", ex);
214           // if we couldn't open Pymol, no point continuing
215           return;
216         }
217       }
218       int num = -1;
219       for (PDBEntry pe : filePDB)
220       {
221         num++;
222         if (pe.getFile() != null)
223         {
224           try
225           {
226             int pos = filePDBpos.get(num).intValue();
227             long startTime = startProgressBar(getViewerName() + " "
228                     + MessageManager.getString("status.opening_file_for")
229                     + " " + pe.getId());
230             binding.openFile(pe);
231             binding.addSequence(pos, binding.getSequence()[pos]);
232             File fl = new File(pe.getFile());
233             DataSourceType protocol = DataSourceType.URL;
234             try
235             {
236               if (fl.exists())
237               {
238                 protocol = DataSourceType.FILE;
239               }
240             } catch (Throwable e)
241             {
242             } finally
243             {
244               stopProgressBar("", startTime);
245             }
246
247             StructureFile pdb = binding.getSsm().setMapping(
248                     binding.getSequence()[pos], binding.getChains()[pos],
249                     pe.getFile(), protocol,
250                     getProgressIndicator());
251             binding.stashFoundChains(pdb, pe.getFile());
252           } catch (Exception ex)
253           {
254             Console.error(
255                     "Couldn't open " + pe.getFile() + " in Chimera viewer!",
256                     ex);
257           } finally
258           {
259             // Cache.debug("File locations are " + files);
260           }
261         }
262       }
263
264       binding.refreshGUI();
265       binding.setFinishedInit(true);
266       binding.setLoadingFromArchive(false);
267
268       /*
269        * ensure that any newly discovered features (e.g. RESNUM)
270        * are added to any open feature settings dialog
271        */
272       FeatureRenderer fr = getBinding().getFeatureRenderer(null);
273       if (fr != null)
274       {
275         fr.featuresAdded();
276       }
277
278       // refresh the sequence colours for the new structure(s)
279       for (AlignmentViewPanel ap : _colourwith)
280       {
281         binding.updateColours(ap);
282       }
283       // do superposition if asked to
284       if (alignAddedStructures)
285       {
286         new Thread(new Runnable()
287         {
288           @Override
289           public void run()
290           {
291             alignStructsWithAllAlignPanels();
292           }
293         }).start();
294       }
295       addingStructures = false;
296     }
297     _started = false;
298     worker = null;
299
300   }
301
302   /**
303    * Launch PyMOL. If we have a session file name, send PyMOL the command to
304    * open its saved session file.
305    */
306   void initPymol()
307   {
308     Desktop.addInternalFrame(this,
309             binding.getViewerTitle(getViewerName(), true),
310             getBounds().width, getBounds().height);
311
312     if (!binding.launchPymol())
313     {
314       JvOptionPane.showMessageDialog(Desktop.desktop,
315               MessageManager.formatMessage("label.open_viewer_failed",
316                       getViewerName()),
317               MessageManager.getString("label.error_loading_file"),
318               JvOptionPane.ERROR_MESSAGE);
319       binding.closeViewer(true);
320       this.dispose();
321       return;
322     }
323
324     if (this.pymolSessionFile != null)
325     {
326       boolean opened = binding.openSession(pymolSessionFile);
327       if (!opened)
328       {
329         Console.error(
330                 "An error occurred opening PyMOL session file "
331                 + pymolSessionFile);
332       }
333     }
334     // binding.startPymolListener();
335   }
336
337   @Override
338   public AAStructureBindingModel getBinding()
339   {
340     return binding;
341   }
342
343   @Override
344   public ViewerType getViewerType()
345   {
346     return ViewerType.PYMOL;
347   }
348
349   @Override
350   protected String getViewerName()
351   {
352     return "PyMOL";
353   }
354   JMenuItem writeFeatures = null;
355   @Override
356   protected void initMenus()
357   {
358     super.initMenus();
359
360     savemenu.setVisible(false); // not yet implemented
361     viewMenu.add(fitToWindow);
362
363     writeFeatures = new JMenuItem(
364             MessageManager.getString("label.create_viewer_attributes"));
365     writeFeatures.setToolTipText(MessageManager
366             .getString("label.create_viewer_attributes_tip"));
367     writeFeatures.addActionListener(new ActionListener()
368     {
369       @Override
370       public void actionPerformed(ActionEvent e)
371       {
372         sendFeaturesToPymol();
373       }
374     });
375     viewerActionMenu.add(writeFeatures);
376   }
377   
378   @Override
379   protected void buildActionMenu()
380   {
381     super.buildActionMenu();
382     viewerActionMenu.add(writeFeatures);
383   }
384
385   protected void sendFeaturesToPymol()
386   {
387     int count = binding.sendFeaturesToViewer(getAlignmentPanel());
388     statusBar.setText(
389             MessageManager.formatMessage("label.attributes_set", count, getViewerName())); 
390   }
391
392 }