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