8f7f2c1209b1af14734510bf9615309f28fa89a9
[jalview.git] / src / jalview / gui / PymolViewer.java
1 package jalview.gui;
2
3 import jalview.api.AlignmentViewPanel;
4 import jalview.api.FeatureRenderer;
5 import jalview.bin.Cache;
6 import jalview.datamodel.PDBEntry;
7 import jalview.datamodel.SequenceI;
8 import jalview.gui.StructureViewer.ViewerType;
9 import jalview.io.DataSourceType;
10 import jalview.io.StructureFile;
11 import jalview.structures.models.AAStructureBindingModel;
12 import jalview.util.MessageManager;
13
14 import java.io.File;
15 import java.util.ArrayList;
16 import java.util.List;
17
18 import javax.swing.JInternalFrame;
19 import javax.swing.event.InternalFrameAdapter;
20 import javax.swing.event.InternalFrameEvent;
21
22 public class PymolViewer extends StructureViewerBase
23 {
24   private static final int myWidth = 500;
25
26   private static final int myHeight = 150;
27
28   private PymolBindingModel binding;
29
30   private String pymolSessionFile;
31
32   public PymolViewer()
33   {
34     super();
35
36     /*
37      * closeViewer will decide whether or not to close this frame
38      * depending on whether user chooses to Cancel or not
39      */
40     setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
41   }
42
43   public PymolViewer(PDBEntry pdb, SequenceI[] seqs, Object object,
44           AlignmentPanel ap)
45   {
46     this();
47     openNewPymol(ap, new PDBEntry[] { pdb },
48             new SequenceI[][]
49             { seqs });
50   }
51
52   public PymolViewer(PDBEntry[] pe, boolean alignAdded, SequenceI[][] seqs,
53           AlignmentPanel ap)
54   {
55     this();
56     setAlignAddedStructures(alignAdded);
57     openNewPymol(ap, pe, seqs);
58   }
59
60   private void openNewPymol(AlignmentPanel ap, PDBEntry[] pe,
61           SequenceI[][] seqs)
62   {
63     createProgressBar();
64     binding = new PymolBindingModel(this, ap.getStructureSelectionManager(),
65             pe, seqs);
66     addAlignmentPanel(ap);
67     useAlignmentPanelForColourbyseq(ap);
68
69     if (pe.length > 1)
70     {
71       useAlignmentPanelForSuperposition(ap);
72     }
73     binding.setColourBySequence(true);
74     setSize(myWidth, myHeight);
75     initMenus();
76     viewerActionMenu.setText("PyMOL");
77     updateTitleAndMenus();
78
79     addingStructures = false;
80     worker = new Thread(this);
81     worker.start();
82
83     this.addInternalFrameListener(new InternalFrameAdapter()
84     {
85       @Override
86       public void internalFrameClosing(
87               InternalFrameEvent internalFrameEvent)
88       {
89         closeViewer(false);
90       }
91     });
92
93   }
94
95   /**
96    * Create a helper to manage progress bar display
97    */
98   protected void createProgressBar()
99   {
100     if (getProgressIndicator() == null)
101     {
102       setProgressIndicator(new ProgressBar(statusPanel, statusBar));
103     }
104   }
105
106   @Override
107   public void run()
108   {
109     // todo pull up much of this
110   
111     StringBuilder errormsgs = new StringBuilder(128);
112     List<PDBEntry> filePDB = new ArrayList<>();
113     List<Integer> filePDBpos = new ArrayList<>();
114     String[] curfiles = binding.getStructureFiles(); // files currently in viewer
115     for (int pi = 0; pi < binding.getPdbCount(); pi++)
116     {
117       String file = null;
118       PDBEntry thePdbEntry = binding.getPdbEntry(pi);
119       if (thePdbEntry.getFile() == null)
120       {
121         /*
122          * Retrieve PDB data, save to file, attach to PDBEntry
123          */
124         file = fetchPdbFile(thePdbEntry);
125         if (file == null)
126         {
127           errormsgs.append("'" + thePdbEntry.getId() + "' ");
128         }
129       }
130       else
131       {
132         /*
133          * got file already
134          */
135         file = new File(thePdbEntry.getFile()).getAbsoluteFile()
136                 .getPath();
137         // todo - skip if already loaded in PyMOL
138       }
139       if (file != null)
140       {
141         filePDB.add(thePdbEntry);
142         filePDBpos.add(Integer.valueOf(pi));
143       }
144     }
145         
146     if (!filePDB.isEmpty())
147     {
148       /*
149        * at least one structure to add to viewer
150        */
151       binding.setFinishedInit(false);
152       if (!addingStructures)
153       {
154         try
155         {
156           initPymol();
157         } catch (Exception ex)
158         {
159           Cache.log.error("Couldn't open PyMOL viewer!", ex);
160         }
161       }
162       int num = -1;
163       for (PDBEntry pe : filePDB)
164       {
165         num++;
166         if (pe.getFile() != null)
167         {
168           try
169           {
170             int pos = filePDBpos.get(num).intValue();
171             long startTime = startProgressBar(getViewerName() + " "
172                     + MessageManager.getString("status.opening_file_for")
173                     + " " + pe.getId());
174             binding.openFile(pe);
175             binding.addSequence(pos, binding.getSequence()[pos]);
176             File fl = new File(pe.getFile());
177             DataSourceType protocol = DataSourceType.URL;
178             try
179             {
180               if (fl.exists())
181               {
182                 protocol = DataSourceType.FILE;
183               }
184             } catch (Throwable e)
185             {
186             } finally
187             {
188               stopProgressBar("", startTime);
189             }
190
191             StructureFile pdb = binding.getSsm().setMapping(
192                     binding.getSequence()[pos], binding.getChains()[pos],
193                     pe.getFile(), protocol,
194                     getProgressIndicator());
195             binding.stashFoundChains(pdb, pe.getFile());
196           } catch (Exception ex)
197           {
198             Cache.log.error(
199                     "Couldn't open " + pe.getFile() + " in Chimera viewer!",
200                     ex);
201           } finally
202           {
203             // Cache.log.debug("File locations are " + files);
204           }
205         }
206       }
207
208       binding.refreshGUI();
209       binding.setFinishedInit(true);
210       binding.setLoadingFromArchive(false);
211
212       /*
213        * ensure that any newly discovered features (e.g. RESNUM)
214        * are added to any open feature settings dialog
215        */
216       FeatureRenderer fr = getBinding().getFeatureRenderer(null);
217       if (fr != null)
218       {
219         fr.featuresAdded();
220       }
221
222       // refresh the sequence colours for the new structure(s)
223       for (AlignmentViewPanel ap : _colourwith)
224       {
225         binding.updateColours(ap);
226       }
227       // do superposition if asked to
228       if (alignAddedStructures)
229       {
230         new Thread(new Runnable()
231         {
232           @Override
233           public void run()
234           {
235             alignStructsWithAllAlignPanels();
236           }
237         }).start();
238       }
239       addingStructures = false;
240     }
241     _started = false;
242     worker = null;
243
244   }
245
246   /**
247    * Launch PyMOL. If we have a session file name, send PyMOL the command to
248    * open its saved session file.
249    */
250   void initPymol()
251   {
252     Desktop.addInternalFrame(this,
253             binding.getViewerTitle(getViewerName(), true),
254             getBounds().width, getBounds().height);
255
256     if (!binding.launchPymol())
257     {
258       JvOptionPane.showMessageDialog(Desktop.desktop,
259               MessageManager.getString("label.pymol_failed"),
260               MessageManager.getString("label.error_loading_file"),
261               JvOptionPane.ERROR_MESSAGE);
262       this.dispose();
263       return;
264     }
265
266     if (this.pymolSessionFile != null)
267     {
268       boolean opened = binding.openSession(pymolSessionFile);
269       if (!opened)
270       {
271         System.err.println("An error occurred opening PyMOL session file "
272                 + pymolSessionFile);
273       }
274     }
275     // binding.startPymolListener();
276   }
277
278   @Override
279   public AAStructureBindingModel getBinding()
280   {
281     return binding;
282   }
283
284   @Override
285   public void closeViewer(boolean closePymol)
286   {
287     if (binding != null && binding.isPymolRunning())
288     {
289       if (!closePymol)
290       {
291         // TODO i18n (and pull up)
292         String prompt = MessageManager
293                 .formatMessage("label.confirm_close_pymol", new Object[]
294                 { binding.getViewerTitle(getViewerName(), false) });
295         prompt = JvSwingUtils.wrapTooltip(true, prompt);
296         int confirm = JvOptionPane.showConfirmDialog(this, prompt,
297                 MessageManager.getString("label.close_viewer"),
298                 JvOptionPane.YES_NO_CANCEL_OPTION);
299         /*
300          * abort closure if user hits escape or Cancel
301          */
302         if (confirm == JvOptionPane.CANCEL_OPTION
303                 || confirm == JvOptionPane.CLOSED_OPTION)
304         {
305           return;
306         }
307         closePymol = confirm == JvOptionPane.YES_OPTION;
308       }
309       binding.closeViewer(closePymol);
310     }
311     setAlignmentPanel(null);
312     _aps.clear();
313     _alignwith.clear();
314     _colourwith.clear();
315     // TODO: check for memory leaks where instance isn't finalised because
316     // binding
317     // holds a reference to the window
318     binding = null;
319     dispose();
320   }
321
322   @Override
323   public String getStateInfo()
324   {
325     return null;
326   }
327
328   @Override
329   public ViewerType getViewerType()
330   {
331     return ViewerType.PYMOL;
332   }
333
334   @Override
335   protected String getViewerName()
336   {
337     return "PyMOL";
338   }
339
340 }