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