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