JAL-1152 avoid NPE on null alignment annotation vector
[jalview.git] / src / jalview / gui / PopupMenu.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.analysis.AAFrequency;
24 import jalview.analysis.AlignmentAnnotationUtils;
25 import jalview.analysis.Conservation;
26 import jalview.commands.ChangeCaseCommand;
27 import jalview.commands.EditCommand;
28 import jalview.datamodel.AlignmentAnnotation;
29 import jalview.datamodel.Annotation;
30 import jalview.datamodel.DBRefEntry;
31 import jalview.datamodel.PDBEntry;
32 import jalview.datamodel.Sequence;
33 import jalview.datamodel.SequenceFeature;
34 import jalview.datamodel.SequenceGroup;
35 import jalview.datamodel.SequenceI;
36 import jalview.io.FormatAdapter;
37 import jalview.io.SequenceAnnotationReport;
38 import jalview.schemes.AnnotationColourGradient;
39 import jalview.schemes.Blosum62ColourScheme;
40 import jalview.schemes.BuriedColourScheme;
41 import jalview.schemes.ClustalxColourScheme;
42 import jalview.schemes.HelixColourScheme;
43 import jalview.schemes.HydrophobicColourScheme;
44 import jalview.schemes.NucleotideColourScheme;
45 import jalview.schemes.PIDColourScheme;
46 import jalview.schemes.PurinePyrimidineColourScheme;
47 import jalview.schemes.ResidueProperties;
48 import jalview.schemes.StrandColourScheme;
49 import jalview.schemes.TaylorColourScheme;
50 import jalview.schemes.TurnColourScheme;
51 import jalview.schemes.UserColourScheme;
52 import jalview.schemes.ZappoColourScheme;
53 import jalview.util.GroupUrlLink;
54 import jalview.util.GroupUrlLink.UrlStringTooLongException;
55 import jalview.util.MessageManager;
56 import jalview.util.UrlLink;
57
58 import java.awt.Color;
59 import java.awt.event.ActionEvent;
60 import java.awt.event.ActionListener;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.Collection;
64 import java.util.Collections;
65 import java.util.Hashtable;
66 import java.util.LinkedHashMap;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.TreeMap;
70 import java.util.Vector;
71
72 import javax.swing.ButtonGroup;
73 import javax.swing.JCheckBoxMenuItem;
74 import javax.swing.JColorChooser;
75 import javax.swing.JMenu;
76 import javax.swing.JMenuItem;
77 import javax.swing.JOptionPane;
78 import javax.swing.JPopupMenu;
79 import javax.swing.JRadioButtonMenuItem;
80
81 /**
82  * DOCUMENT ME!
83  * 
84  * @author $author$
85  * @version $Revision: 1.118 $
86  */
87 public class PopupMenu extends JPopupMenu
88 {
89   private static final String ALL_ANNOTATIONS = "All";
90
91   private static final String COMMA = ",";
92
93   JMenu groupMenu = new JMenu();
94
95   JMenuItem groupName = new JMenuItem();
96
97   protected JRadioButtonMenuItem clustalColour = new JRadioButtonMenuItem();
98
99   protected JRadioButtonMenuItem zappoColour = new JRadioButtonMenuItem();
100
101   protected JRadioButtonMenuItem taylorColour = new JRadioButtonMenuItem();
102
103   protected JRadioButtonMenuItem hydrophobicityColour = new JRadioButtonMenuItem();
104
105   protected JRadioButtonMenuItem helixColour = new JRadioButtonMenuItem();
106
107   protected JRadioButtonMenuItem strandColour = new JRadioButtonMenuItem();
108
109   protected JRadioButtonMenuItem turnColour = new JRadioButtonMenuItem();
110
111   protected JRadioButtonMenuItem buriedColour = new JRadioButtonMenuItem();
112
113   protected JCheckBoxMenuItem abovePIDColour = new JCheckBoxMenuItem();
114
115   protected JRadioButtonMenuItem userDefinedColour = new JRadioButtonMenuItem();
116
117   protected JRadioButtonMenuItem PIDColour = new JRadioButtonMenuItem();
118
119   protected JRadioButtonMenuItem BLOSUM62Colour = new JRadioButtonMenuItem();
120
121   protected JRadioButtonMenuItem purinePyrimidineColour = new JRadioButtonMenuItem();
122
123   protected JRadioButtonMenuItem RNAInteractionColour = new JRadioButtonMenuItem();
124
125   // protected JRadioButtonMenuItem covariationColour = new
126   // JRadioButtonMenuItem();
127
128   JRadioButtonMenuItem noColourmenuItem = new JRadioButtonMenuItem();
129
130   protected JCheckBoxMenuItem conservationMenuItem = new JCheckBoxMenuItem();
131
132   AlignmentPanel ap;
133
134   JMenu sequenceMenu = new JMenu();
135
136   JMenuItem sequenceName = new JMenuItem();
137
138   JMenuItem sequenceDetails = new JMenuItem();
139
140   JMenuItem sequenceSelDetails = new JMenuItem();
141
142   JMenuItem chooseAnnotations = new JMenuItem();
143
144   SequenceI sequence;
145
146   JMenuItem createGroupMenuItem = new JMenuItem();
147
148   JMenuItem unGroupMenuItem = new JMenuItem();
149
150   JMenuItem outline = new JMenuItem();
151
152   JRadioButtonMenuItem nucleotideMenuItem = new JRadioButtonMenuItem();
153
154   JMenu colourMenu = new JMenu();
155
156   JCheckBoxMenuItem showBoxes = new JCheckBoxMenuItem();
157
158   JCheckBoxMenuItem showText = new JCheckBoxMenuItem();
159
160   JCheckBoxMenuItem showColourText = new JCheckBoxMenuItem();
161
162   JCheckBoxMenuItem displayNonconserved = new JCheckBoxMenuItem();
163
164   JMenu editMenu = new JMenu();
165
166   JMenuItem cut = new JMenuItem();
167
168   JMenuItem copy = new JMenuItem();
169
170   JMenuItem upperCase = new JMenuItem();
171
172   JMenuItem lowerCase = new JMenuItem();
173
174   JMenuItem toggle = new JMenuItem();
175
176   JMenu pdbMenu = new JMenu();
177
178   JMenuItem pdbFromFile = new JMenuItem();
179
180   // JBPNote: Commented these out - Should add these services via the web
181   // services menu system.
182   // JMenuItem ContraFold = new JMenuItem();
183
184   // JMenuItem RNAFold = new JMenuItem();
185
186   JMenuItem enterPDB = new JMenuItem();
187
188   JMenuItem discoverPDB = new JMenuItem();
189
190   JMenu outputMenu = new JMenu();
191
192   JMenu seqShowAnnotationsMenu = new JMenu();
193
194   JMenu seqHideAnnotationsMenu = new JMenu();
195
196   JMenuItem seqAddReferenceAnnotations = new JMenuItem();
197
198   JMenu groupShowAnnotationsMenu = new JMenu();
199
200   JMenu groupHideAnnotationsMenu = new JMenu();
201
202   JMenuItem groupAddReferenceAnnotations = new JMenuItem();
203
204   JMenuItem sequenceFeature = new JMenuItem();
205
206   JMenuItem textColour = new JMenuItem();
207
208   JMenu jMenu1 = new JMenu();
209
210   JMenu structureMenu = new JMenu();
211
212   JMenu viewStructureMenu = new JMenu();
213
214   // JMenu colStructureMenu = new JMenu();
215   JMenuItem editSequence = new JMenuItem();
216
217   // JMenuItem annotationMenuItem = new JMenuItem();
218
219   JMenu groupLinksMenu;
220
221   /**
222    * Creates a new PopupMenu object.
223    * 
224    * @param ap
225    *          DOCUMENT ME!
226    * @param seq
227    *          DOCUMENT ME!
228    */
229   public PopupMenu(final AlignmentPanel ap, Sequence seq, Vector links)
230   {
231     this(ap, seq, links, null);
232   }
233
234   /**
235    * 
236    * @param ap
237    * @param seq
238    * @param links
239    * @param groupLinks
240    */
241   public PopupMenu(final AlignmentPanel ap, final SequenceI seq,
242           Vector links, Vector groupLinks)
243   {
244     // /////////////////////////////////////////////////////////
245     // If this is activated from the sequence panel, the user may want to
246     // edit or annotate a particular residue. Therefore display the residue menu
247     //
248     // If from the IDPanel, we must display the sequence menu
249     // ////////////////////////////////////////////////////////
250     this.ap = ap;
251     sequence = seq;
252
253     ButtonGroup colours = new ButtonGroup();
254     colours.add(noColourmenuItem);
255     colours.add(clustalColour);
256     colours.add(zappoColour);
257     colours.add(taylorColour);
258     colours.add(hydrophobicityColour);
259     colours.add(helixColour);
260     colours.add(strandColour);
261     colours.add(turnColour);
262     colours.add(buriedColour);
263     colours.add(abovePIDColour);
264     colours.add(userDefinedColour);
265     colours.add(PIDColour);
266     colours.add(BLOSUM62Colour);
267     colours.add(purinePyrimidineColour);
268     colours.add(RNAInteractionColour);
269     // colours.add(covariationColour);
270
271     for (int i = 0; i < jalview.io.FormatAdapter.WRITEABLE_FORMATS.length; i++)
272     {
273       JMenuItem item = new JMenuItem(
274               jalview.io.FormatAdapter.WRITEABLE_FORMATS[i]);
275
276       item.addActionListener(new java.awt.event.ActionListener()
277       {
278         @Override
279         public void actionPerformed(ActionEvent e)
280         {
281           outputText_actionPerformed(e);
282         }
283       });
284
285       outputMenu.add(item);
286     }
287
288     /*
289      * Build menus for annotation types that may be shown or hidden, and for
290      * 'reference annotations' that may be added to the alignment. First for the
291      * currently selected sequence (if there is one):
292      */
293     final List<SequenceI> selectedSequence = (seq == null ? Collections
294             .<SequenceI> emptyList() : Arrays.asList(seq));
295     buildAnnotationTypesMenus(seqShowAnnotationsMenu,
296             seqHideAnnotationsMenu, selectedSequence);
297     configureReferenceAnnotationsMenu(seqAddReferenceAnnotations,
298             selectedSequence);
299
300     /*
301      * And repeat for the current selection group (if there is one):
302      */
303     final List<SequenceI> selectedGroup = (ap.av.getSelectionGroup() == null ? Collections
304             .<SequenceI> emptyList() : ap.av.getSelectionGroup()
305             .getSequences());
306     buildAnnotationTypesMenus(groupShowAnnotationsMenu,
307             groupHideAnnotationsMenu, selectedGroup);
308     configureReferenceAnnotationsMenu(groupAddReferenceAnnotations,
309             selectedGroup);
310
311     try
312     {
313       jbInit();
314     } catch (Exception e)
315     {
316       e.printStackTrace();
317     }
318
319     JMenuItem menuItem;
320     if (seq != null)
321     {
322       sequenceMenu.setText(sequence.getName());
323
324       if (seq.getDatasetSequence().getPDBId() != null
325               && seq.getDatasetSequence().getPDBId().size() > 0)
326       {
327         java.util.Enumeration e = seq.getDatasetSequence().getPDBId()
328                 .elements();
329
330         while (e.hasMoreElements())
331         {
332           final PDBEntry pdb = (PDBEntry) e.nextElement();
333
334           menuItem = new JMenuItem();
335           menuItem.setText(pdb.getId());
336           menuItem.addActionListener(new ActionListener()
337           {
338             @Override
339             public void actionPerformed(ActionEvent e)
340             {
341               // TODO re JAL-860: optionally open dialog or provide a menu entry
342               // allowing user to open just one structure per sequence
343               // new AppJmol(pdb, ap.av.collateForPDB(new PDBEntry[]
344               // { pdb })[0], null, ap);
345               new StructureViewer(ap.getStructureSelectionManager())
346                       .viewStructures(pdb,
347                               ap.av.collateForPDB(new PDBEntry[]
348                               { pdb })[0], null, ap);
349             }
350           });
351           viewStructureMenu.add(menuItem);
352
353           /*
354            * menuItem = new JMenuItem(); menuItem.setText(pdb.getId());
355            * menuItem.addActionListener(new java.awt.event.ActionListener() {
356            * public void actionPerformed(ActionEvent e) {
357            * colourByStructure(pdb.getId()); } });
358            * colStructureMenu.add(menuItem);
359            */
360         }
361       }
362       else
363       {
364         if (ap.av.getAlignment().isNucleotide() == false)
365         {
366           structureMenu.remove(viewStructureMenu);
367         }
368         // structureMenu.remove(colStructureMenu);
369       }
370
371       if (ap.av.getAlignment().isNucleotide() == true)
372       {
373         AlignmentAnnotation[] aa = ap.av.getAlignment()
374                 .getAlignmentAnnotation();
375         for (int i = 0; aa != null && i < aa.length; i++)
376         {
377           if (aa[i].isValidStruc() && aa[i].sequenceRef == null)
378           {
379             final String rnastruc = aa[i].getRNAStruc();
380             final String structureLine = aa[i].label + " (alignment)";
381             menuItem = new JMenuItem();
382             menuItem.setText(MessageManager.formatMessage(
383                     "label.2d_rna_structure_line", new String[]
384                     { structureLine }));
385             menuItem.addActionListener(new java.awt.event.ActionListener()
386             {
387               @Override
388               public void actionPerformed(ActionEvent e)
389               {
390                 // System.out.println("1:"+structureLine);
391                 System.out.println("1:sname" + seq.getName());
392                 System.out.println("2:seq" + seq);
393
394                 // System.out.println("3:"+seq.getSequenceAsString());
395                 System.out.println("3:strucseq" + rnastruc);
396                 // System.out.println("4:struc"+seq.getRNA());
397                 System.out.println("5:name" + seq.getName());
398                 System.out.println("6:ap" + ap);
399                 new AppVarna(structureLine, seq, seq.getSequenceAsString(),
400                         rnastruc, seq.getName(), ap);
401                 // new AppVarna(seq.getName(),seq,rnastruc,seq.getRNA(),
402                 // seq.getName(), ap);
403                 System.out.println("end");
404               }
405             });
406             viewStructureMenu.add(menuItem);
407           }
408         }
409
410         // SequenceFeatures[] test = seq.getSequenceFeatures();
411
412         if (seq.getAnnotation() != null)
413         {
414           AlignmentAnnotation seqAnno[] = seq.getAnnotation();
415           for (int i = 0; i < seqAnno.length; i++)
416           {
417             if (seqAnno[i].isValidStruc())
418             {
419               final String rnastruc = seqAnno[i].getRNAStruc();
420
421               // TODO: make rnastrucF a bit more nice
422               menuItem = new JMenuItem();
423               menuItem.setText(MessageManager.formatMessage(
424                       "label.2d_rna_sequence_name", new String[]
425                       { seq.getName() }));
426               menuItem.addActionListener(new java.awt.event.ActionListener()
427               {
428                 @Override
429                 public void actionPerformed(ActionEvent e)
430                 {
431                   // TODO: VARNA does'nt print gaps in the sequence
432
433                   new AppVarna(seq.getName() + " structure", seq, seq
434                           .getSequenceAsString(), rnastruc, seq.getName(),
435                           ap);
436                 }
437               });
438               viewStructureMenu.add(menuItem);
439             }
440           }
441         }
442
443       }
444
445       menuItem = new JMenuItem(
446               MessageManager.getString("action.hide_sequences"));
447       menuItem.addActionListener(new java.awt.event.ActionListener()
448       {
449         @Override
450         public void actionPerformed(ActionEvent e)
451         {
452           hideSequences(false);
453         }
454       });
455       add(menuItem);
456
457       if (ap.av.getSelectionGroup() != null
458               && ap.av.getSelectionGroup().getSize() > 1)
459       {
460         menuItem = new JMenuItem(MessageManager.formatMessage(
461                 "label.represent_group_with", new String[]
462                 { seq.getName() }));
463         menuItem.addActionListener(new java.awt.event.ActionListener()
464         {
465           @Override
466           public void actionPerformed(ActionEvent e)
467           {
468             hideSequences(true);
469           }
470         });
471         sequenceMenu.add(menuItem);
472       }
473
474       if (ap.av.hasHiddenRows())
475       {
476         final int index = ap.av.getAlignment().findIndex(seq);
477
478         if (ap.av.adjustForHiddenSeqs(index)
479                 - ap.av.adjustForHiddenSeqs(index - 1) > 1)
480         {
481           menuItem = new JMenuItem(
482                   MessageManager.getString("action.reveal_sequences"));
483           menuItem.addActionListener(new ActionListener()
484           {
485             @Override
486             public void actionPerformed(ActionEvent e)
487             {
488               ap.av.showSequence(index);
489               if (ap.overviewPanel != null)
490               {
491                 ap.overviewPanel.updateOverviewImage();
492               }
493             }
494           });
495           add(menuItem);
496         }
497       }
498     }
499     // for the case when no sequences are even visible
500     if (ap.av.hasHiddenRows())
501     {
502       {
503         menuItem = new JMenuItem(
504                 MessageManager.getString("action.reveal_all"));
505         menuItem.addActionListener(new ActionListener()
506         {
507           @Override
508           public void actionPerformed(ActionEvent e)
509           {
510             ap.av.showAllHiddenSeqs();
511             if (ap.overviewPanel != null)
512             {
513               ap.overviewPanel.updateOverviewImage();
514             }
515           }
516         });
517
518         add(menuItem);
519       }
520
521     }
522
523     SequenceGroup sg = ap.av.getSelectionGroup();
524     boolean isDefinedGroup = (sg != null) ? ap.av.getAlignment()
525             .getGroups().contains(sg) : false;
526
527     if (sg != null && sg.getSize() > 0)
528     {
529       groupName.setText(MessageManager.formatMessage("label.name_param",
530               new String[]
531               { sg.getName() }));
532       groupName.setText(MessageManager
533               .getString("label.edit_name_and_description_current_group"));
534
535       if (sg.cs instanceof ZappoColourScheme)
536       {
537         zappoColour.setSelected(true);
538       }
539       else if (sg.cs instanceof TaylorColourScheme)
540       {
541         taylorColour.setSelected(true);
542       }
543       else if (sg.cs instanceof PIDColourScheme)
544       {
545         PIDColour.setSelected(true);
546       }
547       else if (sg.cs instanceof Blosum62ColourScheme)
548       {
549         BLOSUM62Colour.setSelected(true);
550       }
551       else if (sg.cs instanceof UserColourScheme)
552       {
553         userDefinedColour.setSelected(true);
554       }
555       else if (sg.cs instanceof HydrophobicColourScheme)
556       {
557         hydrophobicityColour.setSelected(true);
558       }
559       else if (sg.cs instanceof HelixColourScheme)
560       {
561         helixColour.setSelected(true);
562       }
563       else if (sg.cs instanceof StrandColourScheme)
564       {
565         strandColour.setSelected(true);
566       }
567       else if (sg.cs instanceof TurnColourScheme)
568       {
569         turnColour.setSelected(true);
570       }
571       else if (sg.cs instanceof BuriedColourScheme)
572       {
573         buriedColour.setSelected(true);
574       }
575       else if (sg.cs instanceof ClustalxColourScheme)
576       {
577         clustalColour.setSelected(true);
578       }
579       else if (sg.cs instanceof PurinePyrimidineColourScheme)
580       {
581         purinePyrimidineColour.setSelected(true);
582       }
583
584       /*
585        * else if (sg.cs instanceof CovariationColourScheme) {
586        * covariationColour.setSelected(true); }
587        */
588       else
589       {
590         noColourmenuItem.setSelected(true);
591       }
592
593       if (sg.cs != null && sg.cs.conservationApplied())
594       {
595         conservationMenuItem.setSelected(true);
596       }
597       displayNonconserved.setSelected(sg.getShowNonconserved());
598       showText.setSelected(sg.getDisplayText());
599       showColourText.setSelected(sg.getColourText());
600       showBoxes.setSelected(sg.getDisplayBoxes());
601       // add any groupURLs to the groupURL submenu and make it visible
602       if (groupLinks != null && groupLinks.size() > 0)
603       {
604         buildGroupURLMenu(sg, groupLinks);
605       }
606       // Add a 'show all structures' for the current selection
607       Hashtable<String, PDBEntry> pdbe = new Hashtable<String, PDBEntry>(), reppdb = new Hashtable<String, PDBEntry>();
608       SequenceI sqass = null;
609       for (SequenceI sq : ap.av.getSequenceSelection())
610       {
611         Vector<PDBEntry> pes = sq.getDatasetSequence().getPDBId();
612         if (pes != null && pes.size() > 0)
613         {
614           reppdb.put(pes.get(0).getId(), pes.get(0));
615           for (PDBEntry pe : pes)
616           {
617             pdbe.put(pe.getId(), pe);
618             if (sqass == null)
619             {
620               sqass = sq;
621             }
622           }
623         }
624       }
625       if (pdbe.size() > 0)
626       {
627         final PDBEntry[] pe = pdbe.values().toArray(
628                 new PDBEntry[pdbe.size()]), pr = reppdb.values().toArray(
629                 new PDBEntry[reppdb.size()]);
630         final JMenuItem gpdbview, rpdbview;
631         if (pdbe.size() == 1)
632         {
633           structureMenu.add(gpdbview = new JMenuItem(MessageManager
634                   .formatMessage("label.view_structure_for", new String[]
635                   { sqass.getDisplayId(false) })));
636         }
637         else
638         {
639           structureMenu.add(gpdbview = new JMenuItem(MessageManager
640                   .formatMessage("label.view_all_structures", new String[]
641                   { new Integer(pdbe.size()).toString() })));
642         }
643         gpdbview.setToolTipText(MessageManager
644                 .getString("label.open_new_jmol_view_with_all_structures_associated_current_selection_superimpose_using_alignment"));
645         gpdbview.addActionListener(new ActionListener()
646         {
647
648           @Override
649           public void actionPerformed(ActionEvent e)
650           {
651             new StructureViewer(ap.getStructureSelectionManager())
652                     .viewStructures(ap, pe, ap.av.collateForPDB(pe));
653           }
654         });
655         if (reppdb.size() > 1 && reppdb.size() < pdbe.size())
656         {
657           structureMenu.add(rpdbview = new JMenuItem(MessageManager
658                   .formatMessage(
659                           "label.view_all_representative_structures",
660                           new String[]
661                           { new Integer(reppdb.size()).toString() })));
662           rpdbview.setToolTipText(MessageManager
663                   .getString("label.open_new_jmol_view_with_all_representative_structures_associated_current_selection_superimpose_using_alignment"));
664           rpdbview.addActionListener(new ActionListener()
665           {
666
667             @Override
668             public void actionPerformed(ActionEvent e)
669             {
670               new StructureViewer(ap.getStructureSelectionManager())
671                       .viewStructures(ap, pr, ap.av.collateForPDB(pr));
672             }
673           });
674         }
675       }
676     }
677     else
678     {
679       groupMenu.setVisible(false);
680       editMenu.setVisible(false);
681     }
682
683     if (!isDefinedGroup)
684     {
685       createGroupMenuItem.setVisible(true);
686       unGroupMenuItem.setVisible(false);
687       jMenu1.setText(MessageManager.getString("action.edit_new_group"));
688     }
689     else
690     {
691       createGroupMenuItem.setVisible(false);
692       unGroupMenuItem.setVisible(true);
693       jMenu1.setText(MessageManager.getString("action.edit_group"));
694     }
695
696     if (seq == null)
697     {
698       sequenceMenu.setVisible(false);
699       structureMenu.setVisible(false);
700     }
701
702     if (links != null && links.size() > 0)
703     {
704
705       JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
706       Vector linkset = new Vector();
707       for (int i = 0; i < links.size(); i++)
708       {
709         String link = links.elementAt(i).toString();
710         UrlLink urlLink = null;
711         try
712         {
713           urlLink = new UrlLink(link);
714         } catch (Exception foo)
715         {
716           jalview.bin.Cache.log.error("Exception for URLLink '" + link
717                   + "'", foo);
718           continue;
719         }
720         ;
721         if (!urlLink.isValid())
722         {
723           jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
724           continue;
725         }
726         final String label = urlLink.getLabel();
727         if (seq != null && urlLink.isDynamic())
728         {
729
730           // collect matching db-refs
731           DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(
732                   seq.getDBRef(), new String[]
733                   { urlLink.getTarget() });
734           // collect id string too
735           String id = seq.getName();
736           String descr = seq.getDescription();
737           if (descr != null && descr.length() < 1)
738           {
739             descr = null;
740           }
741
742           if (dbr != null)
743           {
744             for (int r = 0; r < dbr.length; r++)
745             {
746               if (id != null && dbr[r].getAccessionId().equals(id))
747               {
748                 // suppress duplicate link creation for the bare sequence ID
749                 // string with this link
750                 id = null;
751               }
752               // create Bare ID link for this RUL
753               String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(),
754                       true);
755               if (urls != null)
756               {
757                 for (int u = 0; u < urls.length; u += 2)
758                 {
759                   if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
760                   {
761                     linkset.addElement(urls[u] + "|" + urls[u + 1]);
762                     addshowLink(linkMenu, label + "|" + urls[u],
763                             urls[u + 1]);
764                   }
765                 }
766               }
767             }
768           }
769           if (id != null)
770           {
771             // create Bare ID link for this RUL
772             String[] urls = urlLink.makeUrls(id, true);
773             if (urls != null)
774             {
775               for (int u = 0; u < urls.length; u += 2)
776               {
777                 if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
778                 {
779                   linkset.addElement(urls[u] + "|" + urls[u + 1]);
780                   addshowLink(linkMenu, label, urls[u + 1]);
781                 }
782               }
783             }
784           }
785           // Create urls from description but only for URL links which are regex
786           // links
787           if (descr != null && urlLink.getRegexReplace() != null)
788           {
789             // create link for this URL from description where regex matches
790             String[] urls = urlLink.makeUrls(descr, true);
791             if (urls != null)
792             {
793               for (int u = 0; u < urls.length; u += 2)
794               {
795                 if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
796                 {
797                   linkset.addElement(urls[u] + "|" + urls[u + 1]);
798                   addshowLink(linkMenu, label, urls[u + 1]);
799                 }
800               }
801             }
802           }
803         }
804         else
805         {
806           if (!linkset.contains(label + "|" + urlLink.getUrl_prefix()))
807           {
808             linkset.addElement(label + "|" + urlLink.getUrl_prefix());
809             // Add a non-dynamic link
810             addshowLink(linkMenu, label, urlLink.getUrl_prefix());
811           }
812         }
813       }
814       if (sequence != null)
815       {
816         sequenceMenu.add(linkMenu);
817       }
818       else
819       {
820         add(linkMenu);
821       }
822     }
823   }
824
825   /**
826    * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
827    * "All" is added first, followed by a separator. Then add any annotation
828    * types associated with the current selection. Separate menus are built for
829    * the selected sequence group (if any), and the selected sequence.
830    * <p>
831    * Some annotation rows are always rendered together - these can be identified
832    * by a common graphGroup property > -1. Only one of each group will be marked
833    * as visible (to avoid duplication of the display). For such groups we add a
834    * composite type name, e.g.
835    * <p>
836    * IUPredWS (Long), IUPredWS (Short)
837    * 
838    * @param seq
839    */
840   protected void buildAnnotationTypesMenus(JMenu showMenu, JMenu hideMenu,
841           List<SequenceI> forSequences)
842   {
843     showMenu.removeAll();
844     hideMenu.removeAll();
845
846     final List<String> all = Arrays.asList(ALL_ANNOTATIONS);
847     addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, true);
848     addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
849             false);
850     showMenu.addSeparator();
851     hideMenu.addSeparator();
852
853     final AlignmentAnnotation[] annotations = ap.getAlignment()
854             .getAlignmentAnnotation();
855
856     /*
857      * Find shown/hidden annotations types, distinguished by source (calcId),
858      * and grouped by graphGroup. Using LinkedHashMap means we will retrieve in
859      * the insertion order, which is the order of the annotations on the
860      * alignment.
861      */
862     Map<String, List<List<String>>> shownTypes = new LinkedHashMap<String, List<List<String>>>();
863     Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<String, List<List<String>>>();
864     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes,
865             hiddenTypes,
866             AlignmentAnnotationUtils.asList(annotations),
867             forSequences);
868
869     for (String calcId : hiddenTypes.keySet())
870     {
871       for (List<String> type : hiddenTypes.get(calcId))
872       {
873         addAnnotationTypeToShowHide(showMenu, forSequences,
874                 calcId, type, false, true);
875       }
876     }
877     // grey out 'show annotations' if none are hidden
878     showMenu.setEnabled(!hiddenTypes.isEmpty());
879
880     for (String calcId : shownTypes.keySet())
881     {
882       for (List<String> type : shownTypes.get(calcId))
883       {
884         addAnnotationTypeToShowHide(hideMenu, forSequences,
885                 calcId, type, false, false);
886       }
887     }
888     // grey out 'hide annotations' if none are shown
889     hideMenu.setEnabled(!shownTypes.isEmpty());
890   }
891
892   /**
893    * Returns a list of sequences - either the current selection group (if there
894    * is one), else the specified single sequence.
895    * 
896    * @param seq
897    * @return
898    */
899   protected List<SequenceI> getSequenceScope(SequenceI seq)
900   {
901     List<SequenceI> forSequences = null;
902     final SequenceGroup selectionGroup = ap.av.getSelectionGroup();
903     if (selectionGroup != null && selectionGroup.getSize() > 0)
904     {
905       forSequences = selectionGroup.getSequences();
906     }
907     else
908     {
909       forSequences = seq == null ? Collections.<SequenceI> emptyList()
910               : Arrays.asList(seq);
911     }
912     return forSequences;
913   }
914
915   /**
916    * Add one annotation type to the 'Show Annotations' or 'Hide Annotations'
917    * menus.
918    * 
919    * @param showOrHideMenu
920    *          the menu to add to
921    * @param forSequences
922    *          the sequences whose annotations may be shown or hidden
923    * @param calcId
924    * @param types
925    *          the label to add
926    * @param allTypes
927    *          if true this is a special label meaning 'All'
928    * @param actionIsShow
929    *          if true, the select menu item action is to show the annotation
930    *          type, else hide
931    */
932   protected void addAnnotationTypeToShowHide(JMenu showOrHideMenu,
933           final List<SequenceI> forSequences, String calcId,
934           final List<String> types, final boolean allTypes,
935           final boolean actionIsShow)
936   {
937     String label = types.toString(); // [a, b, c]
938     label = label.substring(1, label.length() - 1);
939     final JMenuItem item = new JMenuItem(label);
940     item.setToolTipText(calcId);
941     item.addActionListener(new java.awt.event.ActionListener()
942     {
943       @Override
944       public void actionPerformed(ActionEvent e)
945       {
946         showHideAnnotation_actionPerformed(types, forSequences, allTypes,
947                 actionIsShow);
948       }
949     });
950     showOrHideMenu.add(item);
951   }
952
953   /**
954    * Action on selecting a list of annotation type (or the 'all types' values)
955    * to show or hide for the specified sequences.
956    * 
957    * @param types
958    * @param forSequences
959    * @param anyType
960    * @param doShow
961    */
962   protected void showHideAnnotation_actionPerformed(
963           Collection<String> types, List<SequenceI> forSequences,
964           boolean anyType, boolean doShow)
965   {
966     for (AlignmentAnnotation aa : ap.getAlignment()
967             .getAlignmentAnnotation())
968     {
969       if (anyType || types.contains(aa.label))
970       {
971         if ((aa.sequenceRef != null)
972                 && forSequences.contains(aa.sequenceRef))
973         {
974           aa.visible = doShow;
975         }
976       }
977     }
978     refresh();
979   }
980
981   private void buildGroupURLMenu(SequenceGroup sg, Vector groupLinks)
982   {
983
984     // TODO: usability: thread off the generation of group url content so root
985     // menu appears asap
986     // sequence only URLs
987     // ID/regex match URLs
988     groupLinksMenu = new JMenu(
989             MessageManager.getString("action.group_link"));
990     JMenu[] linkMenus = new JMenu[]
991     { null, new JMenu(MessageManager.getString("action.ids")),
992         new JMenu(MessageManager.getString("action.sequences")),
993         new JMenu(MessageManager.getString("action.ids_sequences")) }; // three
994                                                                        // types
995                                                                        // of url
996                                                                        // that
997                                                                        // might
998                                                                        // be
999     // created.
1000     SequenceI[] seqs = ap.av.getSelectionAsNewSequence();
1001     String[][] idandseqs = GroupUrlLink.formStrings(seqs);
1002     Hashtable commonDbrefs = new Hashtable();
1003     for (int sq = 0; sq < seqs.length; sq++)
1004     {
1005
1006       int start = seqs[sq].findPosition(sg.getStartRes()), end = seqs[sq]
1007               .findPosition(sg.getEndRes());
1008       // just collect ids from dataset sequence
1009       // TODO: check if IDs collected from selecton group intersects with the
1010       // current selection, too
1011       SequenceI sqi = seqs[sq];
1012       while (sqi.getDatasetSequence() != null)
1013       {
1014         sqi = sqi.getDatasetSequence();
1015       }
1016       DBRefEntry[] dbr = sqi.getDBRef();
1017       if (dbr != null && dbr.length > 0)
1018       {
1019         for (int d = 0; d < dbr.length; d++)
1020         {
1021           String src = dbr[d].getSource(); // jalview.util.DBRefUtils.getCanonicalName(dbr[d].getSource()).toUpperCase();
1022           Object[] sarray = (Object[]) commonDbrefs.get(src);
1023           if (sarray == null)
1024           {
1025             sarray = new Object[2];
1026             sarray[0] = new int[]
1027             { 0 };
1028             sarray[1] = new String[seqs.length];
1029
1030             commonDbrefs.put(src, sarray);
1031           }
1032
1033           if (((String[]) sarray[1])[sq] == null)
1034           {
1035             if (!dbr[d].hasMap()
1036                     || (dbr[d].getMap().locateMappedRange(start, end) != null))
1037             {
1038               ((String[]) sarray[1])[sq] = dbr[d].getAccessionId();
1039               ((int[]) sarray[0])[0]++;
1040             }
1041           }
1042         }
1043       }
1044     }
1045     // now create group links for all distinct ID/sequence sets.
1046     boolean addMenu = false; // indicates if there are any group links to give
1047                              // to user
1048     for (int i = 0; i < groupLinks.size(); i++)
1049     {
1050       String link = groupLinks.elementAt(i).toString();
1051       GroupUrlLink urlLink = null;
1052       try
1053       {
1054         urlLink = new GroupUrlLink(link);
1055       } catch (Exception foo)
1056       {
1057         jalview.bin.Cache.log.error("Exception for GroupURLLink '" + link
1058                 + "'", foo);
1059         continue;
1060       }
1061       ;
1062       if (!urlLink.isValid())
1063       {
1064         jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
1065         continue;
1066       }
1067       final String label = urlLink.getLabel();
1068       boolean usingNames = false;
1069       // Now see which parts of the group apply for this URL
1070       String ltarget = urlLink.getTarget(); // jalview.util.DBRefUtils.getCanonicalName(urlLink.getTarget());
1071       Object[] idset = (Object[]) commonDbrefs.get(ltarget.toUpperCase());
1072       String[] seqstr, ids; // input to makeUrl
1073       if (idset != null)
1074       {
1075         int numinput = ((int[]) idset[0])[0];
1076         String[] allids = ((String[]) idset[1]);
1077         seqstr = new String[numinput];
1078         ids = new String[numinput];
1079         for (int sq = 0, idcount = 0; sq < seqs.length; sq++)
1080         {
1081           if (allids[sq] != null)
1082           {
1083             ids[idcount] = allids[sq];
1084             seqstr[idcount++] = idandseqs[1][sq];
1085           }
1086         }
1087       }
1088       else
1089       {
1090         // just use the id/seq set
1091         seqstr = idandseqs[1];
1092         ids = idandseqs[0];
1093         usingNames = true;
1094       }
1095       // and try and make the groupURL!
1096
1097       Object[] urlset = null;
1098       try
1099       {
1100         urlset = urlLink.makeUrlStubs(ids, seqstr,
1101                 "FromJalview" + System.currentTimeMillis(), false);
1102       } catch (UrlStringTooLongException e)
1103       {
1104       }
1105       if (urlset != null)
1106       {
1107         int type = urlLink.getGroupURLType() & 3;
1108         // System.out.println(urlLink.getGroupURLType()
1109         // +" "+((String[])urlset[3])[0]);
1110         // first two bits ofurlLink type bitfield are sequenceids and sequences
1111         // TODO: FUTURE: ensure the groupURL menu structure can be generalised
1112         addshowLink(linkMenus[type], label
1113                 + (((type & 1) == 1) ? ("("
1114                         + (usingNames ? "Names" : ltarget) + ")") : ""),
1115                 urlLink, urlset);
1116         addMenu = true;
1117       }
1118     }
1119     if (addMenu)
1120     {
1121       groupLinksMenu = new JMenu(
1122               MessageManager.getString("action.group_link"));
1123       for (int m = 0; m < linkMenus.length; m++)
1124       {
1125         if (linkMenus[m] != null
1126                 && linkMenus[m].getMenuComponentCount() > 0)
1127         {
1128           groupLinksMenu.add(linkMenus[m]);
1129         }
1130       }
1131
1132       groupMenu.add(groupLinksMenu);
1133     }
1134   }
1135
1136   /**
1137    * add a show URL menu item to the given linkMenu
1138    * 
1139    * @param linkMenu
1140    * @param label
1141    *          - menu label string
1142    * @param url
1143    *          - url to open
1144    */
1145   private void addshowLink(JMenu linkMenu, String label, final String url)
1146   {
1147     JMenuItem item = new JMenuItem(label);
1148     item.setToolTipText(MessageManager.formatMessage(
1149             "label.open_url_param", new String[]
1150             { url }));
1151     item.addActionListener(new java.awt.event.ActionListener()
1152     {
1153       @Override
1154       public void actionPerformed(ActionEvent e)
1155       {
1156         new Thread(new Runnable()
1157         {
1158
1159           @Override
1160           public void run()
1161           {
1162             showLink(url);
1163           }
1164
1165         }).start();
1166       }
1167     });
1168
1169     linkMenu.add(item);
1170   }
1171
1172   /**
1173    * add a late bound groupURL item to the given linkMenu
1174    * 
1175    * @param linkMenu
1176    * @param label
1177    *          - menu label string
1178    * @param urlgenerator
1179    *          GroupURLLink used to generate URL
1180    * @param urlstub
1181    *          Object array returned from the makeUrlStubs function.
1182    */
1183   private void addshowLink(JMenu linkMenu, String label,
1184           final GroupUrlLink urlgenerator, final Object[] urlstub)
1185   {
1186     JMenuItem item = new JMenuItem(label);
1187     item.setToolTipText(MessageManager.formatMessage(
1188             "label.open_url_seqs_param",
1189             new Object[]
1190             { urlgenerator.getUrl_prefix(),
1191                 urlgenerator.getNumberInvolved(urlstub) }));
1192     // TODO: put in info about what is being sent.
1193     item.addActionListener(new java.awt.event.ActionListener()
1194     {
1195       @Override
1196       public void actionPerformed(ActionEvent e)
1197       {
1198         new Thread(new Runnable()
1199         {
1200
1201           @Override
1202           public void run()
1203           {
1204             try
1205             {
1206               showLink(urlgenerator.constructFrom(urlstub));
1207             } catch (UrlStringTooLongException e)
1208             {
1209             }
1210           }
1211
1212         }).start();
1213       }
1214     });
1215
1216     linkMenu.add(item);
1217   }
1218
1219   /**
1220    * DOCUMENT ME!
1221    * 
1222    * @throws Exception
1223    *           DOCUMENT ME!
1224    */
1225   private void jbInit() throws Exception
1226   {
1227     groupMenu.setText(MessageManager.getString("label.group"));
1228     groupMenu.setText(MessageManager.getString("label.selection"));
1229     groupName.setText(MessageManager.getString("label.name"));
1230     groupName.addActionListener(new java.awt.event.ActionListener()
1231     {
1232       @Override
1233       public void actionPerformed(ActionEvent e)
1234       {
1235         groupName_actionPerformed();
1236       }
1237     });
1238     sequenceMenu.setText(MessageManager.getString("label.sequence"));
1239     sequenceName.setText(MessageManager
1240             .getString("label.edit_name_description"));
1241     sequenceName.addActionListener(new java.awt.event.ActionListener()
1242     {
1243       @Override
1244       public void actionPerformed(ActionEvent e)
1245       {
1246         sequenceName_actionPerformed();
1247       }
1248     });
1249     chooseAnnotations.setText(MessageManager
1250             .getString("label.choose_annotations") + "...");
1251     chooseAnnotations.addActionListener(new java.awt.event.ActionListener()
1252     {
1253       @Override
1254       public void actionPerformed(ActionEvent e)
1255       {
1256         chooseAnnotations_actionPerformed(e);
1257       }
1258     });
1259     sequenceDetails.setText(MessageManager
1260             .getString("label.sequence_details") + "...");
1261     sequenceDetails.addActionListener(new java.awt.event.ActionListener()
1262     {
1263       @Override
1264       public void actionPerformed(ActionEvent e)
1265       {
1266         sequenceDetails_actionPerformed();
1267       }
1268     });
1269     sequenceSelDetails.setText(MessageManager
1270             .getString("label.sequence_details") + "...");
1271     sequenceSelDetails
1272             .addActionListener(new java.awt.event.ActionListener()
1273             {
1274               @Override
1275               public void actionPerformed(ActionEvent e)
1276               {
1277                 sequenceSelectionDetails_actionPerformed();
1278               }
1279             });
1280     PIDColour.setFocusPainted(false);
1281     unGroupMenuItem
1282             .setText(MessageManager.getString("action.remove_group"));
1283     unGroupMenuItem.addActionListener(new java.awt.event.ActionListener()
1284     {
1285       @Override
1286       public void actionPerformed(ActionEvent e)
1287       {
1288         unGroupMenuItem_actionPerformed();
1289       }
1290     });
1291     createGroupMenuItem.setText(MessageManager
1292             .getString("action.create_group"));
1293     createGroupMenuItem
1294             .addActionListener(new java.awt.event.ActionListener()
1295             {
1296               @Override
1297               public void actionPerformed(ActionEvent e)
1298               {
1299                 createGroupMenuItem_actionPerformed();
1300               }
1301             });
1302
1303     outline.setText(MessageManager.getString("action.border_colour"));
1304     outline.addActionListener(new java.awt.event.ActionListener()
1305     {
1306       @Override
1307       public void actionPerformed(ActionEvent e)
1308       {
1309         outline_actionPerformed();
1310       }
1311     });
1312     nucleotideMenuItem
1313             .setText(MessageManager.getString("label.nucleotide"));
1314     nucleotideMenuItem.addActionListener(new ActionListener()
1315     {
1316       @Override
1317       public void actionPerformed(ActionEvent e)
1318       {
1319         nucleotideMenuItem_actionPerformed();
1320       }
1321     });
1322     colourMenu.setText(MessageManager.getString("label.group_colour"));
1323     showBoxes.setText(MessageManager.getString("action.boxes"));
1324     showBoxes.setState(true);
1325     showBoxes.addActionListener(new ActionListener()
1326     {
1327       @Override
1328       public void actionPerformed(ActionEvent e)
1329       {
1330         showBoxes_actionPerformed();
1331       }
1332     });
1333     showText.setText(MessageManager.getString("action.text"));
1334     showText.setState(true);
1335     showText.addActionListener(new ActionListener()
1336     {
1337       @Override
1338       public void actionPerformed(ActionEvent e)
1339       {
1340         showText_actionPerformed();
1341       }
1342     });
1343     showColourText.setText(MessageManager.getString("label.colour_text"));
1344     showColourText.addActionListener(new ActionListener()
1345     {
1346       @Override
1347       public void actionPerformed(ActionEvent e)
1348       {
1349         showColourText_actionPerformed();
1350       }
1351     });
1352     displayNonconserved.setText(MessageManager
1353             .getString("label.show_non_conversed"));
1354     displayNonconserved.setState(true);
1355     displayNonconserved.addActionListener(new ActionListener()
1356     {
1357       @Override
1358       public void actionPerformed(ActionEvent e)
1359       {
1360         showNonconserved_actionPerformed();
1361       }
1362     });
1363     editMenu.setText(MessageManager.getString("action.edit"));
1364     cut.setText(MessageManager.getString("action.cut"));
1365     cut.addActionListener(new ActionListener()
1366     {
1367       @Override
1368       public void actionPerformed(ActionEvent e)
1369       {
1370         cut_actionPerformed();
1371       }
1372     });
1373     upperCase.setText(MessageManager.getString("label.to_upper_case"));
1374     upperCase.addActionListener(new ActionListener()
1375     {
1376       @Override
1377       public void actionPerformed(ActionEvent e)
1378       {
1379         changeCase(e);
1380       }
1381     });
1382     copy.setText(MessageManager.getString("action.copy"));
1383     copy.addActionListener(new ActionListener()
1384     {
1385       @Override
1386       public void actionPerformed(ActionEvent e)
1387       {
1388         copy_actionPerformed();
1389       }
1390     });
1391     lowerCase.setText(MessageManager.getString("label.to_lower_case"));
1392     lowerCase.addActionListener(new ActionListener()
1393     {
1394       @Override
1395       public void actionPerformed(ActionEvent e)
1396       {
1397         changeCase(e);
1398       }
1399     });
1400     toggle.setText(MessageManager.getString("label.toggle_case"));
1401     toggle.addActionListener(new ActionListener()
1402     {
1403       @Override
1404       public void actionPerformed(ActionEvent e)
1405       {
1406         changeCase(e);
1407       }
1408     });
1409     pdbMenu.setText(MessageManager
1410             .getString("label.associate_structure_with_sequence"));
1411     pdbFromFile.setText(MessageManager.getString("label.from_file"));
1412     pdbFromFile.addActionListener(new ActionListener()
1413     {
1414       @Override
1415       public void actionPerformed(ActionEvent e)
1416       {
1417         pdbFromFile_actionPerformed();
1418       }
1419     });
1420     // RNAFold.setText("From RNA Fold with predict2D");
1421     // RNAFold.addActionListener(new ActionListener()
1422     // {
1423     // public void actionPerformed(ActionEvent e)
1424     // {
1425     // try {
1426     // RNAFold_actionPerformed();
1427     // } catch (Exception e1) {
1428     // // TODO Auto-generated catch block
1429     // e1.printStackTrace();
1430     // }
1431     // }
1432     // });
1433     // ContraFold.setText("From Contra Fold with predict2D");
1434     // ContraFold.addActionListener(new ActionListener()
1435     // {
1436     // public void actionPerformed(ActionEvent e)
1437     // {
1438     // try {
1439     // ContraFold_actionPerformed();
1440     // } catch (Exception e1) {
1441     // // TODO Auto-generated catch block
1442     // e1.printStackTrace();
1443     // }
1444     // }
1445     // });
1446     enterPDB.setText(MessageManager.getString("label.enter_pdb_id"));
1447     enterPDB.addActionListener(new ActionListener()
1448     {
1449       @Override
1450       public void actionPerformed(ActionEvent e)
1451       {
1452         enterPDB_actionPerformed();
1453       }
1454     });
1455     discoverPDB.setText(MessageManager.getString("label.discover_pdb_ids"));
1456     discoverPDB.addActionListener(new ActionListener()
1457     {
1458       @Override
1459       public void actionPerformed(ActionEvent e)
1460       {
1461         discoverPDB_actionPerformed();
1462       }
1463     });
1464     outputMenu.setText(MessageManager.getString("label.out_to_textbox")
1465             + "...");
1466     seqShowAnnotationsMenu.setText(MessageManager
1467             .getString("label.show_annotations"));
1468     seqHideAnnotationsMenu.setText(MessageManager
1469             .getString("label.hide_annotations"));
1470     groupShowAnnotationsMenu.setText(MessageManager
1471             .getString("label.show_annotations"));
1472     groupHideAnnotationsMenu.setText(MessageManager
1473             .getString("label.hide_annotations"));
1474     sequenceFeature.setText(MessageManager
1475             .getString("label.create_sequence_feature"));
1476     sequenceFeature.addActionListener(new ActionListener()
1477     {
1478       @Override
1479       public void actionPerformed(ActionEvent e)
1480       {
1481         sequenceFeature_actionPerformed();
1482       }
1483     });
1484     textColour.setText(MessageManager.getString("label.text_colour"));
1485     textColour.addActionListener(new ActionListener()
1486     {
1487       @Override
1488       public void actionPerformed(ActionEvent e)
1489       {
1490         textColour_actionPerformed();
1491       }
1492     });
1493     jMenu1.setText(MessageManager.getString("label.group"));
1494     structureMenu.setText(MessageManager.getString("label.structure"));
1495     viewStructureMenu.setText(MessageManager
1496             .getString("label.view_structure"));
1497     // colStructureMenu.setText("Colour By Structure");
1498     editSequence.setText(MessageManager.getString("label.edit_sequence")
1499             + "...");
1500     editSequence.addActionListener(new ActionListener()
1501     {
1502       @Override
1503       public void actionPerformed(ActionEvent actionEvent)
1504       {
1505         editSequence_actionPerformed(actionEvent);
1506       }
1507     });
1508
1509     /*
1510      * annotationMenuItem.setText("By Annotation");
1511      * annotationMenuItem.addActionListener(new ActionListener() { public void
1512      * actionPerformed(ActionEvent actionEvent) {
1513      * annotationMenuItem_actionPerformed(actionEvent); } });
1514      */
1515     groupMenu.add(sequenceSelDetails);
1516     add(groupMenu);
1517     add(sequenceMenu);
1518     this.add(structureMenu);
1519     // annotations configuration panel suppressed for now
1520     // groupMenu.add(chooseAnnotations);
1521
1522     /*
1523      * Add show/hide annotations to the Sequence menu, and to the Selection menu
1524      * (if a selection group is in force).
1525      */
1526     sequenceMenu.add(seqShowAnnotationsMenu);
1527     sequenceMenu.add(seqHideAnnotationsMenu);
1528     sequenceMenu.add(seqAddReferenceAnnotations);
1529     groupMenu.add(groupShowAnnotationsMenu);
1530     groupMenu.add(groupHideAnnotationsMenu);
1531     groupMenu.add(groupAddReferenceAnnotations);
1532     groupMenu.add(editMenu);
1533     groupMenu.add(outputMenu);
1534     groupMenu.add(sequenceFeature);
1535     groupMenu.add(createGroupMenuItem);
1536     groupMenu.add(unGroupMenuItem);
1537     groupMenu.add(jMenu1);
1538     sequenceMenu.add(sequenceName);
1539     sequenceMenu.add(sequenceDetails);
1540     colourMenu.add(textColour);
1541     colourMenu.add(noColourmenuItem);
1542     colourMenu.add(clustalColour);
1543     colourMenu.add(BLOSUM62Colour);
1544     colourMenu.add(PIDColour);
1545     colourMenu.add(zappoColour);
1546     colourMenu.add(taylorColour);
1547     colourMenu.add(hydrophobicityColour);
1548     colourMenu.add(helixColour);
1549     colourMenu.add(strandColour);
1550     colourMenu.add(turnColour);
1551     colourMenu.add(buriedColour);
1552     colourMenu.add(nucleotideMenuItem);
1553     if (ap.getAlignment().isNucleotide())
1554     {
1555       // JBPNote - commented since the colourscheme isn't functional
1556       // colourMenu.add(RNAInteractionColour);
1557       colourMenu.add(purinePyrimidineColour);
1558     }
1559     // colourMenu.add(covariationColour);
1560     colourMenu.add(userDefinedColour);
1561
1562     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
1563     {
1564       java.util.Enumeration userColours = jalview.gui.UserDefinedColours
1565               .getUserColourSchemes().keys();
1566
1567       while (userColours.hasMoreElements())
1568       {
1569         JMenuItem item = new JMenuItem(userColours.nextElement().toString());
1570         item.addActionListener(new ActionListener()
1571         {
1572           @Override
1573           public void actionPerformed(ActionEvent evt)
1574           {
1575             userDefinedColour_actionPerformed(evt);
1576           }
1577         });
1578         colourMenu.add(item);
1579       }
1580     }
1581
1582     colourMenu.addSeparator();
1583     colourMenu.add(abovePIDColour);
1584     colourMenu.add(conservationMenuItem);
1585     // colourMenu.add(annotationMenuItem);
1586     editMenu.add(copy);
1587     editMenu.add(cut);
1588     editMenu.add(editSequence);
1589     editMenu.add(upperCase);
1590     editMenu.add(lowerCase);
1591     editMenu.add(toggle);
1592     pdbMenu.add(pdbFromFile);
1593     // JBPNote: These shouldn't be added here - should appear in a generic
1594     // 'apply web service to this sequence menu'
1595     // pdbMenu.add(RNAFold);
1596     // pdbMenu.add(ContraFold);
1597     pdbMenu.add(enterPDB);
1598     pdbMenu.add(discoverPDB);
1599     jMenu1.add(groupName);
1600     jMenu1.add(colourMenu);
1601     jMenu1.add(showBoxes);
1602     jMenu1.add(showText);
1603     jMenu1.add(showColourText);
1604     jMenu1.add(outline);
1605     jMenu1.add(displayNonconserved);
1606     structureMenu.add(pdbMenu);
1607     structureMenu.add(viewStructureMenu);
1608     // structureMenu.add(colStructureMenu);
1609     noColourmenuItem.setText(MessageManager.getString("label.none"));
1610     noColourmenuItem.addActionListener(new java.awt.event.ActionListener()
1611     {
1612       @Override
1613       public void actionPerformed(ActionEvent e)
1614       {
1615         noColourmenuItem_actionPerformed();
1616       }
1617     });
1618
1619     clustalColour.setText(MessageManager
1620             .getString("label.clustalx_colours"));
1621     clustalColour.addActionListener(new java.awt.event.ActionListener()
1622     {
1623       @Override
1624       public void actionPerformed(ActionEvent e)
1625       {
1626         clustalColour_actionPerformed();
1627       }
1628     });
1629     zappoColour.setText(MessageManager.getString("label.zappo"));
1630     zappoColour.addActionListener(new java.awt.event.ActionListener()
1631     {
1632       @Override
1633       public void actionPerformed(ActionEvent e)
1634       {
1635         zappoColour_actionPerformed();
1636       }
1637     });
1638     taylorColour.setText(MessageManager.getString("label.taylor"));
1639     taylorColour.addActionListener(new java.awt.event.ActionListener()
1640     {
1641       @Override
1642       public void actionPerformed(ActionEvent e)
1643       {
1644         taylorColour_actionPerformed();
1645       }
1646     });
1647     hydrophobicityColour.setText(MessageManager
1648             .getString("label.hydrophobicity"));
1649     hydrophobicityColour
1650             .addActionListener(new java.awt.event.ActionListener()
1651             {
1652               @Override
1653               public void actionPerformed(ActionEvent e)
1654               {
1655                 hydrophobicityColour_actionPerformed();
1656               }
1657             });
1658     helixColour.setText(MessageManager.getString("label.helix_propensity"));
1659     helixColour.addActionListener(new java.awt.event.ActionListener()
1660     {
1661       @Override
1662       public void actionPerformed(ActionEvent e)
1663       {
1664         helixColour_actionPerformed();
1665       }
1666     });
1667     strandColour.setText(MessageManager
1668             .getString("label.strand_propensity"));
1669     strandColour.addActionListener(new java.awt.event.ActionListener()
1670     {
1671       @Override
1672       public void actionPerformed(ActionEvent e)
1673       {
1674         strandColour_actionPerformed();
1675       }
1676     });
1677     turnColour.setText(MessageManager.getString("label.turn_propensity"));
1678     turnColour.addActionListener(new java.awt.event.ActionListener()
1679     {
1680       @Override
1681       public void actionPerformed(ActionEvent e)
1682       {
1683         turnColour_actionPerformed();
1684       }
1685     });
1686     buriedColour.setText(MessageManager.getString("label.buried_index"));
1687     buriedColour.addActionListener(new java.awt.event.ActionListener()
1688     {
1689       @Override
1690       public void actionPerformed(ActionEvent e)
1691       {
1692         buriedColour_actionPerformed();
1693       }
1694     });
1695     abovePIDColour.setText(MessageManager
1696             .getString("label.above_identity_percentage"));
1697     abovePIDColour.addActionListener(new java.awt.event.ActionListener()
1698     {
1699       @Override
1700       public void actionPerformed(ActionEvent e)
1701       {
1702         abovePIDColour_actionPerformed();
1703       }
1704     });
1705     userDefinedColour.setText(MessageManager
1706             .getString("action.user_defined"));
1707     userDefinedColour.addActionListener(new java.awt.event.ActionListener()
1708     {
1709       @Override
1710       public void actionPerformed(ActionEvent e)
1711       {
1712         userDefinedColour_actionPerformed(e);
1713       }
1714     });
1715     PIDColour
1716             .setText(MessageManager.getString("label.percentage_identity"));
1717     PIDColour.addActionListener(new java.awt.event.ActionListener()
1718     {
1719       @Override
1720       public void actionPerformed(ActionEvent e)
1721       {
1722         PIDColour_actionPerformed();
1723       }
1724     });
1725     BLOSUM62Colour.setText(MessageManager.getString("label.blosum62"));
1726     BLOSUM62Colour.addActionListener(new java.awt.event.ActionListener()
1727     {
1728       @Override
1729       public void actionPerformed(ActionEvent e)
1730       {
1731         BLOSUM62Colour_actionPerformed();
1732       }
1733     });
1734     purinePyrimidineColour.setText(MessageManager
1735             .getString("label.purine_pyrimidine"));
1736     purinePyrimidineColour
1737             .addActionListener(new java.awt.event.ActionListener()
1738             {
1739               @Override
1740               public void actionPerformed(ActionEvent e)
1741               {
1742                 purinePyrimidineColour_actionPerformed();
1743               }
1744             });
1745
1746     /*
1747      * covariationColour.addActionListener(new java.awt.event.ActionListener() {
1748      * public void actionPerformed(ActionEvent e) {
1749      * covariationColour_actionPerformed(); } });
1750      */
1751
1752     conservationMenuItem.setText(MessageManager
1753             .getString("label.conservation"));
1754     conservationMenuItem
1755             .addActionListener(new java.awt.event.ActionListener()
1756             {
1757               @Override
1758               public void actionPerformed(ActionEvent e)
1759               {
1760                 conservationMenuItem_actionPerformed();
1761               }
1762             });
1763   }
1764
1765   /**
1766    * Check for any annotations on the underlying dataset sequences (for the
1767    * current selection group) which are not on the alignment annotations for the
1768    * sequence. If any are found, enable the option to add them to the alignment.
1769    * The criteria for 'on the alignment' is finding an alignment annotation on
1770    * the sequence, that matches on calcId and label. A tooltip is also
1771    * constructed that displays the source (calcId) and type (label) of the
1772    * annotations that can be added.
1773    * 
1774    * @param menuItem
1775    * @param forSequences
1776    */
1777   protected void configureReferenceAnnotationsMenu(
1778           JMenuItem menuItem, List<SequenceI> forSequences)
1779   {
1780     menuItem.setText(MessageManager
1781             .getString("label.add_reference_annotations"));
1782     menuItem.setEnabled(false);
1783     if (forSequences == null)
1784     {
1785       return;
1786     }
1787
1788     /*
1789      * Temporary store to hold distinct calcId / type pairs for the tooltip.
1790      * Using TreeMap means calcIds are shown in alphabetical order.
1791      */
1792     Map<String, String> tipEntries = new TreeMap<String, String>();
1793     StringBuilder tooltip = new StringBuilder(64);
1794     tooltip.append(MessageManager.getString("label.add_annotations_for"));
1795
1796     /*
1797      * For each sequence selected in the alignment, make a list of any
1798      * annotations on the underlying dataset sequence which are not already on
1799      * the sequence in the alignment.
1800      * 
1801      * Build a map of { alignmentSequence, <List of annotations to add> }
1802      */
1803     final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<SequenceI, List<AlignmentAnnotation>>();
1804     for (SequenceI seq : forSequences)
1805     {
1806       SequenceI dataset = seq.getDatasetSequence();
1807       if (dataset == null)
1808       {
1809         continue;
1810       }
1811       AlignmentAnnotation[] datasetAnnotations = dataset.getAnnotation();
1812       if (datasetAnnotations == null)
1813       {
1814         continue;
1815       }
1816       final List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
1817       for (AlignmentAnnotation dsann : datasetAnnotations)
1818       {
1819         /*
1820          * If the sequence has no annotation that matches this one, then add
1821          * this one to the results list.
1822          */
1823         if (seq.getAlignmentAnnotations(dsann.getCalcId(), dsann.label)
1824                 .isEmpty())
1825         {
1826           result.add(dsann);
1827           tipEntries.put(dsann.getCalcId(), dsann.label);
1828         }
1829       }
1830       /*
1831        * Save any addable annotations for this sequence
1832        */
1833       if (!result.isEmpty())
1834       {
1835         candidates.put(seq, result);
1836       }
1837     }
1838     if (!candidates.isEmpty())
1839     {
1840       /*
1841        * Found annotations that could be added. Enable the menu item, and
1842        * configure its tooltip and action.
1843        */
1844       menuItem.setEnabled(true);
1845       for (String calcId : tipEntries.keySet())
1846       {
1847         tooltip.append("<br/>" + calcId + "/" + tipEntries.get(calcId));
1848       }
1849       String tooltipText = JvSwingUtils.wrapTooltip(true,
1850               tooltip.toString());
1851       menuItem.setToolTipText(tooltipText);
1852
1853       menuItem.addActionListener(new ActionListener()
1854       {
1855         @Override
1856         public void actionPerformed(ActionEvent e)
1857         {
1858           addReferenceAnnotations_actionPerformed(candidates);
1859         }
1860       });
1861     }
1862   }
1863
1864   /**
1865    * Add annotations to the sequences and to the alignment.
1866    * 
1867    * @param candidates
1868    *          a map whose keys are sequences on the alignment, and values a list
1869    *          of annotations to add to each sequence
1870    */
1871   protected void addReferenceAnnotations_actionPerformed(
1872           Map<SequenceI, List<AlignmentAnnotation>> candidates)
1873   {
1874     /*
1875      * Add annotations at the top of the annotation, in the same order as their
1876      * related sequences.
1877      */
1878     for (SequenceI seq : candidates.keySet())
1879     {
1880       for (AlignmentAnnotation ann : candidates.get(seq))
1881       {
1882         AlignmentAnnotation copyAnn = new AlignmentAnnotation(ann);
1883         int startRes = 0;
1884         int endRes = ann.annotations.length;
1885         final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
1886         if (selectionGroup != null)
1887         {
1888           startRes = selectionGroup.getStartRes();
1889           endRes = selectionGroup.getEndRes();
1890         }
1891         copyAnn.restrict(startRes, endRes);
1892
1893         // add to the sequence (sets copyAnn.datasetSequence)
1894         seq.addAlignmentAnnotation(copyAnn);
1895         // adjust for gaps
1896         copyAnn.adjustForAlignment();
1897         // add to the alignment and set visible
1898         this.ap.getAlignment().addAnnotation(copyAnn);
1899         copyAnn.visible = true;
1900       }
1901     }
1902     refresh();
1903   }
1904
1905   protected void sequenceSelectionDetails_actionPerformed()
1906   {
1907     createSequenceDetailsReport(ap.av.getSequenceSelection());
1908   }
1909
1910   protected void sequenceDetails_actionPerformed()
1911   {
1912     createSequenceDetailsReport(new SequenceI[]
1913     { sequence });
1914   }
1915
1916   public void createSequenceDetailsReport(SequenceI[] sequences)
1917   {
1918     CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
1919     StringBuffer contents = new StringBuffer();
1920     for (SequenceI seq : sequences)
1921     {
1922       contents.append("<p><h2>"
1923               + MessageManager
1924                       .formatMessage(
1925                               "label.create_sequence_details_report_annotation_for",
1926                               new String[]
1927                               { seq.getDisplayId(true) }) + "</h2></p><p>");
1928       new SequenceAnnotationReport(null)
1929               .createSequenceAnnotationReport(
1930                       contents,
1931                       seq,
1932                       true,
1933                       true,
1934                       false,
1935                       (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr.minmax
1936                               : null);
1937       contents.append("</p>");
1938     }
1939     cap.setText("<html>" + contents.toString() + "</html>");
1940
1941     Desktop.instance.addInternalFrame(cap, MessageManager.formatMessage(
1942             "label.sequece_details_for",
1943             (sequences.length == 1 ? new String[]
1944             { sequences[0].getDisplayId(true) } : new String[]
1945             { MessageManager.getString("label.selection") })), 500, 400);
1946
1947   }
1948
1949   protected void showNonconserved_actionPerformed()
1950   {
1951     getGroup().setShowNonconserved(displayNonconserved.isSelected());
1952     refresh();
1953   }
1954
1955   /**
1956    * call to refresh view after settings change
1957    */
1958   void refresh()
1959   {
1960     ap.updateAnnotation();
1961     ap.paintAlignment(true);
1962
1963     PaintRefresher.Refresh(this, ap.av.getSequenceSetId());
1964   }
1965
1966   /**
1967    * DOCUMENT ME!
1968    * 
1969    * @param e
1970    *          DOCUMENT ME!
1971    */
1972   protected void clustalColour_actionPerformed()
1973   {
1974     SequenceGroup sg = getGroup();
1975     sg.cs = new ClustalxColourScheme(sg, ap.av.getHiddenRepSequences());
1976     refresh();
1977   }
1978
1979   /**
1980    * DOCUMENT ME!
1981    * 
1982    * @param e
1983    *          DOCUMENT ME!
1984    */
1985   protected void zappoColour_actionPerformed()
1986   {
1987     getGroup().cs = new ZappoColourScheme();
1988     refresh();
1989   }
1990
1991   /**
1992    * DOCUMENT ME!
1993    * 
1994    * @param e
1995    *          DOCUMENT ME!
1996    */
1997   protected void taylorColour_actionPerformed()
1998   {
1999     getGroup().cs = new TaylorColourScheme();
2000     refresh();
2001   }
2002
2003   /**
2004    * DOCUMENT ME!
2005    * 
2006    * @param e
2007    *          DOCUMENT ME!
2008    */
2009   protected void hydrophobicityColour_actionPerformed()
2010   {
2011     getGroup().cs = new HydrophobicColourScheme();
2012     refresh();
2013   }
2014
2015   /**
2016    * DOCUMENT ME!
2017    * 
2018    * @param e
2019    *          DOCUMENT ME!
2020    */
2021   protected void helixColour_actionPerformed()
2022   {
2023     getGroup().cs = new HelixColourScheme();
2024     refresh();
2025   }
2026
2027   /**
2028    * DOCUMENT ME!
2029    * 
2030    * @param e
2031    *          DOCUMENT ME!
2032    */
2033   protected void strandColour_actionPerformed()
2034   {
2035     getGroup().cs = new StrandColourScheme();
2036     refresh();
2037   }
2038
2039   /**
2040    * DOCUMENT ME!
2041    * 
2042    * @param e
2043    *          DOCUMENT ME!
2044    */
2045   protected void turnColour_actionPerformed()
2046   {
2047     getGroup().cs = new TurnColourScheme();
2048     refresh();
2049   }
2050
2051   /**
2052    * DOCUMENT ME!
2053    * 
2054    * @param e
2055    *          DOCUMENT ME!
2056    */
2057   protected void buriedColour_actionPerformed()
2058   {
2059     getGroup().cs = new BuriedColourScheme();
2060     refresh();
2061   }
2062
2063   /**
2064    * DOCUMENT ME!
2065    * 
2066    * @param e
2067    *          DOCUMENT ME!
2068    */
2069   public void nucleotideMenuItem_actionPerformed()
2070   {
2071     getGroup().cs = new NucleotideColourScheme();
2072     refresh();
2073   }
2074
2075   protected void purinePyrimidineColour_actionPerformed()
2076   {
2077     getGroup().cs = new PurinePyrimidineColourScheme();
2078     refresh();
2079   }
2080
2081   /*
2082    * protected void covariationColour_actionPerformed() { getGroup().cs = new
2083    * CovariationColourScheme(sequence.getAnnotation()[0]); refresh(); }
2084    */
2085   /**
2086    * DOCUMENT ME!
2087    * 
2088    * @param e
2089    *          DOCUMENT ME!
2090    */
2091   protected void abovePIDColour_actionPerformed()
2092   {
2093     SequenceGroup sg = getGroup();
2094     if (sg.cs == null)
2095     {
2096       return;
2097     }
2098
2099     if (abovePIDColour.isSelected())
2100     {
2101       sg.cs.setConsensus(AAFrequency.calculate(
2102               sg.getSequences(ap.av.getHiddenRepSequences()),
2103               sg.getStartRes(), sg.getEndRes() + 1));
2104
2105       int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup()
2106               .getName());
2107
2108       sg.cs.setThreshold(threshold, ap.av.getIgnoreGapsConsensus());
2109
2110       SliderPanel.showPIDSlider();
2111     }
2112     else
2113     // remove PIDColouring
2114     {
2115       sg.cs.setThreshold(0, ap.av.getIgnoreGapsConsensus());
2116     }
2117
2118     refresh();
2119   }
2120
2121   /**
2122    * DOCUMENT ME!
2123    * 
2124    * @param e
2125    *          DOCUMENT ME!
2126    */
2127   protected void userDefinedColour_actionPerformed(ActionEvent e)
2128   {
2129     SequenceGroup sg = getGroup();
2130
2131     if (e.getSource().equals(userDefinedColour))
2132     {
2133       new UserDefinedColours(ap, sg);
2134     }
2135     else
2136     {
2137       UserColourScheme udc = (UserColourScheme) UserDefinedColours
2138               .getUserColourSchemes().get(e.getActionCommand());
2139
2140       sg.cs = udc;
2141     }
2142     refresh();
2143   }
2144
2145   /**
2146    * Open a panel where the user can choose which types of sequence annotation
2147    * to show or hide.
2148    * 
2149    * @param e
2150    */
2151   protected void chooseAnnotations_actionPerformed(ActionEvent e)
2152   {
2153     // todo correct way to guard against opening a duplicate panel?
2154     new AnnotationChooser(ap);
2155   }
2156
2157   /**
2158    * DOCUMENT ME!
2159    * 
2160    * @param e
2161    *          DOCUMENT ME!
2162    */
2163   protected void PIDColour_actionPerformed()
2164   {
2165     SequenceGroup sg = getGroup();
2166     sg.cs = new PIDColourScheme();
2167     sg.cs.setConsensus(AAFrequency.calculate(
2168             sg.getSequences(ap.av.getHiddenRepSequences()),
2169             sg.getStartRes(), sg.getEndRes() + 1));
2170     refresh();
2171   }
2172
2173   /**
2174    * DOCUMENT ME!
2175    * 
2176    * @param e
2177    *          DOCUMENT ME!
2178    */
2179   protected void BLOSUM62Colour_actionPerformed()
2180   {
2181     SequenceGroup sg = getGroup();
2182
2183     sg.cs = new Blosum62ColourScheme();
2184
2185     sg.cs.setConsensus(AAFrequency.calculate(
2186             sg.getSequences(ap.av.getHiddenRepSequences()),
2187             sg.getStartRes(), sg.getEndRes() + 1));
2188
2189     refresh();
2190   }
2191
2192   /**
2193    * DOCUMENT ME!
2194    * 
2195    * @param e
2196    *          DOCUMENT ME!
2197    */
2198   protected void noColourmenuItem_actionPerformed()
2199   {
2200     getGroup().cs = null;
2201     refresh();
2202   }
2203
2204   /**
2205    * DOCUMENT ME!
2206    * 
2207    * @param e
2208    *          DOCUMENT ME!
2209    */
2210   protected void conservationMenuItem_actionPerformed()
2211   {
2212     SequenceGroup sg = getGroup();
2213     if (sg.cs == null)
2214     {
2215       return;
2216     }
2217
2218     if (conservationMenuItem.isSelected())
2219     {
2220       // JBPNote: Conservation name shouldn't be i18n translated
2221       Conservation c = new Conservation("Group",
2222               ResidueProperties.propHash, 3, sg.getSequences(ap.av
2223                       .getHiddenRepSequences()), sg.getStartRes(),
2224               sg.getEndRes() + 1);
2225
2226       c.calculate();
2227       c.verdict(false, ap.av.getConsPercGaps());
2228
2229       sg.cs.setConservation(c);
2230
2231       SliderPanel.setConservationSlider(ap, sg.cs, sg.getName());
2232       SliderPanel.showConservationSlider();
2233     }
2234     else
2235     // remove ConservationColouring
2236     {
2237       sg.cs.setConservation(null);
2238     }
2239
2240     refresh();
2241   }
2242
2243   public void annotationMenuItem_actionPerformed(ActionEvent actionEvent)
2244   {
2245     SequenceGroup sg = getGroup();
2246     if (sg == null)
2247     {
2248       return;
2249     }
2250
2251     AnnotationColourGradient acg = new AnnotationColourGradient(
2252             sequence.getAnnotation()[0], null,
2253             AnnotationColourGradient.NO_THRESHOLD);
2254
2255     acg.setPredefinedColours(true);
2256     sg.cs = acg;
2257
2258     refresh();
2259   }
2260
2261   /**
2262    * DOCUMENT ME!
2263    * 
2264    * @param e
2265    *          DOCUMENT ME!
2266    */
2267   protected void groupName_actionPerformed()
2268   {
2269
2270     SequenceGroup sg = getGroup();
2271     EditNameDialog dialog = new EditNameDialog(sg.getName(),
2272             sg.getDescription(), "       "
2273                     + MessageManager.getString("label.group_name") + " ",
2274             MessageManager.getString("label.group_description") + " ",
2275             MessageManager.getString("label.edit_group_name_description"),
2276             ap.alignFrame);
2277
2278     if (!dialog.accept)
2279     {
2280       return;
2281     }
2282
2283     sg.setName(dialog.getName());
2284     sg.setDescription(dialog.getDescription());
2285     refresh();
2286   }
2287
2288   /**
2289    * Get selection group - adding it to the alignment if necessary.
2290    * 
2291    * @return sequence group to operate on
2292    */
2293   SequenceGroup getGroup()
2294   {
2295     SequenceGroup sg = ap.av.getSelectionGroup();
2296     // this method won't add a new group if it already exists
2297     if (sg != null)
2298     {
2299       ap.av.getAlignment().addGroup(sg);
2300     }
2301
2302     return sg;
2303   }
2304
2305   /**
2306    * DOCUMENT ME!
2307    * 
2308    * @param e
2309    *          DOCUMENT ME!
2310    */
2311   void sequenceName_actionPerformed()
2312   {
2313     EditNameDialog dialog = new EditNameDialog(sequence.getName(),
2314             sequence.getDescription(),
2315             "       " + MessageManager.getString("label.sequence_name")
2316                     + " ",
2317             MessageManager.getString("label.sequence_description") + " ",
2318             MessageManager
2319                     .getString("label.edit_sequence_name_description"),
2320             ap.alignFrame);
2321
2322     if (!dialog.accept)
2323     {
2324       return;
2325     }
2326
2327     if (dialog.getName() != null)
2328     {
2329       if (dialog.getName().indexOf(" ") > -1)
2330       {
2331         JOptionPane
2332                 .showMessageDialog(
2333                         ap,
2334                         MessageManager
2335                                 .getString("label.spaces_converted_to_backslashes"),
2336                         MessageManager
2337                                 .getString("label.no_spaces_allowed_sequence_name"),
2338                         JOptionPane.WARNING_MESSAGE);
2339       }
2340
2341       sequence.setName(dialog.getName().replace(' ', '_'));
2342       ap.paintAlignment(false);
2343     }
2344
2345     sequence.setDescription(dialog.getDescription());
2346
2347     ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
2348             .getSequences());
2349
2350   }
2351
2352   /**
2353    * DOCUMENT ME!
2354    * 
2355    * @param e
2356    *          DOCUMENT ME!
2357    */
2358   void unGroupMenuItem_actionPerformed()
2359   {
2360     SequenceGroup sg = ap.av.getSelectionGroup();
2361     ap.av.getAlignment().deleteGroup(sg);
2362     ap.av.setSelectionGroup(null);
2363     refresh();
2364   }
2365
2366   void createGroupMenuItem_actionPerformed()
2367   {
2368     getGroup(); // implicitly creates group - note - should apply defaults / use
2369                 // standard alignment window logic for this
2370     refresh();
2371   }
2372
2373   /**
2374    * DOCUMENT ME!
2375    * 
2376    * @param e
2377    *          DOCUMENT ME!
2378    */
2379   protected void outline_actionPerformed()
2380   {
2381     SequenceGroup sg = getGroup();
2382     Color col = JColorChooser.showDialog(this,
2383             MessageManager.getString("label.select_outline_colour"),
2384             Color.BLUE);
2385
2386     if (col != null)
2387     {
2388       sg.setOutlineColour(col);
2389     }
2390
2391     refresh();
2392   }
2393
2394   /**
2395    * DOCUMENT ME!
2396    * 
2397    * @param e
2398    *          DOCUMENT ME!
2399    */
2400   public void showBoxes_actionPerformed()
2401   {
2402     getGroup().setDisplayBoxes(showBoxes.isSelected());
2403     refresh();
2404   }
2405
2406   /**
2407    * DOCUMENT ME!
2408    * 
2409    * @param e
2410    *          DOCUMENT ME!
2411    */
2412   public void showText_actionPerformed()
2413   {
2414     getGroup().setDisplayText(showText.isSelected());
2415     refresh();
2416   }
2417
2418   /**
2419    * DOCUMENT ME!
2420    * 
2421    * @param e
2422    *          DOCUMENT ME!
2423    */
2424   public void showColourText_actionPerformed()
2425   {
2426     getGroup().setColourText(showColourText.isSelected());
2427     refresh();
2428   }
2429
2430   public void showLink(String url)
2431   {
2432     try
2433     {
2434       jalview.util.BrowserLauncher.openURL(url);
2435     } catch (Exception ex)
2436     {
2437       JOptionPane.showInternalMessageDialog(Desktop.desktop,
2438               MessageManager.getString("label.web_browser_not_found_unix"),
2439               MessageManager.getString("label.web_browser_not_found"),
2440               JOptionPane.WARNING_MESSAGE);
2441
2442       ex.printStackTrace();
2443     }
2444   }
2445
2446   void hideSequences(boolean representGroup)
2447   {
2448     SequenceGroup sg = ap.av.getSelectionGroup();
2449     if (sg == null || sg.getSize() < 1)
2450     {
2451       ap.av.hideSequence(new SequenceI[]
2452       { sequence });
2453       return;
2454     }
2455
2456     ap.av.setSelectionGroup(null);
2457
2458     if (representGroup)
2459     {
2460       ap.av.hideRepSequences(sequence, sg);
2461
2462       return;
2463     }
2464
2465     int gsize = sg.getSize();
2466     SequenceI[] hseqs;
2467
2468     hseqs = new SequenceI[gsize];
2469
2470     int index = 0;
2471     for (int i = 0; i < gsize; i++)
2472     {
2473       hseqs[index++] = sg.getSequenceAt(i);
2474     }
2475
2476     ap.av.hideSequence(hseqs);
2477     // refresh(); TODO: ? needed ?
2478     ap.av.sendSelection();
2479   }
2480
2481   public void copy_actionPerformed()
2482   {
2483     ap.alignFrame.copy_actionPerformed(null);
2484   }
2485
2486   public void cut_actionPerformed()
2487   {
2488     ap.alignFrame.cut_actionPerformed(null);
2489   }
2490
2491   void changeCase(ActionEvent e)
2492   {
2493     Object source = e.getSource();
2494     SequenceGroup sg = ap.av.getSelectionGroup();
2495
2496     if (sg != null)
2497     {
2498       int[][] startEnd = ap.av.getVisibleRegionBoundaries(sg.getStartRes(),
2499               sg.getEndRes() + 1);
2500
2501       String description;
2502       int caseChange;
2503
2504       if (source == toggle)
2505       {
2506         description = MessageManager.getString("label.toggle_case");
2507         caseChange = ChangeCaseCommand.TOGGLE_CASE;
2508       }
2509       else if (source == upperCase)
2510       {
2511         description = MessageManager.getString("label.to_upper_case");
2512         caseChange = ChangeCaseCommand.TO_UPPER;
2513       }
2514       else
2515       {
2516         description = MessageManager.getString("label.to_lower_case");
2517         caseChange = ChangeCaseCommand.TO_LOWER;
2518       }
2519
2520       ChangeCaseCommand caseCommand = new ChangeCaseCommand(description,
2521               sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
2522               startEnd, caseChange);
2523
2524       ap.alignFrame.addHistoryItem(caseCommand);
2525
2526       ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
2527               .getSequences());
2528
2529     }
2530   }
2531
2532   public void outputText_actionPerformed(ActionEvent e)
2533   {
2534     CutAndPasteTransfer cap = new CutAndPasteTransfer();
2535     cap.setForInput(null);
2536     Desktop.addInternalFrame(cap, MessageManager.formatMessage(
2537             "label.alignment_output_command", new String[]
2538             { e.getActionCommand() }), 600, 500);
2539
2540     String[] omitHidden = null;
2541
2542     System.out.println("PROMPT USER HERE"); // TODO: decide if a prompt happens
2543     // or we simply trust the user wants
2544     // wysiwig behaviour
2545
2546     cap.setText(new FormatAdapter().formatSequences(e.getActionCommand(),
2547             ap.av, true));
2548   }
2549
2550   public void pdbFromFile_actionPerformed()
2551   {
2552     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
2553             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2554     chooser.setFileView(new jalview.io.JalviewFileView());
2555     chooser.setDialogTitle(MessageManager.formatMessage(
2556             "label.select_pdb_file_for", new String[]
2557             { sequence.getDisplayId(false) }));
2558     chooser.setToolTipText(MessageManager.formatMessage(
2559             "label.load_pdb_file_associate_with_sequence", new String[]
2560             { sequence.getDisplayId(false) }));
2561
2562     int value = chooser.showOpenDialog(null);
2563
2564     if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
2565     {
2566       String choice = chooser.getSelectedFile().getPath();
2567       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
2568       new AssociatePdbFileWithSeq().associatePdbWithSeq(choice,
2569               jalview.io.AppletFormatAdapter.FILE, sequence, true,
2570               Desktop.instance);
2571     }
2572
2573   }
2574
2575   // JBNote: commented out - these won't be instantiated here...!
2576   // public void RNAFold_actionPerformed() throws Exception
2577   // {
2578   // Predict2D P2D = new Predict2D();
2579   // P2D.getStructure2DFromRNAFold("toto");
2580   // }
2581   //
2582   // public void ContraFold_actionPerformed() throws Exception
2583   // {
2584   // Predict2D P2D = new Predict2D();
2585   // P2D.getStructure2DFromContraFold("toto");
2586   // }
2587   public void enterPDB_actionPerformed()
2588   {
2589     String id = JOptionPane.showInternalInputDialog(Desktop.desktop,
2590             MessageManager.getString("label.enter_pdb_id"),
2591             MessageManager.getString("label.enter_pdb_id"),
2592             JOptionPane.QUESTION_MESSAGE);
2593
2594     if (id != null && id.length() > 0)
2595     {
2596       PDBEntry entry = new PDBEntry();
2597       entry.setId(id.toUpperCase());
2598       sequence.getDatasetSequence().addPDBId(entry);
2599     }
2600   }
2601
2602   public void discoverPDB_actionPerformed()
2603   {
2604
2605     final SequenceI[] sequences = ((ap.av.getSelectionGroup() == null) ? new SequenceI[]
2606     { sequence }
2607             : ap.av.getSequenceSelection());
2608     Thread discpdb = new Thread(new Runnable()
2609     {
2610       @Override
2611       public void run()
2612       {
2613
2614         new jalview.ws.DBRefFetcher(sequences, ap.alignFrame)
2615                 .fetchDBRefs(false);
2616       }
2617
2618     });
2619     discpdb.start();
2620   }
2621
2622   public void sequenceFeature_actionPerformed()
2623   {
2624     SequenceGroup sg = ap.av.getSelectionGroup();
2625     if (sg == null)
2626     {
2627       return;
2628     }
2629
2630     int rsize = 0, gSize = sg.getSize();
2631     SequenceI[] rseqs, seqs = new SequenceI[gSize];
2632     SequenceFeature[] tfeatures, features = new SequenceFeature[gSize];
2633
2634     for (int i = 0; i < gSize; i++)
2635     {
2636       int start = sg.getSequenceAt(i).findPosition(sg.getStartRes());
2637       int end = sg.findEndRes(sg.getSequenceAt(i));
2638       if (start <= end)
2639       {
2640         seqs[rsize] = sg.getSequenceAt(i).getDatasetSequence();
2641         features[rsize] = new SequenceFeature(null, null, null, start, end,
2642                 "Jalview");
2643         rsize++;
2644       }
2645     }
2646     rseqs = new SequenceI[rsize];
2647     tfeatures = new SequenceFeature[rsize];
2648     System.arraycopy(seqs, 0, rseqs, 0, rsize);
2649     System.arraycopy(features, 0, tfeatures, 0, rsize);
2650     features = tfeatures;
2651     seqs = rseqs;
2652     if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs,
2653             features, true, ap))
2654     {
2655       ap.alignFrame.setShowSeqFeatures(true);
2656       ap.highlightSearchResults(null);
2657     }
2658   }
2659
2660   public void textColour_actionPerformed()
2661   {
2662     SequenceGroup sg = getGroup();
2663     if (sg != null)
2664     {
2665       new TextColourChooser().chooseColour(ap, sg);
2666     }
2667   }
2668
2669   public void colourByStructure(String pdbid)
2670   {
2671     Annotation[] anots = ap.av.getStructureSelectionManager()
2672             .colourSequenceFromStructure(sequence, pdbid);
2673
2674     AlignmentAnnotation an = new AlignmentAnnotation("Structure",
2675             "Coloured by " + pdbid, anots);
2676
2677     ap.av.getAlignment().addAnnotation(an);
2678     an.createSequenceMapping(sequence, 0, true);
2679     // an.adjustForAlignment();
2680     ap.av.getAlignment().setAnnotationIndex(an, 0);
2681
2682     ap.adjustAnnotationHeight();
2683
2684     sequence.addAlignmentAnnotation(an);
2685
2686   }
2687
2688   public void editSequence_actionPerformed(ActionEvent actionEvent)
2689   {
2690     SequenceGroup sg = ap.av.getSelectionGroup();
2691
2692     if (sg != null)
2693     {
2694       if (sequence == null)
2695       {
2696         sequence = sg.getSequenceAt(0);
2697       }
2698
2699       EditNameDialog dialog = new EditNameDialog(
2700               sequence.getSequenceAsString(sg.getStartRes(),
2701                       sg.getEndRes() + 1), null,
2702               MessageManager.getString("label.edit_sequence"), null,
2703               MessageManager.getString("label.edit_sequence"),
2704               ap.alignFrame);
2705
2706       if (dialog.accept)
2707       {
2708         EditCommand editCommand = new EditCommand(
2709                 MessageManager.getString("label.edit_sequences"),
2710                 EditCommand.REPLACE, dialog.getName().replace(' ',
2711                         ap.av.getGapCharacter()),
2712                 sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
2713                 sg.getStartRes(), sg.getEndRes() + 1, ap.av.getAlignment());
2714
2715         ap.alignFrame.addHistoryItem(editCommand);
2716
2717         ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
2718                 .getSequences());
2719       }
2720     }
2721   }
2722
2723 }