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