JAL-3161 limit tooltip and status updates to visible columns
[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
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(null).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(
915             MessageManager.formatMessage("label.html_content", new Object[]
916             { contents.toString() }));
917   }
918
919   void editName()
920   {
921     EditNameDialog dialog = new EditNameDialog(seq.getName(),
922             seq.getDescription(), "       Sequence Name",
923             "Sequence Description", ap.alignFrame,
924             "Edit Sequence Name / Description", 500, 100, true);
925
926     if (dialog.accept)
927     {
928       seq.setName(dialog.getName());
929       seq.setDescription(dialog.getDescription());
930       ap.paintAlignment(false, false);
931     }
932   }
933
934   void addPDB()
935   {
936     Vector<PDBEntry> pdbs = seq.getAllPDBEntries();
937     if (pdbs != null && !pdbs.isEmpty())
938     {
939       PDBEntry entry = pdbs.firstElement();
940
941       if (ap.av.applet.jmolAvailable)
942       {
943         new AppletJmol(entry, new SequenceI[] { seq }, null, ap,
944                 DataSourceType.URL);
945       }
946       else
947       {
948         new MCview.AppletPDBViewer(entry, new SequenceI[] { seq }, null, ap,
949                 DataSourceType.URL);
950       }
951
952     }
953     else
954     {
955       CutAndPasteTransfer cap = new CutAndPasteTransfer(true,
956               ap.alignFrame);
957       cap.setText(MessageManager.getString("label.paste_pdb_file"));
958       cap.setPDBImport(seq);
959       Frame frame = new Frame();
960       frame.add(cap);
961       JalviewLite.addFrame(frame, MessageManager.formatMessage(
962               "label.paste_pdb_file_for_sequence", new Object[]
963               { seq.getName() }), 400, 300);
964     }
965   }
966
967   private void jbInit() throws Exception
968   {
969     groupMenu.setLabel(MessageManager.getString("label.selection"));
970     sequenceFeature.addActionListener(this);
971
972     editGroupName.addActionListener(this);
973     unGroupMenuItem
974             .setLabel(MessageManager.getString("action.remove_group"));
975     unGroupMenuItem.addActionListener(this);
976
977     createGroupMenuItem
978             .setLabel(MessageManager.getString("action.create_group"));
979     createGroupMenuItem.addActionListener(this);
980
981     modifyPID.setEnabled(abovePIDColour.getState());
982     modifyConservation.setEnabled(conservationColour.getState());
983     colourMenu.setLabel(MessageManager.getString("label.group_colour"));
984     showBoxes.setLabel(MessageManager.getString("action.boxes"));
985     showBoxes.setState(true);
986     showBoxes.addItemListener(this);
987     sequenceName.addActionListener(this);
988     sequenceDetails.addActionListener(this);
989     selSeqDetails.addActionListener(this);
990     displayNonconserved
991             .setLabel(MessageManager.getString("label.show_non_conserved"));
992     displayNonconserved.setState(false);
993     displayNonconserved.addItemListener(this);
994     showText.setLabel(MessageManager.getString("action.text"));
995     showText.addItemListener(this);
996     showColourText.setLabel(MessageManager.getString("label.colour_text"));
997     showColourText.addItemListener(this);
998     outputmenu.setLabel(MessageManager.getString("label.out_to_textbox"));
999     seqMenu.setLabel(MessageManager.getString("label.sequence"));
1000     pdb.setLabel(MessageManager.getString("label.view_pdb_structure"));
1001     hideSeqs.setLabel(MessageManager.getString("action.hide_sequences"));
1002     repGroup.setLabel(MessageManager
1003             .formatMessage("label.represent_group_with", new Object[]
1004             { "" }));
1005     revealAll.setLabel(MessageManager.getString("action.reveal_all"));
1006     revealSeq.setLabel(MessageManager.getString("action.reveal_sequences"));
1007     menu1.setLabel(MessageManager.getString("label.group:"));
1008     add(groupMenu);
1009     this.add(seqMenu);
1010     this.add(hideSeqs);
1011     this.add(revealSeq);
1012     this.add(revealAll);
1013     // groupMenu.add(selSeqDetails);
1014     groupMenu.add(groupShowAnnotationsMenu);
1015     groupMenu.add(groupHideAnnotationsMenu);
1016     groupMenu.add(groupAddReferenceAnnotations);
1017     groupMenu.add(editMenu);
1018     groupMenu.add(outputmenu);
1019     groupMenu.add(sequenceFeature);
1020     groupMenu.add(createGroupMenuItem);
1021     groupMenu.add(unGroupMenuItem);
1022     groupMenu.add(menu1);
1023
1024     colourMenu.add(noColour);
1025     colourMenu.add(clustalColour);
1026     colourMenu.add(BLOSUM62Colour);
1027     colourMenu.add(PIDColour);
1028     colourMenu.add(zappoColour);
1029     colourMenu.add(taylorColour);
1030     colourMenu.add(hydrophobicityColour);
1031     colourMenu.add(helixColour);
1032     colourMenu.add(strandColour);
1033     colourMenu.add(turnColour);
1034     colourMenu.add(buriedColour);
1035     colourMenu.add(nucleotideColour);
1036     colourMenu.add(purinePyrimidineColour);
1037     colourMenu.add(userDefinedColour);
1038     colourMenu.addSeparator();
1039     colourMenu.add(conservationColour);
1040     colourMenu.add(modifyConservation);
1041     colourMenu.add(abovePIDColour);
1042     colourMenu.add(modifyPID);
1043
1044     noColour.setLabel(MessageManager.getString("label.none"));
1045     noColour.addItemListener(this);
1046
1047     /*
1048      * setName allows setSelectedColour to do its thing
1049      */
1050     clustalColour.setLabel(
1051             MessageManager.getString("label.colourScheme_clustal"));
1052     clustalColour.setName(JalviewColourScheme.Clustal.toString());
1053     clustalColour.addItemListener(this);
1054     BLOSUM62Colour.setLabel(
1055             MessageManager.getString("label.colourScheme_blosum62"));
1056     BLOSUM62Colour.setName(JalviewColourScheme.Blosum62.toString());
1057     BLOSUM62Colour.addItemListener(this);
1058     PIDColour.setLabel(
1059             MessageManager.getString("label.colourScheme_%_identity"));
1060     PIDColour.setName(JalviewColourScheme.PID.toString());
1061     PIDColour.addItemListener(this);
1062     zappoColour
1063             .setLabel(MessageManager.getString("label.colourScheme_zappo"));
1064     zappoColour.setName(JalviewColourScheme.Zappo.toString());
1065     zappoColour.addItemListener(this);
1066     taylorColour.setLabel(
1067             MessageManager.getString("label.colourScheme_taylor"));
1068     taylorColour.setName(JalviewColourScheme.Taylor.toString());
1069     taylorColour.addItemListener(this);
1070     hydrophobicityColour.setLabel(
1071             MessageManager.getString("label.colourScheme_hydrophobic"));
1072     hydrophobicityColour
1073             .setName(JalviewColourScheme.Hydrophobic.toString());
1074     hydrophobicityColour.addItemListener(this);
1075     helixColour.setLabel(MessageManager
1076             .getString("label.colourScheme_helix_propensity"));
1077     helixColour.setName(JalviewColourScheme.Helix.toString());
1078     helixColour.addItemListener(this);
1079     strandColour.setLabel(MessageManager
1080             .getString("label.colourScheme_strand_propensity"));
1081     strandColour.setName(JalviewColourScheme.Strand.toString());
1082     strandColour.addItemListener(this);
1083     turnColour.setLabel(
1084             MessageManager.getString("label.colourScheme_turn_propensity"));
1085     turnColour.setName(JalviewColourScheme.Turn.toString());
1086     turnColour.addItemListener(this);
1087     buriedColour.setLabel(
1088             MessageManager.getString("label.colourScheme_buried_index"));
1089     buriedColour.setName(JalviewColourScheme.Buried.toString());
1090     buriedColour.addItemListener(this);
1091     nucleotideColour.setLabel(
1092             MessageManager.getString("label.colourScheme_nucleotide"));
1093     nucleotideColour.setName(JalviewColourScheme.Nucleotide.toString());
1094     nucleotideColour.addItemListener(this);
1095     purinePyrimidineColour.setLabel(MessageManager
1096             .getString("label.colourScheme_purine/pyrimidine"));
1097     purinePyrimidineColour
1098             .setName(JalviewColourScheme.PurinePyrimidine.toString());
1099     purinePyrimidineColour.addItemListener(this);
1100
1101     userDefinedColour
1102             .setLabel(MessageManager.getString("action.user_defined"));
1103     userDefinedColour.addActionListener(this);
1104
1105     abovePIDColour.setLabel(
1106             MessageManager.getString("label.above_identity_threshold"));
1107     abovePIDColour.addItemListener(this);
1108     modifyPID.setLabel(
1109             MessageManager.getString("label.modify_identity_threshold"));
1110     modifyPID.addActionListener(this);
1111     conservationColour
1112             .setLabel(MessageManager.getString("action.by_conservation"));
1113     conservationColour.addItemListener(this);
1114     modifyConservation.setLabel(MessageManager
1115             .getString("label.modify_conservation_threshold"));
1116     modifyConservation.addActionListener(this);
1117
1118     PIDColour.addActionListener(this);
1119     BLOSUM62Colour.addActionListener(this);
1120
1121     editMenu.add(copy);
1122     copy.addActionListener(this);
1123     editMenu.add(cut);
1124     cut.addActionListener(this);
1125
1126     editMenu.add(editSequence);
1127     editSequence.addActionListener(this);
1128
1129     editMenu.add(toUpper);
1130     toUpper.addActionListener(this);
1131     editMenu.add(toLower);
1132     toLower.addActionListener(this);
1133     editMenu.add(toggleCase);
1134     seqMenu.add(seqShowAnnotationsMenu);
1135     seqMenu.add(seqHideAnnotationsMenu);
1136     seqMenu.add(seqAddReferenceAnnotations);
1137     seqMenu.add(sequenceName);
1138     seqMenu.add(makeReferenceSeq);
1139     // seqMenu.add(sequenceDetails);
1140
1141     if (!ap.av.applet.useXtrnalSviewer)
1142     {
1143       seqMenu.add(pdb);
1144     }
1145     seqMenu.add(repGroup);
1146     menu1.add(editGroupName);
1147     menu1.add(colourMenu);
1148     menu1.add(showBoxes);
1149     menu1.add(showText);
1150     menu1.add(showColourText);
1151     menu1.add(displayNonconserved);
1152     toggleCase.addActionListener(this);
1153     pdb.addActionListener(this);
1154     hideSeqs.addActionListener(this);
1155     repGroup.addActionListener(this);
1156     revealAll.addActionListener(this);
1157     revealSeq.addActionListener(this);
1158     makeReferenceSeq.addActionListener(this);
1159   }
1160
1161   void refresh()
1162   {
1163     ap.paintAlignment(true, true);
1164   }
1165
1166   protected void clustalColour_actionPerformed()
1167   {
1168     SequenceGroup sg = getGroup();
1169     sg.cs = new ResidueShader(
1170             new ClustalxColourScheme(sg, ap.av.getHiddenRepSequences()));
1171     refresh();
1172   }
1173
1174   protected void zappoColour_actionPerformed()
1175   {
1176     getGroup().cs = new ResidueShader(new ZappoColourScheme());
1177     refresh();
1178   }
1179
1180   protected void taylorColour_actionPerformed()
1181   {
1182     getGroup().cs = new ResidueShader(new TaylorColourScheme());
1183     refresh();
1184   }
1185
1186   protected void hydrophobicityColour_actionPerformed()
1187   {
1188     getGroup().cs = new ResidueShader(new HydrophobicColourScheme());
1189     refresh();
1190   }
1191
1192   protected void helixColour_actionPerformed()
1193   {
1194     getGroup().cs = new ResidueShader(new HelixColourScheme());
1195     refresh();
1196   }
1197
1198   protected void strandColour_actionPerformed()
1199   {
1200     getGroup().cs = new ResidueShader(new StrandColourScheme());
1201     refresh();
1202   }
1203
1204   protected void turnColour_actionPerformed()
1205   {
1206     getGroup().cs = new ResidueShader(new TurnColourScheme());
1207     refresh();
1208   }
1209
1210   protected void buriedColour_actionPerformed()
1211   {
1212     getGroup().cs = new ResidueShader(new BuriedColourScheme());
1213     refresh();
1214   }
1215
1216   public void nucleotideMenuItem_actionPerformed()
1217   {
1218     getGroup().cs = new ResidueShader(new NucleotideColourScheme());
1219     refresh();
1220   }
1221
1222   public void purinePyrimidineColour_actionPerformed()
1223   {
1224     getGroup().cs = new ResidueShader(new PurinePyrimidineColourScheme());
1225     refresh();
1226   }
1227
1228   protected void abovePIDColour_itemStateChanged()
1229   {
1230     SequenceGroup sg = getGroup();
1231     if (sg.cs == null)
1232     {
1233       return;
1234     }
1235
1236     if (abovePIDColour.getState())
1237     {
1238       sg.cs.setConsensus(AAFrequency.calculate(
1239               sg.getSequences(ap.av.getHiddenRepSequences()), 0,
1240               ap.av.getAlignment().getWidth()));
1241       int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs,
1242               getGroup().getName());
1243
1244       sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus());
1245
1246       SliderPanel.showPIDSlider();
1247
1248     }
1249     else
1250     // remove PIDColouring
1251     {
1252       SliderPanel.hidePIDSlider();
1253       sg.cs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
1254     }
1255     modifyPID.setEnabled(abovePIDColour.getState());
1256     refresh();
1257   }
1258
1259   protected void userDefinedColour_actionPerformed()
1260   {
1261     new UserDefinedColours(ap, getGroup());
1262   }
1263
1264   protected void PIDColour_actionPerformed()
1265   {
1266     SequenceGroup sg = getGroup();
1267     sg.cs = new ResidueShader(new PIDColourScheme());
1268     sg.cs.setConsensus(AAFrequency.calculate(
1269             sg.getSequences(ap.av.getHiddenRepSequences()), 0,
1270             ap.av.getAlignment().getWidth()));
1271     refresh();
1272   }
1273
1274   protected void BLOSUM62Colour_actionPerformed()
1275   {
1276     SequenceGroup sg = getGroup();
1277
1278     sg.cs = new ResidueShader(new Blosum62ColourScheme());
1279
1280     sg.cs.setConsensus(AAFrequency.calculate(
1281             sg.getSequences(ap.av.getHiddenRepSequences()), 0,
1282             ap.av.getAlignment().getWidth()));
1283
1284     refresh();
1285   }
1286
1287   protected void noColourmenuItem_actionPerformed()
1288   {
1289     getGroup().cs = null;
1290     refresh();
1291   }
1292
1293   protected void conservationMenuItem_itemStateChanged()
1294   {
1295     SequenceGroup sg = getGroup();
1296     if (sg.cs == null)
1297     {
1298       return;
1299     }
1300
1301     if (conservationColour.getState())
1302     {
1303       Conservation conservation = Conservation.calculateConservation(
1304               "Group", sg.getSequences(ap.av.getHiddenRepSequences()), 0,
1305               ap.av.getAlignment().getWidth(), false,
1306               ap.av.getConsPercGaps(), false);
1307       sg.getGroupColourScheme().setConservation(conservation);
1308       SliderPanel.setConservationSlider(ap, sg.cs, sg.getName());
1309       SliderPanel.showConservationSlider();
1310     }
1311     else
1312     // remove ConservationColouring
1313     {
1314       SliderPanel.hideConservationSlider();
1315       sg.cs.setConservation(null);
1316     }
1317     modifyConservation.setEnabled(conservationColour.getState());
1318     refresh();
1319   }
1320
1321   SequenceGroup getGroup()
1322   {
1323     SequenceGroup sg = ap.av.getSelectionGroup();
1324
1325     // this method won't add a new group if it already exists
1326     if (sg != null)
1327     {
1328       ap.av.getAlignment().addGroup(sg);
1329     }
1330
1331     return sg;
1332   }
1333
1334   void unGroupMenuItem_actionPerformed()
1335   {
1336     SequenceGroup sg = ap.av.getSelectionGroup();
1337     ap.av.getAlignment().deleteGroup(sg);
1338     ap.av.setSelectionGroup(null);
1339     ap.paintAlignment(true, true);
1340   }
1341
1342   void createGroupMenuItem_actionPerformed()
1343   {
1344     getGroup(); // implicitly create group
1345     refresh();
1346   }
1347
1348   public void showColourText_itemStateChanged()
1349   {
1350     getGroup().setColourText(showColourText.getState());
1351     refresh();
1352   }
1353
1354   public void showText_itemStateChanged()
1355   {
1356     getGroup().setDisplayText(showText.getState());
1357     refresh();
1358   }
1359
1360   public void makeReferenceSeq_actionPerformed()
1361   {
1362     if (!ap.av.getAlignment().hasSeqrep())
1363     {
1364       // initialise the display flags so the user sees something happen
1365       ap.av.setDisplayReferenceSeq(true);
1366       ap.av.setColourByReferenceSeq(true);
1367       ap.av.getAlignment().setSeqrep(seq);
1368     }
1369     else
1370     {
1371       if (ap.av.getAlignment().getSeqrep() == seq)
1372       {
1373         ap.av.getAlignment().setSeqrep(null);
1374       }
1375       else
1376       {
1377         ap.av.getAlignment().setSeqrep(seq);
1378       }
1379     }
1380     refresh();
1381   }
1382
1383   public void showNonconserved_itemStateChanged()
1384   {
1385     getGroup().setShowNonconserved(this.displayNonconserved.getState());
1386     refresh();
1387   }
1388
1389   public void showBoxes_itemStateChanged()
1390   {
1391     getGroup().setDisplayBoxes(showBoxes.getState());
1392     refresh();
1393   }
1394
1395   void hideSequences(boolean representGroup)
1396   {
1397     ap.av.hideSequences(seq, representGroup);
1398   }
1399
1400   /**
1401    * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
1402    * "All" is added first, followed by a separator. Then add any annotation
1403    * types associated with the current selection. Separate menus are built for
1404    * the selected sequence group (if any), and the selected sequence.
1405    * <p>
1406    * Some annotation rows are always rendered together - these can be identified
1407    * by a common graphGroup property > -1. Only one of each group will be marked
1408    * as visible (to avoid duplication of the display). For such groups we add a
1409    * composite type name, e.g.
1410    * <p>
1411    * IUPredWS (Long), IUPredWS (Short)
1412    * 
1413    * @param seq
1414    */
1415   protected void buildAnnotationTypesMenus(Menu showMenu, Menu hideMenu,
1416           List<SequenceI> forSequences)
1417   {
1418     showMenu.removeAll();
1419     hideMenu.removeAll();
1420
1421     final List<String> all = Arrays
1422             .asList(new String[]
1423             { MessageManager.getString("label.all") });
1424     addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true,
1425             true);
1426     addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
1427             false);
1428     showMenu.addSeparator();
1429     hideMenu.addSeparator();
1430
1431     final AlignmentAnnotation[] annotations = ap.getAlignment()
1432             .getAlignmentAnnotation();
1433
1434     /*
1435      * Find shown/hidden annotations types, distinguished by source (calcId),
1436      * and grouped by graphGroup. Using LinkedHashMap means we will retrieve in
1437      * the insertion order, which is the order of the annotations on the
1438      * alignment.
1439      */
1440     Map<String, List<List<String>>> shownTypes = new LinkedHashMap<>();
1441     Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<>();
1442     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
1443             AlignmentAnnotationUtils.asList(annotations), forSequences);
1444
1445     for (String calcId : hiddenTypes.keySet())
1446     {
1447       for (List<String> type : hiddenTypes.get(calcId))
1448       {
1449         addAnnotationTypeToShowHide(showMenu, forSequences, calcId, type,
1450                 false, true);
1451       }
1452     }
1453     // grey out 'show annotations' if none are hidden
1454     showMenu.setEnabled(!hiddenTypes.isEmpty());
1455
1456     for (String calcId : shownTypes.keySet())
1457     {
1458       for (List<String> type : shownTypes.get(calcId))
1459       {
1460         addAnnotationTypeToShowHide(hideMenu, forSequences, calcId, type,
1461                 false, false);
1462       }
1463     }
1464     // grey out 'hide annotations' if none are shown
1465     hideMenu.setEnabled(!shownTypes.isEmpty());
1466   }
1467
1468   /**
1469    * Add one annotation type to the 'Show Annotations' or 'Hide Annotations'
1470    * menus.
1471    * 
1472    * @param showOrHideMenu
1473    *          the menu to add to
1474    * @param forSequences
1475    *          the sequences whose annotations may be shown or hidden
1476    * @param calcId
1477    * @param types
1478    *          the label to add
1479    * @param allTypes
1480    *          if true this is a special label meaning 'All'
1481    * @param actionIsShow
1482    *          if true, the select menu item action is to show the annotation
1483    *          type, else hide
1484    */
1485   protected void addAnnotationTypeToShowHide(Menu showOrHideMenu,
1486           final List<SequenceI> forSequences, String calcId,
1487           final List<String> types, final boolean allTypes,
1488           final boolean actionIsShow)
1489   {
1490     String label = types.toString(); // [a, b, c]
1491     label = label.substring(1, label.length() - 1);
1492     final MenuItem item = new MenuItem(label);
1493     item.addActionListener(new java.awt.event.ActionListener()
1494     {
1495       @Override
1496       public void actionPerformed(ActionEvent e)
1497       {
1498         AlignmentUtils.showOrHideSequenceAnnotations(ap.getAlignment(),
1499                 types, forSequences, allTypes, actionIsShow);
1500         refresh();
1501       }
1502     });
1503     showOrHideMenu.add(item);
1504   }
1505
1506 }