Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.gui

File PopupMenu.java

 

Coverage histogram

../../img/srcFileCovDistChart5.png
43% of files have more coverage

Code metrics

204
715
95
1
2,364
1,715
218
0.3
7.53
95
2.29

Classes

Class Line # Actions
PopupMenu 97 715 218
0.4329388443.3%
 

Contributing tests

This file is covered by 15 tests. .

Source view

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