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 * Created on 24.05.2004 021 * @author Andreas Prlic 022 * 023 */ 024package org.biojava.nbio.structure.align.gui.jmol; 025 026import org.biojava.nbio.structure.*; 027import org.biojava.nbio.structure.align.gui.AlignmentGui; 028import org.biojava.nbio.structure.align.gui.DisplayAFP; 029import org.biojava.nbio.structure.align.gui.MenuCreator; 030import org.biojava.nbio.structure.align.model.AFPChain; 031import org.biojava.nbio.structure.align.model.AfpChainWriter; 032import org.biojava.nbio.structure.align.util.AlignmentTools; 033import org.biojava.nbio.structure.align.util.AtomCache; 034import org.biojava.nbio.structure.align.util.ResourceManager; 035import org.biojava.nbio.structure.align.util.UserConfiguration; 036import org.biojava.nbio.structure.align.webstart.AligUIManager; 037import org.biojava.nbio.structure.gui.util.color.ColorUtils; 038import org.biojava.nbio.structure.jama.Matrix; 039 040import javax.swing.*; 041import javax.swing.event.ChangeEvent; 042import javax.swing.event.ChangeListener; 043 044import java.awt.*; 045import java.awt.event.*; 046import java.io.StringWriter; 047import java.util.ArrayList; 048import java.util.Arrays; 049import java.util.Hashtable; 050import java.util.List; 051 052/** 053 * A class that provides a simple GUI for Jmol 054 * 055 * @author Andreas Prlic 056 * @since 1.6 057 * 058 */ 059public class StructureAlignmentJmol extends AbstractAlignmentJmol implements ChangeListener { 060 061 private Atom[] ca1; 062 private Atom[] ca2; 063 private AFPChain afpChain; 064 065 private static final String LIGAND_DISPLAY_SCRIPT = ResourceManager.getResourceManager("ce") 066 .getString("default.ligand.jmol.script"); 067 068 public static void main(String[] args) { 069 070 try { 071 072 UserConfiguration config = new UserConfiguration(); 073 AtomCache cache = new AtomCache(config); 074 075 Structure struc = cache.getStructure("5pti"); 076 077 StructureAlignmentJmol jmolPanel = new StructureAlignmentJmol(null, null, null); 078 079 jmolPanel.setStructure(struc); 080 081 // send some RASMOL style commands to Jmol 082 jmolPanel.evalString("select * ; color chain;"); 083 jmolPanel.evalString("select *; spacefill off; wireframe off; backbone 0.4; "); 084 085 } catch (Exception e) { 086 e.printStackTrace(); 087 } 088 } 089 090 public StructureAlignmentJmol() { 091 // don;t have an afpChain, set it to null... 092 this(null, null, null); 093 } 094 095 public StructureAlignmentJmol(AFPChain afpChain, Atom[] ca1, Atom[] ca2) { 096 097 AligUIManager.setLookAndFeel(); 098 099 nrOpenWindows++; 100 jmolPanel = new JmolPanel(); 101 102 frame = new JFrame(); 103 104 JMenuBar menu = MenuCreator.initJmolMenu(frame,this, afpChain, null); 105 106 frame.setJMenuBar(menu); 107 //frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 108 this.afpChain = afpChain; 109 this.ca1 = ca1; 110 this.ca2 = ca2; 111 112 frame.addWindowListener( new WindowAdapter() 113 { 114 115 @Override 116 public void windowClosing(WindowEvent e) { 117 118 nrOpenWindows--; 119 120 destroy(); 121 122 if ( nrOpenWindows > 0) { 123 124 frame.dispose(); 125 } 126 else { 127 // check if AlignmentGUI is visible.. 128 129 AlignmentGui gui = AlignmentGui.getInstanceNoVisibilityChange(); 130 if ( gui.isVisible()) { 131 frame.dispose(); 132 gui.requestFocus(); 133 } else { 134 System.exit(0); 135 } 136 } 137 } 138 }); 139 140 Container contentPane = frame.getContentPane(); 141 142 Box vBox = Box.createVerticalBox(); 143 144 //try { 145 146 147 148 jmolPanel.addMouseMotionListener(this); 149 jmolPanel.addMouseListener(this); 150 151 // } catch (ClassNotFoundException e){ 152 // e.printStackTrace(); 153 // System.err.println("Could not find Jmol in classpath, please install first. http://www.jmol.org"); 154 // return; 155 // } 156 jmolPanel.setPreferredSize(new Dimension(DEFAULT_WIDTH,DEFAULT_HEIGHT)); 157 vBox.add(jmolPanel); 158 159 160 /// USER SCRIPTING COMMAND 161 JTextField field = new JTextField(); 162 163 field.setMaximumSize(new Dimension(Short.MAX_VALUE,30)); 164 field.setText(COMMAND_LINE_HELP); 165 RasmolCommandListener listener = new RasmolCommandListener(jmolPanel,field) ; 166 167 field.addActionListener(listener); 168 field.addMouseListener(listener); 169 field.addKeyListener(listener); 170 vBox.add(field); 171 172 173 /// COMBO BOXES 174 Box hBox1 = Box.createHorizontalBox(); 175 hBox1.add(Box.createGlue()); 176 177 String[] styles = new String[] { "Cartoon", "Backbone", "CPK", "Ball and Stick", "Ligands","Ligands and Pocket"}; 178 JComboBox style = new JComboBox(styles); 179 180 hBox1.setMaximumSize(new Dimension(Short.MAX_VALUE,30)); 181 182 hBox1.add(new JLabel("Style")); 183 hBox1.add(style); 184 vBox.add(hBox1); 185 contentPane.add(vBox); 186 187 style.addActionListener(jmolPanel); 188 189 String[] colorModes = new String[] { "Secondary Structure", "By Chain", "Rainbow", "By Element", "By Amino Acid", "Hydrophobicity" ,"Suggest Domains" , "Show SCOP Domains"}; 190 JComboBox colors = new JComboBox(colorModes); 191 colors.addActionListener(jmolPanel); 192 hBox1.add(Box.createGlue()); 193 hBox1.add(new JLabel("Color")); 194 hBox1.add(colors); 195 196 197 // CHeck boxes 198 Box hBox2 = Box.createHorizontalBox(); 199 hBox2.setMaximumSize(new Dimension(Short.MAX_VALUE,30)); 200 201 JButton resetDisplay = new JButton("Reset Display"); 202 203 resetDisplay.addActionListener(new ActionListener() { 204 205 @Override 206 public void actionPerformed(ActionEvent e) { 207 System.out.println("reset!!"); 208 jmolPanel.executeCmd("restore STATE state_1"); 209 210 } 211 }); 212 213 hBox2.add(resetDisplay); 214 hBox2.add(Box.createGlue()); 215 216 217 JCheckBox toggleSelection = new JCheckBox("Show Selection"); 218 toggleSelection.addItemListener( 219 new ItemListener() { 220 221 @Override 222 public void itemStateChanged(ItemEvent e) { 223 boolean showSelection = (e.getStateChange() == ItemEvent.SELECTED); 224 225 if (showSelection){ 226 jmolPanel.executeCmd("set display selected"); 227 } else { 228 jmolPanel.executeCmd("set display off"); 229 } 230 231 } 232 } 233 ); 234 235 236 237 hBox2.add(toggleSelection); 238 239 hBox2.add(Box.createGlue()); 240 vBox.add(hBox2); 241 242 243 // ZOOM SLIDER 244 Box hBox3 = Box.createHorizontalBox(); 245 hBox3.setMaximumSize(new Dimension(Short.MAX_VALUE,30)); 246 247 JLabel sliderLabel = new JLabel("Zoom"); 248 249 hBox3.add(Box.createGlue()); 250 hBox3.add(sliderLabel); 251 252 JSlider zoomSlider = new JSlider(JSlider.HORIZONTAL,0,500,100); 253 254 zoomSlider.addChangeListener(this); 255 256 zoomSlider.setMajorTickSpacing(100); 257 zoomSlider.setPaintTicks(true); 258 259 Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>(); 260 labelTable.put(new Integer(0),new JLabel("0%")); 261 labelTable.put(new Integer(100),new JLabel("100%")); 262 labelTable.put(new Integer(200),new JLabel("200%")); 263 labelTable.put(new Integer(300),new JLabel("300%")); 264 labelTable.put(new Integer(400),new JLabel("400%")); 265 labelTable.put(new Integer(500),new JLabel("500%")); 266 267 zoomSlider.setLabelTable(labelTable); 268 zoomSlider.setPaintLabels(true); 269 270 hBox3.add(zoomSlider); 271 hBox3.add(Box.createGlue()); 272 273 // SPIN CHECKBOX 274 JCheckBox toggleSpin = new JCheckBox("Spin"); 275 toggleSpin.addItemListener( 276 new ItemListener() { 277 278 @Override 279 public void itemStateChanged(ItemEvent e) { 280 boolean spinOn = (e.getStateChange() == ItemEvent.SELECTED); 281 282 if (spinOn){ 283 jmolPanel.executeCmd("spin ON"); 284 } else { 285 jmolPanel.executeCmd("spin OFF"); 286 } 287 288 } 289 } 290 ); 291 292 293 hBox3.add(toggleSpin); 294 hBox3.add(Box.createGlue()); 295 296 vBox.add(hBox3); 297 298 299 // STATUS DISPLAY 300 301 Box hBox = Box.createHorizontalBox(); 302 303 status = new JTextField(); 304 status.setBackground(Color.white); 305 status.setEditable(false); 306 status.setMaximumSize(new Dimension(Short.MAX_VALUE,30)); 307 status.setPreferredSize(new Dimension(DEFAULT_WIDTH / 2,30)); 308 status.setMinimumSize(new Dimension(DEFAULT_WIDTH / 2,30)); 309 hBox.add(status); 310 text = new JTextField(); 311 text.setBackground(Color.white); 312 text.setMaximumSize(new Dimension(Short.MAX_VALUE,30)); 313 text.setPreferredSize(new Dimension(DEFAULT_WIDTH / 2,30)); 314 text.setMinimumSize(new Dimension(DEFAULT_WIDTH / 2,30)); 315 text.setText("Display of Atom info"); 316 text.setEditable(false); 317 hBox.add(text); 318 319 vBox.add(hBox); 320 321 322 323 contentPane.add(vBox); 324 MyJmolStatusListener li = (MyJmolStatusListener) jmolPanel.getStatusListener(); 325 li.setTextField(status); 326 frame.pack(); 327 frame.setVisible(true); 328 329 330 // init coordinates 331 332 initCoords(); 333 334 resetDisplay(); 335 336 } 337 338 @Override 339 protected void initCoords() { 340 try { 341 if (ca1 == null || ca2 == null) { 342 if (structure != null) 343 setStructure(structure); 344 else { 345 // System.err.println("could not find anything to 346 // display!"); 347 return; 348 } 349 } 350 Structure artificial = AlignmentTools.getAlignedStructure(ca1, ca2); 351 PDBHeader header = new PDBHeader(); 352 String title = afpChain.getAlgorithmName() + " V." + afpChain.getVersion() + " : " + afpChain.getName1() 353 + " vs. " + afpChain.getName2(); 354 header.setTitle(title); 355 artificial.setPDBHeader(header); 356 setStructure(artificial); 357 } catch (StructureException e) { 358 e.printStackTrace(); 359 } 360 } 361 362 @Override 363 public void destroy() { 364 super.destroy(); 365 afpChain = null; 366 ca1 = null; 367 ca2 = null; 368 } 369 370 @Override 371 public void actionPerformed(ActionEvent e) { 372 String cmd = e.getActionCommand(); 373 if (cmd.equals(MenuCreator.TEXT_ONLY)) { 374 if (afpChain == null) { 375 System.err.println("Currently not viewing an alignment!"); 376 return; 377 } 378 // Clone the AFPChain to not override the FatCat numbers in alnsymb 379 AFPChain textAFP = (AFPChain) afpChain.clone(); 380 String result = AfpChainWriter.toWebSiteDisplay(textAFP, ca1, ca2); 381 382 DisplayAFP.showAlignmentImage(afpChain, result); 383 384 } else if (cmd.equals(MenuCreator.PAIRS_ONLY)) { 385 if (afpChain == null) { 386 System.err.println("Currently not viewing an alignment!"); 387 return; 388 } 389 String result = AfpChainWriter.toAlignedPairs(afpChain, ca1, ca2); 390 391 DisplayAFP.showAlignmentImage(afpChain, result); 392 393 } else if (cmd.equals(MenuCreator.ALIGNMENT_PANEL)) { 394 if (afpChain == null) { 395 System.err.println("Currently not viewing an alignment!"); 396 return; 397 } 398 try { 399 DisplayAFP.showAlignmentPanel(afpChain, ca1, ca2, this); 400 } catch (Exception e1) { 401 e1.printStackTrace(); 402 return; 403 } 404 405 } else if (cmd.equals(MenuCreator.FATCAT_TEXT)) { 406 if (afpChain == null) { 407 System.err.println("Currently not viewing an alignment!"); 408 return; 409 } 410 String result = afpChain.toFatcat(ca1, ca2); 411 result += AFPChain.newline; 412 result += afpChain.toRotMat(); 413 DisplayAFP.showAlignmentImage(afpChain, result); 414 } 415 } 416 417 public static String getJmolString(AFPChain afpChain, Atom[] ca1, Atom[] ca2) { 418 419 if (afpChain.getBlockNum() > 1) { 420 return getMultiBlockJmolScript(afpChain, ca1, ca2); 421 } 422 423 StringBuffer j = new StringBuffer(); 424 j.append(DEFAULT_SCRIPT); 425 426 // now color the equivalent residues ... 427 StringBuffer sel = new StringBuffer(); 428 List<String> pdb1 = DisplayAFP.getPDBresnum(0, afpChain, ca1); 429 sel.append("select "); 430 int pos = 0; 431 for (String res : pdb1) { 432 if (pos > 0) 433 sel.append(","); 434 pos++; 435 436 sel.append(res); 437 sel.append("/1"); 438 } 439 if (pos == 0) 440 sel.append("none"); 441 sel.append(";"); 442 sel.append("backbone 0.6 ; color orange;"); 443 sel.append("select */2; color lightgrey; model 2; "); 444 // jmol.evalString("select */2; color lightgrey; model 2; "); 445 List<String> pdb2 = DisplayAFP.getPDBresnum(1, afpChain, ca2); 446 sel.append("select "); 447 pos = 0; 448 for (String res : pdb2) { 449 if (pos > 0) 450 sel.append(","); 451 pos++; 452 453 sel.append(res); 454 sel.append("/2"); 455 } 456 if (pos == 0) 457 sel.append("none"); 458 sel.append("; backbone 0.6 ; color cyan;"); 459 // System.out.println(sel); 460 j.append(sel); 461 // now show both models again. 462 j.append("model 0; "); 463 j.append(LIGAND_DISPLAY_SCRIPT); 464 // color [object] cpk , set defaultColors Jmol , set defaultColors 465 // Rasmol 466 467 // and now select the aligned residues... 468 StringBuffer buf = new StringBuffer("select "); 469 int count = 0; 470 for (String res : pdb1) { 471 if (count > 0) 472 buf.append(","); 473 buf.append(res); 474 buf.append("/1"); 475 count++; 476 } 477 478 for (String res : pdb2) { 479 buf.append(","); 480 buf.append(res); 481 buf.append("/2"); 482 } 483 // buf.append("; set display selected;"); 484 485 j.append(buf); 486 487 return j.toString(); 488 } 489 490 public static String getJmolScript4Block(AFPChain afpChain, Atom[] ca1, Atom[] ca2, int blockNr) { 491 int blockNum = afpChain.getBlockNum(); 492 493 if (blockNr >= blockNum) 494 return DEFAULT_SCRIPT; 495 496 int[] optLen = afpChain.getOptLen(); 497 int[][][] optAln = afpChain.getOptAln(); 498 499 if (optLen == null) 500 return DEFAULT_SCRIPT; 501 502 StringWriter jmol = new StringWriter(); 503 jmol.append(DEFAULT_SCRIPT); 504 505 jmol.append("select */2; color lightgrey; model 2; "); 506 507 printJmolScript4Block(ca1, ca2, blockNum, optLen, optAln, jmol, blockNr); 508 509 jmol.append("model 0; "); 510 jmol.append(LIGAND_DISPLAY_SCRIPT); 511 // System.out.println(jmol); 512 return jmol.toString(); 513 514 } 515 516 private static String getMultiBlockJmolScript(AFPChain afpChain, Atom[] ca1, Atom[] ca2) { 517 518 int blockNum = afpChain.getBlockNum(); 519 int[] optLen = afpChain.getOptLen(); 520 int[][][] optAln = afpChain.getOptAln(); 521 522 if (optLen == null) 523 return DEFAULT_SCRIPT; 524 525 StringWriter jmol = new StringWriter(); 526 jmol.append(DEFAULT_SCRIPT); 527 528 jmol.append("select */2; color lightgrey; model 2; "); 529 530 for (int bk = 0; bk < blockNum; bk++) { 531 532 printJmolScript4Block(ca1, ca2, blockNum, optLen, optAln, jmol, bk); 533 } 534 jmol.append("model 0; "); 535 536 jmol.append(LIGAND_DISPLAY_SCRIPT); 537 // System.out.println(jmol); 538 return jmol.toString(); 539 540 } 541 542 private static void printJmolScript4Block(Atom[] ca1, Atom[] ca2, int blockNum, int[] optLen, int[][][] optAln, 543 StringWriter jmol, int bk) { 544 // the block nr determines the color... 545 int colorPos = bk; 546 547 Color c1; 548 Color c2; 549 // If the colors for the block are specified in AFPChain use them, 550 // otherwise the default ones are calculated 551 552 if (colorPos > ColorUtils.colorWheel.length) { 553 colorPos = ColorUtils.colorWheel.length % colorPos; 554 } 555 556 Color end1 = ColorUtils.rotateHue(ColorUtils.orange, (1.0f / 24.0f) * blockNum); 557 Color end2 = ColorUtils.rotateHue(ColorUtils.cyan, (1.0f / 24.0f) * (blockNum + 1)); 558 559 c1 = ColorUtils.getIntermediate(ColorUtils.orange, end1, blockNum, bk); 560 c2 = ColorUtils.getIntermediate(ColorUtils.cyan, end2, blockNum, bk); 561 562 List<String> pdb1 = new ArrayList<String>(); 563 List<String> pdb2 = new ArrayList<String>(); 564 for (int i = 0; i < optLen[bk]; i++) { 565 /// 566 int pos1 = optAln[bk][0][i]; 567 pdb1.add(JmolTools.getPdbInfo(ca1[pos1])); 568 int pos2 = optAln[bk][1][i]; 569 pdb2.add(JmolTools.getPdbInfo(ca2[pos2])); 570 } 571 572 // and now select the aligned residues... 573 StringBuffer buf = new StringBuffer("select "); 574 int count = 0; 575 for (String res : pdb1) { 576 if (count > 0) 577 buf.append(","); 578 buf.append(res); 579 buf.append("/1"); 580 count++; 581 } 582 583 buf.append("; backbone 0.6 ; color [" + c1.getRed() + "," + c1.getGreen() + "," + c1.getBlue() + "]; select "); 584 585 count = 0; 586 for (String res : pdb2) { 587 if (count > 0) 588 buf.append(","); 589 590 buf.append(res); 591 buf.append("/2"); 592 count++; 593 } 594 // buf.append("; set display selected;"); 595 596 buf.append("; backbone 0.6 ; color [" + c2.getRed() + "," + c2.getGreen() + "," + c2.getBlue() + "];"); 597 598 // now color this block: 599 jmol.append(buf); 600 } 601 602 @Override 603 public void resetDisplay() { 604 605 if (afpChain != null && ca1 != null && ca2 != null) { 606 String script = getJmolString(afpChain, ca1, ca2); 607 // System.out.println(script); 608 evalString(script); 609 jmolPanel.evalString("save STATE state_1"); 610 } 611 } 612 613 @Override 614 public List<Matrix> getDistanceMatrices() { 615 if (afpChain == null) 616 return null; 617 else 618 return Arrays.asList(afpChain.getDisTable1(), afpChain.getDisTable2()); 619 } 620 621 @Override 622 public void stateChanged(ChangeEvent e) { 623 JSlider source = (JSlider) e.getSource(); 624 if (!source.getValueIsAdjusting()) { 625 int zoomValue = (int) source.getValue(); 626 jmolPanel.executeCmd("zoom " + zoomValue); 627 } 628 } 629 630 631}