JAL-3949 Complete new abstracted logging framework in jalview.log. Updated log calls...
[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.Cache;
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           Cache.error("Couldn't open PyMOL viewer!", ex);
214         }
215       }
216       int num = -1;
217       for (PDBEntry pe : filePDB)
218       {
219         num++;
220         if (pe.getFile() != null)
221         {
222           try
223           {
224             int pos = filePDBpos.get(num).intValue();
225             long startTime = startProgressBar(getViewerName() + " "
226                     + MessageManager.getString("status.opening_file_for")
227                     + " " + pe.getId());
228             binding.openFile(pe);
229             binding.addSequence(pos, binding.getSequence()[pos]);
230             File fl = new File(pe.getFile());
231             DataSourceType protocol = DataSourceType.URL;
232             try
233             {
234               if (fl.exists())
235               {
236                 protocol = DataSourceType.FILE;
237               }
238             } catch (Throwable e)
239             {
240             } finally
241             {
242               stopProgressBar("", startTime);
243             }
244
245             StructureFile pdb = binding.getSsm().setMapping(
246                     binding.getSequence()[pos], binding.getChains()[pos],
247                     pe.getFile(), protocol,
248                     getProgressIndicator());
249             binding.stashFoundChains(pdb, pe.getFile());
250           } catch (Exception ex)
251           {
252             Cache.error(
253                     "Couldn't open " + pe.getFile() + " in Chimera viewer!",
254                     ex);
255           } finally
256           {
257             // Cache.debug("File locations are " + files);
258           }
259         }
260       }
261
262       binding.refreshGUI();
263       binding.setFinishedInit(true);
264       binding.setLoadingFromArchive(false);
265
266       /*
267        * ensure that any newly discovered features (e.g. RESNUM)
268        * are added to any open feature settings dialog
269        */
270       FeatureRenderer fr = getBinding().getFeatureRenderer(null);
271       if (fr != null)
272       {
273         fr.featuresAdded();
274       }
275
276       // refresh the sequence colours for the new structure(s)
277       for (AlignmentViewPanel ap : _colourwith)
278       {
279         binding.updateColours(ap);
280       }
281       // do superposition if asked to
282       if (alignAddedStructures)
283       {
284         new Thread(new Runnable()
285         {
286           @Override
287           public void run()
288           {
289             alignStructsWithAllAlignPanels();
290           }
291         }).start();
292       }
293       addingStructures = false;
294     }
295     _started = false;
296     worker = null;
297
298   }
299
300   /**
301    * Launch PyMOL. If we have a session file name, send PyMOL the command to
302    * open its saved session file.
303    */
304   void initPymol()
305   {
306     Desktop.addInternalFrame(this,
307             binding.getViewerTitle(getViewerName(), true),
308             getBounds().width, getBounds().height);
309
310     if (!binding.launchPymol())
311     {
312       JvOptionPane.showMessageDialog(Desktop.desktop,
313               MessageManager.formatMessage("label.open_viewer_failed",
314                       getViewerName()),
315               MessageManager.getString("label.error_loading_file"),
316               JvOptionPane.ERROR_MESSAGE);
317       this.dispose();
318       return;
319     }
320
321     if (this.pymolSessionFile != null)
322     {
323       boolean opened = binding.openSession(pymolSessionFile);
324       if (!opened)
325       {
326         Cache.error(
327                 "An error occurred opening PyMOL session file "
328                 + pymolSessionFile);
329       }
330     }
331     // binding.startPymolListener();
332   }
333
334   @Override
335   public AAStructureBindingModel getBinding()
336   {
337     return binding;
338   }
339
340   @Override
341   public ViewerType getViewerType()
342   {
343     return ViewerType.PYMOL;
344   }
345
346   @Override
347   protected String getViewerName()
348   {
349     return "PyMOL";
350   }
351   JMenuItem writeFeatures = null;
352   @Override
353   protected void initMenus()
354   {
355     super.initMenus();
356
357     savemenu.setVisible(false); // not yet implemented
358     viewMenu.add(fitToWindow);
359
360     writeFeatures = new JMenuItem(
361             MessageManager.getString("label.create_viewer_attributes"));
362     writeFeatures.setToolTipText(MessageManager
363             .getString("label.create_viewer_attributes_tip"));
364     writeFeatures.addActionListener(new ActionListener()
365     {
366       @Override
367       public void actionPerformed(ActionEvent e)
368       {
369         sendFeaturesToPymol();
370       }
371     });
372     viewerActionMenu.add(writeFeatures);
373   }
374   
375   @Override
376   protected void buildActionMenu()
377   {
378     super.buildActionMenu();
379     viewerActionMenu.add(writeFeatures);
380   }
381
382   protected void sendFeaturesToPymol()
383   {
384     int count = binding.sendFeaturesToViewer(getAlignmentPanel());
385     statusBar.setText(
386             MessageManager.formatMessage("label.attributes_set", count, getViewerName())); 
387   }
388
389 }