X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FChimeraViewFrame.java;h=1e6da78a6da2a5ca76d7a902751cf408feae7742;hb=59bcebdf52d535ff621840b2d836ecfc0f605ec1;hp=fdc7d0bb71f5caf2d19a32b898ea4c75cb13d4d8;hpb=8f9a21c422709588ddd5893904b47d01244937cd;p=jalview.git diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index fdc7d0b..1e6da78 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -26,13 +26,17 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.Vector; import javax.swing.JCheckBoxMenuItem; @@ -53,6 +57,7 @@ import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.ext.rbvi.chimera.JalviewChimeraBinding; +import jalview.gui.StructureViewer.ViewerType; import jalview.io.AppletFormatAdapter; import jalview.io.JalviewFileChooser; import jalview.io.JalviewFileView; @@ -71,7 +76,7 @@ import jalview.util.Platform; import jalview.ws.dbsources.Pdb; /** - * GUI elements for handlnig an external chimera display + * GUI elements for handling an external chimera display * * @author jprocter * @@ -151,6 +156,8 @@ public class ChimeraViewFrame extends StructureViewerBase } }); viewMenu.add(seqColourBy); + viewMenu.add(fitToWindow); + final ItemListener handler; JMenu alpanels = new ViewSelectionMenu( MessageManager.getString("label.superpose_with"), this, @@ -204,110 +211,161 @@ public class ChimeraViewFrame extends StructureViewerBase String[] chains, final AlignmentPanel ap) { super(); - progressBar = ap.alignFrame; - // //////////////////////////////// - // Is the pdb file already loaded? + + /* + * is the pdb file already loaded? + */ + String pdbId = pdbentry.getId(); String alreadyMapped = ap.getStructureSelectionManager() - .alreadyMappedToFile(pdbentry.getId()); + .alreadyMappedToFile(pdbId); if (alreadyMapped != null) { - int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop, - MessageManager.formatMessage( - "label.pdb_entry_is_already_displayed", new Object[] - { pdbentry.getId() }), MessageManager.formatMessage( - "label.map_sequences_to_visible_window", new Object[] - { pdbentry.getId() }), - JOptionPane.YES_NO_CANCEL_OPTION); - + int option = chooseAddSequencesToViewer(pdbId); if (option == JOptionPane.CANCEL_OPTION) { return; } if (option == JOptionPane.YES_OPTION) { - // TODO : Fix multiple seq to one chain issue here. - ap.getStructureSelectionManager().setMapping(seq, chains, - alreadyMapped, AppletFormatAdapter.FILE); - if (ap.getSeqPanel().seqCanvas.fr != null) - { - ap.getSeqPanel().seqCanvas.fr.featuresAdded(); - ap.paintAlignment(true); - } - - // Now this ChimeraViewFrame is mapped to new sequences. We must add - // them to the existing array - JInternalFrame[] frames = Desktop.instance.getAllFrames(); - - for (JInternalFrame frame : frames) - { - if (frame instanceof ChimeraViewFrame) - { - final ChimeraViewFrame topView = ((ChimeraViewFrame) frame); - // JBPNOTE: this looks like a binding routine, rather than a gui - // routine - for (int pe = 0; pe < topView.jmb.getPdbCount(); pe++) - { - if (topView.jmb.getPdbEntry(pe).getFile() - .equals( - alreadyMapped)) - { - topView.jmb.addSequence(pe, seq); - topView.addAlignmentPanel(ap); - // add it to the set used for colouring - topView.useAlignmentPanelForColourbyseq(ap); - topView.buildActionMenu(); - ap.getStructureSelectionManager() - .sequenceColoursChanged(ap); - break; - } - } - } - } - + addSequenceMappingsToStructure(seq, chains, ap, alreadyMapped); return; } } - // ///////////////////////////////// - // Check if there are other Chimera views involving this alignment - // and prompt user about adding this molecule to one of them + + /* + * Check if there are other Chimera views involving this alignment and give + * user the option to add and align this molecule to one of them + */ List existingViews = getChimeraWindowsFor(ap); - for (ChimeraViewFrame topView : existingViews) + for (ChimeraViewFrame view : existingViews) { - // TODO: highlight topView in view somehow + // TODO: highlight view somehow /* * JAL-1742 exclude view with this structure already mapped (don't offer * to align chain B to chain A of the same structure) */ - if (topView.hasPdbId(pdbentry.getId())) + if (view.hasPdbId(pdbId)) { continue; } - int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop, - MessageManager.formatMessage("label.add_pdbentry_to_view", - new Object[] - { pdbentry.getId(), topView.getTitle() }), - MessageManager - .getString("label.align_to_existing_structure_view"), - JOptionPane.YES_NO_CANCEL_OPTION); + int option = chooseAlignStructureToViewer(pdbId, view); if (option == JOptionPane.CANCEL_OPTION) { return; } if (option == JOptionPane.YES_OPTION) { - topView.useAlignmentPanelForSuperposition(ap); - topView.addStructure(pdbentry, seq, chains, true, ap.alignFrame); + view.useAlignmentPanelForSuperposition(ap); + view.addStructure(pdbentry, seq, chains, true, ap.alignFrame); return; } } - // ///////////////////////////////// + + /* + * If the options above are declined or do not apply, open a new viewer + */ openNewChimera(ap, new PDBEntry[] { pdbentry }, new SequenceI[][] { seq }); } /** + * Presents a dialog with the option to add an align a structure to an + * existing Chimera view + * + * @param pdbId + * @param view + * @return YES, NO or CANCEL JOptionPane code + */ + protected int chooseAlignStructureToViewer(String pdbId, + ChimeraViewFrame view) + { + int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop, + MessageManager.formatMessage("label.add_pdbentry_to_view", + new Object[] + { pdbId, view.getTitle() }), MessageManager + .getString("label.align_to_existing_structure_view"), + JOptionPane.YES_NO_CANCEL_OPTION); + return option; + } + + /** + * Presents a dialog with the option to add sequences to a viewer which + * already has their structure open + * + * @param pdbId + * @return YES, NO or CANCEL JOptionPane code + */ + protected int chooseAddSequencesToViewer(String pdbId) + { + int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop, + MessageManager.formatMessage( + "label.pdb_entry_is_already_displayed", new Object[] + { pdbId }), MessageManager.formatMessage( + "label.map_sequences_to_visible_window", new Object[] + { pdbId }), JOptionPane.YES_NO_CANCEL_OPTION); + return option; + } + + /** + * Adds mappings for the given sequences to an already opened PDB structure, + * and updates any viewers that have the PDB file + * + * @param seq + * @param chains + * @param ap + * @param pdbFilename + */ + protected void addSequenceMappingsToStructure(SequenceI[] seq, + String[] chains, final AlignmentPanel ap, String pdbFilename) + { + // TODO : Fix multiple seq to one chain issue here. + /* + * create the mappings + */ + ap.getStructureSelectionManager().setMapping(seq, chains, pdbFilename, + AppletFormatAdapter.FILE); + + /* + * alert the FeatureRenderer to show new (PDB RESNUM) features + */ + if (ap.getSeqPanel().seqCanvas.fr != null) + { + ap.getSeqPanel().seqCanvas.fr.featuresAdded(); + ap.paintAlignment(true); + } + + /* + * add the sequences to any other Chimera viewers for this pdb file + */ + // JBPNOTE: this looks like a binding routine, rather than a gui routine + for (JInternalFrame frame : Desktop.instance.getAllFrames()) + { + if (frame instanceof ChimeraViewFrame) + { + ChimeraViewFrame chimeraView = ((ChimeraViewFrame) frame); + for (int pe = 0; pe < chimeraView.jmb.getPdbCount(); pe++) + { + if (chimeraView.jmb.getPdbEntry(pe).getFile().equals(pdbFilename)) + { + chimeraView.jmb.addSequence(pe, seq); + chimeraView.addAlignmentPanel(ap); + /* + * add it to the set of alignments used for colouring structure by + * sequence + */ + chimeraView.useAlignmentPanelForColourbyseq(ap); + chimeraView.buildActionMenu(); + ap.getStructureSelectionManager().sequenceColoursChanged(ap); + break; + } + } + } + } + } + + /** * Create a helper to manage progress bar display */ protected void createProgressBar() @@ -327,9 +385,10 @@ public class ChimeraViewFrame extends StructureViewerBase SequenceI[][] seqs) { createProgressBar(); - + String[][] chains = extractChains(seqs); jmb = new JalviewChimeraBindingModel(this, - ap.getStructureSelectionManager(), pdbentrys, seqs, null, null); + ap.getStructureSelectionManager(), pdbentrys, seqs, chains, + null); addAlignmentPanel(ap); useAlignmentPanelForColourbyseq(ap); if (pdbentrys.length > 1) @@ -356,39 +415,58 @@ public class ChimeraViewFrame extends StructureViewerBase } /** - * create a new viewer containing several structures superimposed using the - * given alignPanel. + * Retrieve chains for sequences by inspecting their PDB refs. The hope is + * that the first will be to the sequence's own chain. Really need a more + * managed way of doing this. * - * @param ap - * @param pe * @param seqs + * @return */ - public ChimeraViewFrame(AlignmentPanel ap, PDBEntry[] pe, - SequenceI[][] seqs) + protected String[][] extractChains(SequenceI[][] seqs) { - super(); - openNewChimera(ap, pe, seqs); + String[][] chains = new String[seqs.length][]; + for (int i = 0; i < seqs.length; i++) + { + chains[i] = new String[seqs[i].length]; + int seqno = 0; + for (SequenceI seq : seqs[i]) + { + String chain = null; + if (seq.getDatasetSequence() != null) + { + Vector pdbrefs = seq.getDatasetSequence().getPDBId(); + if (pdbrefs != null && pdbrefs.size() > 0) + { + chain = pdbrefs.get(0).getChainCode(); + } + } + chains[i][seqno++] = chain; + } + } + return chains; } /** * Create a new viewer from saved session state data including Chimera session - * file. - * - * @param chimeraSession + * file * + * @param chimeraSessionFile * @param alignPanel * @param pdbArray * @param seqsArray * @param colourByChimera * @param colourBySequence + * @param newViewId */ - public ChimeraViewFrame(String chimeraSession, AlignmentPanel alignPanel, + public ChimeraViewFrame(String chimeraSessionFile, + AlignmentPanel alignPanel, PDBEntry[] pdbArray, SequenceI[][] seqsArray, boolean colourByChimera, - boolean colourBySequence) + boolean colourBySequence, String newViewId) { super(); - this.chimeraSessionFile = chimeraSession; + setViewId(newViewId); + this.chimeraSessionFile = chimeraSessionFile; openNewChimera(alignPanel, pdbArray, seqsArray); if (colourByChimera) { @@ -405,6 +483,44 @@ public class ChimeraViewFrame extends StructureViewerBase } /** + * create a new viewer containing several structures superimposed using the + * given alignPanel. + * + * @param pe + * @param seqs + * @param ap + */ + public ChimeraViewFrame(PDBEntry[] pe, SequenceI[][] seqs, + AlignmentPanel ap) + { + super(); + openNewChimera(ap, pe, seqs); + } + + public ChimeraViewFrame(Map> toView, + AlignmentPanel alignPanel) + { + super(); + + /* + * Convert the map of sequences per pdb entry into the tied arrays expected + * by openNewChimera + * + * TODO pass the Map down to openNewChimera and its callees instead + */ + final Set pdbEntries = toView.keySet(); + PDBEntry[] pdbs = pdbEntries.toArray(new PDBEntry[pdbEntries.size()]); + SequenceI[][] seqsForPdbs = new SequenceI[pdbEntries.size()][]; + for (int i = 0; i < pdbs.length; i++) + { + final List seqsForPdb = toView.get(pdbs[i]); + seqsForPdbs[i] = seqsForPdb.toArray(new SequenceI[seqsForPdb.size()]); + } + + openNewChimera(alignPanel, pdbs, seqsForPdbs); + } + + /** * add a new structure (with associated sequences and chains) to this viewer, * retrieving it if necessary first. * @@ -455,7 +571,7 @@ public class ChimeraViewFrame extends StructureViewerBase addingStructures = true; _started = false; alignAddedStructures = b; - progressBar = alignFrame; // visual indication happens on caller frame. + // progressBar = alignFrame; // visual indication happens on caller frame. (worker = new Thread(this)).start(); return; } @@ -488,10 +604,17 @@ public class ChimeraViewFrame extends StructureViewerBase jalview.gui.Desktop.addInternalFrame(this, jmb.getViewerTitle("Chimera", true), getBounds().width, getBounds().height); - /* - * Pass an empty 'command' to launch Chimera - */ - jmb.evalStateCommand("", false); + if (!jmb.launchChimera()) + { + JOptionPane + .showMessageDialog( + Desktop.desktop, + MessageManager.getString("label.chimera_failed"), + MessageManager.getString("label.error_loading_file"), + JOptionPane.ERROR_MESSAGE); + this.dispose(); + return; + } if (this.chimeraSessionFile != null) { @@ -508,10 +631,16 @@ public class ChimeraViewFrame extends StructureViewerBase jmb.startChimeraListener(); } + /** + * If the list is not empty, add menu items for 'All' and each individual + * chain to the "View | Show Chain" sub-menu. Multiple selections are allowed. + * + * @param chainNames + */ void setChainMenuItems(List chainNames) { chainMenu.removeAll(); - if (chainNames == null) + if (chainNames == null || chainNames.isEmpty()) { return; } @@ -529,7 +658,7 @@ public class ChimeraViewFrame extends StructureViewerBase ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true); } } - centerViewer(); + showSelectedChains(); allChainsSelected = false; } }); @@ -545,7 +674,7 @@ public class ChimeraViewFrame extends StructureViewerBase { if (!allChainsSelected) { - centerViewer(); + showSelectedChains(); } } }); @@ -554,7 +683,10 @@ public class ChimeraViewFrame extends StructureViewerBase } } - void centerViewer() + /** + * Show only the selected chain(s) in the viewer + */ + void showSelectedChains() { List toshow = new ArrayList(); for (int i = 0; i < chainMenu.getItemCount(); i++) @@ -568,7 +700,7 @@ public class ChimeraViewFrame extends StructureViewerBase } } } - jmb.centerViewer(toshow); + jmb.showChains(toshow); } /** @@ -726,8 +858,8 @@ public class ChimeraViewFrame extends StructureViewerBase stopProgressBar("", startTime); } // Explicitly map to the filename used by Chimera ; - // TODO: use pe.getId() instead of pe.getFile() ? - jmb.getSsm().setMapping(jmb.getSequence()[pos], null, + jmb.getSsm().setMapping(jmb.getSequence()[pos], + jmb.getChains()[pos], pe.getFile(), protocol); } catch (OutOfMemoryError oomerror) @@ -785,9 +917,19 @@ public class ChimeraViewFrame extends StructureViewerBase Pdb pdbclient = new Pdb(); AlignmentI pdbseq = null; String pdbid = processingEntry.getId(); - long hdl = startProgressBar(MessageManager.formatMessage( - "status.fetching_pdb", new Object[] - { pdbid })); + 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 { pdbseq = pdbclient.getSequenceRecords(pdbid); @@ -796,9 +938,10 @@ public class ChimeraViewFrame extends StructureViewerBase new OOMWarning("Retrieving PDB id " + pdbid, oomerror); } finally { - String msg = pdbid + " " + msg = pdbid + " " + MessageManager.getString("label.state_completed"); - stopProgressBar(msg, hdl); + getAlignmentPanel().alignFrame.setProgressBar(msg, handle); + // stopProgressBar(msg, hdl); } /* * If PDB data were saved and are not invalid (empty alignment), return the @@ -833,6 +976,13 @@ public class ChimeraViewFrame extends StructureViewerBase return tm; } + /** + * End the progress bar with the specified handle, leaving a message (if not + * null) on the status bar + * + * @param msg + * @param handle + */ public void stopProgressBar(String msg, long handle) { if (progressBar != null) @@ -897,11 +1047,7 @@ public class ChimeraViewFrame extends StructureViewerBase jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer(); try { - for (int pdbe = 0; pdbe < jmb.getPdbCount(); pdbe++) - { - cap.appendText(jmb.printMapping(jmb.getPdbEntry(pdbe).getFile())); - cap.appendText("\n"); - } + cap.appendText(jmb.printMappings()); } catch (OutOfMemoryError e) { new OOMWarning( @@ -1106,12 +1252,12 @@ public class ChimeraViewFrame extends StructureViewerBase { return; } - ; + if (_alignwith.size() == 0) { _alignwith.add(getAlignmentPanel()); } - ; + try { AlignmentI[] als = new Alignment[_alignwith.size()]; @@ -1135,9 +1281,7 @@ public class ChimeraViewFrame extends StructureViewerBase } Cache.log.info("Couldn't align structures with the " + sp.toString() + "associated alignment panels.", e); - } - } public void setJalviewColourScheme(ColourSchemeI ucs) @@ -1171,30 +1315,85 @@ public class ChimeraViewFrame extends StructureViewerBase } /** - * Ask Chimera to save its session to the designated file path. Returns true - * if successful, else false. + * 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. * * @param filepath * @see getStateInfo */ - public boolean saveSession(String filepath) + protected String saveSession(String filepath) { - boolean result = jmb.saveSession(filepath); - if (result) + String pathUsed = filepath; + try + { + if (pathUsed == null) + { + File tempFile = File.createTempFile("chimera", ".py"); + tempFile.deleteOnExit(); + pathUsed = tempFile.getPath(); + } + boolean result = jmb.saveSession(pathUsed); + if (result) + { + this.chimeraSessionFile = pathUsed; + return pathUsed; + } + } catch (IOException e) { - this.chimeraSessionFile = filepath; } - return result; + return null; } /** - * Returns the file path of the Chimera session file the last time it was - * saved. If it was never saved, returns an empty string. There is no - * guarantee that the Chimera session has not changed since it was saved. + * 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. */ @Override public String getStateInfo() { - return this.chimeraSessionFile; + 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) + { + return ""; + } finally + { + if (is != null) + { + try + { + is.close(); + } catch (IOException e) + { + // ignoreß + } + } + } + // return this.chimeraSessionFile == null ? "" : chimeraSessionFile; + } + + @Override + protected void fitToWindow_actionPerformed() + { + jmb.focusView(); + } + + @Override + public ViewerType getViewerType() + { + return ViewerType.CHIMERA; } }