Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 14:43:25 GMT
  2. Package jalview.gui

File PopupMenu.java

 

Coverage histogram

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

Code metrics

220
793
103
1
2,641
1,900
237
0.3
7.7
103
2.3

Classes

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