1 /* 2 * Jalview - A Sequence Alignment Editor and Viewer (2.11.1.4) 3 * Copyright (C) 2021 The Jalview Authors 4 * 5 * This file is part of Jalview. 6 * 7 * Jalview is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation, either version 3 10 * of the License, or (at your option) any later version. 11 * 12 * Jalview is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty 14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 15 * PURPOSE. See the GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>. 19 * The Jalview Authors are detailed in the 'AUTHORS' file. 20 */ 21 package jalview.appletgui; 22 23 import jalview.bin.JalviewLite; 24 import jalview.datamodel.AlignmentI; 25 import jalview.datamodel.PDBEntry; 26 import jalview.datamodel.SequenceI; 27 import jalview.io.DataSourceType; 28 import jalview.io.FileParse; 29 import jalview.io.StructureFile; 30 import jalview.schemes.BuriedColourScheme; 31 import jalview.schemes.HelixColourScheme; 32 import jalview.schemes.HydrophobicColourScheme; 33 import jalview.schemes.PurinePyrimidineColourScheme; 34 import jalview.schemes.StrandColourScheme; 35 import jalview.schemes.TaylorColourScheme; 36 import jalview.schemes.TurnColourScheme; 37 import jalview.schemes.UserColourScheme; 38 import jalview.schemes.ZappoColourScheme; 39 import jalview.structure.StructureSelectionManager; 40 import jalview.util.MessageManager; 41 42 import java.awt.BorderLayout; 43 import java.awt.CheckboxMenuItem; 44 import java.awt.Color; 45 import java.awt.Dimension; 46 import java.awt.Font; 47 import java.awt.Frame; 48 import java.awt.Graphics; 49 import java.awt.Menu; 50 import java.awt.MenuBar; 51 import java.awt.MenuItem; 52 import java.awt.Panel; 53 import java.awt.TextArea; 54 import java.awt.TextField; 55 import java.awt.event.ActionEvent; 56 import java.awt.event.ActionListener; 57 import java.awt.event.ItemEvent; 58 import java.awt.event.ItemListener; 59 import java.awt.event.KeyEvent; 60 import java.awt.event.KeyListener; 61 import java.awt.event.WindowAdapter; 62 import java.awt.event.WindowEvent; 63 import java.util.ArrayList; 64 import java.util.List; 65 import java.util.Vector; 66 67 public class AppletJmol extends EmbmenuFrame implements 68 // StructureListener, 69 KeyListener, ActionListener, ItemListener 70 71 { 72 Menu fileMenu = new Menu(MessageManager.getString("action.file")); 73 74 Menu viewMenu = new Menu(MessageManager.getString("action.view")); 75 76 Menu coloursMenu = new Menu(MessageManager.getString("action.colour")); 77 78 Menu chainMenu = new Menu(MessageManager.getString("action.show_chain")); 79 80 Menu helpMenu = new Menu(MessageManager.getString("action.help")); 81 82 MenuItem mappingMenuItem = new MenuItem( 83 MessageManager.getString("label.view_mapping")); 84 85 CheckboxMenuItem seqColour = new CheckboxMenuItem( 86 MessageManager.getString("action.by_sequence"), true); 87 88 CheckboxMenuItem jmolColour = new CheckboxMenuItem( 89 MessageManager.getString("action.using_jmol"), false); 90 91 MenuItem chain = new MenuItem( 92 MessageManager.getString("action.by_chain")); 93 94 MenuItem charge = new MenuItem( 95 MessageManager.getString("label.charge_cysteine")); 96 97 MenuItem zappo = new MenuItem( 98 MessageManager.getString("label.colourScheme_zappo")); 99 100 MenuItem taylor = new MenuItem( 101 MessageManager.getString("label.colourScheme_taylor")); 102 103 MenuItem hydro = new MenuItem( 104 MessageManager.getString("label.colourScheme_hydrophobic")); 105 106 MenuItem helix = new MenuItem( 107 MessageManager.getString("label.colourScheme_helix_propensity")); 108 109 MenuItem strand = new MenuItem( 110 MessageManager.getString("label.colourScheme_strand_propensity")); 111 112 MenuItem turn = new MenuItem( 113 MessageManager.getString("label.colourScheme_turn_propensity")); 114 115 MenuItem buried = new MenuItem( 116 MessageManager.getString("label.colourScheme_buried_index")); 117 118 MenuItem purinepyrimidine = new MenuItem( 119 MessageManager.getString("label.colourScheme_purine/pyrimidine")); 120 121 MenuItem user = new MenuItem( 122 MessageManager.getString("label.user_defined_colours")); 123 124 MenuItem jmolHelp = new MenuItem( 125 MessageManager.getString("label.jmol_help")); 126 127 Panel scriptWindow; 128 129 TextField inputLine; 130 131 TextArea history; 132 133 RenderPanel renderPanel; 134 135 AlignmentPanel ap; 136 137 List<AlignmentPanel> _aps = new ArrayList<>(); // remove? never 138 // added to 139 140 String fileLoadingError; 141 142 boolean loadedInline; 143 144 // boolean colourBySequence = true; 145 146 FeatureRenderer fr = null; 147 148 AppletJmolBinding jmb; 149 150 /** 151 * datasource protocol for access to PDBEntry 152 */ 153 String protocol = null; 154 155 /** 156 * Load a bunch of pdb entries associated with sequences in the alignment and 157 * display them - aligning them if necessary. 158 * 159 * @param pdbentries 160 * each pdb file (at least one needed) 161 * @param boundseqs 162 * each set of sequences for each pdb file (must match number of pdb 163 * files) 164 * @param boundchains 165 * the target pdb chain corresponding with each sequence associated 166 * with each pdb file (may be null at any level) 167 * @param align 168 * true/false 169 * @param ap 170 * associated alignment 171 * @param protocol 172 * how to get pdb data 173 */ AppletJmol(PDBEntry[] pdbentries, SequenceI[][] boundseqs, String[][] boundchains, boolean align, AlignmentPanel ap, String protocol)174 public AppletJmol(PDBEntry[] pdbentries, SequenceI[][] boundseqs, 175 String[][] boundchains, boolean align, AlignmentPanel ap, 176 String protocol) 177 { 178 throw new Error(MessageManager.getString("error.not_yet_implemented")); 179 } 180 AppletJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains, AlignmentPanel ap, DataSourceType protocol)181 public AppletJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains, 182 AlignmentPanel ap, DataSourceType protocol) 183 { 184 this.ap = ap; 185 jmb = new AppletJmolBinding(this, ap.getStructureSelectionManager(), 186 new PDBEntry[] 187 { pdbentry }, new SequenceI[][] { seq }, protocol); 188 jmb.setColourBySequence(true); 189 if (pdbentry.getId() == null || pdbentry.getId().length() < 1) 190 { 191 if (protocol == DataSourceType.PASTE) 192 { 193 pdbentry.setId( 194 "PASTED PDB" + (chains == null ? "_" : chains.toString())); 195 } 196 else 197 { 198 pdbentry.setId(pdbentry.getFile()); 199 } 200 } 201 202 if (JalviewLite.debug) 203 { 204 System.err 205 .println("AppletJmol: PDB ID is '" + pdbentry.getId() + "'"); 206 } 207 208 String alreadyMapped = StructureSelectionManager 209 .getStructureSelectionManager(ap.av.applet) 210 .alreadyMappedToFile(pdbentry.getId()); 211 StructureFile reader = null; 212 if (alreadyMapped != null) 213 { 214 reader = StructureSelectionManager 215 .getStructureSelectionManager(ap.av.applet) 216 .setMapping(seq, chains, pdbentry.getFile(), protocol, null); 217 // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW? 218 // FOR NOW, LETS JUST OPEN A NEW WINDOW 219 } 220 MenuBar menuBar = new MenuBar(); 221 menuBar.add(fileMenu); 222 fileMenu.add(mappingMenuItem); 223 menuBar.add(viewMenu); 224 mappingMenuItem.addActionListener(this); 225 viewMenu.add(chainMenu); 226 menuBar.add(coloursMenu); 227 menuBar.add(helpMenu); 228 229 charge.addActionListener(this); 230 hydro.addActionListener(this); 231 chain.addActionListener(this); 232 seqColour.addItemListener(this); 233 jmolColour.addItemListener(this); 234 zappo.addActionListener(this); 235 taylor.addActionListener(this); 236 helix.addActionListener(this); 237 strand.addActionListener(this); 238 turn.addActionListener(this); 239 buried.addActionListener(this); 240 purinepyrimidine.addActionListener(this); 241 user.addActionListener(this); 242 243 jmolHelp.addActionListener(this); 244 245 coloursMenu.add(seqColour); 246 coloursMenu.add(chain); 247 coloursMenu.add(charge); 248 coloursMenu.add(zappo); 249 coloursMenu.add(taylor); 250 coloursMenu.add(hydro); 251 coloursMenu.add(helix); 252 coloursMenu.add(strand); 253 coloursMenu.add(turn); 254 coloursMenu.add(buried); 255 coloursMenu.add(purinepyrimidine); 256 coloursMenu.add(user); 257 coloursMenu.add(jmolColour); 258 helpMenu.add(jmolHelp); 259 this.setLayout(new BorderLayout()); 260 261 setMenuBar(menuBar); 262 263 renderPanel = new RenderPanel(); 264 embedMenuIfNeeded(renderPanel); 265 this.add(renderPanel, BorderLayout.CENTER); 266 scriptWindow = new Panel(); 267 scriptWindow.setVisible(false); 268 // this.add(scriptWindow, BorderLayout.SOUTH); 269 270 try 271 { 272 jmb.allocateViewer(renderPanel, true, 273 ap.av.applet.getName() + "_jmol_", 274 ap.av.applet.getDocumentBase(), ap.av.applet.getCodeBase(), 275 "-applet", scriptWindow, null); 276 } catch (Exception e) 277 { 278 System.err.println( 279 "Couldn't create a jmol viewer. Args to allocate viewer were:\nDocumentBase=" 280 + ap.av.applet.getDocumentBase() + "\nCodebase=" 281 + ap.av.applet.getCodeBase()); 282 e.printStackTrace(); 283 dispose(); 284 return; 285 } 286 // jmb.newJmolPopup(true, "Jmol", true); 287 288 this.addWindowListener(new WindowAdapter() 289 { 290 @Override 291 public void windowClosing(WindowEvent evt) 292 { 293 closeViewer(); 294 } 295 }); 296 pdbentry.setProperty("protocol", protocol); 297 if (pdbentry.getFile() != null) 298 299 { 300 // import structure data from pdbentry.getFile based on given protocol 301 if (protocol == DataSourceType.PASTE) 302 { 303 // TODO: JAL-623 : correctly record file contents for matching up later 304 // pdbentry.getProperty().put("pdbfilehash",""+pdbentry.getFile().hashCode()); 305 loadInline(pdbentry.getFile()); 306 } 307 else if (protocol == DataSourceType.FILE 308 || protocol == DataSourceType.URL) 309 { 310 jmb.viewer.openFile(pdbentry.getFile()); 311 } 312 else 313 { 314 // probably CLASSLOADER based datasource.. 315 // Try and get a reader on the datasource, and pass that to Jmol 316 try 317 { 318 java.io.Reader freader = null; 319 if (reader != null) 320 { 321 if (jalview.bin.JalviewLite.debug) 322 { 323 System.err.println( 324 "AppletJmol:Trying to reuse existing PDBfile IO parser."); 325 } 326 // re-use the one we opened earlier 327 freader = reader.getReader(); 328 } 329 if (freader == null) 330 { 331 if (jalview.bin.JalviewLite.debug) 332 { 333 System.err.println( 334 "AppletJmol:Creating new PDBfile IO parser."); 335 } 336 FileParse fp = new FileParse(pdbentry.getFile(), protocol); 337 fp.mark(); 338 // reader = new MCview.PDBfile(fp); 339 // could set ID, etc. 340 // if (!reader.isValid()) 341 // { 342 // throw new Exception("Invalid datasource. 343 // "+reader.getWarningMessage()); 344 // } 345 // fp.reset(); 346 freader = fp.getReader(); 347 } 348 if (freader == null) 349 { 350 throw new Exception(MessageManager.getString( 351 "exception.invalid_datasource_couldnt_obtain_reader")); 352 } 353 jmb.viewer.openReader(pdbentry.getFile(), pdbentry.getId(), 354 freader); 355 } catch (Exception e) 356 { 357 // give up! 358 System.err.println("Couldn't access pdbentry id=" 359 + pdbentry.getId() + " and file=" + pdbentry.getFile() 360 + " using protocol=" + protocol); 361 e.printStackTrace(); 362 } 363 } 364 } 365 366 jalview.bin.JalviewLite.addFrame(this, jmb.getViewerTitle(), 400, 400); 367 } 368 loadInline(String string)369 public void loadInline(String string) 370 { 371 loadedInline = true; 372 jmb.loadInline(string); 373 } 374 setChainMenuItems(List<String> chains)375 void setChainMenuItems(List<String> chains) 376 { 377 chainMenu.removeAll(); 378 379 MenuItem menuItem = new MenuItem(MessageManager.getString("label.all")); 380 menuItem.addActionListener(this); 381 382 chainMenu.add(menuItem); 383 384 CheckboxMenuItem menuItemCB; 385 for (String ch : chains) 386 { 387 menuItemCB = new CheckboxMenuItem(ch, true); 388 menuItemCB.addItemListener(this); 389 chainMenu.add(menuItemCB); 390 } 391 } 392 393 boolean allChainsSelected = false; 394 centerViewer()395 void centerViewer() 396 { 397 Vector<String> toshow = new Vector<>(); 398 for (int i = 0; i < chainMenu.getItemCount(); i++) 399 { 400 if (chainMenu.getItem(i) instanceof CheckboxMenuItem) 401 { 402 CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i); 403 if (item.getState()) 404 { 405 toshow.addElement(item.getLabel()); 406 } 407 } 408 } 409 jmb.centerViewer(toshow); 410 } 411 closeViewer()412 void closeViewer() 413 { 414 jmb.closeViewer(); 415 jmb = null; 416 this.setVisible(false); 417 } 418 419 @Override actionPerformed(ActionEvent evt)420 public void actionPerformed(ActionEvent evt) 421 { 422 if (evt.getSource() == mappingMenuItem) 423 { 424 jalview.appletgui.CutAndPasteTransfer cap = new jalview.appletgui.CutAndPasteTransfer( 425 false, null); 426 Frame frame = new Frame(); 427 frame.add(cap); 428 429 StringBuffer sb = new StringBuffer(); 430 try 431 { 432 cap.setText(jmb.printMappings()); 433 } catch (OutOfMemoryError ex) 434 { 435 frame.dispose(); 436 System.err.println( 437 "Out of memory when trying to create dialog box with sequence-structure mapping."); 438 return; 439 } 440 jalview.bin.JalviewLite.addFrame(frame, 441 MessageManager.getString("label.pdb_sequence_mapping"), 550, 442 600); 443 } 444 else if (evt.getSource() == charge) 445 { 446 setEnabled(charge); 447 jmb.colourByCharge(); 448 } 449 450 else if (evt.getSource() == chain) 451 { 452 setEnabled(chain); 453 jmb.colourByChain(); 454 } 455 else if (evt.getSource() == zappo) 456 { 457 setEnabled(zappo); 458 jmb.setJalviewColourScheme(new ZappoColourScheme()); 459 } 460 else if (evt.getSource() == taylor) 461 { 462 setEnabled(taylor); 463 jmb.setJalviewColourScheme(new TaylorColourScheme()); 464 } 465 else if (evt.getSource() == hydro) 466 { 467 setEnabled(hydro); 468 jmb.setJalviewColourScheme(new HydrophobicColourScheme()); 469 } 470 else if (evt.getSource() == helix) 471 { 472 setEnabled(helix); 473 jmb.setJalviewColourScheme(new HelixColourScheme()); 474 } 475 else if (evt.getSource() == strand) 476 { 477 setEnabled(strand); 478 jmb.setJalviewColourScheme(new StrandColourScheme()); 479 } 480 else if (evt.getSource() == turn) 481 { 482 setEnabled(turn); 483 jmb.setJalviewColourScheme(new TurnColourScheme()); 484 } 485 else if (evt.getSource() == buried) 486 { 487 setEnabled(buried); 488 jmb.setJalviewColourScheme(new BuriedColourScheme()); 489 } 490 else if (evt.getSource() == purinepyrimidine) 491 { 492 jmb.setJalviewColourScheme(new PurinePyrimidineColourScheme()); 493 } 494 else if (evt.getSource() == user) 495 { 496 setEnabled(user); 497 new UserDefinedColours(this); 498 } 499 else if (evt.getSource() == jmolHelp) 500 { 501 try 502 { 503 ap.av.applet.getAppletContext() 504 .showDocument(new java.net.URL( 505 "http://jmol.sourceforge.net/docs/JmolUserGuide/"), 506 "jmolHelp"); 507 } catch (java.net.MalformedURLException ex) 508 { 509 } 510 } 511 else 512 { 513 allChainsSelected = true; 514 for (int i = 0; i < chainMenu.getItemCount(); i++) 515 { 516 if (chainMenu.getItem(i) instanceof CheckboxMenuItem) 517 { 518 ((CheckboxMenuItem) chainMenu.getItem(i)).setState(true); 519 } 520 } 521 522 centerViewer(); 523 allChainsSelected = false; 524 } 525 } 526 527 /** 528 * tick or untick the seqColour menu entry or jmoColour entry depending upon 529 * if it was selected or not. 530 * 531 * @param itm 532 */ setEnabled(MenuItem itm)533 private void setEnabled(MenuItem itm) 534 { 535 jmolColour.setState(itm == jmolColour); 536 seqColour.setState(itm == seqColour); 537 jmb.setColourBySequence(itm == seqColour); 538 } 539 540 @Override itemStateChanged(ItemEvent evt)541 public void itemStateChanged(ItemEvent evt) 542 { 543 if (evt.getSource() == jmolColour) 544 { 545 setEnabled(jmolColour); 546 jmb.setColourBySequence(false); 547 } 548 else if (evt.getSource() == seqColour) 549 { 550 setEnabled(seqColour); 551 jmb.colourBySequence(ap); 552 } 553 else if (!allChainsSelected) 554 { 555 centerViewer(); 556 } 557 } 558 559 @Override keyPressed(KeyEvent evt)560 public void keyPressed(KeyEvent evt) 561 { 562 if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible()) 563 { 564 jmb.eval(inputLine.getText()); 565 addToHistory("$ " + inputLine.getText()); 566 inputLine.setText(""); 567 } 568 569 } 570 571 @Override keyTyped(KeyEvent evt)572 public void keyTyped(KeyEvent evt) 573 { 574 } 575 576 @Override keyReleased(KeyEvent evt)577 public void keyReleased(KeyEvent evt) 578 { 579 } 580 updateColours(Object source)581 public void updateColours(Object source) 582 { 583 AlignmentPanel panel = (AlignmentPanel) source; 584 jmb.colourBySequence(panel); 585 } 586 updateTitleAndMenus()587 public void updateTitleAndMenus() 588 { 589 if (jmb.hasFileLoadingError()) 590 { 591 repaint(); 592 return; 593 } 594 setChainMenuItems(jmb.getChainNames()); 595 jmb.colourBySequence(ap); 596 597 setTitle(jmb.getViewerTitle()); 598 } 599 showUrl(String url)600 public void showUrl(String url) 601 { 602 try 603 { 604 ap.av.applet.getAppletContext().showDocument(new java.net.URL(url), 605 "jmolOutput"); 606 } catch (java.net.MalformedURLException ex) 607 { 608 } 609 } 610 611 Panel splitPane = null; 612 showConsole(boolean showConsole)613 public void showConsole(boolean showConsole) 614 { 615 if (showConsole) 616 { 617 remove(renderPanel); 618 splitPane = new Panel(); 619 620 splitPane.setLayout(new java.awt.GridLayout(2, 1)); 621 splitPane.add(renderPanel); 622 splitPane.add(scriptWindow); 623 scriptWindow.setVisible(true); 624 this.add(splitPane, BorderLayout.CENTER); 625 splitPane.setVisible(true); 626 splitPane.validate(); 627 } 628 else 629 { 630 scriptWindow.setVisible(false); 631 remove(splitPane); 632 add(renderPanel, BorderLayout.CENTER); 633 splitPane = null; 634 } 635 validate(); 636 } 637 functionXY(String functionName, int x, int y)638 public float[][] functionXY(String functionName, int x, int y) 639 { 640 return null; 641 } 642 643 // /End JmolStatusListener 644 // ///////////////////////////// 645 646 class RenderPanel extends Panel 647 { 648 Dimension currentSize = new Dimension(); 649 650 @Override update(Graphics g)651 public void update(Graphics g) 652 { 653 paint(g); 654 } 655 656 @Override paint(Graphics g)657 public void paint(Graphics g) 658 { 659 currentSize = this.getSize(); 660 661 if (jmb.viewer == null) 662 { 663 g.setColor(Color.black); 664 g.fillRect(0, 0, currentSize.width, currentSize.height); 665 g.setColor(Color.white); 666 g.setFont(new Font("Verdana", Font.BOLD, 14)); 667 g.drawString(MessageManager.getString("label.retrieving_pdb_data"), 668 20, currentSize.height / 2); 669 } 670 else 671 { 672 jmb.viewer.renderScreenImage(g, currentSize.width, 673 currentSize.height); 674 } 675 } 676 } 677 678 /* 679 * @Override public Color getColour(int atomIndex, int pdbResNum, String 680 * chain, String pdbId) { return jmb.getColour(atomIndex, pdbResNum, chain, 681 * pdbId); } 682 * 683 * @Override public String[] getPdbFile() { return jmb.getPdbFile(); } 684 * 685 * @Override public void highlightAtom(int atomIndex, int pdbResNum, String 686 * chain, String pdbId) { jmb.highlightAtom(atomIndex, pdbResNum, chain, 687 * pdbId); 688 * 689 * } 690 * 691 * @Override public void mouseOverStructure(int atomIndex, String strInfo) { 692 * jmb.mouseOverStructure(atomIndex, strInfo); 693 * 694 * } 695 */ setJalviewColourScheme(UserColourScheme ucs)696 public void setJalviewColourScheme(UserColourScheme ucs) 697 { 698 jmb.setJalviewColourScheme(ucs); 699 } 700 getAlignmentPanelFor(AlignmentI alignment)701 public AlignmentPanel getAlignmentPanelFor(AlignmentI alignment) 702 { 703 for (int i = 0; i < _aps.size(); i++) 704 { 705 if (_aps.get(i).av.getAlignment() == alignment) 706 { 707 return (_aps.get(i)); 708 } 709 } 710 return ap; 711 } 712 713 /** 714 * Append the given text to the history object 715 * 716 * @param text 717 */ addToHistory(String text)718 public void addToHistory(String text) 719 { 720 // actually currently never initialised 721 if (history != null) 722 { 723 history.append("\n" + text); 724 } 725 } 726 } 727