1. Project Clover database Fri Dec 6 2024 13:47:14 GMT
  2. Package jalview.gui

File PopupMenu.java

 

Coverage histogram

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

Code metrics

216
782
102
1
2,613
1,872
231
0.3
7.67
102
2.26

Classes

Class
Line #
Actions
PopupMenu 97 782 231
0.4363636443.6%
 

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