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 java.awt.Color; 24 import java.awt.event.ActionEvent; 25 import java.awt.event.ActionListener; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.BitSet; 29 import java.util.Collection; 30 import java.util.Collections; 31 import java.util.Hashtable; 32 import java.util.LinkedHashMap; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Objects; 36 import java.util.SortedMap; 37 import java.util.TreeMap; 38 import java.util.Vector; 39 40 import javax.swing.ButtonGroup; 41 import javax.swing.JCheckBoxMenuItem; 42 import javax.swing.JColorChooser; 43 import javax.swing.JMenu; 44 import javax.swing.JMenuItem; 45 import javax.swing.JPopupMenu; 46 import javax.swing.JRadioButtonMenuItem; 47 48 import jalview.analysis.AAFrequency; 49 import jalview.analysis.AlignmentAnnotationUtils; 50 import jalview.analysis.AlignmentUtils; 51 import jalview.analysis.Conservation; 52 import jalview.api.AlignViewportI; 53 import jalview.bin.Cache; 54 import jalview.commands.ChangeCaseCommand; 55 import jalview.commands.EditCommand; 56 import jalview.commands.EditCommand.Action; 57 import jalview.datamodel.AlignmentAnnotation; 58 import jalview.datamodel.AlignmentI; 59 import jalview.datamodel.DBRefEntry; 60 import jalview.datamodel.HiddenColumns; 61 import jalview.datamodel.MappedFeatures; 62 import jalview.datamodel.PDBEntry; 63 import jalview.datamodel.SequenceFeature; 64 import jalview.datamodel.SequenceGroup; 65 import jalview.datamodel.SequenceI; 66 import jalview.gui.ColourMenuHelper.ColourChangeListener; 67 import jalview.io.FileFormatI; 68 import jalview.io.FileFormats; 69 import jalview.io.FormatAdapter; 70 import jalview.io.SequenceAnnotationReport; 71 import jalview.schemes.Blosum62ColourScheme; 72 import jalview.schemes.ColourSchemeI; 73 import jalview.schemes.ColourSchemes; 74 import jalview.schemes.PIDColourScheme; 75 import jalview.schemes.ResidueColourScheme; 76 import jalview.util.Comparison; 77 import jalview.util.GroupUrlLink; 78 import jalview.util.GroupUrlLink.UrlStringTooLongException; 79 import jalview.util.MessageManager; 80 import jalview.util.StringUtils; 81 import jalview.util.UrlLink; 82 import jalview.viewmodel.seqfeatures.FeatureRendererModel; 83 84 /** 85 * The popup menu that is displayed on right-click on a sequence id, or in the 86 * sequence alignment. 87 */ 88 public class PopupMenu extends JPopupMenu implements ColourChangeListener 89 { 90 /* 91 * maximum length of feature description to include in popup menu item text 92 */ 93 private static final int FEATURE_DESC_MAX = 40; 94 95 /* 96 * true for ID Panel menu, false for alignment panel menu 97 */ 98 private final boolean forIdPanel; 99 100 private final AlignmentPanel ap; 101 102 /* 103 * the sequence under the cursor when clicked 104 * (additional sequences may be selected) 105 */ 106 private final SequenceI sequence; 107 108 JMenu groupMenu = new JMenu(); 109 110 JMenuItem groupName = new JMenuItem(); 111 112 protected JCheckBoxMenuItem abovePIDColour = new JCheckBoxMenuItem(); 113 114 protected JMenuItem modifyPID = new JMenuItem(); 115 116 protected JCheckBoxMenuItem conservationMenuItem = new JCheckBoxMenuItem(); 117 118 protected JRadioButtonMenuItem annotationColour; 119 120 protected JMenuItem modifyConservation = new JMenuItem(); 121 122 JMenu sequenceMenu = new JMenu(); 123 124 JMenuItem makeReferenceSeq = new JMenuItem(); 125 126 JMenuItem createGroupMenuItem = new JMenuItem(); 127 128 JMenuItem unGroupMenuItem = new JMenuItem(); 129 130 JMenu colourMenu = new JMenu(); 131 132 JCheckBoxMenuItem showBoxes = new JCheckBoxMenuItem(); 133 134 JCheckBoxMenuItem showText = new JCheckBoxMenuItem(); 135 136 JCheckBoxMenuItem showColourText = new JCheckBoxMenuItem(); 137 138 JCheckBoxMenuItem displayNonconserved = new JCheckBoxMenuItem(); 139 140 JMenu editMenu = new JMenu(); 141 142 JMenuItem upperCase = new JMenuItem(); 143 144 JMenuItem lowerCase = new JMenuItem(); 145 146 JMenuItem toggle = new JMenuItem(); 147 148 JMenu outputMenu = new JMenu(); 149 150 JMenu seqShowAnnotationsMenu = new JMenu(); 151 152 JMenu seqHideAnnotationsMenu = new JMenu(); 153 154 JMenuItem seqAddReferenceAnnotations = new JMenuItem( 155 MessageManager.getString("label.add_reference_annotations")); 156 157 JMenu groupShowAnnotationsMenu = new JMenu(); 158 159 JMenu groupHideAnnotationsMenu = new JMenu(); 160 161 JMenuItem groupAddReferenceAnnotations = new JMenuItem( 162 MessageManager.getString("label.add_reference_annotations")); 163 164 JMenuItem textColour = new JMenuItem(); 165 166 JMenu editGroupMenu = new JMenu(); 167 168 JMenuItem chooseStructure = new JMenuItem(); 169 170 JMenu rnaStructureMenu = new JMenu(); 171 172 /** 173 * Constructs a menu with sub-menu items for any hyperlinks for the sequence 174 * and/or features provided. Hyperlinks may include a lookup by sequence id, 175 * or database cross-references, depending on which links are enabled in user 176 * preferences. 177 * 178 * @param seq 179 * @param features 180 * @return 181 */ buildLinkMenu(final SequenceI seq, List<SequenceFeature> features)182 protected static JMenu buildLinkMenu(final SequenceI seq, 183 List<SequenceFeature> features) 184 { 185 JMenu linkMenu = new JMenu(MessageManager.getString("action.link")); 186 187 List<String> nlinks = null; 188 if (seq != null) 189 { 190 nlinks = Preferences.sequenceUrlLinks.getLinksForMenu(); 191 UrlLink.sort(nlinks); 192 } 193 else 194 { 195 nlinks = new ArrayList<>(); 196 } 197 198 if (features != null) 199 { 200 for (SequenceFeature sf : features) 201 { 202 if (sf.links != null) 203 { 204 for (String link : sf.links) 205 { 206 nlinks.add(link); 207 } 208 } 209 } 210 } 211 212 /* 213 * instantiate the hyperlinklink templates from sequence data; 214 * note the order of the templates is preserved in the map 215 */ 216 Map<String, List<String>> linkset = new LinkedHashMap<>(); 217 for (String link : nlinks) 218 { 219 UrlLink urlLink = null; 220 try 221 { 222 urlLink = new UrlLink(link); 223 } catch (Exception foo) 224 { 225 Cache.log.error("Exception for URLLink '" + link + "'", foo); 226 continue; 227 } 228 229 if (!urlLink.isValid()) 230 { 231 Cache.log.error(urlLink.getInvalidMessage()); 232 continue; 233 } 234 235 urlLink.createLinksFromSeq(seq, linkset); 236 } 237 238 /* 239 * construct menu items for the hyperlinks (still preserving 240 * the order of the sorted templates) 241 */ 242 addUrlLinks(linkMenu, linkset.values()); 243 244 return linkMenu; 245 } 246 247 /** 248 * A helper method that builds menu items from the given links, with action 249 * handlers to open the link URL, and adds them to the linkMenu. Each provided 250 * link should be a list whose second item is the menu text, and whose fourth 251 * item is the URL to open when the menu item is selected. 252 * 253 * @param linkMenu 254 * @param linkset 255 */ addUrlLinks(JMenu linkMenu, Collection<List<String>> linkset)256 static private void addUrlLinks(JMenu linkMenu, 257 Collection<List<String>> linkset) 258 { 259 for (List<String> linkstrset : linkset) 260 { 261 final String url = linkstrset.get(3); 262 JMenuItem item = new JMenuItem(linkstrset.get(1)); 263 item.setToolTipText(MessageManager 264 .formatMessage("label.open_url_param", new Object[] 265 { url })); 266 item.addActionListener(new ActionListener() 267 { 268 @Override 269 public void actionPerformed(ActionEvent e) 270 { 271 new Thread(new Runnable() 272 { 273 @Override 274 public void run() 275 { 276 showLink(url); 277 } 278 }).start(); 279 } 280 }); 281 linkMenu.add(item); 282 } 283 } 284 285 /** 286 * Opens the provided url in the default web browser, or shows an error 287 * message if this fails 288 * 289 * @param url 290 */ showLink(String url)291 static void showLink(String url) 292 { 293 try 294 { 295 jalview.util.BrowserLauncher.openURL(url); 296 } catch (Exception ex) 297 { 298 JvOptionPane.showInternalMessageDialog(Desktop.desktop, 299 MessageManager.getString("label.web_browser_not_found_unix"), 300 MessageManager.getString("label.web_browser_not_found"), 301 JvOptionPane.WARNING_MESSAGE); 302 303 ex.printStackTrace(); 304 } 305 } 306 307 /** 308 * add a late bound groupURL item to the given linkMenu 309 * 310 * @param linkMenu 311 * @param label 312 * - menu label string 313 * @param urlgenerator 314 * GroupURLLink used to generate URL 315 * @param urlstub 316 * Object array returned from the makeUrlStubs function. 317 */ addshowLink(JMenu linkMenu, String label, final GroupUrlLink urlgenerator, final Object[] urlstub)318 static void addshowLink(JMenu linkMenu, String label, 319 final GroupUrlLink urlgenerator, final Object[] urlstub) 320 { 321 JMenuItem item = new JMenuItem(label); 322 item.setToolTipText(MessageManager 323 .formatMessage("label.open_url_seqs_param", new Object[] 324 { urlgenerator.getUrl_prefix(), 325 urlgenerator.getNumberInvolved(urlstub) })); 326 // TODO: put in info about what is being sent. 327 item.addActionListener(new ActionListener() 328 { 329 @Override 330 public void actionPerformed(ActionEvent e) 331 { 332 new Thread(new Runnable() 333 { 334 335 @Override 336 public void run() 337 { 338 try 339 { 340 showLink(urlgenerator.constructFrom(urlstub)); 341 } catch (UrlStringTooLongException e2) 342 { 343 } 344 } 345 346 }).start(); 347 } 348 }); 349 350 linkMenu.add(item); 351 } 352 353 /** 354 * Constructor for a PopupMenu for a click in the alignment panel (on a residue) 355 * 356 * @param ap 357 * the panel in which the mouse is clicked 358 * @param seq 359 * the sequence under the mouse 360 * @throws NullPointerException 361 * if seq is null 362 */ PopupMenu(final AlignmentPanel ap, SequenceI seq, int column)363 public PopupMenu(final AlignmentPanel ap, SequenceI seq, int column) 364 { 365 this(false, ap, seq, column, null); 366 } 367 368 /** 369 * Constructor for a PopupMenu for a click in the sequence id panel 370 * 371 * @param alignPanel 372 * the panel in which the mouse is clicked 373 * @param seq 374 * the sequence under the mouse click 375 * @param groupLinks 376 * templates for sequence external links 377 * @throws NullPointerException 378 * if seq is null 379 */ PopupMenu(final AlignmentPanel alignPanel, final SequenceI seq, List<String> groupLinks)380 public PopupMenu(final AlignmentPanel alignPanel, final SequenceI seq, 381 List<String> groupLinks) 382 { 383 this(true, alignPanel, seq, -1, groupLinks); 384 } 385 386 /** 387 * Private constructor that constructs a popup menu for either sequence ID 388 * Panel, or alignment context 389 * 390 * @param fromIdPanel 391 * @param alignPanel 392 * @param seq 393 * @param column 394 * aligned column position (0...) 395 * @param groupLinks 396 */ PopupMenu(boolean fromIdPanel, final AlignmentPanel alignPanel, final SequenceI seq, final int column, List<String> groupLinks)397 private PopupMenu(boolean fromIdPanel, 398 final AlignmentPanel alignPanel, 399 final SequenceI seq, final int column, List<String> groupLinks) 400 { 401 Objects.requireNonNull(seq); 402 this.forIdPanel = fromIdPanel; 403 this.ap = alignPanel; 404 sequence = seq; 405 406 for (String ff : FileFormats.getInstance().getWritableFormats(true)) 407 { 408 JMenuItem item = new JMenuItem(ff); 409 410 item.addActionListener(new ActionListener() 411 { 412 @Override 413 public void actionPerformed(ActionEvent e) 414 { 415 outputText_actionPerformed(e); 416 } 417 }); 418 419 outputMenu.add(item); 420 } 421 422 /* 423 * Build menus for annotation types that may be shown or hidden, and for 424 * 'reference annotations' that may be added to the alignment. First for the 425 * currently selected sequence (if there is one): 426 */ 427 final List<SequenceI> selectedSequence = (forIdPanel && seq != null 428 ? Arrays.asList(seq) 429 : Collections.<SequenceI> emptyList()); 430 buildAnnotationTypesMenus(seqShowAnnotationsMenu, 431 seqHideAnnotationsMenu, selectedSequence); 432 configureReferenceAnnotationsMenu(seqAddReferenceAnnotations, 433 selectedSequence); 434 435 /* 436 * And repeat for the current selection group (if there is one): 437 */ 438 final List<SequenceI> selectedGroup = (alignPanel.av.getSelectionGroup() == null 439 ? Collections.<SequenceI> emptyList() 440 : alignPanel.av.getSelectionGroup().getSequences()); 441 buildAnnotationTypesMenus(groupShowAnnotationsMenu, 442 groupHideAnnotationsMenu, selectedGroup); 443 configureReferenceAnnotationsMenu(groupAddReferenceAnnotations, 444 selectedGroup); 445 446 try 447 { 448 jbInit(); 449 } catch (Exception e) 450 { 451 e.printStackTrace(); 452 } 453 454 if (forIdPanel) 455 { 456 JMenuItem menuItem; 457 sequenceMenu.setText(sequence.getName()); 458 if (seq == alignPanel.av.getAlignment().getSeqrep()) 459 { 460 makeReferenceSeq.setText( 461 MessageManager.getString("action.unmark_as_reference")); 462 } 463 else 464 { 465 makeReferenceSeq.setText( 466 MessageManager.getString("action.set_as_reference")); 467 } 468 469 if (!alignPanel.av.getAlignment().isNucleotide()) 470 { 471 remove(rnaStructureMenu); 472 } 473 else 474 { 475 int origCount = rnaStructureMenu.getItemCount(); 476 /* 477 * add menu items to 2D-render any alignment or sequence secondary 478 * structure annotation 479 */ 480 AlignmentAnnotation[] aas = alignPanel.av.getAlignment() 481 .getAlignmentAnnotation(); 482 if (aas != null) 483 { 484 for (final AlignmentAnnotation aa : aas) 485 { 486 if (aa.isValidStruc() && aa.sequenceRef == null) 487 { 488 /* 489 * valid alignment RNA secondary structure annotation 490 */ 491 menuItem = new JMenuItem(); 492 menuItem.setText(MessageManager.formatMessage( 493 "label.2d_rna_structure_line", new Object[] 494 { aa.label })); 495 menuItem.addActionListener(new ActionListener() 496 { 497 @Override 498 public void actionPerformed(ActionEvent e) 499 { 500 new AppVarna(seq, aa, alignPanel); 501 } 502 }); 503 rnaStructureMenu.add(menuItem); 504 } 505 } 506 } 507 508 if (seq.getAnnotation() != null) 509 { 510 AlignmentAnnotation seqAnns[] = seq.getAnnotation(); 511 for (final AlignmentAnnotation aa : seqAnns) 512 { 513 if (aa.isValidStruc()) 514 { 515 /* 516 * valid sequence RNA secondary structure annotation 517 */ 518 // TODO: make rnastrucF a bit more nice 519 menuItem = new JMenuItem(); 520 menuItem.setText(MessageManager.formatMessage( 521 "label.2d_rna_sequence_name", new Object[] 522 { seq.getName() })); 523 menuItem.addActionListener(new ActionListener() 524 { 525 @Override 526 public void actionPerformed(ActionEvent e) 527 { 528 // TODO: VARNA does'nt print gaps in the sequence 529 new AppVarna(seq, aa, alignPanel); 530 } 531 }); 532 rnaStructureMenu.add(menuItem); 533 } 534 } 535 } 536 if (rnaStructureMenu.getItemCount() == origCount) 537 { 538 remove(rnaStructureMenu); 539 } 540 } 541 542 menuItem = new JMenuItem( 543 MessageManager.getString("action.hide_sequences")); 544 menuItem.addActionListener(new ActionListener() 545 { 546 @Override 547 public void actionPerformed(ActionEvent e) 548 { 549 hideSequences(false); 550 } 551 }); 552 add(menuItem); 553 554 if (alignPanel.av.getSelectionGroup() != null 555 && alignPanel.av.getSelectionGroup().getSize() > 1) 556 { 557 menuItem = new JMenuItem(MessageManager 558 .formatMessage("label.represent_group_with", new Object[] 559 { seq.getName() })); 560 menuItem.addActionListener(new ActionListener() 561 { 562 @Override 563 public void actionPerformed(ActionEvent e) 564 { 565 hideSequences(true); 566 } 567 }); 568 sequenceMenu.add(menuItem); 569 } 570 571 if (alignPanel.av.hasHiddenRows()) 572 { 573 final int index = alignPanel.av.getAlignment().findIndex(seq); 574 575 if (alignPanel.av.adjustForHiddenSeqs(index) 576 - alignPanel.av.adjustForHiddenSeqs(index - 1) > 1) 577 { 578 menuItem = new JMenuItem( 579 MessageManager.getString("action.reveal_sequences")); 580 menuItem.addActionListener(new ActionListener() 581 { 582 @Override 583 public void actionPerformed(ActionEvent e) 584 { 585 alignPanel.av.showSequence(index); 586 if (alignPanel.overviewPanel != null) 587 { 588 alignPanel.overviewPanel.updateOverviewImage(); 589 } 590 } 591 }); 592 add(menuItem); 593 } 594 } 595 } 596 597 /* 598 * offer 'Reveal All' 599 * - in the IdPanel (seq not null) if any sequence is hidden 600 * - in the IdPanel or SeqPanel if all sequences are hidden (seq is null) 601 */ 602 if (alignPanel.av.hasHiddenRows()) 603 { 604 boolean addOption = seq != null; 605 if (!addOption && alignPanel.av.getAlignment().getHeight() == 0) 606 { 607 addOption = true; 608 } 609 if (addOption) 610 { 611 JMenuItem menuItem = new JMenuItem( 612 MessageManager.getString("action.reveal_all")); 613 menuItem.addActionListener(new ActionListener() 614 { 615 @Override 616 public void actionPerformed(ActionEvent e) 617 { 618 alignPanel.av.showAllHiddenSeqs(); 619 if (alignPanel.overviewPanel != null) 620 { 621 alignPanel.overviewPanel.updateOverviewImage(); 622 } 623 } 624 }); 625 add(menuItem); 626 } 627 } 628 629 SequenceGroup sg = alignPanel.av.getSelectionGroup(); 630 boolean isDefinedGroup = (sg != null) 631 ? alignPanel.av.getAlignment().getGroups().contains(sg) 632 : false; 633 634 if (sg != null && sg.getSize() > 0) 635 { 636 groupName.setText(MessageManager 637 .getString("label.edit_name_and_description_current_group")); 638 639 ColourMenuHelper.setColourSelected(colourMenu, sg.getColourScheme()); 640 641 conservationMenuItem.setEnabled(!sg.isNucleotide()); 642 643 if (sg.cs != null) 644 { 645 if (sg.cs.conservationApplied()) 646 { 647 conservationMenuItem.setSelected(true); 648 } 649 if (sg.cs.getThreshold() > 0) 650 { 651 abovePIDColour.setSelected(true); 652 } 653 } 654 modifyConservation.setEnabled(conservationMenuItem.isSelected()); 655 modifyPID.setEnabled(abovePIDColour.isSelected()); 656 displayNonconserved.setSelected(sg.getShowNonconserved()); 657 showText.setSelected(sg.getDisplayText()); 658 showColourText.setSelected(sg.getColourText()); 659 showBoxes.setSelected(sg.getDisplayBoxes()); 660 // add any groupURLs to the groupURL submenu and make it visible 661 if (groupLinks != null && groupLinks.size() > 0) 662 { 663 buildGroupURLMenu(sg, groupLinks); 664 } 665 // Add a 'show all structures' for the current selection 666 Hashtable<String, PDBEntry> pdbe = new Hashtable<>(), reppdb = new Hashtable<>(); 667 668 SequenceI sqass = null; 669 for (SequenceI sq : alignPanel.av.getSequenceSelection()) 670 { 671 Vector<PDBEntry> pes = sq.getDatasetSequence().getAllPDBEntries(); 672 if (pes != null && pes.size() > 0) 673 { 674 reppdb.put(pes.get(0).getId(), pes.get(0)); 675 for (PDBEntry pe : pes) 676 { 677 pdbe.put(pe.getId(), pe); 678 if (sqass == null) 679 { 680 sqass = sq; 681 } 682 } 683 } 684 } 685 if (pdbe.size() > 0) 686 { 687 final PDBEntry[] pe = pdbe.values() 688 .toArray(new PDBEntry[pdbe.size()]), 689 pr = reppdb.values().toArray(new PDBEntry[reppdb.size()]); 690 final JMenuItem gpdbview, rpdbview; 691 } 692 } 693 else 694 { 695 groupMenu.setVisible(false); 696 editMenu.setVisible(false); 697 } 698 699 if (!isDefinedGroup) 700 { 701 createGroupMenuItem.setVisible(true); 702 unGroupMenuItem.setVisible(false); 703 editGroupMenu.setText(MessageManager.getString("action.edit_new_group")); 704 } 705 else 706 { 707 createGroupMenuItem.setVisible(false); 708 unGroupMenuItem.setVisible(true); 709 editGroupMenu.setText(MessageManager.getString("action.edit_group")); 710 } 711 712 if (!forIdPanel) 713 { 714 sequenceMenu.setVisible(false); 715 chooseStructure.setVisible(false); 716 rnaStructureMenu.setVisible(false); 717 } 718 719 addLinksAndFeatures(seq, column); 720 } 721 722 /** 723 * Adds 724 * <ul> 725 * <li>configured sequence database links (ID panel popup menu)</li> 726 * <li>non-positional feature links (ID panel popup menu)</li> 727 * <li>positional feature links (alignment panel popup menu)</li> 728 * <li>feature details links (alignment panel popup menu)</li> 729 * </ul> 730 * If this panel is also showed complementary (CDS/protein) features, then links 731 * to their feature details are also added. 732 * 733 * @param seq 734 * @param column 735 */ addLinksAndFeatures(final SequenceI seq, final int column)736 void addLinksAndFeatures(final SequenceI seq, final int column) 737 { 738 List<SequenceFeature> features = null; 739 if (forIdPanel) 740 { 741 features = sequence.getFeatures().getNonPositionalFeatures(); 742 } 743 else 744 { 745 features = ap.getFeatureRenderer().findFeaturesAtColumn(sequence, 746 column + 1); 747 } 748 749 addLinks(seq, features); 750 751 if (!forIdPanel) 752 { 753 addFeatureDetails(features, seq, column); 754 } 755 } 756 757 /** 758 * Add a menu item to show feature details for each sequence feature. Any 759 * linked 'virtual' features (CDS/protein) are also optionally found and 760 * included. 761 * 762 * @param features 763 * @param seq 764 * @param column 765 */ addFeatureDetails(List<SequenceFeature> features, final SequenceI seq, final int column)766 protected void addFeatureDetails(List<SequenceFeature> features, 767 final SequenceI seq, final int column) 768 { 769 /* 770 * add features in CDS/protein complement at the corresponding 771 * position if configured to do so 772 */ 773 MappedFeatures mf = null; 774 if (ap.av.isShowComplementFeatures()) 775 { 776 if (!Comparison.isGap(sequence.getCharAt(column))) 777 { 778 AlignViewportI complement = ap.getAlignViewport() 779 .getCodingComplement(); 780 AlignFrame af = Desktop.getAlignFrameFor(complement); 781 FeatureRendererModel fr2 = af.getFeatureRenderer(); 782 int seqPos = sequence.findPosition(column); 783 mf = fr2.findComplementFeaturesAtResidue(sequence, seqPos); 784 } 785 } 786 787 if (features.isEmpty() && mf == null) 788 { 789 /* 790 * no features to show at this position 791 */ 792 return; 793 } 794 795 JMenu details = new JMenu( 796 MessageManager.getString("label.feature_details")); 797 add(details); 798 799 String name = seq.getName(); 800 for (final SequenceFeature sf : features) 801 { 802 addFeatureDetailsMenuItem(details, name, sf, null); 803 } 804 805 if (mf != null) 806 { 807 for (final SequenceFeature sf : mf.features) 808 { 809 addFeatureDetailsMenuItem(details, name, sf, mf); 810 } 811 } 812 } 813 814 /** 815 * A helper method to add one menu item whose action is to show details for 816 * one feature. The menu text includes feature description, but this may be 817 * truncated. 818 * 819 * @param details 820 * @param seqName 821 * @param sf 822 * @param mf 823 */ addFeatureDetailsMenuItem(JMenu details, final String seqName, final SequenceFeature sf, MappedFeatures mf)824 void addFeatureDetailsMenuItem(JMenu details, final String seqName, 825 final SequenceFeature sf, MappedFeatures mf) 826 { 827 int start = sf.getBegin(); 828 int end = sf.getEnd(); 829 if (mf != null) 830 { 831 /* 832 * show local rather than linked feature coordinates 833 */ 834 int[] beginRange = mf.getMappedPositions(start, start); 835 int[] endRange = mf.getMappedPositions(end, end); 836 if (beginRange == null || endRange == null) 837 { 838 // e.g. variant extending to stop codon so not mappable 839 return; 840 } 841 start = beginRange[0]; 842 end = endRange[endRange.length - 1]; 843 int[] localRange = mf.getMappedPositions(start, end); 844 if (localRange == null) 845 { 846 return; 847 } 848 start = localRange[0]; 849 end = localRange[localRange.length - 1]; 850 } 851 StringBuilder desc = new StringBuilder(); 852 desc.append(sf.getType()).append(" ").append(String.valueOf(start)); 853 if (start != end) 854 { 855 desc.append(sf.isContactFeature() ? ":" : "-"); 856 desc.append(String.valueOf(end)); 857 } 858 String description = sf.getDescription(); 859 if (description != null) 860 { 861 desc.append(" "); 862 description = StringUtils.stripHtmlTags(description); 863 864 /* 865 * truncate overlong descriptions unless they contain an href 866 * (as truncation could leave corrupted html) 867 */ 868 boolean hasLink = description.indexOf("a href") > -1; 869 if (description.length() > FEATURE_DESC_MAX && !hasLink) 870 { 871 description = description.substring(0, FEATURE_DESC_MAX) + "..."; 872 } 873 desc.append(description); 874 } 875 String featureGroup = sf.getFeatureGroup(); 876 if (featureGroup != null) 877 { 878 desc.append(" (").append(featureGroup).append(")"); 879 } 880 String htmlText = JvSwingUtils.wrapTooltip(true, desc.toString()); 881 JMenuItem item = new JMenuItem(htmlText); 882 item.addActionListener(new ActionListener() 883 { 884 @Override 885 public void actionPerformed(ActionEvent e) 886 { 887 showFeatureDetails(sf, seqName, mf); 888 } 889 }); 890 details.add(item); 891 } 892 893 /** 894 * Opens a panel showing a text report of feature details 895 * 896 * @param sf 897 * @param seqName 898 * @param mf 899 */ showFeatureDetails(SequenceFeature sf, String seqName, MappedFeatures mf)900 protected void showFeatureDetails(SequenceFeature sf, String seqName, 901 MappedFeatures mf) 902 { 903 CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer(); 904 // it appears Java's CSS does not support border-collapse :-( 905 cap.addStylesheetRule("table { border-collapse: collapse;}"); 906 cap.addStylesheetRule("table, td, th {border: 1px solid black;}"); 907 cap.setText(sf.getDetailsReport(seqName, mf)); 908 909 Desktop.addInternalFrame(cap, 910 MessageManager.getString("label.feature_details"), 500, 500); 911 } 912 913 /** 914 * Adds a 'Link' menu item with a sub-menu item for each hyperlink provided. 915 * When seq is not null, these are links for the sequence id, which may be to 916 * external web sites for the sequence accession, and/or links embedded in 917 * non-positional features. When seq is null, only links embedded in the 918 * provided features are added. If no links are found, the menu is not added. 919 * 920 * @param seq 921 * @param features 922 */ addLinks(final SequenceI seq, List<SequenceFeature> features)923 void addLinks(final SequenceI seq, List<SequenceFeature> features) 924 { 925 JMenu linkMenu = buildLinkMenu(forIdPanel ? seq : null, features); 926 927 // only add link menu if it has entries 928 if (linkMenu.getItemCount() > 0) 929 { 930 if (forIdPanel) 931 { 932 sequenceMenu.add(linkMenu); 933 } 934 else 935 { 936 add(linkMenu); 937 } 938 } 939 } 940 941 /** 942 * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus. 943 * "All" is added first, followed by a separator. Then add any annotation 944 * types associated with the current selection. Separate menus are built for 945 * the selected sequence group (if any), and the selected sequence. 946 * <p> 947 * Some annotation rows are always rendered together - these can be identified 948 * by a common graphGroup property > -1. Only one of each group will be marked 949 * as visible (to avoid duplication of the display). For such groups we add a 950 * composite type name, e.g. 951 * <p> 952 * IUPredWS (Long), IUPredWS (Short) 953 * 954 * @param seq 955 */ buildAnnotationTypesMenus(JMenu showMenu, JMenu hideMenu, List<SequenceI> forSequences)956 protected void buildAnnotationTypesMenus(JMenu showMenu, JMenu hideMenu, 957 List<SequenceI> forSequences) 958 { 959 showMenu.removeAll(); 960 hideMenu.removeAll(); 961 962 final List<String> all = Arrays 963 .asList(new String[] 964 { MessageManager.getString("label.all") }); 965 addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, 966 true); 967 addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true, 968 false); 969 showMenu.addSeparator(); 970 hideMenu.addSeparator(); 971 972 final AlignmentAnnotation[] annotations = ap.getAlignment() 973 .getAlignmentAnnotation(); 974 975 /* 976 * Find shown/hidden annotations types, distinguished by source (calcId), 977 * and grouped by graphGroup. Using LinkedHashMap means we will retrieve in 978 * the insertion order, which is the order of the annotations on the 979 * alignment. 980 */ 981 Map<String, List<List<String>>> shownTypes = new LinkedHashMap<>(); 982 Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<>(); 983 AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes, 984 AlignmentAnnotationUtils.asList(annotations), forSequences); 985 986 for (String calcId : hiddenTypes.keySet()) 987 { 988 for (List<String> type : hiddenTypes.get(calcId)) 989 { 990 addAnnotationTypeToShowHide(showMenu, forSequences, calcId, type, 991 false, true); 992 } 993 } 994 // grey out 'show annotations' if none are hidden 995 showMenu.setEnabled(!hiddenTypes.isEmpty()); 996 997 for (String calcId : shownTypes.keySet()) 998 { 999 for (List<String> type : shownTypes.get(calcId)) 1000 { 1001 addAnnotationTypeToShowHide(hideMenu, forSequences, calcId, type, 1002 false, false); 1003 } 1004 } 1005 // grey out 'hide annotations' if none are shown 1006 hideMenu.setEnabled(!shownTypes.isEmpty()); 1007 } 1008 1009 /** 1010 * Returns a list of sequences - either the current selection group (if there 1011 * is one), else the specified single sequence. 1012 * 1013 * @param seq 1014 * @return 1015 */ getSequenceScope(SequenceI seq)1016 protected List<SequenceI> getSequenceScope(SequenceI seq) 1017 { 1018 List<SequenceI> forSequences = null; 1019 final SequenceGroup selectionGroup = ap.av.getSelectionGroup(); 1020 if (selectionGroup != null && selectionGroup.getSize() > 0) 1021 { 1022 forSequences = selectionGroup.getSequences(); 1023 } 1024 else 1025 { 1026 forSequences = seq == null ? Collections.<SequenceI> emptyList() 1027 : Arrays.asList(seq); 1028 } 1029 return forSequences; 1030 } 1031 1032 /** 1033 * Add one annotation type to the 'Show Annotations' or 'Hide Annotations' 1034 * menus. 1035 * 1036 * @param showOrHideMenu 1037 * the menu to add to 1038 * @param forSequences 1039 * the sequences whose annotations may be shown or hidden 1040 * @param calcId 1041 * @param types 1042 * the label to add 1043 * @param allTypes 1044 * if true this is a special label meaning 'All' 1045 * @param actionIsShow 1046 * if true, the select menu item action is to show the annotation 1047 * type, else hide 1048 */ addAnnotationTypeToShowHide(JMenu showOrHideMenu, final List<SequenceI> forSequences, String calcId, final List<String> types, final boolean allTypes, final boolean actionIsShow)1049 protected void addAnnotationTypeToShowHide(JMenu showOrHideMenu, 1050 final List<SequenceI> forSequences, String calcId, 1051 final List<String> types, final boolean allTypes, 1052 final boolean actionIsShow) 1053 { 1054 String label = types.toString(); // [a, b, c] 1055 label = label.substring(1, label.length() - 1); // a, b, c 1056 final JMenuItem item = new JMenuItem(label); 1057 item.setToolTipText(calcId); 1058 item.addActionListener(new ActionListener() 1059 { 1060 @Override 1061 public void actionPerformed(ActionEvent e) 1062 { 1063 AlignmentUtils.showOrHideSequenceAnnotations(ap.getAlignment(), 1064 types, forSequences, allTypes, actionIsShow); 1065 refresh(); 1066 } 1067 }); 1068 showOrHideMenu.add(item); 1069 } 1070 buildGroupURLMenu(SequenceGroup sg, List<String> groupLinks)1071 private void buildGroupURLMenu(SequenceGroup sg, List<String> groupLinks) 1072 { 1073 1074 // TODO: usability: thread off the generation of group url content so root 1075 // menu appears asap 1076 // sequence only URLs 1077 // ID/regex match URLs 1078 JMenu groupLinksMenu = new JMenu( 1079 MessageManager.getString("action.group_link")); 1080 // three types of url that might be created. 1081 JMenu[] linkMenus = new JMenu[] { null, 1082 new JMenu(MessageManager.getString("action.ids")), 1083 new JMenu(MessageManager.getString("action.sequences")), 1084 new JMenu(MessageManager.getString("action.ids_sequences")) }; 1085 1086 SequenceI[] seqs = ap.av.getSelectionAsNewSequence(); 1087 String[][] idandseqs = GroupUrlLink.formStrings(seqs); 1088 Hashtable<String, Object[]> commonDbrefs = new Hashtable<>(); 1089 for (int sq = 0; sq < seqs.length; sq++) 1090 { 1091 1092 int start = seqs[sq].findPosition(sg.getStartRes()), 1093 end = seqs[sq].findPosition(sg.getEndRes()); 1094 // just collect ids from dataset sequence 1095 // TODO: check if IDs collected from selecton group intersects with the 1096 // current selection, too 1097 SequenceI sqi = seqs[sq]; 1098 while (sqi.getDatasetSequence() != null) 1099 { 1100 sqi = sqi.getDatasetSequence(); 1101 } 1102 DBRefEntry[] dbr = sqi.getDBRefs(); 1103 if (dbr != null && dbr.length > 0) 1104 { 1105 for (int d = 0; d < dbr.length; d++) 1106 { 1107 String src = dbr[d].getSource(); // jalview.util.DBRefUtils.getCanonicalName(dbr[d].getSource()).toUpperCase(); 1108 Object[] sarray = commonDbrefs.get(src); 1109 if (sarray == null) 1110 { 1111 sarray = new Object[2]; 1112 sarray[0] = new int[] { 0 }; 1113 sarray[1] = new String[seqs.length]; 1114 1115 commonDbrefs.put(src, sarray); 1116 } 1117 1118 if (((String[]) sarray[1])[sq] == null) 1119 { 1120 if (!dbr[d].hasMap() || (dbr[d].getMap() 1121 .locateMappedRange(start, end) != null)) 1122 { 1123 ((String[]) sarray[1])[sq] = dbr[d].getAccessionId(); 1124 ((int[]) sarray[0])[0]++; 1125 } 1126 } 1127 } 1128 } 1129 } 1130 // now create group links for all distinct ID/sequence sets. 1131 boolean addMenu = false; // indicates if there are any group links to give 1132 // to user 1133 for (String link : groupLinks) 1134 { 1135 GroupUrlLink urlLink = null; 1136 try 1137 { 1138 urlLink = new GroupUrlLink(link); 1139 } catch (Exception foo) 1140 { 1141 Cache.log.error("Exception for GroupURLLink '" + link + "'", foo); 1142 continue; 1143 } 1144 ; 1145 if (!urlLink.isValid()) 1146 { 1147 Cache.log.error(urlLink.getInvalidMessage()); 1148 continue; 1149 } 1150 final String label = urlLink.getLabel(); 1151 boolean usingNames = false; 1152 // Now see which parts of the group apply for this URL 1153 String ltarget = urlLink.getTarget(); // jalview.util.DBRefUtils.getCanonicalName(urlLink.getTarget()); 1154 Object[] idset = commonDbrefs.get(ltarget.toUpperCase()); 1155 String[] seqstr, ids; // input to makeUrl 1156 if (idset != null) 1157 { 1158 int numinput = ((int[]) idset[0])[0]; 1159 String[] allids = ((String[]) idset[1]); 1160 seqstr = new String[numinput]; 1161 ids = new String[numinput]; 1162 for (int sq = 0, idcount = 0; sq < seqs.length; sq++) 1163 { 1164 if (allids[sq] != null) 1165 { 1166 ids[idcount] = allids[sq]; 1167 seqstr[idcount++] = idandseqs[1][sq]; 1168 } 1169 } 1170 } 1171 else 1172 { 1173 // just use the id/seq set 1174 seqstr = idandseqs[1]; 1175 ids = idandseqs[0]; 1176 usingNames = true; 1177 } 1178 // and try and make the groupURL! 1179 1180 Object[] urlset = null; 1181 try 1182 { 1183 urlset = urlLink.makeUrlStubs(ids, seqstr, 1184 "FromJalview" + System.currentTimeMillis(), false); 1185 } catch (UrlStringTooLongException e) 1186 { 1187 } 1188 if (urlset != null) 1189 { 1190 int type = urlLink.getGroupURLType() & 3; 1191 // first two bits ofurlLink type bitfield are sequenceids and sequences 1192 // TODO: FUTURE: ensure the groupURL menu structure can be generalised 1193 addshowLink(linkMenus[type], 1194 label + (((type & 1) == 1) 1195 ? ("(" + (usingNames ? "Names" : ltarget) + ")") 1196 : ""), 1197 urlLink, urlset); 1198 addMenu = true; 1199 } 1200 } 1201 if (addMenu) 1202 { 1203 groupLinksMenu = new JMenu( 1204 MessageManager.getString("action.group_link")); 1205 for (int m = 0; m < linkMenus.length; m++) 1206 { 1207 if (linkMenus[m] != null 1208 && linkMenus[m].getMenuComponentCount() > 0) 1209 { 1210 groupLinksMenu.add(linkMenus[m]); 1211 } 1212 } 1213 1214 groupMenu.add(groupLinksMenu); 1215 } 1216 } 1217 1218 /** 1219 * DOCUMENT ME! 1220 * 1221 * @throws Exception 1222 * DOCUMENT ME! 1223 */ jbInit()1224 private void jbInit() throws Exception 1225 { 1226 groupMenu.setText(MessageManager.getString("label.selection")); 1227 groupName.setText(MessageManager.getString("label.name")); 1228 groupName.addActionListener(new ActionListener() 1229 { 1230 @Override 1231 public void actionPerformed(ActionEvent e) 1232 { 1233 groupName_actionPerformed(); 1234 } 1235 }); 1236 sequenceMenu.setText(MessageManager.getString("label.sequence")); 1237 1238 JMenuItem sequenceName = new JMenuItem( 1239 MessageManager.getString("label.edit_name_description")); 1240 sequenceName.addActionListener(new ActionListener() 1241 { 1242 @Override 1243 public void actionPerformed(ActionEvent e) 1244 { 1245 sequenceName_actionPerformed(); 1246 } 1247 }); 1248 JMenuItem chooseAnnotations = new JMenuItem( 1249 MessageManager.getString("action.choose_annotations")); 1250 chooseAnnotations.addActionListener(new ActionListener() 1251 { 1252 @Override 1253 public void actionPerformed(ActionEvent e) 1254 { 1255 chooseAnnotations_actionPerformed(e); 1256 } 1257 }); 1258 JMenuItem sequenceDetails = new JMenuItem( 1259 MessageManager.getString("label.sequence_details")); 1260 sequenceDetails.addActionListener(new ActionListener() 1261 { 1262 @Override 1263 public void actionPerformed(ActionEvent e) 1264 { 1265 createSequenceDetailsReport(new SequenceI[] { sequence }); 1266 } 1267 }); 1268 JMenuItem sequenceSelDetails = new JMenuItem( 1269 MessageManager.getString("label.sequence_details")); 1270 sequenceSelDetails.addActionListener(new ActionListener() 1271 { 1272 @Override 1273 public void actionPerformed(ActionEvent e) 1274 { 1275 createSequenceDetailsReport(ap.av.getSequenceSelection()); 1276 } 1277 }); 1278 1279 unGroupMenuItem 1280 .setText(MessageManager.getString("action.remove_group")); 1281 unGroupMenuItem.addActionListener(new ActionListener() 1282 { 1283 @Override 1284 public void actionPerformed(ActionEvent e) 1285 { 1286 unGroupMenuItem_actionPerformed(); 1287 } 1288 }); 1289 createGroupMenuItem 1290 .setText(MessageManager.getString("action.create_group")); 1291 createGroupMenuItem.addActionListener(new ActionListener() 1292 { 1293 @Override 1294 public void actionPerformed(ActionEvent e) 1295 { 1296 createGroupMenuItem_actionPerformed(); 1297 } 1298 }); 1299 1300 JMenuItem outline = new JMenuItem( 1301 MessageManager.getString("action.border_colour")); 1302 outline.addActionListener(new ActionListener() 1303 { 1304 @Override 1305 public void actionPerformed(ActionEvent e) 1306 { 1307 outline_actionPerformed(); 1308 } 1309 }); 1310 showBoxes.setText(MessageManager.getString("action.boxes")); 1311 showBoxes.setState(true); 1312 showBoxes.addActionListener(new ActionListener() 1313 { 1314 @Override 1315 public void actionPerformed(ActionEvent e) 1316 { 1317 showBoxes_actionPerformed(); 1318 } 1319 }); 1320 showText.setText(MessageManager.getString("action.text")); 1321 showText.setState(true); 1322 showText.addActionListener(new ActionListener() 1323 { 1324 @Override 1325 public void actionPerformed(ActionEvent e) 1326 { 1327 showText_actionPerformed(); 1328 } 1329 }); 1330 showColourText.setText(MessageManager.getString("label.colour_text")); 1331 showColourText.addActionListener(new ActionListener() 1332 { 1333 @Override 1334 public void actionPerformed(ActionEvent e) 1335 { 1336 showColourText_actionPerformed(); 1337 } 1338 }); 1339 displayNonconserved 1340 .setText(MessageManager.getString("label.show_non_conserved")); 1341 displayNonconserved.setState(true); 1342 displayNonconserved.addActionListener(new ActionListener() 1343 { 1344 @Override 1345 public void actionPerformed(ActionEvent e) 1346 { 1347 showNonconserved_actionPerformed(); 1348 } 1349 }); 1350 editMenu.setText(MessageManager.getString("action.edit")); 1351 JMenuItem cut = new JMenuItem(MessageManager.getString("action.cut")); 1352 cut.addActionListener(new ActionListener() 1353 { 1354 @Override 1355 public void actionPerformed(ActionEvent e) 1356 { 1357 cut_actionPerformed(); 1358 } 1359 }); 1360 upperCase.setText(MessageManager.getString("label.to_upper_case")); 1361 upperCase.addActionListener(new ActionListener() 1362 { 1363 @Override 1364 public void actionPerformed(ActionEvent e) 1365 { 1366 changeCase(e); 1367 } 1368 }); 1369 JMenuItem copy = new JMenuItem(MessageManager.getString("action.copy")); 1370 copy.addActionListener(new ActionListener() 1371 { 1372 @Override 1373 public void actionPerformed(ActionEvent e) 1374 { 1375 copy_actionPerformed(); 1376 } 1377 }); 1378 lowerCase.setText(MessageManager.getString("label.to_lower_case")); 1379 lowerCase.addActionListener(new ActionListener() 1380 { 1381 @Override 1382 public void actionPerformed(ActionEvent e) 1383 { 1384 changeCase(e); 1385 } 1386 }); 1387 toggle.setText(MessageManager.getString("label.toggle_case")); 1388 toggle.addActionListener(new ActionListener() 1389 { 1390 @Override 1391 public void actionPerformed(ActionEvent e) 1392 { 1393 changeCase(e); 1394 } 1395 }); 1396 outputMenu.setText( 1397 MessageManager.getString("label.out_to_textbox") + "..."); 1398 seqShowAnnotationsMenu 1399 .setText(MessageManager.getString("label.show_annotations")); 1400 seqHideAnnotationsMenu 1401 .setText(MessageManager.getString("label.hide_annotations")); 1402 groupShowAnnotationsMenu 1403 .setText(MessageManager.getString("label.show_annotations")); 1404 groupHideAnnotationsMenu 1405 .setText(MessageManager.getString("label.hide_annotations")); 1406 JMenuItem sequenceFeature = new JMenuItem( 1407 MessageManager.getString("label.create_sequence_feature")); 1408 sequenceFeature.addActionListener(new ActionListener() 1409 { 1410 @Override 1411 public void actionPerformed(ActionEvent e) 1412 { 1413 sequenceFeature_actionPerformed(); 1414 } 1415 }); 1416 editGroupMenu.setText(MessageManager.getString("label.group")); 1417 chooseStructure.setText( 1418 MessageManager.getString("label.show_pdbstruct_dialog")); 1419 chooseStructure.addActionListener(new ActionListener() 1420 { 1421 @Override 1422 public void actionPerformed(ActionEvent actionEvent) 1423 { 1424 SequenceI[] selectedSeqs = new SequenceI[] { sequence }; 1425 if (ap.av.getSelectionGroup() != null) 1426 { 1427 selectedSeqs = ap.av.getSequenceSelection(); 1428 } 1429 new StructureChooser(selectedSeqs, sequence, ap); 1430 } 1431 }); 1432 1433 rnaStructureMenu 1434 .setText(MessageManager.getString("label.view_rna_structure")); 1435 1436 // colStructureMenu.setText("Colour By Structure"); 1437 JMenuItem editSequence = new JMenuItem( 1438 MessageManager.getString("label.edit_sequence") + "..."); 1439 editSequence.addActionListener(new ActionListener() 1440 { 1441 @Override 1442 public void actionPerformed(ActionEvent actionEvent) 1443 { 1444 editSequence_actionPerformed(actionEvent); 1445 } 1446 }); 1447 makeReferenceSeq.setText( 1448 MessageManager.getString("label.mark_as_representative")); 1449 makeReferenceSeq.addActionListener(new ActionListener() 1450 { 1451 1452 @Override 1453 public void actionPerformed(ActionEvent actionEvent) 1454 { 1455 makeReferenceSeq_actionPerformed(actionEvent); 1456 1457 } 1458 }); 1459 1460 groupMenu.add(sequenceSelDetails); 1461 add(groupMenu); 1462 add(sequenceMenu); 1463 add(rnaStructureMenu); 1464 add(chooseStructure); 1465 if (forIdPanel) 1466 { 1467 JMenuItem hideInsertions = new JMenuItem( 1468 MessageManager.getString("label.hide_insertions")); 1469 hideInsertions.addActionListener(new ActionListener() 1470 { 1471 1472 @Override 1473 public void actionPerformed(ActionEvent e) 1474 { 1475 hideInsertions_actionPerformed(e); 1476 } 1477 }); 1478 add(hideInsertions); 1479 } 1480 // annotations configuration panel suppressed for now 1481 // groupMenu.add(chooseAnnotations); 1482 1483 /* 1484 * Add show/hide annotations to the Sequence menu, and to the Selection menu 1485 * (if a selection group is in force). 1486 */ 1487 sequenceMenu.add(seqShowAnnotationsMenu); 1488 sequenceMenu.add(seqHideAnnotationsMenu); 1489 sequenceMenu.add(seqAddReferenceAnnotations); 1490 groupMenu.add(groupShowAnnotationsMenu); 1491 groupMenu.add(groupHideAnnotationsMenu); 1492 groupMenu.add(groupAddReferenceAnnotations); 1493 groupMenu.add(editMenu); 1494 groupMenu.add(outputMenu); 1495 groupMenu.add(sequenceFeature); 1496 groupMenu.add(createGroupMenuItem); 1497 groupMenu.add(unGroupMenuItem); 1498 groupMenu.add(editGroupMenu); 1499 sequenceMenu.add(sequenceName); 1500 sequenceMenu.add(sequenceDetails); 1501 sequenceMenu.add(makeReferenceSeq); 1502 1503 initColourMenu(); 1504 buildColourMenu(); 1505 1506 editMenu.add(copy); 1507 editMenu.add(cut); 1508 editMenu.add(editSequence); 1509 editMenu.add(upperCase); 1510 editMenu.add(lowerCase); 1511 editMenu.add(toggle); 1512 editGroupMenu.add(groupName); 1513 editGroupMenu.add(colourMenu); 1514 editGroupMenu.add(showBoxes); 1515 editGroupMenu.add(showText); 1516 editGroupMenu.add(showColourText); 1517 editGroupMenu.add(outline); 1518 editGroupMenu.add(displayNonconserved); 1519 } 1520 1521 /** 1522 * Constructs the entries for the colour menu 1523 */ initColourMenu()1524 protected void initColourMenu() 1525 { 1526 colourMenu.setText(MessageManager.getString("label.group_colour")); 1527 textColour.setText(MessageManager.getString("label.text_colour")); 1528 textColour.addActionListener(new ActionListener() 1529 { 1530 @Override 1531 public void actionPerformed(ActionEvent e) 1532 { 1533 textColour_actionPerformed(); 1534 } 1535 }); 1536 1537 abovePIDColour.setText( 1538 MessageManager.getString("label.above_identity_threshold")); 1539 abovePIDColour.addActionListener(new ActionListener() 1540 { 1541 @Override 1542 public void actionPerformed(ActionEvent e) 1543 { 1544 abovePIDColour_actionPerformed(abovePIDColour.isSelected()); 1545 } 1546 }); 1547 1548 modifyPID.setText( 1549 MessageManager.getString("label.modify_identity_threshold")); 1550 modifyPID.addActionListener(new ActionListener() 1551 { 1552 @Override 1553 public void actionPerformed(ActionEvent e) 1554 { 1555 modifyPID_actionPerformed(); 1556 } 1557 }); 1558 1559 conservationMenuItem 1560 .setText(MessageManager.getString("action.by_conservation")); 1561 conservationMenuItem.addActionListener(new ActionListener() 1562 { 1563 @Override 1564 public void actionPerformed(ActionEvent e) 1565 { 1566 conservationMenuItem_actionPerformed( 1567 conservationMenuItem.isSelected()); 1568 } 1569 }); 1570 1571 annotationColour = new JRadioButtonMenuItem( 1572 MessageManager.getString("action.by_annotation")); 1573 annotationColour.setName(ResidueColourScheme.ANNOTATION_COLOUR); 1574 annotationColour.setEnabled(false); 1575 annotationColour.setToolTipText( 1576 MessageManager.getString("label.by_annotation_tooltip")); 1577 1578 modifyConservation.setText(MessageManager 1579 .getString("label.modify_conservation_threshold")); 1580 modifyConservation.addActionListener(new ActionListener() 1581 { 1582 @Override 1583 public void actionPerformed(ActionEvent e) 1584 { 1585 modifyConservation_actionPerformed(); 1586 } 1587 }); 1588 } 1589 1590 /** 1591 * Builds the group colour sub-menu, including any user-defined colours which 1592 * were loaded at startup or during the Jalview session 1593 */ buildColourMenu()1594 protected void buildColourMenu() 1595 { 1596 SequenceGroup sg = ap.av.getSelectionGroup(); 1597 if (sg == null) 1598 { 1599 /* 1600 * popup menu with no sequence group scope 1601 */ 1602 return; 1603 } 1604 colourMenu.removeAll(); 1605 colourMenu.add(textColour); 1606 colourMenu.addSeparator(); 1607 1608 ButtonGroup bg = ColourMenuHelper.addMenuItems(colourMenu, this, sg, 1609 false); 1610 bg.add(annotationColour); 1611 colourMenu.add(annotationColour); 1612 1613 colourMenu.addSeparator(); 1614 colourMenu.add(conservationMenuItem); 1615 colourMenu.add(modifyConservation); 1616 colourMenu.add(abovePIDColour); 1617 colourMenu.add(modifyPID); 1618 } 1619 modifyConservation_actionPerformed()1620 protected void modifyConservation_actionPerformed() 1621 { 1622 SequenceGroup sg = getGroup(); 1623 if (sg.cs != null) 1624 { 1625 SliderPanel.setConservationSlider(ap, sg.cs, sg.getName()); 1626 SliderPanel.showConservationSlider(); 1627 } 1628 } 1629 modifyPID_actionPerformed()1630 protected void modifyPID_actionPerformed() 1631 { 1632 SequenceGroup sg = getGroup(); 1633 if (sg.cs != null) 1634 { 1635 // int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup() 1636 // .getName()); 1637 // sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus()); 1638 SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup().getName()); 1639 SliderPanel.showPIDSlider(); 1640 } 1641 } 1642 1643 /** 1644 * Check for any annotations on the underlying dataset sequences (for the 1645 * current selection group) which are not 'on the alignment'.If any are found, 1646 * enable the option to add them to the alignment. The criteria for 'on the 1647 * alignment' is finding an alignment annotation on the alignment, matched on 1648 * calcId, label and sequenceRef. 1649 * 1650 * A tooltip is also constructed that displays the source (calcId) and type 1651 * (label) of the annotations that can be added. 1652 * 1653 * @param menuItem 1654 * @param forSequences 1655 */ configureReferenceAnnotationsMenu(JMenuItem menuItem, List<SequenceI> forSequences)1656 protected void configureReferenceAnnotationsMenu(JMenuItem menuItem, 1657 List<SequenceI> forSequences) 1658 { 1659 menuItem.setEnabled(false); 1660 1661 /* 1662 * Temporary store to hold distinct calcId / type pairs for the tooltip. 1663 * Using TreeMap means calcIds are shown in alphabetical order. 1664 */ 1665 SortedMap<String, String> tipEntries = new TreeMap<>(); 1666 final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<>(); 1667 AlignmentI al = this.ap.av.getAlignment(); 1668 AlignmentUtils.findAddableReferenceAnnotations(forSequences, tipEntries, 1669 candidates, al); 1670 if (!candidates.isEmpty()) 1671 { 1672 StringBuilder tooltip = new StringBuilder(64); 1673 tooltip.append(MessageManager.getString("label.add_annotations_for")); 1674 1675 /* 1676 * Found annotations that could be added. Enable the menu item, and 1677 * configure its tooltip and action. 1678 */ 1679 menuItem.setEnabled(true); 1680 for (String calcId : tipEntries.keySet()) 1681 { 1682 tooltip.append("<br/>" + calcId + "/" + tipEntries.get(calcId)); 1683 } 1684 String tooltipText = JvSwingUtils.wrapTooltip(true, 1685 tooltip.toString()); 1686 menuItem.setToolTipText(tooltipText); 1687 1688 menuItem.addActionListener(new ActionListener() 1689 { 1690 @Override 1691 public void actionPerformed(ActionEvent e) 1692 { 1693 addReferenceAnnotations_actionPerformed(candidates); 1694 } 1695 }); 1696 } 1697 } 1698 1699 /** 1700 * Add annotations to the sequences and to the alignment. 1701 * 1702 * @param candidates 1703 * a map whose keys are sequences on the alignment, and values a list 1704 * of annotations to add to each sequence 1705 */ addReferenceAnnotations_actionPerformed( Map<SequenceI, List<AlignmentAnnotation>> candidates)1706 protected void addReferenceAnnotations_actionPerformed( 1707 Map<SequenceI, List<AlignmentAnnotation>> candidates) 1708 { 1709 final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup(); 1710 final AlignmentI alignment = this.ap.getAlignment(); 1711 AlignmentUtils.addReferenceAnnotations(candidates, alignment, 1712 selectionGroup); 1713 refresh(); 1714 } 1715 makeReferenceSeq_actionPerformed(ActionEvent actionEvent)1716 protected void makeReferenceSeq_actionPerformed(ActionEvent actionEvent) 1717 { 1718 if (!ap.av.getAlignment().hasSeqrep()) 1719 { 1720 // initialise the display flags so the user sees something happen 1721 ap.av.setDisplayReferenceSeq(true); 1722 ap.av.setColourByReferenceSeq(true); 1723 ap.av.getAlignment().setSeqrep(sequence); 1724 } 1725 else 1726 { 1727 if (ap.av.getAlignment().getSeqrep() == sequence) 1728 { 1729 ap.av.getAlignment().setSeqrep(null); 1730 } 1731 else 1732 { 1733 ap.av.getAlignment().setSeqrep(sequence); 1734 } 1735 } 1736 refresh(); 1737 } 1738 hideInsertions_actionPerformed(ActionEvent actionEvent)1739 protected void hideInsertions_actionPerformed(ActionEvent actionEvent) 1740 { 1741 HiddenColumns hidden = ap.av.getAlignment().getHiddenColumns(); 1742 BitSet inserts = new BitSet(); 1743 1744 boolean markedPopup = false; 1745 // mark inserts in current selection 1746 if (ap.av.getSelectionGroup() != null) 1747 { 1748 // mark just the columns in the selection group to be hidden 1749 inserts.set(ap.av.getSelectionGroup().getStartRes(), 1750 ap.av.getSelectionGroup().getEndRes() + 1); // TODO why +1? 1751 1752 // now clear columns without gaps 1753 for (SequenceI sq : ap.av.getSelectionGroup().getSequences()) 1754 { 1755 if (sq == sequence) 1756 { 1757 markedPopup = true; 1758 } 1759 inserts.and(sq.getInsertionsAsBits()); 1760 } 1761 hidden.clearAndHideColumns(inserts, ap.av.getSelectionGroup().getStartRes(), 1762 ap.av.getSelectionGroup().getEndRes()); 1763 } 1764 1765 // now mark for sequence under popup if we haven't already done it 1766 else if (!markedPopup && sequence != null) 1767 { 1768 inserts.or(sequence.getInsertionsAsBits()); 1769 1770 // and set hidden columns accordingly 1771 hidden.hideColumns(inserts); 1772 } 1773 refresh(); 1774 } 1775 sequenceSelectionDetails_actionPerformed()1776 protected void sequenceSelectionDetails_actionPerformed() 1777 { 1778 createSequenceDetailsReport(ap.av.getSequenceSelection()); 1779 } 1780 createSequenceDetailsReport(SequenceI[] sequences)1781 public void createSequenceDetailsReport(SequenceI[] sequences) 1782 { 1783 CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer(); 1784 StringBuilder contents = new StringBuilder(128); 1785 for (SequenceI seq : sequences) 1786 { 1787 contents.append("<p><h2>" + MessageManager.formatMessage( 1788 "label.create_sequence_details_report_annotation_for", 1789 new Object[] 1790 { seq.getDisplayId(true) }) + "</h2></p><p>"); 1791 new SequenceAnnotationReport(false).createSequenceAnnotationReport( 1792 contents, seq, true, true, ap.getSeqPanel().seqCanvas.fr); 1793 contents.append("</p>"); 1794 } 1795 cap.setText("<html>" + contents.toString() + "</html>"); 1796 1797 Desktop.addInternalFrame(cap, 1798 MessageManager.formatMessage("label.sequence_details_for", 1799 (sequences.length == 1 ? new Object[] 1800 { sequences[0].getDisplayId(true) } 1801 : new Object[] 1802 { MessageManager 1803 .getString("label.selection") })), 1804 500, 400); 1805 1806 } 1807 showNonconserved_actionPerformed()1808 protected void showNonconserved_actionPerformed() 1809 { 1810 getGroup().setShowNonconserved(displayNonconserved.isSelected()); 1811 refresh(); 1812 } 1813 1814 /** 1815 * call to refresh view after settings change 1816 */ refresh()1817 void refresh() 1818 { 1819 ap.updateAnnotation(); 1820 // removed paintAlignment(true) here: 1821 // updateAnnotation calls paintAlignment already, so don't need to call 1822 // again 1823 1824 PaintRefresher.Refresh(this, ap.av.getSequenceSetId()); 1825 } 1826 1827 /* 1828 * protected void covariationColour_actionPerformed() { getGroup().cs = new 1829 * CovariationColourScheme(sequence.getAnnotation()[0]); refresh(); } 1830 */ 1831 /** 1832 * DOCUMENT ME! 1833 * 1834 * @param selected 1835 * 1836 * @param e 1837 * DOCUMENT ME! 1838 */ abovePIDColour_actionPerformed(boolean selected)1839 public void abovePIDColour_actionPerformed(boolean selected) 1840 { 1841 SequenceGroup sg = getGroup(); 1842 if (sg.cs == null) 1843 { 1844 return; 1845 } 1846 1847 if (selected) 1848 { 1849 sg.cs.setConsensus(AAFrequency.calculate( 1850 sg.getSequences(ap.av.getHiddenRepSequences()), 1851 sg.getStartRes(), sg.getEndRes() + 1)); 1852 1853 int threshold = SliderPanel.setPIDSliderSource(ap, 1854 sg.getGroupColourScheme(), getGroup().getName()); 1855 1856 sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus()); 1857 1858 SliderPanel.showPIDSlider(); 1859 } 1860 else 1861 // remove PIDColouring 1862 { 1863 sg.cs.setThreshold(0, ap.av.isIgnoreGapsConsensus()); 1864 SliderPanel.hidePIDSlider(); 1865 } 1866 modifyPID.setEnabled(selected); 1867 1868 refresh(); 1869 } 1870 1871 /** 1872 * Open a panel where the user can choose which types of sequence annotation 1873 * to show or hide. 1874 * 1875 * @param e 1876 */ chooseAnnotations_actionPerformed(ActionEvent e)1877 protected void chooseAnnotations_actionPerformed(ActionEvent e) 1878 { 1879 // todo correct way to guard against opening a duplicate panel? 1880 new AnnotationChooser(ap); 1881 } 1882 1883 /** 1884 * DOCUMENT ME! 1885 * 1886 * @param e 1887 * DOCUMENT ME! 1888 */ conservationMenuItem_actionPerformed(boolean selected)1889 public void conservationMenuItem_actionPerformed(boolean selected) 1890 { 1891 SequenceGroup sg = getGroup(); 1892 if (sg.cs == null) 1893 { 1894 return; 1895 } 1896 1897 if (selected) 1898 { 1899 // JBPNote: Conservation name shouldn't be i18n translated 1900 Conservation c = new Conservation("Group", 1901 sg.getSequences(ap.av.getHiddenRepSequences()), 1902 sg.getStartRes(), sg.getEndRes() + 1); 1903 1904 c.calculate(); 1905 c.verdict(false, ap.av.getConsPercGaps()); 1906 sg.cs.setConservation(c); 1907 1908 SliderPanel.setConservationSlider(ap, sg.getGroupColourScheme(), 1909 sg.getName()); 1910 SliderPanel.showConservationSlider(); 1911 } 1912 else 1913 // remove ConservationColouring 1914 { 1915 sg.cs.setConservation(null); 1916 SliderPanel.hideConservationSlider(); 1917 } 1918 modifyConservation.setEnabled(selected); 1919 1920 refresh(); 1921 } 1922 1923 /** 1924 * DOCUMENT ME! 1925 * 1926 * @param e 1927 * DOCUMENT ME! 1928 */ groupName_actionPerformed()1929 protected void groupName_actionPerformed() 1930 { 1931 1932 SequenceGroup sg = getGroup(); 1933 EditNameDialog dialog = new EditNameDialog(sg.getName(), 1934 sg.getDescription(), 1935 " " + MessageManager.getString("label.group_name") + " ", 1936 MessageManager.getString("label.group_description") + " ", 1937 MessageManager.getString("label.edit_group_name_description"), 1938 ap.alignFrame); 1939 1940 if (!dialog.accept) 1941 { 1942 return; 1943 } 1944 1945 sg.setName(dialog.getName()); 1946 sg.setDescription(dialog.getDescription()); 1947 refresh(); 1948 } 1949 1950 /** 1951 * Get selection group - adding it to the alignment if necessary. 1952 * 1953 * @return sequence group to operate on 1954 */ getGroup()1955 SequenceGroup getGroup() 1956 { 1957 SequenceGroup sg = ap.av.getSelectionGroup(); 1958 // this method won't add a new group if it already exists 1959 if (sg != null) 1960 { 1961 ap.av.getAlignment().addGroup(sg); 1962 } 1963 1964 return sg; 1965 } 1966 1967 /** 1968 * Shows a dialog where the sequence name and description may be edited. If a 1969 * name containing spaces is entered, these are converted to underscores, with a 1970 * warning message. 1971 */ sequenceName_actionPerformed()1972 void sequenceName_actionPerformed() 1973 { 1974 EditNameDialog dialog = new EditNameDialog(sequence.getName(), 1975 sequence.getDescription(), 1976 " " + MessageManager.getString("label.sequence_name") 1977 + " ", 1978 MessageManager.getString("label.sequence_description") + " ", 1979 MessageManager.getString( 1980 "label.edit_sequence_name_description"), 1981 ap.alignFrame); 1982 1983 if (!dialog.accept) 1984 { 1985 return; 1986 } 1987 1988 String name = dialog.getName(); 1989 if (name != null) 1990 { 1991 if (name.indexOf(" ") > -1) 1992 { 1993 JvOptionPane.showMessageDialog(ap, 1994 MessageManager 1995 .getString("label.spaces_converted_to_backslashes"), 1996 MessageManager 1997 .getString("label.no_spaces_allowed_sequence_name"), 1998 JvOptionPane.WARNING_MESSAGE); 1999 name = name.replace(' ', '_'); 2000 } 2001 2002 sequence.setName(name); 2003 ap.paintAlignment(false, false); 2004 } 2005 2006 sequence.setDescription(dialog.getDescription()); 2007 2008 ap.av.firePropertyChange("alignment", null, 2009 ap.av.getAlignment().getSequences()); 2010 2011 } 2012 2013 /** 2014 * DOCUMENT ME! 2015 * 2016 * @param e 2017 * DOCUMENT ME! 2018 */ unGroupMenuItem_actionPerformed()2019 void unGroupMenuItem_actionPerformed() 2020 { 2021 SequenceGroup sg = ap.av.getSelectionGroup(); 2022 ap.av.getAlignment().deleteGroup(sg); 2023 ap.av.setSelectionGroup(null); 2024 refresh(); 2025 } 2026 createGroupMenuItem_actionPerformed()2027 void createGroupMenuItem_actionPerformed() 2028 { 2029 getGroup(); // implicitly creates group - note - should apply defaults / use 2030 // standard alignment window logic for this 2031 refresh(); 2032 } 2033 2034 /** 2035 * DOCUMENT ME! 2036 * 2037 * @param e 2038 * DOCUMENT ME! 2039 */ outline_actionPerformed()2040 protected void outline_actionPerformed() 2041 { 2042 SequenceGroup sg = getGroup(); 2043 Color col = JColorChooser.showDialog(this, 2044 MessageManager.getString("label.select_outline_colour"), 2045 Color.BLUE); 2046 2047 if (col != null) 2048 { 2049 sg.setOutlineColour(col); 2050 } 2051 2052 refresh(); 2053 } 2054 2055 /** 2056 * DOCUMENT ME! 2057 * 2058 * @param e 2059 * DOCUMENT ME! 2060 */ showBoxes_actionPerformed()2061 public void showBoxes_actionPerformed() 2062 { 2063 getGroup().setDisplayBoxes(showBoxes.isSelected()); 2064 refresh(); 2065 } 2066 2067 /** 2068 * DOCUMENT ME! 2069 * 2070 * @param e 2071 * DOCUMENT ME! 2072 */ showText_actionPerformed()2073 public void showText_actionPerformed() 2074 { 2075 getGroup().setDisplayText(showText.isSelected()); 2076 refresh(); 2077 } 2078 2079 /** 2080 * DOCUMENT ME! 2081 * 2082 * @param e 2083 * DOCUMENT ME! 2084 */ showColourText_actionPerformed()2085 public void showColourText_actionPerformed() 2086 { 2087 getGroup().setColourText(showColourText.isSelected()); 2088 refresh(); 2089 } 2090 hideSequences(boolean representGroup)2091 void hideSequences(boolean representGroup) 2092 { 2093 ap.av.hideSequences(sequence, representGroup); 2094 } 2095 copy_actionPerformed()2096 public void copy_actionPerformed() 2097 { 2098 ap.alignFrame.copy_actionPerformed(null); 2099 } 2100 cut_actionPerformed()2101 public void cut_actionPerformed() 2102 { 2103 ap.alignFrame.cut_actionPerformed(null); 2104 } 2105 changeCase(ActionEvent e)2106 void changeCase(ActionEvent e) 2107 { 2108 Object source = e.getSource(); 2109 SequenceGroup sg = ap.av.getSelectionGroup(); 2110 2111 if (sg != null) 2112 { 2113 List<int[]> startEnd = ap.av.getVisibleRegionBoundaries( 2114 sg.getStartRes(), sg.getEndRes() + 1); 2115 2116 String description; 2117 int caseChange; 2118 2119 if (source == toggle) 2120 { 2121 description = MessageManager.getString("label.toggle_case"); 2122 caseChange = ChangeCaseCommand.TOGGLE_CASE; 2123 } 2124 else if (source == upperCase) 2125 { 2126 description = MessageManager.getString("label.to_upper_case"); 2127 caseChange = ChangeCaseCommand.TO_UPPER; 2128 } 2129 else 2130 { 2131 description = MessageManager.getString("label.to_lower_case"); 2132 caseChange = ChangeCaseCommand.TO_LOWER; 2133 } 2134 2135 ChangeCaseCommand caseCommand = new ChangeCaseCommand(description, 2136 sg.getSequencesAsArray(ap.av.getHiddenRepSequences()), 2137 startEnd, caseChange); 2138 2139 ap.alignFrame.addHistoryItem(caseCommand); 2140 2141 ap.av.firePropertyChange("alignment", null, 2142 ap.av.getAlignment().getSequences()); 2143 2144 } 2145 } 2146 outputText_actionPerformed(ActionEvent e)2147 public void outputText_actionPerformed(ActionEvent e) 2148 { 2149 CutAndPasteTransfer cap = new CutAndPasteTransfer(); 2150 cap.setForInput(null); 2151 Desktop.addInternalFrame(cap, MessageManager 2152 .formatMessage("label.alignment_output_command", new Object[] 2153 { e.getActionCommand() }), 600, 500); 2154 2155 String[] omitHidden = null; 2156 2157 System.out.println("PROMPT USER HERE"); // TODO: decide if a prompt happens 2158 // or we simply trust the user wants 2159 // wysiwig behaviour 2160 2161 FileFormatI fileFormat = FileFormats.getInstance() 2162 .forName(e.getActionCommand()); 2163 cap.setText( 2164 new FormatAdapter(ap).formatSequences(fileFormat, ap, true)); 2165 } 2166 sequenceFeature_actionPerformed()2167 public void sequenceFeature_actionPerformed() 2168 { 2169 SequenceGroup sg = ap.av.getSelectionGroup(); 2170 if (sg == null) 2171 { 2172 return; 2173 } 2174 2175 List<SequenceI> seqs = new ArrayList<>(); 2176 List<SequenceFeature> features = new ArrayList<>(); 2177 2178 /* 2179 * assemble dataset sequences, and template new sequence features, 2180 * for the amend features dialog 2181 */ 2182 int gSize = sg.getSize(); 2183 for (int i = 0; i < gSize; i++) 2184 { 2185 int start = sg.getSequenceAt(i).findPosition(sg.getStartRes()); 2186 int end = sg.findEndRes(sg.getSequenceAt(i)); 2187 if (start <= end) 2188 { 2189 seqs.add(sg.getSequenceAt(i).getDatasetSequence()); 2190 features.add(new SequenceFeature(null, null, start, end, null)); 2191 } 2192 } 2193 2194 /* 2195 * an entirely gapped region will generate empty lists of sequence / features 2196 */ 2197 if (!seqs.isEmpty()) 2198 { 2199 if (ap.getSeqPanel().seqCanvas.getFeatureRenderer() 2200 .amendFeatures(seqs, features, true, ap)) 2201 { 2202 ap.alignFrame.setShowSeqFeatures(true); 2203 ap.av.setSearchResults(null); // clear highlighting 2204 ap.repaint(); // draw new/amended features 2205 } 2206 } 2207 } 2208 textColour_actionPerformed()2209 public void textColour_actionPerformed() 2210 { 2211 SequenceGroup sg = getGroup(); 2212 if (sg != null) 2213 { 2214 new TextColourChooser().chooseColour(ap, sg); 2215 } 2216 } 2217 editSequence_actionPerformed(ActionEvent actionEvent)2218 public void editSequence_actionPerformed(ActionEvent actionEvent) 2219 { 2220 SequenceGroup sg = ap.av.getSelectionGroup(); 2221 2222 SequenceI seq = sequence; 2223 if (sg != null) 2224 { 2225 if (seq == null) 2226 { 2227 seq = sg.getSequenceAt(0); 2228 } 2229 2230 EditNameDialog dialog = new EditNameDialog( 2231 seq.getSequenceAsString(sg.getStartRes(), 2232 sg.getEndRes() + 1), 2233 null, MessageManager.getString("label.edit_sequence"), null, 2234 MessageManager.getString("label.edit_sequence"), 2235 ap.alignFrame); 2236 2237 if (dialog.accept) 2238 { 2239 EditCommand editCommand = new EditCommand( 2240 MessageManager.getString("label.edit_sequences"), 2241 Action.REPLACE, 2242 dialog.getName().replace(' ', ap.av.getGapCharacter()), 2243 sg.getSequencesAsArray(ap.av.getHiddenRepSequences()), 2244 sg.getStartRes(), sg.getEndRes() + 1, ap.av.getAlignment()); 2245 2246 ap.alignFrame.addHistoryItem(editCommand); 2247 2248 ap.av.firePropertyChange("alignment", null, 2249 ap.av.getAlignment().getSequences()); 2250 } 2251 } 2252 } 2253 2254 /** 2255 * Action on user selecting an item from the colour menu (that does not have 2256 * its bespoke action handler) 2257 * 2258 * @return 2259 */ 2260 @Override changeColour_actionPerformed(String colourSchemeName)2261 public void changeColour_actionPerformed(String colourSchemeName) 2262 { 2263 SequenceGroup sg = getGroup(); 2264 /* 2265 * switch to the chosen colour scheme (or null for None) 2266 */ 2267 ColourSchemeI colourScheme = ColourSchemes.getInstance() 2268 .getColourScheme(colourSchemeName, ap.av, sg, 2269 ap.av.getHiddenRepSequences()); 2270 sg.setColourScheme(colourScheme); 2271 if (colourScheme instanceof Blosum62ColourScheme 2272 || colourScheme instanceof PIDColourScheme) 2273 { 2274 sg.cs.setConsensus(AAFrequency.calculate( 2275 sg.getSequences(ap.av.getHiddenRepSequences()), 2276 sg.getStartRes(), sg.getEndRes() + 1)); 2277 } 2278 2279 refresh(); 2280 } 2281 2282 } 2283