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