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