JAL-722 updated from 2.11.2 develop branch - needs further work before release
[jalview.git] / src / jalview / appletgui / APopupMenu.java
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.appletgui;
22
23 import java.awt.CheckboxMenuItem;
24 import java.awt.Frame;
25 import java.awt.Menu;
26 import java.awt.MenuItem;
27 import java.awt.event.ActionEvent;
28 import java.awt.event.ActionListener;
29 import java.awt.event.ItemEvent;
30 import java.awt.event.ItemListener;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.LinkedHashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.SortedMap;
39 import java.util.TreeMap;
40 import java.util.Vector;
41
42 import jalview.analysis.AAFrequency;
43 import jalview.analysis.AlignmentAnnotationUtils;
44 import jalview.analysis.AlignmentUtils;
45 import jalview.analysis.Conservation;
46 import jalview.bin.JalviewLite;
47 import jalview.commands.ChangeCaseCommand;
48 import jalview.commands.EditCommand;
49 import jalview.commands.EditCommand.Action;
50 import jalview.datamodel.AlignmentAnnotation;
51 import jalview.datamodel.AlignmentI;
52 import jalview.datamodel.PDBEntry;
53 import jalview.datamodel.SequenceFeature;
54 import jalview.datamodel.SequenceGroup;
55 import jalview.datamodel.SequenceI;
56 import jalview.io.AppletFormatAdapter;
57 import jalview.io.DataSourceType;
58 import jalview.io.FileFormatI;
59 import jalview.io.FileFormats;
60 import jalview.io.SequenceAnnotationReport;
61 import jalview.renderer.ResidueShader;
62 import jalview.renderer.ResidueShaderI;
63 import jalview.schemes.Blosum62ColourScheme;
64 import jalview.schemes.BuriedColourScheme;
65 import jalview.schemes.ClustalxColourScheme;
66 import jalview.schemes.HelixColourScheme;
67 import jalview.schemes.HydrophobicColourScheme;
68 import jalview.schemes.JalviewColourScheme;
69 import jalview.schemes.NucleotideColourScheme;
70 import jalview.schemes.PIDColourScheme;
71 import jalview.schemes.PurinePyrimidineColourScheme;
72 import jalview.schemes.StrandColourScheme;
73 import jalview.schemes.TaylorColourScheme;
74 import jalview.schemes.TurnColourScheme;
75 import jalview.schemes.ZappoColourScheme;
76 import jalview.util.MessageManager;
77 import jalview.util.UrlLink;
78
79 public class APopupMenu extends java.awt.PopupMenu
80         implements ActionListener, ItemListener
81 {
82   Menu groupMenu = new Menu();
83
84   MenuItem editGroupName = new MenuItem();
85
86   CheckboxMenuItem noColour = new CheckboxMenuItem();
87
88   protected CheckboxMenuItem clustalColour = new CheckboxMenuItem();
89
90   protected CheckboxMenuItem zappoColour = new CheckboxMenuItem();
91
92   protected CheckboxMenuItem taylorColour = new CheckboxMenuItem();
93
94   protected CheckboxMenuItem hydrophobicityColour = new CheckboxMenuItem();
95
96   protected CheckboxMenuItem helixColour = new CheckboxMenuItem();
97
98   protected CheckboxMenuItem strandColour = new CheckboxMenuItem();
99
100   protected CheckboxMenuItem turnColour = new CheckboxMenuItem();
101
102   protected CheckboxMenuItem buriedColour = new CheckboxMenuItem();
103
104   protected CheckboxMenuItem PIDColour = new CheckboxMenuItem();
105
106   protected CheckboxMenuItem BLOSUM62Colour = new CheckboxMenuItem();
107
108   CheckboxMenuItem nucleotideColour = new CheckboxMenuItem();
109
110   CheckboxMenuItem purinePyrimidineColour = new CheckboxMenuItem();
111
112   protected MenuItem userDefinedColour = new MenuItem();
113
114   protected CheckboxMenuItem abovePIDColour = new CheckboxMenuItem();
115
116   MenuItem modifyPID = new MenuItem();
117
118   protected CheckboxMenuItem conservationColour = new CheckboxMenuItem();
119
120   MenuItem modifyConservation = new MenuItem();
121
122   MenuItem noColourmenuItem = new MenuItem();
123
124   final AlignmentPanel ap;
125
126   MenuItem unGroupMenuItem = new MenuItem();
127
128   MenuItem createGroupMenuItem = new MenuItem();
129
130   Menu colourMenu = new Menu();
131
132   CheckboxMenuItem showBoxes = new CheckboxMenuItem();
133
134   CheckboxMenuItem showText = new CheckboxMenuItem();
135
136   CheckboxMenuItem showColourText = new CheckboxMenuItem();
137
138   CheckboxMenuItem displayNonconserved = new CheckboxMenuItem();
139
140   Menu seqShowAnnotationsMenu = new Menu(
141           MessageManager.getString("label.show_annotations"));
142
143   Menu seqHideAnnotationsMenu = new Menu(
144           MessageManager.getString("label.hide_annotations"));
145
146   MenuItem seqAddReferenceAnnotations = new MenuItem(
147           MessageManager.getString("label.add_reference_annotations"));
148
149   Menu groupShowAnnotationsMenu = new Menu(
150           MessageManager.getString("label.show_annotations"));
151
152   Menu groupHideAnnotationsMenu = new Menu(
153           MessageManager.getString("label.hide_annotations"));
154
155   MenuItem groupAddReferenceAnnotations = new MenuItem(
156           MessageManager.getString("label.add_reference_annotations"));
157
158   Menu editMenu = new Menu(MessageManager.getString("action.edit"));
159
160   MenuItem copy = new MenuItem(MessageManager.getString("action.copy"));
161
162   MenuItem cut = new MenuItem(MessageManager.getString("action.cut"));
163
164   MenuItem toUpper = new MenuItem(
165           MessageManager.getString("label.to_upper_case"));
166
167   MenuItem toLower = new MenuItem(
168           MessageManager.getString("label.to_lower_case"));
169
170   MenuItem toggleCase = new MenuItem(
171           MessageManager.getString("label.toggle_case"));
172
173   Menu outputmenu = new Menu();
174
175   Menu seqMenu = new Menu();
176
177   MenuItem pdb = new MenuItem();
178
179   MenuItem hideSeqs = new MenuItem();
180
181   MenuItem repGroup = new MenuItem();
182
183   MenuItem sequenceName = new MenuItem(
184           MessageManager.getString("label.edit_name_description"));
185
186   MenuItem sequenceFeature = new MenuItem(
187           MessageManager.getString("label.create_sequence_feature"));
188
189   MenuItem editSequence = new MenuItem(
190           MessageManager.getString("label.edit_sequence"));
191
192   MenuItem sequenceDetails = new MenuItem(
193           MessageManager.getString("label.sequence_details"));
194
195   MenuItem selSeqDetails = new MenuItem(
196           MessageManager.getString("label.sequence_details"));
197
198   MenuItem makeReferenceSeq = new MenuItem();
199
200   SequenceI seq;
201
202   MenuItem revealAll = new MenuItem();
203
204   MenuItem revealSeq = new MenuItem();
205
206   /**
207    * index of sequence to be revealed
208    */
209   int revealSeq_index = -1;
210
211   Menu menu1 = new Menu();
212
213   public APopupMenu(AlignmentPanel apanel, final SequenceI seq,
214           List<String> links)
215   {
216     // /////////////////////////////////////////////////////////
217     // If this is activated from the sequence panel, the user may want to
218     // edit or annotate a particular residue. Therefore display the residue menu
219     //
220     // If from the IDPanel, we must display the sequence menu
221     // ////////////////////////////////////////////////////////
222
223     this.ap = apanel;
224     this.seq = seq;
225
226     try
227     {
228       jbInit();
229     } catch (Exception e)
230     {
231       e.printStackTrace();
232     }
233
234     for (String ff : FileFormats.getInstance().getWritableFormats(true))
235     {
236       MenuItem item = new MenuItem(ff);
237
238       item.addActionListener(this);
239       outputmenu.add(item);
240     }
241
242     buildAnnotationSubmenus();
243
244     SequenceGroup sg = ap.av.getSelectionGroup();
245     if (sg != null && sg.getSize() > 0)
246     {
247       if (sg.isNucleotide())
248       {
249         conservationColour.setEnabled(false);
250         clustalColour.setEnabled(false);
251         BLOSUM62Colour.setEnabled(false);
252         zappoColour.setEnabled(false);
253         taylorColour.setEnabled(false);
254         hydrophobicityColour.setEnabled(false);
255         helixColour.setEnabled(false);
256         strandColour.setEnabled(false);
257         turnColour.setEnabled(false);
258         buriedColour.setEnabled(false);
259       }
260       else
261       {
262         purinePyrimidineColour.setEnabled(false);
263         nucleotideColour.setEnabled(false);
264       }
265       editGroupName.setLabel(
266               MessageManager.formatMessage("label.name_param", new Object[]
267               { sg.getName() }));
268       showText.setState(sg.getDisplayText());
269       showColourText.setState(sg.getColourText());
270       showBoxes.setState(sg.getDisplayBoxes());
271       displayNonconserved.setState(sg.getShowNonconserved());
272       if (!ap.av.getAlignment().getGroups().contains(sg))
273       {
274         menu1.setLabel(MessageManager.getString("action.edit_new_group"));
275         groupMenu.remove(unGroupMenuItem);
276       }
277       else
278       {
279         menu1.setLabel(MessageManager.getString("action.edit_group"));
280         groupMenu.remove(createGroupMenuItem);
281         if (sg.cs != null)
282         {
283           abovePIDColour.setState(sg.cs.getThreshold() > 0);
284           conservationColour.setState(sg.cs.conservationApplied());
285           modifyPID.setEnabled(abovePIDColour.getState());
286           modifyConservation.setEnabled(conservationColour.getState());
287         }
288       }
289       setSelectedColour(sg.cs);
290     }
291     else
292     {
293       remove(hideSeqs);
294       remove(groupMenu);
295     }
296
297     if (links != null && links.size() > 0)
298     {
299       addFeatureLinks(seq, links);
300     }
301
302     // TODO: add group link menu entry here
303     if (seq != null)
304     {
305       seqMenu.setLabel(seq.getName());
306       if (seq == ap.av.getAlignment().getSeqrep())
307       {
308         makeReferenceSeq.setLabel(
309                 MessageManager.getString("action.unmark_as_reference"));// Unmark
310                                                                         // representative");
311       }
312       else
313       {
314         makeReferenceSeq.setLabel(
315                 MessageManager.getString("action.set_as_reference")); // );
316       }
317       repGroup.setLabel(MessageManager
318               .formatMessage("label.represent_group_with", new Object[]
319               { seq.getName() }));
320     }
321     else
322     {
323       remove(seqMenu);
324     }
325
326     if (!ap.av.hasHiddenRows())
327     {
328       remove(revealAll);
329       remove(revealSeq);
330     }
331     else
332     {
333       final int index = ap.av.getAlignment().findIndex(seq);
334
335       if (ap.av.adjustForHiddenSeqs(index)
336               - ap.av.adjustForHiddenSeqs(index - 1) > 1)
337       {
338         revealSeq_index = index;
339       }
340       else
341       {
342         remove(revealSeq);
343       }
344     }
345   }
346
347   /**
348    * Select the menu item (if any) matching the current colour scheme. This
349    * works by matching the menu item name (not display text) to the canonical
350    * name of the colour scheme.
351    * 
352    * @param cs
353    */
354   protected void setSelectedColour(ResidueShaderI cs)
355   {
356     if (cs == null || cs.getColourScheme() == null)
357     {
358       noColour.setState(true);
359     }
360     else
361     {
362       String name = cs.getColourScheme().getSchemeName();
363       for (int i = 0; i < colourMenu.getItemCount(); i++)
364       {
365         MenuItem item = colourMenu.getItem(i);
366         if (item instanceof CheckboxMenuItem)
367         {
368           if (name.equals(item.getName()))
369           {
370             ((CheckboxMenuItem) item).setState(true);
371           }
372         }
373       }
374     }
375   }
376
377   /**
378    * Adds a 'Link' menu item with a sub-menu item for each hyperlink provided.
379    * 
380    * @param seq
381    * @param links
382    */
383   void addFeatureLinks(final SequenceI seq, List<String> links)
384   {
385     Menu linkMenu = new Menu(MessageManager.getString("action.link"));
386     Map<String, List<String>> linkset = new LinkedHashMap<>();
387
388     for (String link : links)
389     {
390       UrlLink urlLink = null;
391       try
392       {
393         urlLink = new UrlLink(link);
394       } catch (Exception foo)
395       {
396         System.err.println("Exception for URLLink '" + link + "': "
397                 + foo.getMessage());
398         continue;
399       }
400
401       if (!urlLink.isValid())
402       {
403         System.err.println(urlLink.getInvalidMessage());
404         continue;
405       }
406
407       urlLink.createLinksFromSeq(seq, linkset);
408     }
409
410     addshowLinks(linkMenu, linkset.values());
411
412     // disable link menu if there are no valid entries
413     if (linkMenu.getItemCount() > 0)
414     {
415       linkMenu.setEnabled(true);
416     }
417     else
418     {
419       linkMenu.setEnabled(false);
420     }
421
422     if (seq != null)
423     {
424       seqMenu.add(linkMenu);
425     }
426     else
427     {
428       add(linkMenu);
429     }
430
431   }
432
433   private void addshowLinks(Menu linkMenu, Collection<List<String>> linkset)
434   {
435     for (List<String> linkstrset : linkset)
436     {
437       // split linkstr into label and url
438       addshowLink(linkMenu, linkstrset.get(1), linkstrset.get(3));
439     }
440   }
441
442   /**
443    * Build menus for annotation types that may be shown or hidden, and for
444    * 'reference annotations' that may be added to the alignment.
445    */
446   private void buildAnnotationSubmenus()
447   {
448     /*
449      * First for the currently selected sequence (if there is one):
450      */
451     final List<SequenceI> selectedSequence = (seq == null
452             ? Collections.<SequenceI> emptyList()
453             : Arrays.asList(seq));
454     buildAnnotationTypesMenus(seqShowAnnotationsMenu,
455             seqHideAnnotationsMenu, selectedSequence);
456     configureReferenceAnnotationsMenu(seqAddReferenceAnnotations,
457             selectedSequence);
458
459     /*
460      * and repeat for the current selection group (if there is one):
461      */
462     final List<SequenceI> selectedGroup = (ap.av.getSelectionGroup() == null
463             ? Collections.<SequenceI> emptyList()
464             : ap.av.getSelectionGroup().getSequences());
465     buildAnnotationTypesMenus(groupShowAnnotationsMenu,
466             groupHideAnnotationsMenu, selectedGroup);
467     configureReferenceAnnotationsMenu(groupAddReferenceAnnotations,
468             selectedGroup);
469   }
470
471   /**
472    * Determine whether or not to enable 'add reference annotations' menu item.
473    * It is enable if there are any annotations, on any of the selected
474    * sequences, which are not yet on the alignment (visible or not).
475    * 
476    * @param menu
477    * @param forSequences
478    */
479   private void configureReferenceAnnotationsMenu(MenuItem menuItem,
480           List<SequenceI> forSequences)
481   {
482     menuItem.setEnabled(false);
483
484     /*
485      * Temporary store to hold distinct calcId / type pairs for the tooltip.
486      * Using TreeMap means calcIds are shown in alphabetical order.
487      */
488     SortedMap<String, String> tipEntries = new TreeMap<>();
489     final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<>();
490     AlignmentI al = this.ap.av.getAlignment();
491     AlignmentUtils.findAddableReferenceAnnotations(forSequences, tipEntries,
492             candidates, al);
493     if (!candidates.isEmpty())
494     {
495       StringBuilder tooltip = new StringBuilder(64);
496       tooltip.append(MessageManager.getString("label.add_annotations_for"));
497
498       /*
499        * Found annotations that could be added. Enable the menu item, and
500        * configure its action.
501        */
502       menuItem.setEnabled(true);
503
504       menuItem.addActionListener(new ActionListener()
505       {
506         @Override
507         public void actionPerformed(ActionEvent e)
508         {
509           addReferenceAnnotations_actionPerformed(candidates);
510         }
511       });
512     }
513   }
514
515   /**
516    * Add annotations to the sequences and to the alignment.
517    * 
518    * @param candidates
519    *          a map whose keys are sequences on the alignment, and values a list
520    *          of annotations to add to each sequence
521    */
522   protected void addReferenceAnnotations_actionPerformed(
523           Map<SequenceI, List<AlignmentAnnotation>> candidates)
524   {
525     final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
526     final AlignmentI alignment = this.ap.getAlignment();
527     AlignmentUtils.addReferenceAnnotations(candidates, alignment,
528             selectionGroup);
529     refresh();
530   }
531
532   /**
533    * add a show URL menu item to the given linkMenu
534    * 
535    * @param linkMenu
536    * @param target
537    *          - menu label string
538    * @param url
539    *          - url to open
540    */
541   private void addshowLink(Menu linkMenu, final String target,
542           final String url)
543   {
544     addshowLink(linkMenu, target, target, url);
545   }
546
547   /**
548    * add a show URL menu item to the given linkMenu
549    * 
550    * @param linkMenu
551    * @param target
552    *          - URL target window
553    * @param label
554    *          - menu label string
555    * @param url
556    *          - url to open
557    */
558   private void addshowLink(Menu linkMenu, final String target,
559           final String label, final String url)
560   {
561     MenuItem item = new MenuItem(label);
562     item.addActionListener(new java.awt.event.ActionListener()
563     {
564       @Override
565       public void actionPerformed(ActionEvent e)
566       {
567         ap.alignFrame.showURL(url, target);
568       }
569     });
570     linkMenu.add(item);
571   }
572
573   /**
574    * Actions on selecting / unselecting a checkbox menu item
575    */
576   @Override
577   public void itemStateChanged(ItemEvent evt)
578   {
579     Object source = evt.getSource();
580     if (source == noColour)
581     {
582       noColourmenuItem_actionPerformed();
583     }
584     else if (source == clustalColour)
585     {
586       clustalColour_actionPerformed();
587     }
588     else if (source == BLOSUM62Colour)
589     {
590       BLOSUM62Colour_actionPerformed();
591     }
592     else if (evt.getSource() == PIDColour)
593     {
594       PIDColour_actionPerformed();
595     }
596     else if (source == zappoColour)
597     {
598       zappoColour_actionPerformed();
599     }
600     else if (source == taylorColour)
601     {
602       taylorColour_actionPerformed();
603     }
604     else if (source == hydrophobicityColour)
605     {
606       hydrophobicityColour_actionPerformed();
607     }
608     else if (source == helixColour)
609     {
610       helixColour_actionPerformed();
611     }
612     else if (source == strandColour)
613     {
614       strandColour_actionPerformed();
615     }
616     else if (source == turnColour)
617     {
618       turnColour_actionPerformed();
619     }
620     else if (source == buriedColour)
621     {
622       buriedColour_actionPerformed();
623     }
624     else if (source == nucleotideColour)
625     {
626       nucleotideMenuItem_actionPerformed();
627     }
628     else if (source == purinePyrimidineColour)
629     {
630       purinePyrimidineColour_actionPerformed();
631     }
632     else if (source == abovePIDColour)
633     {
634       abovePIDColour_itemStateChanged();
635     }
636     else if (source == conservationColour)
637     {
638       conservationMenuItem_itemStateChanged();
639     }
640     else if (source == showColourText)
641     {
642       showColourText_itemStateChanged();
643     }
644     else if (source == showText)
645     {
646       showText_itemStateChanged();
647     }
648     else if (source == showBoxes)
649     {
650       showBoxes_itemStateChanged();
651     }
652     else if (source == displayNonconserved)
653     {
654       this.showNonconserved_itemStateChanged();
655     }
656   }
657
658   /**
659    * Actions on clicking a menu item
660    */
661   @Override
662   public void actionPerformed(ActionEvent evt)
663   {
664     Object source = evt.getSource();
665     if (source == userDefinedColour)
666     {
667       userDefinedColour_actionPerformed();
668     }
669     else if (source == modifyConservation)
670     {
671       conservationMenuItem_itemStateChanged();
672     }
673     else if (source == modifyPID)
674     {
675       abovePIDColour_itemStateChanged();
676     }
677     else if (source == unGroupMenuItem)
678     {
679       unGroupMenuItem_actionPerformed();
680     }
681
682     else if (source == createGroupMenuItem)
683     {
684       createGroupMenuItem_actionPerformed();
685     }
686
687     else if (source == sequenceName)
688     {
689       editName();
690     }
691     else if (source == makeReferenceSeq)
692     {
693       makeReferenceSeq_actionPerformed();
694     }
695     else if (source == sequenceDetails)
696     {
697       showSequenceDetails();
698     }
699     else if (source == selSeqDetails)
700     {
701       showSequenceSelectionDetails();
702     }
703     else if (source == pdb)
704     {
705       addPDB();
706     }
707     else if (source == hideSeqs)
708     {
709       hideSequences(false);
710     }
711     else if (source == repGroup)
712     {
713       hideSequences(true);
714     }
715     else if (source == revealSeq)
716     {
717       ap.av.showSequence(revealSeq_index);
718     }
719     else if (source == revealAll)
720     {
721       ap.av.showAllHiddenSeqs();
722     }
723
724     else if (source == editGroupName)
725     {
726       EditNameDialog dialog = new EditNameDialog(getGroup().getName(),
727               getGroup().getDescription(), "       Group Name",
728               "Group Description", ap.alignFrame,
729               "Edit Group Name / Description", 500, 100, true);
730
731       if (dialog.accept)
732       {
733         getGroup().setName(dialog.getName().replace(' ', '_'));
734         getGroup().setDescription(dialog.getDescription());
735       }
736
737     }
738     else if (source == copy)
739     {
740       ap.alignFrame.copy_actionPerformed();
741     }
742     else if (source == cut)
743     {
744       ap.alignFrame.cut_actionPerformed();
745     }
746     else if (source == editSequence)
747     {
748       SequenceGroup sg = ap.av.getSelectionGroup();
749
750       if (sg != null)
751       {
752         if (seq == null)
753         {
754           seq = sg.getSequenceAt(0);
755         }
756
757         EditNameDialog dialog = new EditNameDialog(
758                 seq.getSequenceAsString(sg.getStartRes(),
759                         sg.getEndRes() + 1),
760                 null, "Edit Sequence ", null,
761
762                 ap.alignFrame, "Edit Sequence", 500, 100, true);
763
764         if (dialog.accept)
765         {
766           EditCommand editCommand = new EditCommand(
767                   MessageManager.getString("label.edit_sequences"),
768                   Action.REPLACE,
769                   dialog.getName().replace(' ', ap.av.getGapCharacter()),
770                   sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
771                   sg.getStartRes(), sg.getEndRes() + 1,
772                   ap.av.getAlignment());
773
774           ap.alignFrame.addHistoryItem(editCommand);
775
776           ap.av.firePropertyChange("alignment", null,
777                   ap.av.getAlignment().getSequences());
778         }
779       }
780     }
781     else if (source == toUpper || source == toLower || source == toggleCase)
782     {
783       SequenceGroup sg = ap.av.getSelectionGroup();
784       if (sg != null)
785       {
786         List<int[]> startEnd = ap.av.getVisibleRegionBoundaries(
787                 sg.getStartRes(), sg.getEndRes() + 1);
788
789         String description;
790         int caseChange;
791
792         if (source == toggleCase)
793         {
794           description = "Toggle Case";
795           caseChange = ChangeCaseCommand.TOGGLE_CASE;
796         }
797         else if (source == toUpper)
798         {
799           description = "To Upper Case";
800           caseChange = ChangeCaseCommand.TO_UPPER;
801         }
802         else
803         {
804           description = "To Lower Case";
805           caseChange = ChangeCaseCommand.TO_LOWER;
806         }
807
808         ChangeCaseCommand caseCommand = new ChangeCaseCommand(description,
809                 sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
810                 startEnd, caseChange);
811
812         ap.alignFrame.addHistoryItem(caseCommand);
813
814         ap.av.firePropertyChange("alignment", null,
815                 ap.av.getAlignment().getSequences());
816
817       }
818     }
819     else if (source == sequenceFeature)
820     {
821       SequenceGroup sg = ap.av.getSelectionGroup();
822       if (sg == null)
823       {
824         return;
825       }
826
827       int gSize = sg.getSize();
828       List<SequenceI> seqs = new ArrayList<>();
829       List<SequenceFeature> features = new ArrayList<>();
830
831       for (int i = 0; i < gSize; i++)
832       {
833         int start = sg.getSequenceAt(i).findPosition(sg.getStartRes());
834         int end = sg.findEndRes(sg.getSequenceAt(i));
835         if (start <= end)
836         {
837           seqs.add(sg.getSequenceAt(i));
838           features.add(new SequenceFeature(null, null, start, end,
839                   "Jalview"));
840         }
841       }
842
843       if (!seqs.isEmpty())
844       {
845         if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs,
846                 features, true, ap))
847         {
848           ap.alignFrame.sequenceFeatures.setState(true);
849           ap.av.setShowSequenceFeatures(true);
850           ap.av.setSearchResults(null); // clear highlighting
851           ap.repaint(); // draw new/amended features
852         }
853       }
854     }
855     else
856     {
857       outputText(evt);
858     }
859
860   }
861
862   void outputText(ActionEvent e)
863   {
864     CutAndPasteTransfer cap = new CutAndPasteTransfer(true, ap.alignFrame);
865
866     Frame frame = new Frame();
867     frame.add(cap);
868     JalviewLite.addFrame(frame, MessageManager
869             .formatMessage("label.selection_output_command", new Object[]
870             { e.getActionCommand() }), 600, 500);
871     // JBPNote: getSelectionAsNewSequence behaviour has changed - this method
872     // now returns a full copy of sequence data
873     // TODO consider using getSequenceSelection instead here
874
875     FileFormatI fileFormat = FileFormats.getInstance()
876             .forName(e.getActionCommand());
877     cap.setText(new AppletFormatAdapter().formatSequences(fileFormat,
878             ap.av.getShowJVSuffix(), ap, true));
879
880   }
881
882   protected void showSequenceSelectionDetails()
883   {
884     createSequenceDetailsReport(ap.av.getSequenceSelection());
885   }
886
887   protected void showSequenceDetails()
888   {
889     createSequenceDetailsReport(new SequenceI[] { seq });
890   }
891
892   public void createSequenceDetailsReport(SequenceI[] sequences)
893   {
894
895     CutAndPasteTransfer cap = new CutAndPasteTransfer(false, ap.alignFrame);
896
897     StringBuilder contents = new StringBuilder(128);
898     for (SequenceI seq : sequences)
899     {
900       contents.append(MessageManager
901               .formatMessage("label.annotation_for_displayid", new Object[]
902               { seq.getDisplayId(true) }));
903       new SequenceAnnotationReport(false).createSequenceAnnotationReport(
904               contents, seq, true, true, ap.seqPanel.seqCanvas.fr);
905       contents.append("</p>");
906     }
907     Frame frame = new Frame();
908     frame.add(cap);
909     jalview.bin.JalviewLite.addFrame(frame,
910             "Sequence Details for " + (sequences.length == 1
911                     ? sequences[0].getDisplayId(true)
912                     : "Selection"),
913             600, 500);
914     cap.setText("<html>" + contents.toString() + "</html>");
915   }
916
917   void editName()
918   {
919     EditNameDialog dialog = new EditNameDialog(seq.getName(),
920             seq.getDescription(), "       Sequence Name",
921             "Sequence Description", ap.alignFrame,
922             "Edit Sequence Name / Description", 500, 100, true);
923
924     if (dialog.accept)
925     {
926       seq.setName(dialog.getName());
927       seq.setDescription(dialog.getDescription());
928       ap.paintAlignment(false, false);
929     }
930   }
931
932   void addPDB()
933   {
934     Vector<PDBEntry> pdbs = seq.getAllPDBEntries();
935     if (pdbs != null && !pdbs.isEmpty())
936     {
937       PDBEntry entry = pdbs.firstElement();
938
939       if (ap.av.applet.jmolAvailable)
940       {
941         new AppletJmol(entry, new SequenceI[] { seq }, null, ap,
942                 DataSourceType.URL);
943       }
944       else
945       {
946         new mc_view.AppletPDBViewer(entry, new SequenceI[] { seq }, null, ap,
947                 DataSourceType.URL);
948       }
949
950     }
951     else
952     {
953       CutAndPasteTransfer cap = new CutAndPasteTransfer(true,
954               ap.alignFrame);
955       cap.setText(MessageManager.getString("label.paste_pdb_file"));
956       cap.setPDBImport(seq);
957       Frame frame = new Frame();
958       frame.add(cap);
959       JalviewLite.addFrame(frame, MessageManager.formatMessage(
960               "label.paste_pdb_file_for_sequence", new Object[]
961               { seq.getName() }), 400, 300);
962     }
963   }
964
965   private void jbInit() throws Exception
966   {
967     groupMenu.setLabel(MessageManager.getString("label.selection"));
968     sequenceFeature.addActionListener(this);
969
970     editGroupName.addActionListener(this);
971     unGroupMenuItem
972             .setLabel(MessageManager.getString("action.remove_group"));
973     unGroupMenuItem.addActionListener(this);
974
975     createGroupMenuItem
976             .setLabel(MessageManager.getString("action.create_group"));
977     createGroupMenuItem.addActionListener(this);
978
979     modifyPID.setEnabled(abovePIDColour.getState());
980     modifyConservation.setEnabled(conservationColour.getState());
981     colourMenu.setLabel(MessageManager.getString("label.group_colour"));
982     showBoxes.setLabel(MessageManager.getString("action.boxes"));
983     showBoxes.setState(true);
984     showBoxes.addItemListener(this);
985     sequenceName.addActionListener(this);
986     sequenceDetails.addActionListener(this);
987     selSeqDetails.addActionListener(this);
988     displayNonconserved
989             .setLabel(MessageManager.getString("label.show_non_conserved"));
990     displayNonconserved.setState(false);
991     displayNonconserved.addItemListener(this);
992     showText.setLabel(MessageManager.getString("action.text"));
993     showText.addItemListener(this);
994     showColourText.setLabel(MessageManager.getString("label.colour_text"));
995     showColourText.addItemListener(this);
996     outputmenu.setLabel(MessageManager.getString("label.out_to_textbox"));
997     seqMenu.setLabel(MessageManager.getString("label.sequence"));
998     pdb.setLabel(MessageManager.getString("label.view_pdb_structure"));
999     hideSeqs.setLabel(MessageManager.getString("action.hide_sequences"));
1000     repGroup.setLabel(MessageManager
1001             .formatMessage("label.represent_group_with", new Object[]
1002             { "" }));
1003     revealAll.setLabel(MessageManager.getString("action.reveal_all"));
1004     revealSeq.setLabel(MessageManager.getString("action.reveal_sequences"));
1005     menu1.setLabel(MessageManager.getString("label.group:"));
1006     add(groupMenu);
1007     this.add(seqMenu);
1008     this.add(hideSeqs);
1009     this.add(revealSeq);
1010     this.add(revealAll);
1011     // groupMenu.add(selSeqDetails);
1012     groupMenu.add(groupShowAnnotationsMenu);
1013     groupMenu.add(groupHideAnnotationsMenu);
1014     groupMenu.add(groupAddReferenceAnnotations);
1015     groupMenu.add(editMenu);
1016     groupMenu.add(outputmenu);
1017     groupMenu.add(sequenceFeature);
1018     groupMenu.add(createGroupMenuItem);
1019     groupMenu.add(unGroupMenuItem);
1020     groupMenu.add(menu1);
1021
1022     colourMenu.add(noColour);
1023     colourMenu.add(clustalColour);
1024     colourMenu.add(BLOSUM62Colour);
1025     colourMenu.add(PIDColour);
1026     colourMenu.add(zappoColour);
1027     colourMenu.add(taylorColour);
1028     colourMenu.add(hydrophobicityColour);
1029     colourMenu.add(helixColour);
1030     colourMenu.add(strandColour);
1031     colourMenu.add(turnColour);
1032     colourMenu.add(buriedColour);
1033     colourMenu.add(nucleotideColour);
1034     colourMenu.add(purinePyrimidineColour);
1035     colourMenu.add(userDefinedColour);
1036     colourMenu.addSeparator();
1037     colourMenu.add(conservationColour);
1038     colourMenu.add(modifyConservation);
1039     colourMenu.add(abovePIDColour);
1040     colourMenu.add(modifyPID);
1041
1042     noColour.setLabel(MessageManager.getString("label.none"));
1043     noColour.addItemListener(this);
1044
1045     /*
1046      * setName allows setSelectedColour to do its thing
1047      */
1048     clustalColour.setLabel(
1049             MessageManager.getString("label.colourScheme_clustal"));
1050     clustalColour.setName(JalviewColourScheme.Clustal.toString());
1051     clustalColour.addItemListener(this);
1052     BLOSUM62Colour.setLabel(
1053             MessageManager.getString("label.colourScheme_blosum62"));
1054     BLOSUM62Colour.setName(JalviewColourScheme.Blosum62.toString());
1055     BLOSUM62Colour.addItemListener(this);
1056     PIDColour.setLabel(
1057             MessageManager.getString("label.colourScheme_%identity"));
1058     PIDColour.setName(JalviewColourScheme.PID.toString());
1059     PIDColour.addItemListener(this);
1060     zappoColour
1061             .setLabel(MessageManager.getString("label.colourScheme_zappo"));
1062     zappoColour.setName(JalviewColourScheme.Zappo.toString());
1063     zappoColour.addItemListener(this);
1064     taylorColour.setLabel(
1065             MessageManager.getString("label.colourScheme_taylor"));
1066     taylorColour.setName(JalviewColourScheme.Taylor.toString());
1067     taylorColour.addItemListener(this);
1068     hydrophobicityColour.setLabel(
1069             MessageManager.getString("label.colourScheme_hydrophobic"));
1070     hydrophobicityColour
1071             .setName(JalviewColourScheme.Hydrophobic.toString());
1072     hydrophobicityColour.addItemListener(this);
1073     helixColour.setLabel(MessageManager
1074             .getString("label.colourScheme_helixpropensity"));
1075     helixColour.setName(JalviewColourScheme.Helix.toString());
1076     helixColour.addItemListener(this);
1077     strandColour.setLabel(MessageManager
1078             .getString("label.colourScheme_strandpropensity"));
1079     strandColour.setName(JalviewColourScheme.Strand.toString());
1080     strandColour.addItemListener(this);
1081     turnColour.setLabel(
1082             MessageManager.getString("label.colourScheme_turnpropensity"));
1083     turnColour.setName(JalviewColourScheme.Turn.toString());
1084     turnColour.addItemListener(this);
1085     buriedColour.setLabel(
1086             MessageManager.getString("label.colourScheme_buriedindex"));
1087     buriedColour.setName(JalviewColourScheme.Buried.toString());
1088     buriedColour.addItemListener(this);
1089     nucleotideColour.setLabel(
1090             MessageManager.getString("label.colourScheme_nucleotide"));
1091     nucleotideColour.setName(JalviewColourScheme.Nucleotide.toString());
1092     nucleotideColour.addItemListener(this);
1093     purinePyrimidineColour.setLabel(MessageManager
1094             .getString("label.colourScheme_purine/pyrimidine"));
1095     purinePyrimidineColour
1096             .setName(JalviewColourScheme.PurinePyrimidine.toString());
1097     purinePyrimidineColour.addItemListener(this);
1098
1099     userDefinedColour
1100             .setLabel(MessageManager.getString("action.user_defined"));
1101     userDefinedColour.addActionListener(this);
1102
1103     abovePIDColour.setLabel(
1104             MessageManager.getString("label.above_identity_threshold"));
1105     abovePIDColour.addItemListener(this);
1106     modifyPID.setLabel(
1107             MessageManager.getString("label.modify_identity_threshold"));
1108     modifyPID.addActionListener(this);
1109     conservationColour
1110             .setLabel(MessageManager.getString("action.by_conservation"));
1111     conservationColour.addItemListener(this);
1112     modifyConservation.setLabel(MessageManager
1113             .getString("label.modify_conservation_threshold"));
1114     modifyConservation.addActionListener(this);
1115
1116     PIDColour.addActionListener(this);
1117     BLOSUM62Colour.addActionListener(this);
1118
1119     editMenu.add(copy);
1120     copy.addActionListener(this);
1121     editMenu.add(cut);
1122     cut.addActionListener(this);
1123
1124     editMenu.add(editSequence);
1125     editSequence.addActionListener(this);
1126
1127     editMenu.add(toUpper);
1128     toUpper.addActionListener(this);
1129     editMenu.add(toLower);
1130     toLower.addActionListener(this);
1131     editMenu.add(toggleCase);
1132     seqMenu.add(seqShowAnnotationsMenu);
1133     seqMenu.add(seqHideAnnotationsMenu);
1134     seqMenu.add(seqAddReferenceAnnotations);
1135     seqMenu.add(sequenceName);
1136     seqMenu.add(makeReferenceSeq);
1137     // seqMenu.add(sequenceDetails);
1138
1139     if (!ap.av.applet.useXtrnalSviewer)
1140     {
1141       seqMenu.add(pdb);
1142     }
1143     seqMenu.add(repGroup);
1144     menu1.add(editGroupName);
1145     menu1.add(colourMenu);
1146     menu1.add(showBoxes);
1147     menu1.add(showText);
1148     menu1.add(showColourText);
1149     menu1.add(displayNonconserved);
1150     toggleCase.addActionListener(this);
1151     pdb.addActionListener(this);
1152     hideSeqs.addActionListener(this);
1153     repGroup.addActionListener(this);
1154     revealAll.addActionListener(this);
1155     revealSeq.addActionListener(this);
1156     makeReferenceSeq.addActionListener(this);
1157   }
1158
1159   void refresh()
1160   {
1161     ap.paintAlignment(true, true);
1162   }
1163
1164   protected void clustalColour_actionPerformed()
1165   {
1166     SequenceGroup sg = getGroup();
1167     sg.cs = new ResidueShader(
1168             new ClustalxColourScheme(sg, ap.av.getHiddenRepSequences()));
1169     refresh();
1170   }
1171
1172   protected void zappoColour_actionPerformed()
1173   {
1174     getGroup().cs = new ResidueShader(new ZappoColourScheme());
1175     refresh();
1176   }
1177
1178   protected void taylorColour_actionPerformed()
1179   {
1180     getGroup().cs = new ResidueShader(new TaylorColourScheme());
1181     refresh();
1182   }
1183
1184   protected void hydrophobicityColour_actionPerformed()
1185   {
1186     getGroup().cs = new ResidueShader(new HydrophobicColourScheme());
1187     refresh();
1188   }
1189
1190   protected void helixColour_actionPerformed()
1191   {
1192     getGroup().cs = new ResidueShader(new HelixColourScheme());
1193     refresh();
1194   }
1195
1196   protected void strandColour_actionPerformed()
1197   {
1198     getGroup().cs = new ResidueShader(new StrandColourScheme());
1199     refresh();
1200   }
1201
1202   protected void turnColour_actionPerformed()
1203   {
1204     getGroup().cs = new ResidueShader(new TurnColourScheme());
1205     refresh();
1206   }
1207
1208   protected void buriedColour_actionPerformed()
1209   {
1210     getGroup().cs = new ResidueShader(new BuriedColourScheme());
1211     refresh();
1212   }
1213
1214   public void nucleotideMenuItem_actionPerformed()
1215   {
1216     getGroup().cs = new ResidueShader(new NucleotideColourScheme());
1217     refresh();
1218   }
1219
1220   public void purinePyrimidineColour_actionPerformed()
1221   {
1222     getGroup().cs = new ResidueShader(new PurinePyrimidineColourScheme());
1223     refresh();
1224   }
1225
1226   protected void abovePIDColour_itemStateChanged()
1227   {
1228     SequenceGroup sg = getGroup();
1229     if (sg.cs == null)
1230     {
1231       return;
1232     }
1233
1234     if (abovePIDColour.getState())
1235     {
1236       sg.cs.setConsensus(AAFrequency.calculate(
1237               sg.getSequences(ap.av.getHiddenRepSequences()), 0,
1238               ap.av.getAlignment().getWidth()));
1239       int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs,
1240               getGroup().getName());
1241
1242       sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus());
1243
1244       SliderPanel.showPIDSlider();
1245
1246     }
1247     else
1248     // remove PIDColouring
1249     {
1250       SliderPanel.hidePIDSlider();
1251       sg.cs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
1252     }
1253     modifyPID.setEnabled(abovePIDColour.getState());
1254     refresh();
1255   }
1256
1257   protected void userDefinedColour_actionPerformed()
1258   {
1259     new UserDefinedColours(ap, getGroup());
1260   }
1261
1262   protected void PIDColour_actionPerformed()
1263   {
1264     SequenceGroup sg = getGroup();
1265     sg.cs = new ResidueShader(new PIDColourScheme());
1266     sg.cs.setConsensus(AAFrequency.calculate(
1267             sg.getSequences(ap.av.getHiddenRepSequences()), 0,
1268             ap.av.getAlignment().getWidth()));
1269     refresh();
1270   }
1271
1272   protected void BLOSUM62Colour_actionPerformed()
1273   {
1274     SequenceGroup sg = getGroup();
1275
1276     sg.cs = new ResidueShader(new Blosum62ColourScheme());
1277
1278     sg.cs.setConsensus(AAFrequency.calculate(
1279             sg.getSequences(ap.av.getHiddenRepSequences()), 0,
1280             ap.av.getAlignment().getWidth()));
1281
1282     refresh();
1283   }
1284
1285   protected void noColourmenuItem_actionPerformed()
1286   {
1287     getGroup().cs = null;
1288     refresh();
1289   }
1290
1291   protected void conservationMenuItem_itemStateChanged()
1292   {
1293     SequenceGroup sg = getGroup();
1294     if (sg.cs == null)
1295     {
1296       return;
1297     }
1298
1299     if (conservationColour.getState())
1300     {
1301       Conservation conservation = Conservation.calculateConservation(
1302               "Group", sg.getSequences(ap.av.getHiddenRepSequences()), 0,
1303               ap.av.getAlignment().getWidth(), false,
1304               ap.av.getConsPercGaps(), false);
1305       sg.getGroupColourScheme().setConservation(conservation);
1306       SliderPanel.setConservationSlider(ap, sg.cs, sg.getName());
1307       SliderPanel.showConservationSlider();
1308     }
1309     else
1310     // remove ConservationColouring
1311     {
1312       SliderPanel.hideConservationSlider();
1313       sg.cs.setConservation(null);
1314     }
1315     modifyConservation.setEnabled(conservationColour.getState());
1316     refresh();
1317   }
1318
1319   SequenceGroup getGroup()
1320   {
1321     SequenceGroup sg = ap.av.getSelectionGroup();
1322
1323     // this method won't add a new group if it already exists
1324     if (sg != null)
1325     {
1326       ap.av.getAlignment().addGroup(sg);
1327     }
1328
1329     return sg;
1330   }
1331
1332   void unGroupMenuItem_actionPerformed()
1333   {
1334     SequenceGroup sg = ap.av.getSelectionGroup();
1335     ap.av.getAlignment().deleteGroup(sg);
1336     ap.av.setSelectionGroup(null);
1337     ap.paintAlignment(true, true);
1338   }
1339
1340   void createGroupMenuItem_actionPerformed()
1341   {
1342     getGroup(); // implicitly create group
1343     refresh();
1344   }
1345
1346   public void showColourText_itemStateChanged()
1347   {
1348     getGroup().setColourText(showColourText.getState());
1349     refresh();
1350   }
1351
1352   public void showText_itemStateChanged()
1353   {
1354     getGroup().setDisplayText(showText.getState());
1355     refresh();
1356   }
1357
1358   public void makeReferenceSeq_actionPerformed()
1359   {
1360     if (!ap.av.getAlignment().hasSeqrep())
1361     {
1362       // initialise the display flags so the user sees something happen
1363       ap.av.setDisplayReferenceSeq(true);
1364       ap.av.setColourByReferenceSeq(true);
1365       ap.av.getAlignment().setSeqrep(seq);
1366     }
1367     else
1368     {
1369       if (ap.av.getAlignment().getSeqrep() == seq)
1370       {
1371         ap.av.getAlignment().setSeqrep(null);
1372       }
1373       else
1374       {
1375         ap.av.getAlignment().setSeqrep(seq);
1376       }
1377     }
1378     refresh();
1379   }
1380
1381   public void showNonconserved_itemStateChanged()
1382   {
1383     getGroup().setShowNonconserved(this.displayNonconserved.getState());
1384     refresh();
1385   }
1386
1387   public void showBoxes_itemStateChanged()
1388   {
1389     getGroup().setDisplayBoxes(showBoxes.getState());
1390     refresh();
1391   }
1392
1393   void hideSequences(boolean representGroup)
1394   {
1395     ap.av.hideSequences(seq, representGroup);
1396   }
1397
1398   /**
1399    * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
1400    * "All" is added first, followed by a separator. Then add any annotation
1401    * types associated with the current selection. Separate menus are built for
1402    * the selected sequence group (if any), and the selected sequence.
1403    * <p>
1404    * Some annotation rows are always rendered together - these can be identified
1405    * by a common graphGroup property > -1. Only one of each group will be marked
1406    * as visible (to avoid duplication of the display). For such groups we add a
1407    * composite type name, e.g.
1408    * <p>
1409    * IUPredWS (Long), IUPredWS (Short)
1410    * 
1411    * @param seq
1412    */
1413   protected void buildAnnotationTypesMenus(Menu showMenu, Menu hideMenu,
1414           List<SequenceI> forSequences)
1415   {
1416     showMenu.removeAll();
1417     hideMenu.removeAll();
1418
1419     final List<String> all = Arrays
1420             .asList(new String[]
1421             { MessageManager.getString("label.all") });
1422     addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true,
1423             true);
1424     addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
1425             false);
1426     showMenu.addSeparator();
1427     hideMenu.addSeparator();
1428
1429     final AlignmentAnnotation[] annotations = ap.getAlignment()
1430             .getAlignmentAnnotation();
1431
1432     /*
1433      * Find shown/hidden annotations types, distinguished by source (calcId),
1434      * and grouped by graphGroup. Using LinkedHashMap means we will retrieve in
1435      * the insertion order, which is the order of the annotations on the
1436      * alignment.
1437      */
1438     Map<String, List<List<String>>> shownTypes = new LinkedHashMap<>();
1439     Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<>();
1440     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
1441             AlignmentAnnotationUtils.asList(annotations), forSequences);
1442
1443     for (String calcId : hiddenTypes.keySet())
1444     {
1445       for (List<String> type : hiddenTypes.get(calcId))
1446       {
1447         addAnnotationTypeToShowHide(showMenu, forSequences, calcId, type,
1448                 false, true);
1449       }
1450     }
1451     // grey out 'show annotations' if none are hidden
1452     showMenu.setEnabled(!hiddenTypes.isEmpty());
1453
1454     for (String calcId : shownTypes.keySet())
1455     {
1456       for (List<String> type : shownTypes.get(calcId))
1457       {
1458         addAnnotationTypeToShowHide(hideMenu, forSequences, calcId, type,
1459                 false, false);
1460       }
1461     }
1462     // grey out 'hide annotations' if none are shown
1463     hideMenu.setEnabled(!shownTypes.isEmpty());
1464   }
1465
1466   /**
1467    * Add one annotation type to the 'Show Annotations' or 'Hide Annotations'
1468    * menus.
1469    * 
1470    * @param showOrHideMenu
1471    *          the menu to add to
1472    * @param forSequences
1473    *          the sequences whose annotations may be shown or hidden
1474    * @param calcId
1475    * @param types
1476    *          the label to add
1477    * @param allTypes
1478    *          if true this is a special label meaning 'All'
1479    * @param actionIsShow
1480    *          if true, the select menu item action is to show the annotation
1481    *          type, else hide
1482    */
1483   protected void addAnnotationTypeToShowHide(Menu showOrHideMenu,
1484           final List<SequenceI> forSequences, String calcId,
1485           final List<String> types, final boolean allTypes,
1486           final boolean actionIsShow)
1487   {
1488     String label = types.toString(); // [a, b, c]
1489     label = label.substring(1, label.length() - 1);
1490     final MenuItem item = new MenuItem(label);
1491     item.addActionListener(new java.awt.event.ActionListener()
1492     {
1493       @Override
1494       public void actionPerformed(ActionEvent e)
1495       {
1496         AlignmentUtils.showOrHideSequenceAnnotations(ap.getAlignment(),
1497                 types, forSequences, allTypes, actionIsShow);
1498         refresh();
1499       }
1500     });
1501     showOrHideMenu.add(item);
1502   }
1503
1504 }