/* * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6) * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with Jalview. If not, see . */ package jalview.gui; import java.util.regex.*; import java.util.*; import java.awt.*; import javax.swing.*; import javax.swing.event.*; import java.awt.event.*; import java.io.*; import jalview.jbgui.GStructureViewer; import jalview.api.SequenceStructureBinding; import jalview.bin.Cache; import jalview.datamodel.*; import jalview.gui.*; import jalview.structure.*; import jalview.datamodel.PDBEntry; import jalview.io.*; import jalview.schemes.*; import jalview.ws.ebi.EBIFetchClient; import org.jmol.api.*; import org.jmol.adapter.smarter.SmarterJmolAdapter; import org.jmol.popup.*; import org.jmol.viewer.JmolConstants; import org.openscience.jmol.app.jmolpanel.AppConsole; public class AppJmol extends GStructureViewer implements Runnable, SequenceStructureBinding { AppJmolBinding jmb; JPanel scriptWindow; JSplitPane splitPane; RenderPanel renderPanel; AlignmentPanel ap; Vector atomsPicked = new Vector(); private boolean addingStructures = false; /** * * @param file * @param id * @param seq * @param ap * @param loadStatus * @param bounds * @deprecated defaults to AppJmol(String[] files, ... , viewid); */ public AppJmol(String file, String id, SequenceI[] seq, AlignmentPanel ap, String loadStatus, Rectangle bounds) { this(file, id, seq, ap, loadStatus, bounds, null); } /** * @deprecated */ public AppJmol(String file, String id, SequenceI[] seq, AlignmentPanel ap, String loadStatus, Rectangle bounds, String viewid) { this(new String[] { file }, new String[] { id }, new SequenceI[][] { seq }, ap, loadStatus, bounds, viewid); } /** * * @param files * @param ids * @param seqs * @param ap * @param loadStatus * @param bounds * @param viewid */ public AppJmol(String[] files, String[] ids, SequenceI[][] seqs, AlignmentPanel ap, 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 AppJmolBinding(this, pdbentrys, seqs, null, null); jmb.setLoadingFromArchive(true); this.ap = ap; this.setBounds(bounds); jmb.setColourBySequence(false); seqColour.setSelected(false); 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! } IProgressIndicator progressBar = null; public AppJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains, AlignmentPanel ap) { progressBar = ap.alignFrame; // //////////////////////////////// // Is the pdb file already loaded? String alreadyMapped = StructureSelectionManager .getStructureSelectionManager().alreadyMappedToFile( pdbentry.getId()); if (alreadyMapped != null) { int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop, pdbentry.getId() + " is already displayed." + "\nDo you want to re-use this viewer ?", "Map Sequences to Visible Window: " + pdbentry.getId(), JOptionPane.YES_NO_OPTION); if (option == JOptionPane.YES_OPTION) { StructureSelectionManager.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++) { if (frames[i] instanceof AppJmol) { AppJmol topJmol = ((AppJmol) 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); break; } } } } return; } } // ///////////////////////////////// // 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()) { AppJmol topJmol = (AppJmol) jm.nextElement(); // TODO: highlight topJmol in view somehow int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop, "Do you want to add " + pdbentry.getId() + " to the view called\n'" + topJmol.getTitle() + "'\n", "Align to existing structure view", JOptionPane.YES_NO_OPTION); if (option == JOptionPane.YES_OPTION) { topJmol.addStructure(pdbentry, seq, chains, true, ap.alignFrame); return; } } } // ///////////////////////////////// jmb = new AppJmolBinding(this, new PDBEntry[] { pdbentry }, new SequenceI[][] { seq }, null, null); this.ap = ap; setSize(400, 400); // probably should be a configurable/dynamic default here if (pdbentry.getFile() != null) { initJmol("load \"" + pdbentry.getFile() + "\""); } else { addingStructures = false; worker = new Thread(this); worker.start(); } this.addInternalFrameListener(new InternalFrameAdapter() { public void internalFrameClosing(InternalFrameEvent internalFrameEvent) { closeViewer(); } }); } /** * pdb retrieval thread. */ private Thread worker = null; /** * add a new structure (with associated sequences and chains) to this viewer, * retrieving it if necessary first. * * @param pdbentry * @param seq * @param chains * @param alignFrame * @param align * if true, new structure(s) will be align using associated alignment */ private void addStructure(final PDBEntry pdbentry, final SequenceI[] seq, final String[] chains, final boolean b, final IProgressIndicator alignFrame) { 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; } private Vector getJmolsFor(AlignmentPanel ap2) { 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(); for (int i = 0; i < frames.length; i++) { if (frames[i] instanceof AppJmol) { AppJmol topJmol = ((AppJmol) frames[i]); if (topJmol.ap == ap2) { otherJmols.addElement(topJmol); } } } return otherJmols; } void initJmol(String command) { jmb.setFinishedInit(false); renderPanel = new RenderPanel(); // TODO: consider waiting until the structure/view is fully loaded before // displaying this.getContentPane().add(renderPanel, java.awt.BorderLayout.CENTER); jalview.gui.Desktop.addInternalFrame(this, jmb.getViewerTitle(), getBounds().width, getBounds().height); if (scriptWindow == null) { BorderLayout bl = new BorderLayout(); bl.setHgap(0); bl.setVgap(0); scriptWindow = new JPanel(bl); scriptWindow.setVisible(false); }; jmb.allocateViewer(renderPanel, true, "", null, null, "", scriptWindow, null); jmb.newJmolPopup(true, "Jmol", true); jmb.evalStateCommand(command); jmb.setFinishedInit(true); } void setChainMenuItems(Vector chains) { chainMenu.removeAll(); if (chains == null) { return; } JMenuItem menuItem = new JMenuItem("All"); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { 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; } }); chainMenu.add(menuItem); for (int c = 0; c < chains.size(); c++) { menuItem = new JCheckBoxMenuItem(chains.elementAt(c).toString(), true); menuItem.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent evt) { if (!allChainsSelected) centerViewer(); } }); chainMenu.add(menuItem); } } boolean allChainsSelected = false; private boolean alignAddedStructures = false; void centerViewer() { Vector toshow = new Vector(); String lbl; int mlength, p, mnum; for (int i = 0; i < chainMenu.getItemCount(); i++) { if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem) { JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i); if (item.isSelected()) { toshow.addElement(item.getText()); } } } jmb.centerViewer(toshow); } void closeViewer() { jmb.closeViewer(); // TODO: check for memory leaks where instance isn't finalised because jmb // holds a reference to the window jmb = null; } /** * state flag for PDB retrieval thread */ private boolean _started = false; public void run() { _started = true; String pdbid = ""; // todo - record which pdbids were successfuly imported. StringBuffer errormsgs = new StringBuffer(), files = new StringBuffer(); try { String[] curfiles = jmb.getPdbFile(); // 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++) { String file = jmb.pdbentry[pi].getFile(); if (file == 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("Fetching PDB " + 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) { progressBar.setProgressBar("Finished.", hdl); } if (pdbseq != null) { // just transfer the file name from the first sequence's first // PDBEntry jmb.pdbentry[pi].setFile(file = ((PDBEntry) pdbseq .getSequenceAt(0).getPDBId().elementAt(0)).getFile()); files.append(" \"" + file + "\""); } else { errormsgs.append("'" + pdbid + "' "); } } else { if (curfiles != null && curfiles.length > 0) { addingStructures = true; // already files loaded. for (int c = 0; c < curfiles.length; c++) { if (curfiles[c].equals(file)) { file = null; break; } } } if (file != null) { files.append(" \"" + file + "\""); } } } } catch (OutOfMemoryError oomerror) { new OOMWarning("Retrieving PDB files: " + pdbid, oomerror); } catch (Exception ex) { ex.printStackTrace(); errormsgs.append("When retrieving pdbfiles : current was: '" + pdbid + "'"); } if (errormsgs.length() > 0) { JOptionPane.showInternalMessageDialog(Desktop.desktop, "The following pdb entries could not be retrieved from the PDB:\n" + errormsgs.toString() + "\nPlease try downloading them manually.", "Couldn't load file", JOptionPane.ERROR_MESSAGE); } if (files.length() > 0) { if (!addingStructures) { try { initJmol("load FILES " + files.toString()); } catch (OutOfMemoryError oomerror) { new OOMWarning("When trying to open the Jmol viewer!", oomerror); Cache.log.debug("File locations are " + files); } catch (Exception ex) { Cache.log.error("Couldn't open Jmol viewer!", ex); } } else { StringBuffer cmd = new StringBuffer(); cmd.append("loadingJalviewdata=true\nload APPEND "); cmd.append(files.toString()); cmd.append("\nloadingJalviewdata=null"); final String command = cmd.toString(); cmd = null; try { jmb.evalStateCommand(command); } catch (OutOfMemoryError oomerror) { new OOMWarning( "When trying to add structures to the Jmol viewer!", oomerror); Cache.log.debug("File locations are " + files); } catch (Exception ex) { Cache.log.error("Couldn't add files to Jmol viewer!", ex); } long lastnotify=jmb.getLoadNotifiesHandled(); // need to wait around until script has finished while (lastnotify>=jmb.getLoadNotifiesHandled()); { try { Thread.sleep(35); } catch (Exception e) { } } // refresh the sequence colours for the new structure(s) jmb.updateColours(ap); // do superposition if asked to if (alignAddedStructures) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { jmb.superposeStructures(ap.av.getAlignment(), -1, null); } }); alignAddedStructures = false; } addingStructures = false; } } _started = false; worker = null; } public void pdbFile_actionPerformed(ActionEvent actionEvent) { JalviewFileChooser chooser = new JalviewFileChooser( jalview.bin.Cache.getProperty("LAST_DIRECTORY")); chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Save PDB File"); chooser.setToolTipText("Save"); int value = chooser.showSaveDialog(this); if (value == JalviewFileChooser.APPROVE_OPTION) { 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(); } } } public void viewMapping_actionPerformed(ActionEvent actionEvent) { jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer(); try { for (int pdbe = 0; pdbe < jmb.pdbentry.length; pdbe++) { cap.appendText(StructureSelectionManager .getStructureSelectionManager().printMapping( jmb.pdbentry[pdbe].getFile())); cap.appendText("\n"); } } catch (OutOfMemoryError e) { new OOMWarning( "composing sequence-structure alignments for display in text box.", e); cap.dispose(); return; } jalview.gui.Desktop.addInternalFrame(cap, "PDB - Sequence Mapping", 550, 600); } /** * DOCUMENT ME! * * @param e * DOCUMENT ME! */ public void eps_actionPerformed(ActionEvent e) { makePDBImage(jalview.util.ImageMaker.EPS); } /** * DOCUMENT ME! * * @param e * DOCUMENT ME! */ public void png_actionPerformed(ActionEvent e) { makePDBImage(jalview.util.ImageMaker.PNG); } void makePDBImage(int type) { int width = getWidth(); int height = getHeight(); jalview.util.ImageMaker im; if (type == jalview.util.ImageMaker.PNG) { im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.PNG, "Make PNG image from view", width, height, null, null); } else { im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.EPS, "Make EPS file from view", width, height, null, this.getTitle()); } if (im.getGraphics() != null) { Rectangle rect = new Rectangle(width, height); jmb.viewer.renderScreenImage(im.getGraphics(), rect.getSize(), rect); im.writeImage(); } } public void seqColour_actionPerformed(ActionEvent actionEvent) { jmb.setColourBySequence(seqColour.isSelected()); // Set the colour using the current view for the associated alignframe jmb.colourBySequence(ap.alignFrame.viewport.showSequenceFeatures, ap.alignFrame.viewport.alignment); } 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 userColour_actionPerformed(ActionEvent actionEvent) { userColour.setSelected(true); new UserDefinedColours(this, null); } public void backGround_actionPerformed(ActionEvent actionEvent) { java.awt.Color col = JColorChooser.showDialog(this, "Select Background Colour", null); if (col != null) { jmb.setBackgroundColour(col); } } public void jmolHelp_actionPerformed(ActionEvent actionEvent) { try { jalview.util.BrowserLauncher .openURL("http://jmol.sourceforge.net/docs/JmolUserGuide/"); } catch (Exception ex) { } } public void showConsole(boolean showConsole) { if (showConsole) { if (splitPane == null) { splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); splitPane.setTopComponent(renderPanel); splitPane.setBottomComponent(scriptWindow); this.getContentPane().add(splitPane, BorderLayout.CENTER); splitPane.setDividerLocation(getHeight() - 200); scriptWindow.setVisible(true); scriptWindow.validate(); splitPane.validate(); } } else { if (splitPane != null) { splitPane.setVisible(false); } splitPane = null; this.getContentPane().add(renderPanel, BorderLayout.CENTER); } validate(); } class RenderPanel extends JPanel { final Dimension currentSize = new Dimension(); final Rectangle rectClip = new Rectangle(); public void paintComponent(Graphics g) { getSize(currentSize); g.getClipBounds(rectClip); if (jmb.fileLoadingError != null) { g.setColor(Color.black); g.fillRect(0, 0, currentSize.width, currentSize.height); g.setColor(Color.white); g.setFont(new Font("Verdana", Font.BOLD, 14)); g.drawString("Error loading file...", 20, currentSize.height / 2); StringBuffer sb = new StringBuffer(); int lines = 0; for (int e = 0; e < jmb.pdbentry.length; e++) { sb.append(jmb.pdbentry[e].getId()); if (e < jmb.pdbentry.length - 1) { sb.append(","); } if (e == jmb.pdbentry.length - 1 || sb.length() > 20) { lines++; g.drawString(sb.toString(), 20, currentSize.height / 2 - lines * g.getFontMetrics().getHeight()); } } } else if (jmb == null || jmb.viewer == null || !jmb.isFinishedInit()) { g.setColor(Color.black); g.fillRect(0, 0, currentSize.width, currentSize.height); g.setColor(Color.white); g.setFont(new Font("Verdana", Font.BOLD, 14)); g.drawString("Retrieving PDB data....", 20, currentSize.height / 2); } else { jmb.viewer.renderScreenImage(g, currentSize, rectClip); } } } 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); jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap.av.alignment); this.setTitle(jmb.getViewerTitle()); if (jmb.getPdbFile().length > 1 && jmb.sequence.length > 1) { jmolActionMenu.setVisible(true); } } /* * (non-Javadoc) * * @see * jalview.jbgui.GStructureViewer#alignStructs_actionPerformed(java.awt.event * .ActionEvent) */ @Override protected void alignStructs_actionPerformed(ActionEvent actionEvent) { try { jmb.superposeStructures(ap.av.getAlignment(), -1, ap.av.getColumnSelection()); } catch (Exception e) { Cache.log.info("Couldn't align structures in alignframe " + ap.alignFrame.getTitle(), e); } } public void setJalviewColourScheme(ColourSchemeI ucs) { jmb.setJalviewColourScheme(ucs); } }