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