X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FChimeraViewFrame.java;h=83926066c4aa18af9cf55787e7fac4e7f3e69006;hb=refs%2Fheads%2Ffeature%2FJAL-3190jalviewjsChimera;hp=236d094537f6ffc0aaad8d6bfe40adb83c0f28f4;hpb=aced09c4feeaf3406269442c14e54abeeb4cad81;p=jalview.git diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index 236d094..8392606 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -1,6 +1,6 @@ /* - * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2) - * Copyright (C) 2014 The Jalview Authors + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * @@ -20,627 +20,353 @@ */ package jalview.gui; -import jalview.api.SequenceStructureBinding; -import jalview.api.structures.JalviewStructureDisplayI; +import jalview.api.FeatureRenderer; import jalview.bin.Cache; -import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; -import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; -import jalview.gui.ViewSelectionMenu.ViewSetProvider; -import jalview.io.AppletFormatAdapter; -import jalview.io.JalviewFileChooser; -import jalview.io.JalviewFileView; -import jalview.jbgui.GStructureViewer; -import jalview.schemes.BuriedColourScheme; -import jalview.schemes.ColourSchemeI; -import jalview.schemes.HelixColourScheme; -import jalview.schemes.HydrophobicColourScheme; -import jalview.schemes.PurinePyrimidineColourScheme; -import jalview.schemes.StrandColourScheme; -import jalview.schemes.TaylorColourScheme; -import jalview.schemes.TurnColourScheme; -import jalview.schemes.ZappoColourScheme; +import jalview.ext.rbvi.chimera.ChimeraCommands; +import jalview.ext.rbvi.chimera.JalviewChimeraBinding; +import jalview.gui.StructureViewer.ViewerType; +import jalview.io.DataSourceType; +import jalview.io.StructureFile; +import jalview.structures.models.AAStructureBindingModel; +import jalview.util.BrowserLauncher; +import jalview.util.ImageMaker.TYPE; import jalview.util.MessageManager; import jalview.util.Platform; +import jalview.ws.dbsources.Pdb; -import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.io.BufferedReader; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.PrintWriter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; -import java.util.Enumeration; +import java.util.Collections; import java.util.List; -import java.util.Vector; +import java.util.Random; import javax.swing.JCheckBoxMenuItem; -import javax.swing.JColorChooser; import javax.swing.JInternalFrame; import javax.swing.JMenu; import javax.swing.JMenuItem; -import javax.swing.JOptionPane; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; -import javax.swing.event.MenuEvent; -import javax.swing.event.MenuListener; /** - * GUI elements for handlnig an external chimera display + * GUI elements for handling an external chimera display * * @author jprocter * */ -public class ChimeraViewFrame extends GStructureViewer implements Runnable, - ViewSetProvider, JalviewStructureDisplayI - +public class ChimeraViewFrame extends StructureViewerBase { - JalviewChimeraBindingModel jmb; + private JalviewChimeraBinding jmb; + + private IProgressIndicator progressBar = null; - AlignmentPanel ap; + /* + * Path to Chimera session file. This is set when an open Jalview/Chimera + * session is saved, or on restore from a Jalview project (if it holds the + * filename of any saved Chimera sessions). + */ + private String chimeraSessionFile = null; - Vector atomsPicked = new Vector(); + private Random random = new Random(); - private boolean addingStructures = false; + private int myWidth = 500; - ViewSelectionMenu seqColourBy; + private int myHeight = 150; /** - * - * @param files - * @param ids - * @param seqs - * @param ap - * @param usetoColour - * - add the alignment panel to the list used for colouring these - * structures - * @param useToAlign - * - add the alignment panel to the list used for aligning these - * structures - * @param leaveColouringToJmol - * - do not update the colours from any other source. Jmol is - * handling them - * @param loadStatus - * @param bounds - * @param viewid - * - * public ChimeraViewFrame(String[] files, String[] ids, - * SequenceI[][] seqs, AlignmentPanel ap, boolean usetoColour, - * boolean useToAlign, boolean leaveColouringToJmol, String - * loadStatus, Rectangle bounds, String viewid) { PDBEntry[] - * pdbentrys = new PDBEntry[files.length]; for (int i = 0; i < - * pdbentrys.length; i++) { PDBEntry pdbentry = new PDBEntry(); - * pdbentry.setFile(files[i]); pdbentry.setId(ids[i]); pdbentrys[i] = - * pdbentry; } // / TODO: check if protocol is needed to be set, and - * if chains are // autodiscovered. jmb = new - * JalviewChimeraBindingModel(this, - * ap.getStructureSelectionManager(), pdbentrys, seqs, null, null); - * - * jmb.setLoadingFromArchive(true); addAlignmentPanel(ap); if - * (useToAlign) { useAlignmentPanelForSuperposition(ap); } if - * (leaveColouringToJmol || !usetoColour) { - * jmb.setColourBySequence(false); seqColour.setSelected(false); - * jmolColour.setSelected(true); } if (usetoColour) { - * useAlignmentPanelForColourbyseq(ap); - * jmb.setColourBySequence(true); seqColour.setSelected(true); - * jmolColour.setSelected(false); } this.setBounds(bounds); - * initMenus(); viewId = viewid; // - * jalview.gui.Desktop.addInternalFrame(this, "Loading File", // - * bounds.width,bounds.height); - * - * this.addInternalFrameListener(new InternalFrameAdapter() { public - * void internalFrameClosing(InternalFrameEvent internalFrameEvent) { - * closeViewer(); } }); initJmol(loadStatus); // pdbentry, seq, - * JBPCHECK! - * - * } + * Initialise menu options. */ - private void initMenus() + @Override + protected void initMenus() { - seqColour.setSelected(jmb.isColourBySequence()); - jmolColour.setSelected(!jmb.isColourBySequence()); - if (_colourwith == null) - { - _colourwith = new Vector(); - } - if (_alignwith == null) - { - _alignwith = new Vector(); - } + super.initMenus(); - seqColourBy = new ViewSelectionMenu(MessageManager.getString("label.colour_by"), this, _colourwith, - new ItemListener() - { + viewerActionMenu.setText(MessageManager.getString("label.chimera")); - @Override - public void itemStateChanged(ItemEvent e) - { - if (!seqColour.isSelected()) - { - seqColour.doClick(); - } - else - { - // update the jmol display now. - seqColour_actionPerformed(null); - } - } - }); - viewMenu.add(seqColourBy); - final ItemListener handler; - JMenu alpanels = new ViewSelectionMenu(MessageManager.getString("label.superpose_with"), this, - _alignwith, handler = new ItemListener() - { + viewerColour + .setText(MessageManager.getString("label.colour_with_chimera")); + viewerColour.setToolTipText(MessageManager + .getString("label.let_chimera_manage_structure_colours")); - @Override - public void itemStateChanged(ItemEvent e) - { - alignStructs.setEnabled(_alignwith.size() > 0); - alignStructs.setToolTipText(MessageManager - .formatMessage( - "label.align_structures_using_linked_alignment_views", - new String[] - { new Integer(_alignwith.size()).toString() })); - } - }); - handler.itemStateChanged(null); - jmolActionMenu.add(alpanels); - jmolActionMenu.addMenuListener(new MenuListener() - { + helpItem.setText(MessageManager.getString("label.chimera_help")); + savemenu.setVisible(false); // not yet implemented + viewMenu.add(fitToWindow); + JMenuItem writeFeatures = new JMenuItem( + MessageManager.getString("label.create_chimera_attributes")); + writeFeatures.setToolTipText(MessageManager + .getString("label.create_chimera_attributes_tip")); + writeFeatures.addActionListener(new ActionListener() + { @Override - public void menuSelected(MenuEvent e) + public void actionPerformed(ActionEvent e) { - handler.itemStateChanged(null); + sendFeaturesToChimera(); } + }); + viewerActionMenu.add(writeFeatures); - @Override - public void menuDeselected(MenuEvent e) - { - // TODO Auto-generated method stub - - } + final JMenu fetchAttributes = new JMenu( + MessageManager.getString("label.fetch_chimera_attributes")); + fetchAttributes.setToolTipText( + MessageManager.getString("label.fetch_chimera_attributes_tip")); + fetchAttributes.addMouseListener(new MouseAdapter() + { @Override - public void menuCanceled(MenuEvent e) + public void mouseEntered(MouseEvent e) { - // TODO Auto-generated method stub - + buildAttributesMenu(fetchAttributes); } }); + viewerActionMenu.add(fetchAttributes); } - IProgressIndicator progressBar = null; - /** - * add a single PDB structure to a new or existing Jmol view + * Query Chimera for its residue attribute names and add them as items off the + * attributes menu * - * @param pdbentry - * @param seq - * @param chains - * @param ap + * @param attributesMenu */ - public ChimeraViewFrame(PDBEntry pdbentry, SequenceI[] seq, - String[] chains, final AlignmentPanel ap) + protected void buildAttributesMenu(JMenu attributesMenu) { - progressBar = ap.alignFrame; - // //////////////////////////////// - // Is the pdb file already loaded? - String alreadyMapped = ap.getStructureSelectionManager() - .alreadyMappedToFile(pdbentry.getId()); - - if (alreadyMapped != null) + List atts = jmb.sendChimeraCommand("list resattr", true); + if (atts == null) { - int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop, - MessageManager.formatMessage( - "label.pdb_entry_is_already_displayed", new String[] - { pdbentry.getId() }), MessageManager.formatMessage( - "label.map_sequences_to_visible_window", new String[] - { pdbentry.getId() }), JOptionPane.YES_NO_OPTION); + return; + } + attributesMenu.removeAll(); + Collections.sort(atts); + for (String att : atts) + { + final String attName = att.split(" ")[1]; - if (option == JOptionPane.YES_OPTION) + /* + * ignore 'jv_*' attributes, as these are Jalview features that have + * been transferred to residue attributes in Chimera! + */ + if (!attName.startsWith(ChimeraCommands.NAMESPACE_PREFIX)) { - // TODO : Fix multiple seq to one chain issue here. - ap.getStructureSelectionManager().setMapping(seq, chains, - alreadyMapped, AppletFormatAdapter.FILE); - if (ap.seqPanel.seqCanvas.fr != null) - { - ap.seqPanel.seqCanvas.fr.featuresAdded(); - ap.paintAlignment(true); - } - - // Now this AppJmol is mapped to new sequences. We must add them to - // the exisiting array - JInternalFrame[] frames = Desktop.instance.getAllFrames(); - - for (int i = 0; i < frames.length; i++) + JMenuItem menuItem = new JMenuItem(attName); + menuItem.addActionListener(new ActionListener() { - if (frames[i] instanceof ChimeraViewFrame) + @Override + public void actionPerformed(ActionEvent e) { - final ChimeraViewFrame topJmol = ((ChimeraViewFrame) frames[i]); - // JBPNOTE: this looks like a binding routine, rather than a gui - // routine - for (int pe = 0; pe < topJmol.jmb.pdbentry.length; pe++) - { - if (topJmol.jmb.pdbentry[pe].getFile().equals(alreadyMapped)) - { - topJmol.jmb.addSequence(pe, seq); - topJmol.addAlignmentPanel(ap); - // add it to the set used for colouring - topJmol.useAlignmentPanelForColourbyseq(ap); - topJmol.buildChimeraActionMenu(); - ap.getStructureSelectionManager() - .sequenceColoursChanged(ap); - break; - } - } + getChimeraAttributes(attName); } - } - - return; + }); + attributesMenu.add(menuItem); } } - // ///////////////////////////////// - // Check if there are other Jmol views involving this alignment - // and prompt user about adding this molecule to one of them - Vector existingViews = getJmolsFor(ap); - if (existingViews.size() > 0) - { - Enumeration jm = existingViews.elements(); - while (jm.hasMoreElements()) - { - ChimeraViewFrame topJmol = (ChimeraViewFrame) jm.nextElement(); - // TODO: highlight topJmol in view somehow - int option = JOptionPane - .showInternalConfirmDialog( - Desktop.desktop, - MessageManager.formatMessage( - "label.add_pdbentry_to_view", new String[] - { pdbentry.getId(), topJmol.getTitle() }), - MessageManager - .getString("label.align_to_existing_structure_view"), - JOptionPane.YES_NO_OPTION); - if (option == JOptionPane.YES_OPTION) - { - topJmol.useAlignmentPanelForSuperposition(ap); - topJmol.addStructure(pdbentry, seq, chains, true, ap.alignFrame); - return; - } - } - } - // ///////////////////////////////// - openNewJmol(ap, new PDBEntry[] - { pdbentry }, new SequenceI[][] - { seq }); - } - - private void openNewJmol(AlignmentPanel ap, PDBEntry[] pdbentrys, - SequenceI[][] seqs) - { - progressBar = ap.alignFrame; - jmb = new JalviewChimeraBindingModel(this, - ap.getStructureSelectionManager(), pdbentrys, seqs, null, null); - addAlignmentPanel(ap); - useAlignmentPanelForColourbyseq(ap); - if (pdbentrys.length > 1) - { - alignAddedStructures = true; - useAlignmentPanelForSuperposition(ap); - } - jmb.setColourBySequence(true); - setSize(400, 400); // probably should be a configurable/dynamic default here - initMenus(); - worker = null; - { - addingStructures = false; - worker = new Thread(this); - worker.start(); - } - this.addInternalFrameListener(new InternalFrameAdapter() - { - public void internalFrameClosing(InternalFrameEvent internalFrameEvent) - { - closeViewer(); - } - }); - } /** - * create a new Jmol containing several structures superimposed using the - * given alignPanel. + * Read residues in Chimera with the given attribute name, and set as features + * on the corresponding sequence positions (if any) * - * @param ap - * @param pe - * @param seqs + * @param attName */ - public ChimeraViewFrame(AlignmentPanel ap, PDBEntry[] pe, - SequenceI[][] seqs) + protected void getChimeraAttributes(String attName) { - openNewJmol(ap, pe, seqs); + jmb.copyStructureAttributesToFeatures(attName, getAlignmentPanel()); } /** - * list of sequenceSet ids associated with the view + * Send a command to Chimera to create residue attributes for Jalview features + *

+ * The syntax is: setattr r + *

+ * For example: setattr r jv:chain "Ferredoxin-1, Chloroplastic" #0:94.A */ - ArrayList _aps = new ArrayList(); - - public AlignmentPanel[] getAllAlignmentPanels() + protected void sendFeaturesToChimera() { - AlignmentPanel[] t, list = new AlignmentPanel[0]; - for (String setid : _aps) - { - AlignmentPanel[] panels = PaintRefresher.getAssociatedPanels(setid); - if (panels != null) - { - t = new AlignmentPanel[list.length + panels.length]; - System.arraycopy(list, 0, t, 0, list.length); - System.arraycopy(panels, 0, t, list.length, panels.length); - list = t; - } - } - - return list; + int count = jmb.sendFeaturesToViewer(getAlignmentPanel()); + statusBar.setText( + MessageManager.formatMessage("label.attributes_set", count)); } /** - * list of alignment panels to use for superposition - */ - Vector _alignwith = new Vector(); - - /** - * list of alignment panels that are used for colouring structures by aligned - * sequences - */ - Vector _colourwith = new Vector(); - - /** - * set the primary alignmentPanel reference and add another alignPanel to the - * list of ones to use for colouring and aligning + * open a single PDB structure in a new Chimera view * - * @param nap + * @param pdbentry + * @param seq + * @param chains + * @param ap */ - public void addAlignmentPanel(AlignmentPanel nap) + public ChimeraViewFrame(PDBEntry pdbentry, SequenceI[] seq, + String[] chains, final AlignmentPanel ap) { - if (ap == null) - { - ap = nap; - } - if (!_aps.contains(nap.av.getSequenceSetId())) - { - _aps.add(nap.av.getSequenceSetId()); - } + this(); + + openNewChimera(ap, new PDBEntry[] { pdbentry }, + new SequenceI[][] + { seq }); } /** - * remove any references held to the given alignment panel - * - * @param nap + * Create a helper to manage progress bar display */ - public void removeAlignmentPanel(AlignmentPanel nap) + protected void createProgressBar() { - try - { - _alignwith.remove(nap); - _colourwith.remove(nap); - if (ap == nap) - { - ap = null; - for (AlignmentPanel aps : getAllAlignmentPanels()) - { - if (aps != nap) - { - ap = aps; - break; - } - } - } - } catch (Exception ex) + if (progressBar == null) { - } - if (ap != null) - { - buildChimeraActionMenu(); + progressBar = new ProgressBar(statusPanel, statusBar); } } - public void useAlignmentPanelForSuperposition(AlignmentPanel nap) + private void openNewChimera(AlignmentPanel ap, PDBEntry[] pdbentrys, + SequenceI[][] seqs) { - addAlignmentPanel(nap); - if (!_alignwith.contains(nap)) + createProgressBar(); + jmb = new JalviewChimeraBindingModel(this, + ap.getStructureSelectionManager(), pdbentrys, seqs, null); + addAlignmentPanel(ap); + useAlignmentPanelForColourbyseq(ap); + + if (pdbentrys.length > 1) { - _alignwith.add(nap); + useAlignmentPanelForSuperposition(ap); } - } + jmb.setColourBySequence(true); + setSize(myWidth, myHeight); + initMenus(); - public void excludeAlignmentPanelForSuperposition(AlignmentPanel nap) - { - if (_alignwith.contains(nap)) + addingStructures = false; + worker = new Thread(this); + worker.start(); + + this.addInternalFrameListener(new InternalFrameAdapter() { - _alignwith.remove(nap); - } - } + @Override + public void internalFrameClosing( + InternalFrameEvent internalFrameEvent) + { + closeViewer(false); + } + }); - public void useAlignmentPanelForColourbyseq(AlignmentPanel nap, - boolean enableColourBySeq) - { - useAlignmentPanelForColourbyseq(nap); - jmb.setColourBySequence(enableColourBySeq); - seqColour.setSelected(enableColourBySeq); - jmolColour.setSelected(!enableColourBySeq); } - public void useAlignmentPanelForColourbyseq(AlignmentPanel nap) + /** + * Create a new viewer from saved session state data including Chimera session + * file + * + * @param chimeraSessionFile + * @param alignPanel + * @param pdbArray + * @param seqsArray + * @param colourByChimera + * @param colourBySequence + * @param newViewId + */ + public ChimeraViewFrame(String chimeraSessionFile, + AlignmentPanel alignPanel, PDBEntry[] pdbArray, + SequenceI[][] seqsArray, boolean colourByChimera, + boolean colourBySequence, String newViewId) { - addAlignmentPanel(nap); - if (!_colourwith.contains(nap)) + this(); + setViewId(newViewId); + this.chimeraSessionFile = chimeraSessionFile; + openNewChimera(alignPanel, pdbArray, seqsArray); + if (colourByChimera) { - _colourwith.add(nap); + jmb.setColourBySequence(false); + seqColour.setSelected(false); + viewerColour.setSelected(true); } - } - - public void excludeAlignmentPanelForColourbyseq(AlignmentPanel nap) - { - if (_colourwith.contains(nap)) + else if (colourBySequence) { - _colourwith.remove(nap); + jmb.setColourBySequence(true); + seqColour.setSelected(true); + viewerColour.setSelected(false); } } /** - * pdb retrieval thread. - */ - private Thread worker = null; - - /** - * add a new structure (with associated sequences and chains) to this viewer, - * retrieving it if necessary first. + * create a new viewer containing several structures, optionally superimposed + * using the given alignPanel. * - * @param pdbentry - * @param seq - * @param chains - * @param alignFrame - * @param align - * if true, new structure(s) will be align using associated alignment + * @param pe + * @param seqs + * @param ap */ - private void addStructure(final PDBEntry pdbentry, final SequenceI[] seq, - final String[] chains, final boolean b, - final IProgressIndicator alignFrame) + public ChimeraViewFrame(PDBEntry[] pe, boolean alignAdded, + SequenceI[][] seqs, + AlignmentPanel ap) { - if (pdbentry.getFile() == null) - { - if (worker != null && worker.isAlive()) - { - // a retrieval is in progress, wait around and add ourselves to the - // queue. - new Thread(new Runnable() - { - public void run() - { - while (worker != null && worker.isAlive() && _started) - { - try - { - Thread.sleep(100 + ((int) Math.random() * 100)); - - } catch (Exception e) - { - } - - } - // and call ourselves again. - addStructure(pdbentry, seq, chains, b, alignFrame); - } - }).start(); - return; - } - } - // otherwise, start adding the structure. - jmb.addSequenceAndChain(new PDBEntry[] - { pdbentry }, new SequenceI[][] - { seq }, new String[][] - { chains }); - addingStructures = true; - _started = false; - alignAddedStructures = b; - progressBar = alignFrame; // visual indication happens on caller frame. - (worker = new Thread(this)).start(); - return; + this(); + setAlignAddedStructures(alignAdded); + openNewChimera(ap, pe, seqs); } - private Vector getJmolsFor(AlignmentPanel ap2) + /** + * Default constructor + */ + public ChimeraViewFrame() { - Vector otherJmols = new Vector(); - // Now this AppJmol is mapped to new sequences. We must add them to - // the exisiting array - JInternalFrame[] frames = Desktop.instance.getAllFrames(); + super(); - for (int i = 0; i < frames.length; i++) - { - if (frames[i] instanceof ChimeraViewFrame) - { - ChimeraViewFrame topJmol = ((ChimeraViewFrame) frames[i]); - if (topJmol.isLinkedWith(ap2)) - { - otherJmols.addElement(topJmol); - } - } - } - return otherJmols; + /* + * closeViewer will decide whether or not to close this frame + * depending on whether user chooses to Cancel or not + */ + setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE); } - void initChimera(String command) + /** + * Launch Chimera. If we have a chimera session file name, send Chimera the + * command to open its saved session file. + */ + void initChimera() { jmb.setFinishedInit(false); - // TODO: consider waiting until the structure/view is fully loaded before - // displaying - jalview.gui.Desktop.addInternalFrame(this, jmb.getViewerTitle(), - getBounds().width, getBounds().height); - if (command == null) - { - command = ""; - } - jmb.evalStateCommand(command, false); - jmb.setFinishedInit(true); - } + Desktop.addInternalFrame(this, + jmb.getViewerTitle(getViewerName(), true), getBounds().width, + getBounds().height); - void setChainMenuItems(Vector chains) - { - chainMenu.removeAll(); - if (chains == null) + if (!jmb.launchChimera()) { + JvOptionPane.showMessageDialog(Desktop.desktop, + MessageManager.getString("label.chimera_failed"), + MessageManager.getString("label.error_loading_file"), + JvOptionPane.ERROR_MESSAGE); + this.dispose(); return; } - JMenuItem menuItem = new JMenuItem( - MessageManager.getString("label.all")); - menuItem.addActionListener(new ActionListener() + + if (this.chimeraSessionFile != null) { - public void actionPerformed(ActionEvent evt) + boolean opened = jmb.openSession(chimeraSessionFile); + if (!opened) { - allChainsSelected = true; - for (int i = 0; i < chainMenu.getItemCount(); i++) - { - if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem) - ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true); - } - centerViewer(); - allChainsSelected = false; + System.err.println("An error occurred opening Chimera session file " + + chimeraSessionFile); } - }); - - chainMenu.add(menuItem); + } - for (int c = 0; c < chains.size(); c++) + if (!Platform.isJS()) { - menuItem = new JCheckBoxMenuItem(chains.elementAt(c).toString(), true); - menuItem.addItemListener(new ItemListener() - { - public void itemStateChanged(ItemEvent evt) - { - if (!allChainsSelected) - centerViewer(); - } - }); - - chainMenu.add(menuItem); + jmb.startChimeraListener(); } } - boolean allChainsSelected = false; - - private boolean alignAddedStructures = false; - - void centerViewer() + /** + * Show only the selected chain(s) in the viewer + */ + @Override + void showSelectedChains() { - Vector toshow = new Vector(); - String lbl; - int mlength, p, mnum; + List toshow = new ArrayList<>(); for (int i = 0; i < chainMenu.getItemCount(); i++) { if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem) @@ -648,93 +374,99 @@ public class ChimeraViewFrame extends GStructureViewer implements Runnable, JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i); if (item.isSelected()) { - toshow.addElement(item.getText()); + toshow.add(item.getText()); } } } - jmb.centerViewer(toshow); + jmb.showChains(toshow); } - public void closeViewer() + /** + * Close down this instance of Jalview's Chimera viewer, giving the user the + * option to close the associated Chimera window (process). They may wish to + * keep it open until they have had an opportunity to save any work. + * + * @param closeChimera + * if true, close any linked Chimera process; if false, prompt first + */ + @Override + public void closeViewer(boolean closeChimera) { - jmb.closeViewer(); - ap = null; + if (jmb != null && jmb.isChimeraRunning()) + { + if (!closeChimera) + { + String prompt = MessageManager + .formatMessage("label.confirm_close_chimera", new Object[] + { jmb.getViewerTitle(getViewerName(), false) }); + prompt = JvSwingUtils.wrapTooltip(true, prompt); + int confirm = JvOptionPane.showConfirmDialog(this, prompt, + MessageManager.getString("label.close_viewer"), + JvOptionPane.YES_NO_CANCEL_OPTION); + /* + * abort closure if user hits escape or Cancel + */ + if (confirm == JvOptionPane.CANCEL_OPTION + || confirm == JvOptionPane.CLOSED_OPTION) + { + return; + } + closeChimera = confirm == JvOptionPane.YES_OPTION; + } + jmb.closeViewer(closeChimera); + } + setAlignmentPanel(null); _aps.clear(); _alignwith.clear(); _colourwith.clear(); // TODO: check for memory leaks where instance isn't finalised because jmb // holds a reference to the window jmb = null; + dispose(); } /** - * state flag for PDB retrieval thread + * Open any newly added PDB structures in Chimera, having first fetched data + * from PDB (if not already saved). */ - private boolean _started = false; - + @Override public void run() { _started = true; - String pdbid = ""; - // todo - record which pdbids were successfuly imported. - StringBuffer errormsgs = new StringBuffer(), files = new StringBuffer(); - List fileToLoad=new ArrayList(); - List filePDB = new ArrayList(); - List filePDBpos =new ArrayList(); + // todo - record which pdbids were successfully imported. + StringBuilder errormsgs = new StringBuilder(128); + StringBuilder files = new StringBuilder(128); + List filePDB = new ArrayList<>(); + List filePDBpos = new ArrayList<>(); + PDBEntry thePdbEntry = null; + StructureFile pdb = null; try { - String[] curfiles = jmb.getPdbFile(); // files currently in viewer + String[] curfiles = jmb.getStructureFiles(); // files currently in viewer // TODO: replace with reference fetching/transfer code (validate PDBentry // as a DBRef?) - jalview.ws.dbsources.Pdb pdbclient = new jalview.ws.dbsources.Pdb(); - for (int pi = 0; pi < jmb.pdbentry.length; pi++) + for (int pi = 0; pi < jmb.getPdbCount(); pi++) { String file = null; - if (jmb.pdbentry[pi].getFile()==null) + thePdbEntry = jmb.getPdbEntry(pi); + if (thePdbEntry.getFile() == null) { - // retrieve the pdb and store it locally - AlignmentI pdbseq = null; - pdbid = jmb.pdbentry[pi].getId(); - long hdl = pdbid.hashCode() - System.currentTimeMillis(); - if (progressBar != null) - { - progressBar.setProgressBar(MessageManager.formatMessage("status.fetching_pdb", new String[]{pdbid}), hdl); - } - try - { - pdbseq = pdbclient.getSequenceRecords(pdbid = jmb.pdbentry[pi] - .getId()); - } catch (OutOfMemoryError oomerror) - { - new OOMWarning("Retrieving PDB id " + pdbid, oomerror); - } catch (Exception ex) - { - ex.printStackTrace(); - errormsgs.append("'" + pdbid + "'"); - } - if (progressBar != null) + /* + * Retrieve PDB data, save to file, attach to PDBEntry + */ + file = fetchPdbFile(thePdbEntry); + if (file == null) { - progressBar.setProgressBar(MessageManager.getString("label.state_completed"), hdl); - } - if (pdbseq != null) - { - // just transfer the file name from the first sequence's first - // PDBEntry - file = new File(((PDBEntry) pdbseq.getSequenceAt(0).getPDBId() - .elementAt(0)).getFile()).getAbsolutePath(); - jmb.pdbentry[pi].setFile(file); - - files.append(" \"" + Platform.escapeString(file) + "\""); - } - else - { - errormsgs.append("'" + pdbid + "' "); + errormsgs.append("'" + thePdbEntry.getId() + "' "); } } else { - file = new File(jmb.pdbentry[pi].getFile()) - .getAbsoluteFile().getPath(); + /* + * Got file already - ignore if already loaded in Chimera. + */ + file = new File(thePdbEntry.getFile()).getAbsoluteFile() + .getPath(); if (curfiles != null && curfiles.length > 0) { addingStructures = true; // already files loaded. @@ -747,50 +479,49 @@ public class ChimeraViewFrame extends GStructureViewer implements Runnable, } } } - - if (file != null) - { - fileToLoad.add(file); - filePDB.add(jmb.pdbentry[pi]); - filePDBpos.add(Integer.valueOf(pi)); - files.append(" \"" + Platform.escapeString(file) + "\""); - } + } + if (file != null) + { + filePDB.add(thePdbEntry); + filePDBpos.add(Integer.valueOf(pi)); + files.append(" \"" + Platform.escapeString(file) + "\""); } } } catch (OutOfMemoryError oomerror) { - new OOMWarning("Retrieving PDB files: " + pdbid, oomerror); + new OOMWarning("Retrieving PDB files: " + thePdbEntry.getId(), + oomerror); } catch (Exception ex) { ex.printStackTrace(); - errormsgs.append("When retrieving pdbfiles : current was: '" + pdbid - + "'"); + errormsgs.append( + "When retrieving pdbfiles for '" + thePdbEntry.getId() + "'"); } if (errormsgs.length() > 0) { - JOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager - .formatMessage("label.pdb_entries_couldnt_be_retrieved", - new String[] - { errormsgs.toString() }), MessageManager - .getString("label.couldnt_load_file"), - JOptionPane.ERROR_MESSAGE); - + JvOptionPane.showInternalMessageDialog(Desktop.desktop, + MessageManager.formatMessage( + "label.pdb_entries_couldnt_be_retrieved", new Object[] + { errormsgs.toString() }), + MessageManager.getString("label.couldnt_load_file"), + JvOptionPane.ERROR_MESSAGE); } - long lastnotify = jmb.getLoadNotifiesHandled(); + if (files.length() > 0) { + jmb.setFinishedInit(false); if (!addingStructures) { try { - initChimera(""); + initChimera(); } catch (Exception ex) { Cache.log.error("Couldn't open Chimera viewer!", ex); } - } - int num=-1; + } + int num = -1; for (PDBEntry pe : filePDB) { num++; @@ -798,26 +529,33 @@ public class ChimeraViewFrame extends GStructureViewer implements Runnable, { try { - int pos=filePDBpos.get(num).intValue(); + int pos = filePDBpos.get(num).intValue(); + long startTime = startProgressBar(getViewerName() + " " + + MessageManager.getString("status.opening_file_for") + + " " + pe.getId()); jmb.openFile(pe); - jmb.addSequence(pos, jmb.sequence[pos]); - File fl=new File(pe.getFile()); - String protocol = AppletFormatAdapter.URL; + jmb.addSequence(pos, jmb.getSequence()[pos]); + File fl = new File(pe.getFile()); + DataSourceType protocol = DataSourceType.URL; try { if (fl.exists()) - { - protocol = AppletFormatAdapter.FILE; - } - } catch (Exception e) - { - } catch (Error e) { + protocol = DataSourceType.FILE; } - // Explicitly map to the filename used by Jmol ; - jmb.ssm.setMapping(jmb.sequence[pos], null, pe.getFile(), - protocol); - // pdbentry[pe].getFile(), protocol); + } catch (Throwable e) + { + } finally + { + stopProgressBar("", startTime); + } + // Explicitly map to the filename used by Chimera ; + + pdb = jmb.getSsm().setMapping(jmb.getSequence()[pos], + jmb.getChains()[pos], pe.getFile(), protocol, + progressBar); + stashFoundChains(pdb, pe.getFile()); + } catch (OutOfMemoryError oomerror) { new OOMWarning( @@ -825,18 +563,30 @@ public class ChimeraViewFrame extends GStructureViewer implements Runnable, oomerror); } catch (Exception ex) { - Cache.log.error("Couldn't open " + pe.getFile() - + " in Chimera viewer!", ex); + Cache.log.error( + "Couldn't open " + pe.getFile() + " in Chimera viewer!", + ex); } finally { Cache.log.debug("File locations are " + files); } } } - // jmb.getPdbFile(); + + jmb.refreshGUI(); jmb.setFinishedInit(true); jmb.setLoadingFromArchive(false); - + + /* + * ensure that any newly discovered features (e.g. RESNUM) + * are added to any open feature settings dialog + */ + FeatureRenderer fr = getBinding().getFeatureRenderer(null); + if (fr != null) + { + fr.featuresAdded(); + } + // refresh the sequence colours for the new structure(s) for (AlignmentPanel ap : _colourwith) { @@ -847,12 +597,12 @@ public class ChimeraViewFrame extends GStructureViewer implements Runnable, { new Thread(new Runnable() { + @Override public void run() { alignStructs_withAllAlignPanels(); } }).start(); - alignAddedStructures = false; } addingStructures = false; } @@ -860,370 +610,237 @@ public class ChimeraViewFrame extends GStructureViewer implements Runnable, worker = null; } - public void pdbFile_actionPerformed(ActionEvent actionEvent) - { - JalviewFileChooser chooser = new JalviewFileChooser( - jalview.bin.Cache.getProperty("LAST_DIRECTORY")); - - chooser.setFileView(new JalviewFileView()); - chooser.setDialogTitle(MessageManager.getString("label.save_pdb_file")); - chooser.setToolTipText(MessageManager.getString("action.save")); - - int value = chooser.showSaveDialog(this); + /** + * Fetch PDB data and save to a local file. Returns the full path to the file, + * or null if fetch fails. TODO: refactor to common with Jmol ? duplication + * + * @param processingEntry + * @return + * @throws Exception + */ - if (value == JalviewFileChooser.APPROVE_OPTION) + private void stashFoundChains(StructureFile pdb, String file) + { + for (int i = 0; i < pdb.getChains().size(); i++) { - try - { - // TODO: cope with multiple PDB files in view - BufferedReader in = new BufferedReader(new FileReader( - jmb.getPdbFile()[0])); - File outFile = chooser.getSelectedFile(); - - PrintWriter out = new PrintWriter(new FileOutputStream(outFile)); - String data; - while ((data = in.readLine()) != null) - { - if (!(data.indexOf("

") > -1 || data.indexOf("
") > -1)) - { - out.println(data); - } - } - out.close(); - } catch (Exception ex) - { - ex.printStackTrace(); - } + String chid = new String( + pdb.getId() + ":" + pdb.getChains().elementAt(i).id); + jmb.getChainNames().add(chid); + jmb.getChainFile().put(chid, file); } } - public void viewMapping_actionPerformed(ActionEvent actionEvent) + private String fetchPdbFile(PDBEntry processingEntry) throws Exception { - jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer(); + String filePath = null; + Pdb pdbclient = new Pdb(); + AlignmentI pdbseq = null; + String pdbid = processingEntry.getId(); + long handle = System.currentTimeMillis() + + Thread.currentThread().hashCode(); + + /* + * Write 'fetching PDB' progress on AlignFrame as we are not yet visible + */ + String msg = MessageManager.formatMessage("status.fetching_pdb", + new Object[] + { pdbid }); + getAlignmentPanel().alignFrame.setProgressBar(msg, handle); + // long hdl = startProgressBar(MessageManager.formatMessage( + // "status.fetching_pdb", new Object[] + // { pdbid })); try { - for (int pdbe = 0; pdbe < jmb.pdbentry.length; pdbe++) - { - cap.appendText(jmb.printMapping(jmb.pdbentry[pdbe].getFile())); - cap.appendText("\n"); - } - } catch (OutOfMemoryError e) + pdbseq = pdbclient.getSequenceRecords(pdbid); + } catch (OutOfMemoryError oomerror) { - new OOMWarning( - "composing sequence-structure alignments for display in text box.", - e); - cap.dispose(); - return; + new OOMWarning("Retrieving PDB id " + pdbid, oomerror); + } finally + { + msg = pdbid + " " + MessageManager.getString("label.state_completed"); + getAlignmentPanel().alignFrame.setProgressBar(msg, handle); + // stopProgressBar(msg, hdl); + } + /* + * If PDB data were saved and are not invalid (empty alignment), return the + * file path. + */ + if (pdbseq != null && pdbseq.getHeight() > 0) + { + // just use the file name from the first sequence's first PDBEntry + filePath = new File(pdbseq.getSequenceAt(0).getAllPDBEntries() + .elementAt(0).getFile()).getAbsolutePath(); + processingEntry.setFile(filePath); } - jalview.gui.Desktop.addInternalFrame(cap, - MessageManager.getString("label.pdb_sequence_mapping"), 550, - 600); + return filePath; } /** - * DOCUMENT ME! + * Convenience method to update the progress bar if there is one. Be sure to + * call stopProgressBar with the returned handle to remove the message. * - * @param e - * DOCUMENT ME! + * @param msg + * @param handle */ - public void eps_actionPerformed(ActionEvent e) + public long startProgressBar(String msg) { - throw new Error(MessageManager.getString("error.eps_generation_not_implemented")); + // TODO would rather have startProgress/stopProgress as the + // IProgressIndicator interface + long tm = random.nextLong(); + if (progressBar != null) + { + progressBar.setProgressBar(msg, tm); + } + return tm; } /** - * DOCUMENT ME! + * End the progress bar with the specified handle, leaving a message (if not + * null) on the status bar * - * @param e - * DOCUMENT ME! + * @param msg + * @param handle */ - public void png_actionPerformed(ActionEvent e) - { - throw new Error(MessageManager.getString("error.png_generation_not_implemented")); - } - - public void jmolColour_actionPerformed(ActionEvent actionEvent) + public void stopProgressBar(String msg, long handle) { - if (jmolColour.isSelected()) + if (progressBar != null) { - // disable automatic sequence colouring. - jmb.setColourBySequence(false); + progressBar.setProgressBar(msg, handle); } } - public void seqColour_actionPerformed(ActionEvent actionEvent) - { - jmb.setColourBySequence(seqColour.isSelected()); - if (_colourwith == null) - { - _colourwith = new Vector(); - } - if (jmb.isColourBySequence()) - { - if (!jmb.isLoadingFromArchive()) - { - if (_colourwith.size() == 0 && ap != null) - { - // Make the currently displayed alignment panel the associated view - _colourwith.add(ap.alignFrame.alignPanel); - } - } - // Set the colour using the current view for the associated alignframe - for (AlignmentPanel ap : _colourwith) - { - jmb.colourBySequence(ap.av.showSequenceFeatures, ap); - } - } - } - - public void chainColour_actionPerformed(ActionEvent actionEvent) - { - chainColour.setSelected(true); - jmb.colourByChain(); - } - - public void chargeColour_actionPerformed(ActionEvent actionEvent) - { - chargeColour.setSelected(true); - jmb.colourByCharge(); - } - - public void zappoColour_actionPerformed(ActionEvent actionEvent) - { - zappoColour.setSelected(true); - jmb.setJalviewColourScheme(new ZappoColourScheme()); - } - - public void taylorColour_actionPerformed(ActionEvent actionEvent) - { - taylorColour.setSelected(true); - jmb.setJalviewColourScheme(new TaylorColourScheme()); - } - - public void hydroColour_actionPerformed(ActionEvent actionEvent) - { - hydroColour.setSelected(true); - jmb.setJalviewColourScheme(new HydrophobicColourScheme()); - } - - public void helixColour_actionPerformed(ActionEvent actionEvent) - { - helixColour.setSelected(true); - jmb.setJalviewColourScheme(new HelixColourScheme()); - } - - public void strandColour_actionPerformed(ActionEvent actionEvent) - { - strandColour.setSelected(true); - jmb.setJalviewColourScheme(new StrandColourScheme()); - } - - public void turnColour_actionPerformed(ActionEvent actionEvent) - { - turnColour.setSelected(true); - jmb.setJalviewColourScheme(new TurnColourScheme()); - } - - public void buriedColour_actionPerformed(ActionEvent actionEvent) - { - buriedColour.setSelected(true); - jmb.setJalviewColourScheme(new BuriedColourScheme()); - } - - public void purinePyrimidineColour_actionPerformed(ActionEvent actionEvent) - { - setJalviewColourScheme(new PurinePyrimidineColourScheme()); - } - - public void userColour_actionPerformed(ActionEvent actionEvent) + @Override + public void makePDBImage(TYPE imageType) { - userColour.setSelected(true); - new UserDefinedColours(this, null); + throw new UnsupportedOperationException( + "Image export for Chimera is not implemented"); } - public void backGround_actionPerformed(ActionEvent actionEvent) - { - java.awt.Color col = JColorChooser.showDialog(this, - MessageManager.getString("label.select_backgroud_colour"), null); - if (col != null) - { - jmb.setBackgroundColour(col); - } - } - - public void jmolHelp_actionPerformed(ActionEvent actionEvent) + @Override + public void showHelp_actionPerformed(ActionEvent actionEvent) { try { - jalview.util.BrowserLauncher + BrowserLauncher .openURL("https://www.cgl.ucsf.edu/chimera/docs/UsersGuide"); - } catch (Exception ex) + } catch (IOException ex) { } } - String viewId = null; - - public String getViewId() - { - if (viewId == null) - { - viewId = System.currentTimeMillis() + "." + this.hashCode(); - } - return viewId; - } - - public void updateTitleAndMenus() - { - if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0) - { - repaint(); - return; - } - setChainMenuItems(jmb.chainNames); - - this.setTitle(jmb.getViewerTitle()); - if (jmb.getPdbFile().length > 1 && jmb.sequence.length > 1) - { - jmolActionMenu.setVisible(true); - } - if (!jmb.isLoadingFromArchive()) - { - seqColour_actionPerformed(null); - } - } - - protected void buildChimeraActionMenu() + @Override + public AAStructureBindingModel getBinding() { - if (_alignwith == null) - { - _alignwith = new Vector(); - } - if (_alignwith.size() == 0 && ap != null) - { - _alignwith.add(ap); - } - ; - for (Component c : jmolActionMenu.getMenuComponents()) - { - if (c != alignStructs) - { - jmolActionMenu.remove((JMenuItem) c); - } - } - final ItemListener handler; + return jmb; } - /* - * (non-Javadoc) + /** + * Ask Chimera to save its session to the designated file path, or to a + * temporary file if the path is null. Returns the file path if successful, + * else null. * - * @see - * jalview.jbgui.GStructureViewer#alignStructs_actionPerformed(java.awt.event - * .ActionEvent) + * @param filepath + * @see getStateInfo */ - @Override - protected void alignStructs_actionPerformed(ActionEvent actionEvent) - { - alignStructs_withAllAlignPanels(); - } - - private void alignStructs_withAllAlignPanels() + protected String saveSession(String filepath) { - if (ap == null) - { - return; - } - ; - if (_alignwith.size() == 0) - { - _alignwith.add(ap); - } - ; + String pathUsed = filepath; try { - AlignmentI[] als = new Alignment[_alignwith.size()]; - ColumnSelection[] alc = new ColumnSelection[_alignwith.size()]; - int[] alm = new int[_alignwith.size()]; - int a = 0; - - for (AlignmentPanel ap : _alignwith) + if (pathUsed == null) { - als[a] = ap.av.getAlignment(); - alm[a] = -1; - alc[a++] = ap.av.getColumnSelection(); + File tempFile = File.createTempFile("chimera", ".py"); + tempFile.deleteOnExit(); + pathUsed = tempFile.getPath(); } - jmb.superposeStructures(als, alm, alc); - } catch (Exception e) - { - StringBuffer sp = new StringBuffer(); - for (AlignmentPanel ap : _alignwith) + boolean result = jmb.saveSession(pathUsed); + if (result) { - sp.append("'" + ap.alignFrame.getTitle() + "' "); + this.chimeraSessionFile = pathUsed; + return pathUsed; } - Cache.log.info("Couldn't align structures with the " + sp.toString() - + "associated alignment panels.", e); - + } catch (IOException e) + { } - - } - - public void setJalviewColourScheme(ColourSchemeI ucs) - { - jmb.setJalviewColourScheme(ucs); - + return null; } /** - * - * @param alignment - * @return first alignment panel displaying given alignment, or the default - * alignment panel + * Returns a string representing the state of the Chimera session. This is + * done by requesting Chimera to save its session to a temporary file, then + * reading the file contents. Returns an empty string on any error. */ - public AlignmentPanel getAlignmentPanelFor(AlignmentI alignment) + @Override + public String getStateInfo() { - for (AlignmentPanel ap : getAllAlignmentPanels()) + String sessionFile = saveSession(null); + if (sessionFile == null) + { + return ""; + } + InputStream is = null; + try + { + File f = new File(sessionFile); + byte[] bytes = new byte[(int) f.length()]; + is = new FileInputStream(sessionFile); + is.read(bytes); + return new String(bytes); + } catch (IOException e) { - if (ap.av.getAlignment() == alignment) + return ""; + } finally + { + if (is != null) { - return ap; + try + { + is.close(); + } catch (IOException e) + { + // ignore + } } } - return ap; } - /** - * - * @param ap2 - * @return true if this Jmol instance is linked with the given alignPanel - */ - public boolean isLinkedWith(AlignmentPanel ap2) + @Override + protected void fitToWindow_actionPerformed() { - return _aps.contains(ap2.av.getSequenceSetId()); + jmb.focusView(); } - public boolean isUsedforaligment(AlignmentPanel ap2) + @Override + public ViewerType getViewerType() { - - return (_alignwith != null) && _alignwith.contains(ap2); + return ViewerType.CHIMERA; } - public boolean isUsedforcolourby(AlignmentPanel ap2) + @Override + protected String getViewerName() { - return (_colourwith != null) && _colourwith.contains(ap2); + return "Chimera"; } /** + * Sends commands to align structures according to associated alignment(s). * - * @return TRUE if the view is NOT being coloured by sequence associations. + * @return */ - public boolean isColouredByJmol() + @Override + protected String alignStructs_withAllAlignPanels() { - return !jmb.isColourBySequence(); + String reply = super.alignStructs_withAllAlignPanels(); + if (reply != null) + { + statusBar.setText("Superposition failed: " + reply); + } + return reply; } - public SequenceStructureBinding getBinding() + @Override + protected IProgressIndicator getIProgressIndicator() { - return jmb; + return progressBar; } - }