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