2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignmentViewPanel;
24 import jalview.api.FeatureRenderer;
25 import jalview.bin.Cache;
26 import jalview.datamodel.PDBEntry;
27 import jalview.datamodel.SequenceI;
28 import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
29 import jalview.gui.StructureViewer.ViewerType;
30 import jalview.io.DataSourceType;
31 import jalview.io.StructureFile;
32 import jalview.structures.models.AAStructureBindingModel;
33 import jalview.util.BrowserLauncher;
34 import jalview.util.MessageManager;
35 import jalview.util.Platform;
37 import java.awt.event.ActionEvent;
38 import java.awt.event.ActionListener;
39 import java.awt.event.MouseAdapter;
40 import java.awt.event.MouseEvent;
42 import java.io.FileInputStream;
43 import java.io.IOException;
44 import java.io.InputStream;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.List;
49 import javax.swing.JInternalFrame;
50 import javax.swing.JMenu;
51 import javax.swing.JMenuItem;
52 import javax.swing.event.InternalFrameAdapter;
53 import javax.swing.event.InternalFrameEvent;
56 * GUI elements for handling an external chimera display
61 public class ChimeraViewFrame extends StructureViewerBase
63 private JalviewChimeraBinding jmb;
66 * Path to Chimera session file. This is set when an open Jalview/Chimera
67 * session is saved, or on restore from a Jalview project (if it holds the
68 * filename of any saved Chimera sessions).
70 private String chimeraSessionFile = null;
72 private int myWidth = 500;
74 private int myHeight = 150;
77 * Initialise menu options.
80 protected void initMenus()
84 viewerActionMenu.setText(MessageManager.getString("label.chimera"));
87 .setText(MessageManager.getString("label.colour_with_chimera"));
88 viewerColour.setToolTipText(MessageManager
89 .getString("label.let_chimera_manage_structure_colours"));
91 helpItem.setText(MessageManager.getString("label.chimera_help"));
92 savemenu.setVisible(false); // not yet implemented
93 viewMenu.add(fitToWindow);
95 JMenuItem writeFeatures = new JMenuItem(
96 MessageManager.getString("label.create_chimera_attributes"));
97 writeFeatures.setToolTipText(MessageManager
98 .getString("label.create_chimera_attributes_tip"));
99 writeFeatures.addActionListener(new ActionListener()
102 public void actionPerformed(ActionEvent e)
104 sendFeaturesToChimera();
107 viewerActionMenu.add(writeFeatures);
109 final JMenu fetchAttributes = new JMenu(
110 MessageManager.getString("label.fetch_chimera_attributes"));
111 fetchAttributes.setToolTipText(
112 MessageManager.getString("label.fetch_chimera_attributes_tip"));
113 fetchAttributes.addMouseListener(new MouseAdapter()
117 public void mouseEntered(MouseEvent e)
119 buildAttributesMenu(fetchAttributes);
122 viewerActionMenu.add(fetchAttributes);
126 * Query Chimera for its residue attribute names and add them as items off the
129 * @param attributesMenu
131 protected void buildAttributesMenu(JMenu attributesMenu)
133 List<String> atts = jmb.getChimeraAttributes();
134 attributesMenu.removeAll();
135 Collections.sort(atts);
136 for (String attName : atts)
138 JMenuItem menuItem = new JMenuItem(attName);
139 menuItem.addActionListener(new ActionListener()
142 public void actionPerformed(ActionEvent e)
144 getChimeraAttributes(attName);
147 attributesMenu.add(menuItem);
152 * Read residues in Chimera with the given attribute name, and set as features
153 * on the corresponding sequence positions (if any)
157 protected void getChimeraAttributes(String attName)
159 jmb.copyStructureAttributesToFeatures(attName, getAlignmentPanel());
163 * Send a command to Chimera to create residue attributes for Jalview features
165 * The syntax is: setattr r <attName> <attValue> <atomSpec>
167 * For example: setattr r jv_chain "Ferredoxin-1, Chloroplastic" #0:94.A
169 protected void sendFeaturesToChimera()
171 int count = jmb.sendFeaturesToViewer(getAlignmentPanel());
173 MessageManager.formatMessage("label.attributes_set", count));
177 * open a single PDB structure in a new Chimera view
184 public ChimeraViewFrame(PDBEntry pdbentry, SequenceI[] seq,
185 String[] chains, final AlignmentPanel ap)
189 openNewChimera(ap, new PDBEntry[] { pdbentry },
195 * Create a helper to manage progress bar display
197 protected void createProgressBar()
199 if (getProgressIndicator() == null)
201 setProgressIndicator(new ProgressBar(statusPanel, statusBar));
205 private void openNewChimera(AlignmentPanel ap, PDBEntry[] pdbentrys,
209 jmb = newBindingModel(ap, pdbentrys, seqs);
210 addAlignmentPanel(ap);
211 useAlignmentPanelForColourbyseq(ap);
213 if (pdbentrys.length > 1)
215 useAlignmentPanelForSuperposition(ap);
217 jmb.setColourBySequence(true);
218 setSize(myWidth, myHeight);
221 addingStructures = false;
222 worker = new Thread(this);
225 this.addInternalFrameListener(new InternalFrameAdapter()
228 public void internalFrameClosing(
229 InternalFrameEvent internalFrameEvent)
237 protected JalviewChimeraBindingModel newBindingModel(AlignmentPanel ap,
238 PDBEntry[] pdbentrys, SequenceI[][] seqs)
240 return new JalviewChimeraBindingModel(this,
241 ap.getStructureSelectionManager(), pdbentrys, seqs, null);
245 * Create a new viewer from saved session state data including Chimera session
248 * @param chimeraSessionFile
252 * @param colourByChimera
253 * @param colourBySequence
256 public ChimeraViewFrame(String chimeraSessionFile,
257 AlignmentPanel alignPanel, PDBEntry[] pdbArray,
258 SequenceI[][] seqsArray, boolean colourByChimera,
259 boolean colourBySequence, String newViewId)
262 setViewId(newViewId);
263 this.chimeraSessionFile = chimeraSessionFile;
264 openNewChimera(alignPanel, pdbArray, seqsArray);
267 jmb.setColourBySequence(false);
268 seqColour.setSelected(false);
269 viewerColour.setSelected(true);
271 else if (colourBySequence)
273 jmb.setColourBySequence(true);
274 seqColour.setSelected(true);
275 viewerColour.setSelected(false);
280 * create a new viewer containing several structures, optionally superimposed
281 * using the given alignPanel.
287 public ChimeraViewFrame(PDBEntry[] pe, boolean alignAdded,
292 setAlignAddedStructures(alignAdded);
293 openNewChimera(ap, pe, seqs);
297 * Default constructor
299 public ChimeraViewFrame()
304 * closeViewer will decide whether or not to close this frame
305 * depending on whether user chooses to Cancel or not
307 setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
311 * Launch Chimera. If we have a chimera session file name, send Chimera the
312 * command to open its saved session file.
316 jmb.setFinishedInit(false);
317 Desktop.addInternalFrame(this,
318 jmb.getViewerTitle(getViewerName(), true), getBounds().width,
321 if (!jmb.launchChimera())
323 JvOptionPane.showMessageDialog(Desktop.desktop,
324 MessageManager.getString("label.chimera_failed"),
325 MessageManager.getString("label.error_loading_file"),
326 JvOptionPane.ERROR_MESSAGE);
331 if (this.chimeraSessionFile != null)
333 boolean opened = jmb.openSession(chimeraSessionFile);
336 System.err.println("An error occurred opening Chimera session file "
337 + chimeraSessionFile);
341 jmb.startChimeraListener();
345 * Close down this instance of Jalview's Chimera viewer, giving the user the
346 * option to close the associated Chimera window (process). They may wish to
347 * keep it open until they have had an opportunity to save any work.
349 * @param closeChimera
350 * if true, close any linked Chimera process; if false, prompt first
353 public void closeViewer(boolean closeChimera)
355 if (jmb != null && jmb.isChimeraRunning())
359 String prompt = MessageManager
360 .formatMessage("label.confirm_close_chimera", new Object[]
361 { jmb.getViewerTitle(getViewerName(), false) });
362 prompt = JvSwingUtils.wrapTooltip(true, prompt);
363 int confirm = JvOptionPane.showConfirmDialog(this, prompt,
364 MessageManager.getString("label.close_viewer"),
365 JvOptionPane.YES_NO_CANCEL_OPTION);
367 * abort closure if user hits escape or Cancel
369 if (confirm == JvOptionPane.CANCEL_OPTION
370 || confirm == JvOptionPane.CLOSED_OPTION)
374 closeChimera = confirm == JvOptionPane.YES_OPTION;
376 jmb.closeViewer(closeChimera);
378 setAlignmentPanel(null);
382 // TODO: check for memory leaks where instance isn't finalised because jmb
383 // holds a reference to the window
389 * Open any newly added PDB structures in Chimera, having first fetched data
390 * from PDB (if not already saved).
396 // todo - record which pdbids were successfully imported.
397 StringBuilder errormsgs = new StringBuilder(128);
398 StringBuilder files = new StringBuilder(128);
399 List<PDBEntry> filePDB = new ArrayList<>();
400 List<Integer> filePDBpos = new ArrayList<>();
401 PDBEntry thePdbEntry = null;
402 StructureFile pdb = null;
405 String[] curfiles = jmb.getStructureFiles(); // files currently in viewer
406 // TODO: replace with reference fetching/transfer code (validate PDBentry
408 for (int pi = 0; pi < jmb.getPdbCount(); pi++)
411 thePdbEntry = jmb.getPdbEntry(pi);
412 if (thePdbEntry.getFile() == null)
415 * Retrieve PDB data, save to file, attach to PDBEntry
417 file = fetchPdbFile(thePdbEntry);
420 errormsgs.append("'" + thePdbEntry.getId() + "' ");
426 * Got file already - ignore if already loaded in Chimera.
428 file = new File(thePdbEntry.getFile()).getAbsoluteFile()
430 if (curfiles != null && curfiles.length > 0)
432 addingStructures = true; // already files loaded.
433 for (int c = 0; c < curfiles.length; c++)
435 if (curfiles[c].equals(file))
445 filePDB.add(thePdbEntry);
446 filePDBpos.add(Integer.valueOf(pi));
447 files.append(" \"" + Platform.escapeBackslashes(file) + "\"");
450 } catch (OutOfMemoryError oomerror)
452 new OOMWarning("Retrieving PDB files: " + thePdbEntry.getId(),
454 } catch (Exception ex)
456 ex.printStackTrace();
458 "When retrieving pdbfiles for '" + thePdbEntry.getId() + "'");
460 if (errormsgs.length() > 0)
463 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
464 MessageManager.formatMessage(
465 "label.pdb_entries_couldnt_be_retrieved", new Object[]
466 { errormsgs.toString() }),
467 MessageManager.getString("label.couldnt_load_file"),
468 JvOptionPane.ERROR_MESSAGE);
471 if (files.length() > 0)
473 jmb.setFinishedInit(false);
474 if (!addingStructures)
479 } catch (Exception ex)
481 Cache.log.error("Couldn't open Chimera viewer!", ex);
485 for (PDBEntry pe : filePDB)
488 if (pe.getFile() != null)
492 int pos = filePDBpos.get(num).intValue();
493 long startTime = startProgressBar(getViewerName() + " "
494 + MessageManager.getString("status.opening_file_for")
497 jmb.addSequence(pos, jmb.getSequence()[pos]);
498 File fl = new File(pe.getFile());
499 DataSourceType protocol = DataSourceType.URL;
504 protocol = DataSourceType.FILE;
506 } catch (Throwable e)
510 stopProgressBar("", startTime);
512 // Explicitly map to the filename used by Chimera ;
514 pdb = jmb.getSsm().setMapping(jmb.getSequence()[pos],
515 jmb.getChains()[pos], pe.getFile(), protocol,
516 getProgressIndicator());
517 jmb.stashFoundChains(pdb, pe.getFile());
519 } catch (OutOfMemoryError oomerror)
522 "When trying to open and map structures from Chimera!",
524 } catch (Exception ex)
527 "Couldn't open " + pe.getFile() + " in Chimera viewer!",
531 Cache.log.debug("File locations are " + files);
537 jmb.setFinishedInit(true);
538 jmb.setLoadingFromArchive(false);
541 * ensure that any newly discovered features (e.g. RESNUM)
542 * are added to any open feature settings dialog
544 FeatureRenderer fr = getBinding().getFeatureRenderer(null);
550 // refresh the sequence colours for the new structure(s)
551 for (AlignmentViewPanel ap : _colourwith)
553 jmb.updateColours(ap);
555 // do superposition if asked to
556 if (alignAddedStructures)
558 new Thread(new Runnable()
563 alignStructsWithAllAlignPanels();
567 addingStructures = false;
574 public void eps_actionPerformed()
576 throw new Error(MessageManager
577 .getString("error.eps_generation_not_implemented"));
581 public void png_actionPerformed()
583 throw new Error(MessageManager
584 .getString("error.png_generation_not_implemented"));
588 public void showHelp_actionPerformed()
592 String url = jmb.getHelpURL();
593 BrowserLauncher.openURL(url);
594 } catch (IOException ex)
597 .println("Show Chimera help failed with: " + ex.getMessage());
602 public AAStructureBindingModel getBinding()
608 protected void fitToWindow_actionPerformed()
614 public ViewerType getViewerType()
616 return ViewerType.CHIMERA;
620 protected String getViewerName()