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.gui; 22 23 import jalview.analysis.AlignmentUtils; 24 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; 25 import jalview.api.AlignViewportI; 26 import jalview.api.AlignmentViewPanel; 27 import jalview.api.FeatureColourI; 28 import jalview.api.FeatureSettingsModelI; 29 import jalview.api.FeaturesDisplayedI; 30 import jalview.api.ViewStyleI; 31 import jalview.bin.Cache; 32 import jalview.commands.CommandI; 33 import jalview.datamodel.AlignedCodonFrame; 34 import jalview.datamodel.Alignment; 35 import jalview.datamodel.AlignmentI; 36 import jalview.datamodel.ColumnSelection; 37 import jalview.datamodel.HiddenColumns; 38 import jalview.datamodel.SearchResults; 39 import jalview.datamodel.SearchResultsI; 40 import jalview.datamodel.SequenceGroup; 41 import jalview.datamodel.SequenceI; 42 import jalview.renderer.ResidueShader; 43 import jalview.schemes.ColourSchemeI; 44 import jalview.schemes.ColourSchemeProperty; 45 import jalview.schemes.ResidueColourScheme; 46 import jalview.schemes.UserColourScheme; 47 import jalview.structure.SelectionSource; 48 import jalview.structure.StructureSelectionManager; 49 import jalview.structure.VamsasSource; 50 import jalview.util.ColorUtils; 51 import jalview.util.MessageManager; 52 import jalview.viewmodel.AlignmentViewport; 53 import jalview.ws.params.AutoCalcSetting; 54 55 import java.awt.Container; 56 import java.awt.Dimension; 57 import java.awt.Font; 58 import java.awt.FontMetrics; 59 import java.awt.Rectangle; 60 import java.util.ArrayList; 61 import java.util.Hashtable; 62 import java.util.List; 63 64 import javax.swing.JInternalFrame; 65 66 /** 67 * DOCUMENT ME! 68 * 69 * @author $author$ 70 * @version $Revision: 1.141 $ 71 */ 72 public class AlignViewport extends AlignmentViewport 73 implements SelectionSource 74 { 75 Font font; 76 77 boolean cursorMode = false; 78 79 boolean antiAlias = false; 80 81 private Rectangle explodedGeometry = null; 82 83 private String viewName = null; 84 85 /* 86 * Flag set true on the view that should 'gather' multiple views of the same 87 * sequence set id when a project is reloaded. Set false on all views when 88 * they are 'exploded' into separate windows. Set true on the current view 89 * when 'Gather' is performed, and also on the first tab when the first new 90 * view is created. 91 */ 92 private boolean gatherViewsHere = false; 93 94 private AnnotationColumnChooser annotationColumnSelectionState; 95 96 /** 97 * Creates a new AlignViewport object. 98 * 99 * @param al 100 * alignment to view 101 */ AlignViewport(AlignmentI al)102 public AlignViewport(AlignmentI al) 103 { 104 super(al); 105 init(); 106 } 107 108 /** 109 * Create a new AlignViewport object with a specific sequence set ID 110 * 111 * @param al 112 * @param seqsetid 113 * (may be null - but potential for ambiguous constructor exception) 114 */ AlignViewport(AlignmentI al, String seqsetid)115 public AlignViewport(AlignmentI al, String seqsetid) 116 { 117 this(al, seqsetid, null); 118 } 119 AlignViewport(AlignmentI al, String seqsetid, String viewid)120 public AlignViewport(AlignmentI al, String seqsetid, String viewid) 121 { 122 super(al); 123 sequenceSetID = seqsetid; 124 viewId = viewid; 125 // TODO remove these once 2.4.VAMSAS release finished 126 if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null) 127 { 128 Cache.log.debug( 129 "Setting viewport's sequence set id : " + sequenceSetID); 130 } 131 if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null) 132 { 133 Cache.log.debug("Setting viewport's view id : " + viewId); 134 } 135 init(); 136 137 } 138 139 /** 140 * Create a new AlignViewport with hidden regions 141 * 142 * @param al 143 * AlignmentI 144 * @param hiddenColumns 145 * ColumnSelection 146 */ AlignViewport(AlignmentI al, HiddenColumns hiddenColumns)147 public AlignViewport(AlignmentI al, HiddenColumns hiddenColumns) 148 { 149 super(al); 150 if (hiddenColumns != null) 151 { 152 al.setHiddenColumns(hiddenColumns); 153 } 154 init(); 155 } 156 157 /** 158 * New viewport with hidden columns and an existing sequence set id 159 * 160 * @param al 161 * @param hiddenColumns 162 * @param seqsetid 163 * (may be null) 164 */ AlignViewport(AlignmentI al, HiddenColumns hiddenColumns, String seqsetid)165 public AlignViewport(AlignmentI al, HiddenColumns hiddenColumns, 166 String seqsetid) 167 { 168 this(al, hiddenColumns, seqsetid, null); 169 } 170 171 /** 172 * New viewport with hidden columns and an existing sequence set id and viewid 173 * 174 * @param al 175 * @param hiddenColumns 176 * @param seqsetid 177 * (may be null) 178 * @param viewid 179 * (may be null) 180 */ AlignViewport(AlignmentI al, HiddenColumns hiddenColumns, String seqsetid, String viewid)181 public AlignViewport(AlignmentI al, HiddenColumns hiddenColumns, 182 String seqsetid, String viewid) 183 { 184 super(al); 185 sequenceSetID = seqsetid; 186 viewId = viewid; 187 // TODO remove these once 2.4.VAMSAS release finished 188 if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null) 189 { 190 Cache.log.debug( 191 "Setting viewport's sequence set id : " + sequenceSetID); 192 } 193 if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null) 194 { 195 Cache.log.debug("Setting viewport's view id : " + viewId); 196 } 197 198 if (hiddenColumns != null) 199 { 200 al.setHiddenColumns(hiddenColumns); 201 } 202 init(); 203 } 204 205 /** 206 * Apply any settings saved in user preferences 207 */ applyViewProperties()208 private void applyViewProperties() 209 { 210 antiAlias = Cache.getDefault("ANTI_ALIAS", true); 211 212 viewStyle.setShowJVSuffix(Cache.getDefault("SHOW_JVSUFFIX", true)); 213 setShowAnnotation(Cache.getDefault("SHOW_ANNOTATIONS", true)); 214 215 setRightAlignIds(Cache.getDefault("RIGHT_ALIGN_IDS", false)); 216 setCentreColumnLabels(Cache.getDefault("CENTRE_COLUMN_LABELS", false)); 217 autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true); 218 219 setPadGaps(Cache.getDefault("PAD_GAPS", true)); 220 setShowNPFeats(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true)); 221 setShowDBRefs(Cache.getDefault("SHOW_DBREFS_TOOLTIP", true)); 222 viewStyle.setSeqNameItalics(Cache.getDefault("ID_ITALICS", true)); 223 viewStyle.setWrapAlignment(Cache.getDefault("WRAP_ALIGNMENT", false)); 224 viewStyle.setShowUnconserved( 225 Cache.getDefault("SHOW_UNCONSERVED", false)); 226 sortByTree = Cache.getDefault("SORT_BY_TREE", false); 227 followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true); 228 sortAnnotationsBy = SequenceAnnotationOrder 229 .valueOf(Cache.getDefault(Preferences.SORT_ANNOTATIONS, 230 SequenceAnnotationOrder.NONE.name())); 231 showAutocalculatedAbove = Cache 232 .getDefault(Preferences.SHOW_AUTOCALC_ABOVE, false); 233 viewStyle.setScaleProteinAsCdna( 234 Cache.getDefault(Preferences.SCALE_PROTEIN_TO_CDNA, true)); 235 } 236 init()237 void init() 238 { 239 applyViewProperties(); 240 241 String fontName = Cache.getDefault("FONT_NAME", "SansSerif"); 242 String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + ""); 243 String fontSize = Cache.getDefault("FONT_SIZE", "10"); 244 245 int style = 0; 246 247 if (fontStyle.equals("bold")) 248 { 249 style = 1; 250 } 251 else if (fontStyle.equals("italic")) 252 { 253 style = 2; 254 } 255 256 setFont(new Font(fontName, style, Integer.parseInt(fontSize)), true); 257 258 alignment 259 .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0)); 260 261 // We must set conservation and consensus before setting colour, 262 // as Blosum and Clustal require this to be done 263 if (hconsensus == null && !isDataset) 264 { 265 if (!alignment.isNucleotide()) 266 { 267 showConservation = Cache.getDefault("SHOW_CONSERVATION", true); 268 showQuality = Cache.getDefault("SHOW_QUALITY", true); 269 showGroupConservation = Cache.getDefault("SHOW_GROUP_CONSERVATION", 270 false); 271 } 272 showConsensusHistogram = Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM", 273 true); 274 showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", false); 275 normaliseSequenceLogo = Cache.getDefault("NORMALISE_CONSENSUS_LOGO", 276 false); 277 showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false); 278 showConsensus = Cache.getDefault("SHOW_IDENTITY", true); 279 280 showOccupancy = Cache.getDefault(Preferences.SHOW_OCCUPANCY, true); 281 } 282 initAutoAnnotation(); 283 String colourProperty = alignment.isNucleotide() 284 ? Preferences.DEFAULT_COLOUR_NUC 285 : Preferences.DEFAULT_COLOUR_PROT; 286 String schemeName = Cache.getProperty(colourProperty); 287 if (schemeName == null) 288 { 289 // only DEFAULT_COLOUR available in Jalview before 2.9 290 schemeName = Cache.getDefault(Preferences.DEFAULT_COLOUR, 291 ResidueColourScheme.NONE); 292 } 293 ColourSchemeI colourScheme = ColourSchemeProperty 294 .getColourScheme(this, alignment, schemeName); 295 residueShading = new ResidueShader(colourScheme); 296 297 if (colourScheme instanceof UserColourScheme) 298 { 299 residueShading = new ResidueShader( 300 UserDefinedColours.loadDefaultColours()); 301 residueShading.setThreshold(0, isIgnoreGapsConsensus()); 302 } 303 304 if (residueShading != null) 305 { 306 residueShading.setConsensus(hconsensus); 307 } 308 setColourAppliesToAllGroups(true); 309 } 310 311 boolean validCharWidth; 312 313 /** 314 * {@inheritDoc} 315 */ 316 @Override setFont(Font f, boolean setGrid)317 public void setFont(Font f, boolean setGrid) 318 { 319 font = f; 320 321 Container c = new Container(); 322 323 if (setGrid) 324 { 325 FontMetrics fm = c.getFontMetrics(font); 326 int ww = fm.charWidth('M'); 327 setCharHeight(fm.getHeight()); 328 setCharWidth(ww); 329 } 330 viewStyle.setFontName(font.getName()); 331 viewStyle.setFontStyle(font.getStyle()); 332 viewStyle.setFontSize(font.getSize()); 333 334 validCharWidth = true; 335 } 336 337 @Override setViewStyle(ViewStyleI settingsForView)338 public void setViewStyle(ViewStyleI settingsForView) 339 { 340 super.setViewStyle(settingsForView); 341 setFont(new Font(viewStyle.getFontName(), viewStyle.getFontStyle(), 342 viewStyle.getFontSize()), false); 343 } 344 345 /** 346 * DOCUMENT ME! 347 * 348 * @return DOCUMENT ME! 349 */ getFont()350 public Font getFont() 351 { 352 return font; 353 } 354 355 /** 356 * DOCUMENT ME! 357 * 358 * @param align 359 * DOCUMENT ME! 360 */ 361 @Override setAlignment(AlignmentI align)362 public void setAlignment(AlignmentI align) 363 { 364 replaceMappings(align); 365 super.setAlignment(align); 366 } 367 368 /** 369 * Replace any codon mappings for this viewport with those for the given 370 * viewport 371 * 372 * @param align 373 */ replaceMappings(AlignmentI align)374 public void replaceMappings(AlignmentI align) 375 { 376 377 /* 378 * Deregister current mappings (if any) 379 */ 380 deregisterMappings(); 381 382 /* 383 * Register new mappings (if any) 384 */ 385 if (align != null) 386 { 387 StructureSelectionManager ssm = StructureSelectionManager 388 .getStructureSelectionManager(Desktop.instance); 389 ssm.registerMappings(align.getCodonFrames()); 390 } 391 392 /* 393 * replace mappings on our alignment 394 */ 395 if (alignment != null && align != null) 396 { 397 alignment.setCodonFrames(align.getCodonFrames()); 398 } 399 } 400 deregisterMappings()401 protected void deregisterMappings() 402 { 403 AlignmentI al = getAlignment(); 404 if (al != null) 405 { 406 List<AlignedCodonFrame> mappings = al.getCodonFrames(); 407 if (mappings != null) 408 { 409 StructureSelectionManager ssm = StructureSelectionManager 410 .getStructureSelectionManager(Desktop.instance); 411 for (AlignedCodonFrame acf : mappings) 412 { 413 if (noReferencesTo(acf)) 414 { 415 ssm.deregisterMapping(acf); 416 } 417 } 418 } 419 } 420 } 421 422 /** 423 * DOCUMENT ME! 424 * 425 * @return DOCUMENT ME! 426 */ 427 @Override getGapCharacter()428 public char getGapCharacter() 429 { 430 return getAlignment().getGapCharacter(); 431 } 432 433 /** 434 * DOCUMENT ME! 435 * 436 * @param gap 437 * DOCUMENT ME! 438 */ setGapCharacter(char gap)439 public void setGapCharacter(char gap) 440 { 441 if (getAlignment() != null) 442 { 443 getAlignment().setGapCharacter(gap); 444 } 445 } 446 447 /** 448 * get hash of undo and redo list for the alignment 449 * 450 * @return long[] { historyList.hashCode, redoList.hashCode }; 451 */ getUndoRedoHash()452 public long[] getUndoRedoHash() 453 { 454 // TODO: JAL-1126 455 if (historyList == null || redoList == null) 456 { 457 return new long[] { -1, -1 }; 458 } 459 return new long[] { historyList.hashCode(), this.redoList.hashCode() }; 460 } 461 462 /** 463 * test if a particular set of hashcodes are different to the hashcodes for 464 * the undo and redo list. 465 * 466 * @param undoredo 467 * the stored set of hashcodes as returned by getUndoRedoHash 468 * @return true if the hashcodes differ (ie the alignment has been edited) or 469 * the stored hashcode array differs in size 470 */ isUndoRedoHashModified(long[] undoredo)471 public boolean isUndoRedoHashModified(long[] undoredo) 472 { 473 if (undoredo == null) 474 { 475 return true; 476 } 477 long[] cstate = getUndoRedoHash(); 478 if (cstate.length != undoredo.length) 479 { 480 return true; 481 } 482 483 for (int i = 0; i < cstate.length; i++) 484 { 485 if (cstate[i] != undoredo[i]) 486 { 487 return true; 488 } 489 } 490 return false; 491 } 492 493 public boolean followSelection = true; 494 495 /** 496 * @return true if view selection should always follow the selections 497 * broadcast by other selection sources 498 */ getFollowSelection()499 public boolean getFollowSelection() 500 { 501 return followSelection; 502 } 503 504 /** 505 * Send the current selection to be broadcast to any selection listeners. 506 */ 507 @Override sendSelection()508 public void sendSelection() 509 { 510 jalview.structure.StructureSelectionManager 511 .getStructureSelectionManager(Desktop.instance) 512 .sendSelection(new SequenceGroup(getSelectionGroup()), 513 new ColumnSelection(getColumnSelection()), 514 new HiddenColumns(getAlignment().getHiddenColumns()), 515 this); 516 } 517 518 /** 519 * return the alignPanel containing the given viewport. Use this to get the 520 * components currently handling the given viewport. 521 * 522 * @param av 523 * @return null or an alignPanel guaranteed to have non-null alignFrame 524 * reference 525 */ getAlignPanel()526 public AlignmentPanel getAlignPanel() 527 { 528 AlignmentPanel[] aps = PaintRefresher 529 .getAssociatedPanels(this.getSequenceSetId()); 530 for (int p = 0; aps != null && p < aps.length; p++) 531 { 532 if (aps[p].av == this) 533 { 534 return aps[p]; 535 } 536 } 537 return null; 538 } 539 getSortByTree()540 public boolean getSortByTree() 541 { 542 return sortByTree; 543 } 544 setSortByTree(boolean sort)545 public void setSortByTree(boolean sort) 546 { 547 sortByTree = sort; 548 } 549 550 /** 551 * Returns the (Desktop) instance of the StructureSelectionManager 552 */ 553 @Override getStructureSelectionManager()554 public StructureSelectionManager getStructureSelectionManager() 555 { 556 return StructureSelectionManager 557 .getStructureSelectionManager(Desktop.instance); 558 } 559 560 @Override isNormaliseSequenceLogo()561 public boolean isNormaliseSequenceLogo() 562 { 563 return normaliseSequenceLogo; 564 } 565 setNormaliseSequenceLogo(boolean state)566 public void setNormaliseSequenceLogo(boolean state) 567 { 568 normaliseSequenceLogo = state; 569 } 570 571 /** 572 * 573 * @return true if alignment characters should be displayed 574 */ 575 @Override isValidCharWidth()576 public boolean isValidCharWidth() 577 { 578 return validCharWidth; 579 } 580 581 private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<>(); 582 getCalcIdSettingsFor(String calcId)583 public AutoCalcSetting getCalcIdSettingsFor(String calcId) 584 { 585 return calcIdParams.get(calcId); 586 } 587 setCalcIdSettingsFor(String calcId, AutoCalcSetting settings, boolean needsUpdate)588 public void setCalcIdSettingsFor(String calcId, AutoCalcSetting settings, 589 boolean needsUpdate) 590 { 591 calcIdParams.put(calcId, settings); 592 // TODO: create a restart list to trigger any calculations that need to be 593 // restarted after load 594 // calculator.getRegisteredWorkersOfClass(settings.getWorkerClass()) 595 if (needsUpdate) 596 { 597 Cache.log.debug("trigger update for " + calcId); 598 } 599 } 600 601 /** 602 * Method called when another alignment's edit (or possibly other) command is 603 * broadcast to here. 604 * 605 * To allow for sequence mappings (e.g. protein to cDNA), we have to first 606 * 'unwind' the command on the source sequences (in simulation, not in fact), 607 * and then for each edit in turn: 608 * <ul> 609 * <li>compute the equivalent edit on the mapped sequences</li> 610 * <li>apply the mapped edit</li> 611 * <li>'apply' the source edit to the working copy of the source 612 * sequences</li> 613 * </ul> 614 * 615 * @param command 616 * @param undo 617 * @param ssm 618 */ 619 @Override mirrorCommand(CommandI command, boolean undo, StructureSelectionManager ssm, VamsasSource source)620 public void mirrorCommand(CommandI command, boolean undo, 621 StructureSelectionManager ssm, VamsasSource source) 622 { 623 /* 624 * Do nothing unless we are a 'complement' of the source. May replace this 625 * with direct calls not via SSM. 626 */ 627 if (source instanceof AlignViewportI 628 && ((AlignViewportI) source).getCodingComplement() == this) 629 { 630 // ok to continue; 631 } 632 else 633 { 634 return; 635 } 636 637 CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(), 638 getGapCharacter()); 639 if (mappedCommand != null) 640 { 641 AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments(); 642 mappedCommand.doCommand(views); 643 getAlignPanel().alignmentChanged(); 644 } 645 } 646 647 /** 648 * Add the sequences from the given alignment to this viewport. Optionally, 649 * may give the user the option to open a new frame, or split panel, with cDNA 650 * and protein linked. 651 * 652 * @param toAdd 653 * @param title 654 */ addAlignment(AlignmentI toAdd, String title)655 public void addAlignment(AlignmentI toAdd, String title) 656 { 657 // TODO: promote to AlignViewportI? applet CutAndPasteTransfer is different 658 659 // JBPComment: title is a largely redundant parameter at the moment 660 // JBPComment: this really should be an 'insert/pre/append' controller 661 // JBPComment: but the DNA/Protein check makes it a bit more complex 662 663 // refactored from FileLoader / CutAndPasteTransfer / SequenceFetcher with 664 // this comment: 665 // TODO: create undo object for this JAL-1101 666 667 /* 668 * Ensure datasets are created for the new alignment as 669 * mappings operate on dataset sequences 670 */ 671 toAdd.setDataset(null); 672 673 /* 674 * Check if any added sequence could be the object of a mapping or 675 * cross-reference; if so, make the mapping explicit 676 */ 677 getAlignment().realiseMappings(toAdd.getSequences()); 678 679 /* 680 * If any cDNA/protein mappings exist or can be made between the alignments, 681 * offer to open a split frame with linked alignments 682 */ 683 if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true)) 684 { 685 if (AlignmentUtils.isMappable(toAdd, getAlignment())) 686 { 687 if (openLinkedAlignment(toAdd, title)) 688 { 689 return; 690 } 691 } 692 } 693 694 /* 695 * No mappings, or offer declined - add sequences to this alignment 696 */ 697 // TODO: JAL-407 regardless of above - identical sequences (based on ID and 698 // provenance) should share the same dataset sequence 699 700 AlignmentI al = getAlignment(); 701 String gap = String.valueOf(al.getGapCharacter()); 702 for (int i = 0; i < toAdd.getHeight(); i++) 703 { 704 SequenceI seq = toAdd.getSequenceAt(i); 705 /* 706 * experimental! 707 * - 'align' any mapped sequences as per existing 708 * e.g. cdna to genome, domain hit to protein sequence 709 * very experimental! (need a separate menu option for this) 710 * - only add mapped sequences ('select targets from a dataset') 711 */ 712 if (true /*AlignmentUtils.alignSequenceAs(seq, al, gap, true, true)*/) 713 { 714 al.addSequence(seq); 715 } 716 } 717 718 ranges.setEndSeq(getAlignment().getHeight()); 719 firePropertyChange("alignment", null, getAlignment().getSequences()); 720 } 721 722 /** 723 * Show a dialog with the option to open and link (cDNA <-> protein) as a new 724 * alignment, either as a standalone alignment or in a split frame. Returns 725 * true if the new alignment was opened, false if not, because the user 726 * declined the offer. 727 * 728 * @param al 729 * @param title 730 */ openLinkedAlignment(AlignmentI al, String title)731 protected boolean openLinkedAlignment(AlignmentI al, String title) 732 { 733 String[] options = new String[] { MessageManager.getString("action.no"), 734 MessageManager.getString("label.split_window"), 735 MessageManager.getString("label.new_window"), }; 736 final String question = JvSwingUtils.wrapTooltip(true, 737 MessageManager.getString("label.open_split_window?")); 738 int response = JvOptionPane.showOptionDialog(Desktop.desktop, question, 739 MessageManager.getString("label.open_split_window"), 740 JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null, 741 options, options[0]); 742 743 if (response != 1 && response != 2) 744 { 745 return false; 746 } 747 final boolean openSplitPane = (response == 1); 748 final boolean openInNewWindow = (response == 2); 749 750 /* 751 * Identify protein and dna alignments. Make a copy of this one if opening 752 * in a new split pane. 753 */ 754 AlignmentI thisAlignment = openSplitPane ? new Alignment(getAlignment()) 755 : getAlignment(); 756 AlignmentI protein = al.isNucleotide() ? thisAlignment : al; 757 final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment; 758 759 /* 760 * Map sequences. At least one should get mapped as we have already passed 761 * the test for 'mappability'. Any mappings made will be added to the 762 * protein alignment. Note creating dataset sequences on the new alignment 763 * is a pre-requisite for building mappings. 764 */ 765 al.setDataset(null); 766 AlignmentUtils.mapProteinAlignmentToCdna(protein, cdna); 767 768 /* 769 * Create the AlignFrame for the added alignment. If it is protein, mappings 770 * are registered with StructureSelectionManager as a side-effect. 771 */ 772 AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH, 773 AlignFrame.DEFAULT_HEIGHT); 774 newAlignFrame.setTitle(title); 775 newAlignFrame.setStatus(MessageManager 776 .formatMessage("label.successfully_loaded_file", new Object[] 777 { title })); 778 779 // TODO if we want this (e.g. to enable reload of the alignment from file), 780 // we will need to add parameters to the stack. 781 // if (!protocol.equals(DataSourceType.PASTE)) 782 // { 783 // alignFrame.setFileName(file, format); 784 // } 785 786 if (openInNewWindow) 787 { 788 Desktop.addInternalFrame(newAlignFrame, title, 789 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); 790 } 791 792 try 793 { 794 newAlignFrame.setMaximum( 795 jalview.bin.Cache.getDefault("SHOW_FULLSCREEN", false)); 796 } catch (java.beans.PropertyVetoException ex) 797 { 798 } 799 800 if (openSplitPane) 801 { 802 al.alignAs(thisAlignment); 803 protein = openSplitFrame(newAlignFrame, thisAlignment); 804 } 805 806 return true; 807 } 808 809 /** 810 * Helper method to open a new SplitFrame holding linked dna and protein 811 * alignments. 812 * 813 * @param newAlignFrame 814 * containing a new alignment to be shown 815 * @param complement 816 * cdna/protein complement alignment to show in the other split half 817 * @return the protein alignment in the split frame 818 */ openSplitFrame(AlignFrame newAlignFrame, AlignmentI complement)819 protected AlignmentI openSplitFrame(AlignFrame newAlignFrame, 820 AlignmentI complement) 821 { 822 /* 823 * Make a new frame with a copy of the alignment we are adding to. If this 824 * is protein, the mappings to cDNA will be registered with 825 * StructureSelectionManager as a side-effect. 826 */ 827 AlignFrame copyMe = new AlignFrame(complement, AlignFrame.DEFAULT_WIDTH, 828 AlignFrame.DEFAULT_HEIGHT); 829 copyMe.setTitle(getAlignPanel().alignFrame.getTitle()); 830 831 AlignmentI al = newAlignFrame.viewport.getAlignment(); 832 final AlignFrame proteinFrame = al.isNucleotide() ? copyMe 833 : newAlignFrame; 834 final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame : copyMe; 835 cdnaFrame.setVisible(true); 836 proteinFrame.setVisible(true); 837 String linkedTitle = MessageManager 838 .getString("label.linked_view_title"); 839 840 /* 841 * Open in split pane. DNA sequence above, protein below. 842 */ 843 JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame); 844 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1); 845 846 return proteinFrame.viewport.getAlignment(); 847 } 848 getAnnotationColumnSelectionState()849 public AnnotationColumnChooser getAnnotationColumnSelectionState() 850 { 851 return annotationColumnSelectionState; 852 } 853 setAnnotationColumnSelectionState( AnnotationColumnChooser currentAnnotationColumnSelectionState)854 public void setAnnotationColumnSelectionState( 855 AnnotationColumnChooser currentAnnotationColumnSelectionState) 856 { 857 this.annotationColumnSelectionState = currentAnnotationColumnSelectionState; 858 } 859 860 @Override setIdWidth(int i)861 public void setIdWidth(int i) 862 { 863 super.setIdWidth(i); 864 AlignmentPanel ap = getAlignPanel(); 865 if (ap != null) 866 { 867 // modify GUI elements to reflect geometry change 868 Dimension idw = ap.getIdPanel().getIdCanvas().getPreferredSize(); 869 idw.width = i; 870 ap.getIdPanel().getIdCanvas().setPreferredSize(idw); 871 } 872 } 873 getExplodedGeometry()874 public Rectangle getExplodedGeometry() 875 { 876 return explodedGeometry; 877 } 878 setExplodedGeometry(Rectangle explodedPosition)879 public void setExplodedGeometry(Rectangle explodedPosition) 880 { 881 this.explodedGeometry = explodedPosition; 882 } 883 isGatherViewsHere()884 public boolean isGatherViewsHere() 885 { 886 return gatherViewsHere; 887 } 888 setGatherViewsHere(boolean gatherViewsHere)889 public void setGatherViewsHere(boolean gatherViewsHere) 890 { 891 this.gatherViewsHere = gatherViewsHere; 892 } 893 894 /** 895 * If this viewport has a (Protein/cDNA) complement, then scroll the 896 * complementary alignment to match this one. 897 */ scrollComplementaryAlignment()898 public void scrollComplementaryAlignment() 899 { 900 /* 901 * Populate a SearchResults object with the mapped location to scroll to. If 902 * there is no complement, or it is not following highlights, or no mapping 903 * is found, the result will be empty. 904 */ 905 SearchResultsI sr = new SearchResults(); 906 int verticalOffset = findComplementScrollTarget(sr); 907 if (!sr.isEmpty()) 908 { 909 // TODO would like next line without cast but needs more refactoring... 910 final AlignmentPanel complementPanel = ((AlignViewport) getCodingComplement()) 911 .getAlignPanel(); 912 complementPanel.setToScrollComplementPanel(false); 913 complementPanel.scrollToCentre(sr, verticalOffset); 914 complementPanel.setToScrollComplementPanel(true); 915 } 916 } 917 918 /** 919 * Answers true if no alignment holds a reference to the given mapping 920 * 921 * @param acf 922 * @return 923 */ noReferencesTo(AlignedCodonFrame acf)924 protected boolean noReferencesTo(AlignedCodonFrame acf) 925 { 926 AlignFrame[] frames = Desktop.getAlignFrames(); 927 if (frames == null) 928 { 929 return true; 930 } 931 for (AlignFrame af : frames) 932 { 933 if (!af.isClosed()) 934 { 935 for (AlignmentViewPanel ap : af.getAlignPanels()) 936 { 937 AlignmentI al = ap.getAlignment(); 938 if (al != null && al.getCodonFrames().contains(acf)) 939 { 940 return false; 941 } 942 } 943 } 944 } 945 return true; 946 } 947 948 /** 949 * Applies the supplied feature settings descriptor to currently known 950 * features. This supports an 'initial configuration' of feature colouring 951 * based on a preset or user favourite. This may then be modified in the usual 952 * way using the Feature Settings dialogue. 953 * 954 * @param featureSettings 955 */ 956 @Override applyFeaturesStyle(FeatureSettingsModelI featureSettings)957 public void applyFeaturesStyle(FeatureSettingsModelI featureSettings) 958 { 959 transferFeaturesStyles(featureSettings, false); 960 } 961 962 /** 963 * Applies the supplied feature settings descriptor to currently known features. 964 * This supports an 'initial configuration' of feature colouring based on a 965 * preset or user favourite. This may then be modified in the usual way using 966 * the Feature Settings dialogue. 967 * 968 * @param featureSettings 969 */ 970 @Override mergeFeaturesStyle(FeatureSettingsModelI featureSettings)971 public void mergeFeaturesStyle(FeatureSettingsModelI featureSettings) 972 { 973 transferFeaturesStyles(featureSettings, true); 974 } 975 976 /** 977 * when mergeOnly is set, then group and feature visibility or feature colours 978 * are not modified for features and groups already known to the feature 979 * renderer. Feature ordering is always adjusted, and transparency is always set 980 * regardless. 981 * 982 * @param featureSettings 983 * @param mergeOnly 984 */ transferFeaturesStyles(FeatureSettingsModelI featureSettings, boolean mergeOnly)985 private void transferFeaturesStyles(FeatureSettingsModelI featureSettings, 986 boolean mergeOnly) 987 { 988 if (featureSettings == null) 989 { 990 return; 991 } 992 993 FeatureRenderer fr = getAlignPanel().getSeqPanel().seqCanvas 994 .getFeatureRenderer(); 995 List<String> origRenderOrder = new ArrayList<>(); 996 List<String> origGroups = new ArrayList<>(); 997 // preserve original render order - allows differentiation between user configured colours and autogenerated ones 998 origRenderOrder.addAll(fr.getRenderOrder()); 999 origGroups.addAll(fr.getFeatureGroups()); 1000 1001 fr.findAllFeatures(true); 1002 List<String> renderOrder = fr.getRenderOrder(); 1003 FeaturesDisplayedI displayed = fr.getFeaturesDisplayed(); 1004 if (!mergeOnly) 1005 { 1006 // only clear displayed features if we are mergeing 1007 // displayed.clear(); 1008 } 1009 // TODO this clears displayed.featuresRegistered - do we care? 1010 // 1011 // JAL-3330 - JBP - yes we do - calling applyFeatureStyle to a view where 1012 // feature visibility has already been configured is not very friendly 1013 /* 1014 * set feature colour if specified by feature settings 1015 * set visibility of all features 1016 */ 1017 for (String type : renderOrder) 1018 { 1019 FeatureColourI preferredColour = featureSettings 1020 .getFeatureColour(type); 1021 FeatureColourI origColour = fr.getFeatureStyle(type); 1022 if (!mergeOnly || (!origRenderOrder.contains(type) 1023 || origColour == null 1024 || (!origColour.isGraduatedColour() 1025 && origColour.getColour() != null 1026 && origColour.getColour().equals( 1027 ColorUtils.createColourFromName(type))))) 1028 { 1029 // if we are merging, only update if there wasn't already a colour defined for 1030 // this type 1031 if (preferredColour != null) 1032 { 1033 fr.setColour(type, preferredColour); 1034 } 1035 if (featureSettings.isFeatureDisplayed(type)) 1036 { 1037 displayed.setVisible(type); 1038 } 1039 else if (featureSettings.isFeatureHidden(type)) 1040 { 1041 displayed.setHidden(type); 1042 } 1043 } 1044 } 1045 1046 /* 1047 * set visibility of feature groups 1048 */ 1049 for (String group : fr.getFeatureGroups()) 1050 { 1051 if (!mergeOnly || !origGroups.contains(group)) 1052 { 1053 // when merging, display groups only if the aren't already marked as not visible 1054 fr.setGroupVisibility(group, 1055 featureSettings.isGroupDisplayed(group)); 1056 } 1057 } 1058 1059 /* 1060 * order the features 1061 */ 1062 if (featureSettings.optimiseOrder()) 1063 { 1064 // TODO not supported (yet?) 1065 } 1066 else 1067 { 1068 fr.orderFeatures(featureSettings); 1069 } 1070 fr.setTransparency(featureSettings.getTransparency()); 1071 1072 fr.notifyFeaturesChanged(); 1073 } 1074 getViewName()1075 public String getViewName() 1076 { 1077 return viewName; 1078 } 1079 setViewName(String viewName)1080 public void setViewName(String viewName) 1081 { 1082 this.viewName = viewName; 1083 } 1084 } 1085