001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.nbio.structure.align.gui.jmol; 022 023import java.awt.BorderLayout; 024import java.awt.Color; 025import java.awt.Container; 026import java.awt.Dimension; 027import java.awt.event.ActionEvent; 028import java.awt.event.ActionListener; 029import java.awt.event.ItemEvent; 030import java.awt.event.ItemListener; 031import java.awt.event.WindowAdapter; 032import java.awt.event.WindowEvent; 033import java.io.StringWriter; 034import java.util.ArrayList; 035import java.util.List; 036 037import javax.swing.Box; 038import javax.swing.JButton; 039import javax.swing.JCheckBox; 040import javax.swing.JComboBox; 041import javax.swing.JFrame; 042import javax.swing.JLabel; 043import javax.swing.JMenuBar; 044import javax.swing.JPanel; 045import javax.swing.JTextField; 046 047import org.biojava.nbio.structure.Atom; 048import org.biojava.nbio.structure.PDBHeader; 049import org.biojava.nbio.structure.Structure; 050import org.biojava.nbio.structure.StructureException; 051import org.biojava.nbio.structure.StructureIdentifier; 052import org.biojava.nbio.structure.align.gui.MenuCreator; 053import org.biojava.nbio.structure.align.gui.MultipleAlignmentGUI; 054import org.biojava.nbio.structure.align.gui.MultipleAlignmentJmolDisplay; 055import org.biojava.nbio.structure.align.multiple.Block; 056import org.biojava.nbio.structure.align.multiple.BlockSet; 057import org.biojava.nbio.structure.align.multiple.MultipleAlignment; 058import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentTools; 059import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentWriter; 060import org.biojava.nbio.structure.align.webstart.AligUIManager; 061import org.biojava.nbio.structure.gui.WrapLayout; 062import org.biojava.nbio.structure.jama.Matrix; 063import org.forester.archaeopteryx.Archaeopteryx; 064import org.forester.phylogeny.Phylogeny; 065import org.jcolorbrewer.ColorBrewer; 066import org.slf4j.Logger; 067import org.slf4j.LoggerFactory; 068 069/** 070 * A class that provides a 3D visualization Frame in Jmol for 071 * {@link MultipleAlignment}s. 072 * 073 * @author Aleix Lafita 074 * @since 4.1.0 075 * 076 */ 077public class MultipleAlignmentJmol extends AbstractAlignmentJmol { 078 079 private MultipleAlignment multAln; 080 private List<Atom[]> transformedAtoms; 081 private JCheckBox colorByBlocks; 082 private List<JCheckBox> selectedStructures; 083 084 private static final String LIGAND_DISPLAY_SCRIPT = "select ligand; wireframe 40; spacefill 120; color CPK;"; 085 086 private static final Logger logger = LoggerFactory 087 .getLogger(MultipleAlignmentJmol.class); 088 089 /** 090 * Default constructor creates an empty JmolPanel window, from where 091 * alignments can be made through the align menu. 092 */ 093 public MultipleAlignmentJmol() { 094 this(null, null); 095 } 096 097 /** 098 * The constructor displays the Mutltiple Alignment in a new JmolPanel 099 * Frame. 100 * 101 * @param msa 102 * : contains the aligned residues. 103 * @param rotatedAtoms 104 * : contains the transformed Atom coordinates. 105 */ 106 public MultipleAlignmentJmol(MultipleAlignment msa, 107 List<Atom[]> rotatedAtoms) { 108 109 AligUIManager.setLookAndFeel(); 110 nrOpenWindows++; 111 jmolPanel = new JmolPanel(); 112 frame = new JFrame(); 113 JMenuBar menu = MenuCreator.initJmolMenu(frame, this, null, msa); 114 115 frame.setJMenuBar(menu); 116 this.multAln = msa; 117 this.transformedAtoms = rotatedAtoms; 118 this.selectedStructures = new ArrayList<JCheckBox>(); 119 120 frame.addWindowListener(new WindowAdapter() { 121 122 @Override 123 public void windowClosing(WindowEvent e) { 124 125 nrOpenWindows--; 126 destroy(); 127 128 if (nrOpenWindows > 0) 129 frame.dispose(); 130 else { 131 MultipleAlignmentGUI gui = MultipleAlignmentGUI 132 .getInstanceNoVisibilityChange(); 133 if (gui.isVisible()) { 134 frame.dispose(); 135 gui.requestFocus(); 136 } else 137 System.exit(0); 138 } 139 } 140 }); 141 142 Container contentPane = frame.getContentPane(); 143 144 jmolPanel.addMouseMotionListener(this); 145 jmolPanel.addMouseListener(this); 146 jmolPanel.setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT)); 147 contentPane.add(jmolPanel,BorderLayout.CENTER); 148 149 Box vBox = Box.createVerticalBox(); 150 151 // / USER SCRIPTING COMMAND 152 JTextField field = new JTextField(); 153 154 field.setMaximumSize(new Dimension(Short.MAX_VALUE, 30)); 155 field.setText(COMMAND_LINE_HELP); 156 RasmolCommandListener listener = new RasmolCommandListener(jmolPanel, 157 field); 158 159 field.addActionListener(listener); 160 field.addMouseListener(listener); 161 field.addKeyListener(listener); 162 vBox.add(field); 163 164 // / STRUCTURE SELECTION 165 if (multAln != null) { 166 167 JPanel modelSelection = new JPanel(); 168 modelSelection.setLayout(new WrapLayout(WrapLayout.LEFT)); 169 modelSelection.setSize(new Dimension(DEFAULT_WIDTH,30)); 170 vBox.add(modelSelection); 171 172 JButton show = new JButton("Show Only: "); 173 show.addActionListener(new ActionListener() { 174 175 @Override 176 public void actionPerformed(ActionEvent e) { 177 jmolPanel.evalString("save selection;"); 178 String cmd = getJmolString(multAln, 179 transformedAtoms, colorPalette, 180 colorByBlocks.isSelected()); 181 cmd += "; restrict "; 182 for (int st = 0; st < multAln.size(); st++) { 183 if (selectedStructures.get(st).isSelected()) { 184 cmd += "*/" + (st + 1) + ", "; 185 } 186 } 187 cmd += "none;"; 188 jmolPanel.executeCmd(cmd + " restore selection;"); 189 } 190 }); 191 modelSelection.add(show); 192 193 // Check boxes for all models 194 for(int str = 0; str < multAln.size();str++) { 195 JCheckBox structureSelection = new JCheckBox(multAln 196 .getEnsemble().getStructureIdentifiers().get(str) 197 .getIdentifier()); 198 modelSelection.add(structureSelection); 199 structureSelection.setSelected(true); 200 selectedStructures.add(structureSelection); 201 } 202 } 203 204 // / COMBO BOXES 205 Box hBox1 = Box.createHorizontalBox(); 206 hBox1.add(Box.createGlue()); 207 208 String[] styles = new String[] { "Cartoon", "Backbone", "CPK", 209 "Ball and Stick", "Ligands", "Ligands and Pocket" }; 210 JComboBox<String> style = new JComboBox<>(styles); 211 212 hBox1.setMaximumSize(new Dimension(Short.MAX_VALUE, 30)); 213 214 hBox1.add(new JLabel("Style")); 215 hBox1.add(style); 216 vBox.add(hBox1); 217 218 style.addActionListener(jmolPanel); 219 220 String[] colorModes = new String[] { "Secondary Structure", "By Chain", 221 "Rainbow", "By Element", "By Amino Acid", "Hydrophobicity", 222 "Suggest Domains", "Show SCOP Domains" }; 223 JComboBox<String> jcolors = new JComboBox<>(colorModes); 224 jcolors.addActionListener(jmolPanel); 225 226 hBox1.add(Box.createGlue()); 227 hBox1.add(new JLabel("Color")); 228 hBox1.add(jcolors); 229 230 String[] cPalette = { "Spectral", "Set1", "Set2", "Pastel" }; 231 JComboBox<String> palette = new JComboBox<>(cPalette); 232 233 palette.addActionListener(new ActionListener() { 234 @Override 235 public void actionPerformed(ActionEvent e) { 236 237 @SuppressWarnings("unchecked") 238 JComboBox<String> source = (JComboBox<String>) e.getSource(); 239 String value = source.getSelectedItem().toString(); 240 evalString("save selection; select *; color grey; " 241 + "select ligand; color CPK;"); 242 243 if (value == "Set1") { 244 colorPalette = ColorBrewer.Set1; 245 } else if (value == "Set2") { 246 colorPalette = ColorBrewer.Set2; 247 } else if (value == "Spectral") { 248 colorPalette = ColorBrewer.Spectral; 249 } else if (value == "Pastel") { 250 colorPalette = ColorBrewer.Pastel1; 251 } 252 String script = getJmolString(multAln, transformedAtoms, 253 colorPalette, colorByBlocks.isSelected()); 254 evalString(script + "; restore selection; "); 255 } 256 }); 257 258 hBox1.add(Box.createGlue()); 259 hBox1.add(new JLabel("Palette")); 260 hBox1.add(palette); 261 262 // / CHECK BOXES 263 Box hBox2 = Box.createHorizontalBox(); 264 hBox2.setMaximumSize(new Dimension(Short.MAX_VALUE, 30)); 265 266 JButton resetDisplay = new JButton("Reset Display"); 267 resetDisplay.addActionListener(new ActionListener() { 268 269 @Override 270 public void actionPerformed(ActionEvent e) { 271 logger.info("reset!!"); 272 jmolPanel.executeCmd("restore STATE state_1"); 273 274 } 275 }); 276 277 hBox2.add(resetDisplay); 278 hBox2.add(Box.createGlue()); 279 280 JCheckBox toggleSelection = new JCheckBox("Show Selection"); 281 toggleSelection.addItemListener(new ItemListener() { 282 283 @Override 284 public void itemStateChanged(ItemEvent e) { 285 boolean showSelection = (e.getStateChange() == ItemEvent.SELECTED); 286 287 if (showSelection) { 288 jmolPanel.executeCmd("set display selected"); 289 } else 290 jmolPanel.executeCmd("set display off"); 291 } 292 }); 293 hBox2.add(toggleSelection); 294 hBox2.add(Box.createGlue()); 295 296 colorByBlocks = new JCheckBox("Color By Block"); 297 colorByBlocks.addItemListener(new ItemListener() { 298 @Override 299 public void itemStateChanged(ItemEvent e) { 300 evalString("save selection; " 301 + getJmolString(multAln, transformedAtoms, 302 colorPalette, colorByBlocks.isSelected()) 303 + "; restore selection;"); 304 } 305 }); 306 307 hBox2.add(colorByBlocks); 308 hBox2.add(Box.createGlue()); 309 310 vBox.add(hBox2); 311 312 // STATUS DISPLAY 313 Box hBox = Box.createHorizontalBox(); 314 315 status = new JTextField(); 316 status.setBackground(Color.white); 317 status.setEditable(false); 318 status.setMaximumSize(new Dimension(Short.MAX_VALUE, 30)); 319 status.setPreferredSize(new Dimension(DEFAULT_WIDTH / 2, 30)); 320 status.setMinimumSize(new Dimension(DEFAULT_WIDTH / 2, 30)); 321 hBox.add(status); 322 323 text = new JTextField(); 324 text.setBackground(Color.white); 325 text.setMaximumSize(new Dimension(Short.MAX_VALUE, 30)); 326 text.setPreferredSize(new Dimension(DEFAULT_WIDTH / 2, 30)); 327 text.setMinimumSize(new Dimension(DEFAULT_WIDTH / 2, 30)); 328 text.setText("Display of Atom info"); 329 text.setEditable(false); 330 hBox.add(text); 331 332 vBox.add(hBox); 333 334 contentPane.add(vBox,BorderLayout.SOUTH); 335 MyJmolStatusListener li = (MyJmolStatusListener) jmolPanel 336 .getStatusListener(); 337 338 li.setTextField(status); 339 frame.pack(); 340 frame.setVisible(true); 341 342 initCoords(); 343 resetDisplay(); 344 } 345 346 @Override 347 protected void initCoords() { 348 try { 349 if (multAln == null) { 350 if (structure != null) 351 setStructure(structure); 352 else { 353 logger.error("Could not find anything to display!"); 354 return; 355 } 356 } 357 PDBHeader header = new PDBHeader(); 358 String title = multAln.getEnsemble().getAlgorithmName() + " V." 359 + multAln.getEnsemble().getVersion() + " : "; 360 361 for (StructureIdentifier name : multAln.getEnsemble() 362 .getStructureIdentifiers()) { 363 title += name.getIdentifier() + " "; 364 } 365 Structure artificial = MultipleAlignmentJmolDisplay 366 .getAlignedStructure(transformedAtoms); 367 368 artificial.setPDBHeader(header); 369 setStructure(artificial); 370 header.setTitle(title); 371 logger.info(title); 372 373 } catch (StructureException e) { 374 e.printStackTrace(); 375 } 376 } 377 378 @Override 379 public void destroy() { 380 super.destroy(); 381 multAln = null; 382 transformedAtoms = null; 383 } 384 385 @Override 386 public void actionPerformed(ActionEvent ae) { 387 String cmd = ae.getActionCommand(); 388 if (multAln == null) { 389 logger.error("Currently not viewing an alignment!"); 390 return; 391 } 392 try { 393 if (cmd.equals(MenuCreator.TEXT_ONLY)) { 394 logger.warn("Option not available for MultipleAlignments"); 395 396 } else if (cmd.equals(MenuCreator.PAIRS_ONLY)) { 397 String result = MultipleAlignmentWriter 398 .toAlignedResidues(multAln); 399 MultipleAlignmentJmolDisplay 400 .showAlignmentImage(multAln, result); 401 402 } else if (cmd.equals(MenuCreator.ALIGNMENT_PANEL)) { 403 MultipleAlignmentJmolDisplay.showMultipleAligmentPanel(multAln, 404 this); 405 406 } else if (cmd.equals(MenuCreator.FATCAT_TEXT)) { 407 String result = MultipleAlignmentWriter.toFatCat(multAln) 408 + "\n"; 409 result += MultipleAlignmentWriter.toTransformMatrices(multAln); 410 MultipleAlignmentJmolDisplay 411 .showAlignmentImage(multAln, result); 412 413 } else if (cmd.equals(MenuCreator.PHYLOGENETIC_TREE)) { 414 415 // Kimura, Structural and Fractional Dissimilarity Score 416 Phylogeny kimura = MultipleAlignmentTools 417 .getKimuraTree(multAln); 418 Phylogeny sdm = MultipleAlignmentTools.getHSDMTree(multAln); 419 // Phylogeny structural = MultipleAlignmentTools 420 // .getStructuralTree(multAln); 421 422 Archaeopteryx 423 .createApplication(new Phylogeny[] { kimura, sdm }); 424 } 425 } catch (Exception e) { 426 logger.error("Could not complete display option.", e); 427 } 428 } 429 430 /** 431 * Generate a Jmol command String that colors the aligned residues of every 432 * structure. 433 */ 434 public static String getJmolString(MultipleAlignment multAln, 435 List<Atom[]> transformedAtoms, ColorBrewer colorPalette, 436 boolean colorByBlocks) { 437 438 // Color by blocks if specified 439 if (colorByBlocks) 440 return getMultiBlockJmolString(multAln, transformedAtoms, 441 colorPalette, colorByBlocks); 442 Color[] colors = colorPalette.getColorPalette(multAln.size()); 443 444 StringBuffer j = new StringBuffer(); 445 j.append(DEFAULT_SCRIPT); 446 447 // Color the equivalent residues of every structure 448 StringBuffer sel = new StringBuffer(); 449 sel.append("select *; color lightgrey; backbone 0.1; "); 450 List<List<String>> allPDB = new ArrayList<List<String>>(); 451 452 // Get the aligned residues of every structure 453 for (int i = 0; i < multAln.size(); i++) { 454 455 List<String> pdb = MultipleAlignmentJmolDisplay.getPDBresnum(i, 456 multAln, transformedAtoms.get(i)); 457 458 allPDB.add(pdb); 459 sel.append("select "); 460 int pos = 0; 461 for (String res : pdb) { 462 if (pos > 0) 463 sel.append(","); 464 pos++; 465 466 sel.append(res); 467 sel.append("/" + (i + 1)); 468 } 469 if (pos == 0) 470 sel.append("none"); 471 sel.append("; backbone 0.3 ; color [" + colors[i].getRed() + "," 472 + colors[i].getGreen() + "," + colors[i].getBlue() + "]; "); 473 } 474 475 j.append(sel); 476 j.append("model 0; "); 477 j.append(LIGAND_DISPLAY_SCRIPT); 478 479 return j.toString(); 480 } 481 482 /** 483 * Colors every Block of the structures with a different color, following 484 * the palette. It colors each Block differently, no matter if it is from 485 * the same or different BlockSet. 486 */ 487 public static String getMultiBlockJmolString(MultipleAlignment multAln, 488 List<Atom[]> transformedAtoms, ColorBrewer colorPalette, 489 boolean colorByBlocks) { 490 491 StringWriter jmol = new StringWriter(); 492 jmol.append(DEFAULT_SCRIPT); 493 jmol.append("select *; color lightgrey; backbone 0.1; "); 494 495 int blockNum = multAln.getBlocks().size(); 496 Color[] colors = colorPalette.getColorPalette(blockNum); 497 498 // For every structure color all the blocks with the printBlock method 499 for (int str = 0; str < transformedAtoms.size(); str++) { 500 jmol.append("select */" + (str + 1) + "; color lightgrey; model " 501 + (str + 1) + "; "); 502 503 int index = 0; 504 for (BlockSet bs : multAln.getBlockSets()) { 505 for (Block b : bs.getBlocks()) { 506 507 List<List<Integer>> alignRes = b.getAlignRes(); 508 printJmolScript4Block(transformedAtoms.get(str), alignRes, 509 colors[index], jmol, str, index, blockNum); 510 index++; 511 } 512 } 513 } 514 515 jmol.append("model 0; "); 516 jmol.append(LIGAND_DISPLAY_SCRIPT); 517 518 return jmol.toString(); 519 } 520 521 private static void printJmolScript4Block(Atom[] atoms, 522 List<List<Integer>> alignRes, Color blockColor, StringWriter jmol, 523 int str, int colorPos, int blockNum) { 524 525 // Obtain the residues aligned in this block of the structure 526 List<String> pdb = new ArrayList<String>(); 527 for (int i = 0; i < alignRes.get(str).size(); i++) { 528 529 // Handle gaps - only color if it is not null 530 if (alignRes.get(str).get(i) != null) { 531 int pos = alignRes.get(str).get(i); 532 pdb.add(JmolTools.getPdbInfo(atoms[pos])); 533 } 534 } 535 536 // Select the aligned residues 537 StringBuffer buf = new StringBuffer("select "); 538 int count = 0; 539 for (String res : pdb) { 540 if (count > 0) 541 buf.append(","); 542 buf.append(res); 543 buf.append("/" + (str + 1)); 544 count++; 545 } 546 547 buf.append("; backbone 0.3 ; color [" + blockColor.getRed() + "," 548 + blockColor.getGreen() + "," + blockColor.getBlue() + "]; "); 549 550 jmol.append(buf); 551 } 552 553 @Override 554 public void resetDisplay() { 555 556 if (multAln != null && transformedAtoms != null) { 557 String script = getJmolString(multAln, transformedAtoms, 558 colorPalette, colorByBlocks.isSelected()); 559 logger.debug(script); 560 evalString(script); 561 jmolPanel.evalString("save STATE state_1"); 562 } 563 } 564 565 @Override 566 public List<Matrix> getDistanceMatrices() { 567 if (multAln == null) 568 return null; 569 else 570 return multAln.getEnsemble().getDistanceMatrix(); 571 } 572 573 public void setColorByBlocks(boolean colorByBlocks) { 574 this.colorByBlocks.setSelected(colorByBlocks); 575 resetDisplay(); 576 } 577 578 public JFrame getFrame() { 579 return frame; 580 } 581 582 public MultipleAlignment getMultipleAlignment() { 583 return multAln; 584 } 585 586}