JAL-2629 hmmalign can now be run from the sequence pop-up menu
[jalview.git] / src / jalview / gui / PopupMenu.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.analysis.AAFrequency;
24 import jalview.analysis.AlignmentAnnotationUtils;
25 import jalview.analysis.AlignmentUtils;
26 import jalview.analysis.Conservation;
27 import jalview.bin.Cache;
28 import jalview.commands.ChangeCaseCommand;
29 import jalview.commands.EditCommand;
30 import jalview.commands.EditCommand.Action;
31 import jalview.datamodel.AlignmentAnnotation;
32 import jalview.datamodel.AlignmentI;
33 import jalview.datamodel.Annotation;
34 import jalview.datamodel.DBRefEntry;
35 import jalview.datamodel.HiddenColumns;
36 import jalview.datamodel.HiddenMarkovModel;
37 import jalview.datamodel.PDBEntry;
38 import jalview.datamodel.Sequence;
39 import jalview.datamodel.SequenceFeature;
40 import jalview.datamodel.SequenceGroup;
41 import jalview.datamodel.SequenceI;
42 import jalview.gui.ColourMenuHelper.ColourChangeListener;
43 import jalview.hmmer.HMMAlignThread;
44 import jalview.hmmer.HMMBuildThread;
45 import jalview.io.FileFormatI;
46 import jalview.io.FileFormats;
47 import jalview.io.FormatAdapter;
48 import jalview.io.SequenceAnnotationReport;
49 import jalview.schemes.Blosum62ColourScheme;
50 import jalview.schemes.ColourSchemeI;
51 import jalview.schemes.ColourSchemes;
52 import jalview.schemes.PIDColourScheme;
53 import jalview.util.GroupUrlLink;
54 import jalview.util.GroupUrlLink.UrlStringTooLongException;
55 import jalview.util.MessageManager;
56 import jalview.util.UrlLink;
57
58 import java.awt.Color;
59 import java.awt.event.ActionEvent;
60 import java.awt.event.ActionListener;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.Collection;
64 import java.util.Collections;
65 import java.util.Hashtable;
66 import java.util.LinkedHashMap;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.SortedMap;
70 import java.util.TreeMap;
71 import java.util.Vector;
72
73 import javax.swing.JCheckBoxMenuItem;
74 import javax.swing.JColorChooser;
75 import javax.swing.JMenu;
76 import javax.swing.JMenuItem;
77 import javax.swing.JPopupMenu;
78
79 /**
80  * DOCUMENT ME!
81  * 
82  * @author $author$
83  * @version $Revision: 1.118 $
84  */
85 public class PopupMenu extends JPopupMenu implements ColourChangeListener
86 {
87   JMenu groupMenu = new JMenu();
88
89   JMenuItem groupName = new JMenuItem();
90
91   protected JCheckBoxMenuItem abovePIDColour = new JCheckBoxMenuItem();
92
93   protected JMenuItem modifyPID = new JMenuItem();
94
95   protected JCheckBoxMenuItem conservationMenuItem = new JCheckBoxMenuItem();
96
97   protected JMenuItem modifyConservation = new JMenuItem();
98
99   AlignmentPanel ap;
100
101   JMenu sequenceMenu = new JMenu();
102
103   JMenuItem sequenceName = new JMenuItem();
104
105   JMenuItem sequenceDetails = new JMenuItem();
106
107   JMenuItem sequenceSelDetails = new JMenuItem();
108
109   JMenuItem makeReferenceSeq = new JMenuItem();
110
111   JMenuItem chooseAnnotations = new JMenuItem();
112
113   SequenceI sequence;
114
115   JMenuItem createGroupMenuItem = new JMenuItem();
116
117   JMenuItem unGroupMenuItem = new JMenuItem();
118
119   JMenuItem outline = new JMenuItem();
120
121   JMenu colourMenu = new JMenu();
122
123   JCheckBoxMenuItem showBoxes = new JCheckBoxMenuItem();
124
125   JCheckBoxMenuItem showText = new JCheckBoxMenuItem();
126
127   JCheckBoxMenuItem showColourText = new JCheckBoxMenuItem();
128
129   JCheckBoxMenuItem displayNonconserved = new JCheckBoxMenuItem();
130
131   JMenu editMenu = new JMenu();
132
133   JMenuItem cut = new JMenuItem();
134
135   JMenuItem copy = new JMenuItem();
136
137   JMenuItem upperCase = new JMenuItem();
138
139   JMenuItem lowerCase = new JMenuItem();
140
141   JMenuItem toggle = new JMenuItem();
142
143   JMenu pdbMenu = new JMenu();
144
145   JMenu outputMenu = new JMenu();
146
147   JMenu seqShowAnnotationsMenu = new JMenu();
148
149   JMenu seqHideAnnotationsMenu = new JMenu();
150
151   JMenuItem seqAddReferenceAnnotations = new JMenuItem(
152           MessageManager.getString("label.add_reference_annotations"));
153
154   JMenu groupShowAnnotationsMenu = new JMenu();
155
156   JMenu groupHideAnnotationsMenu = new JMenu();
157
158   JMenuItem groupAddReferenceAnnotations = new JMenuItem(
159           MessageManager.getString("label.add_reference_annotations"));
160
161   JMenuItem sequenceFeature = new JMenuItem();
162
163   JMenuItem hmmBuildGroup = new JMenuItem();
164
165   JMenuItem textColour = new JMenuItem();
166
167   JMenu jMenu1 = new JMenu();
168
169   JMenuItem pdbStructureDialog = new JMenuItem();
170
171   JMenu rnaStructureMenu = new JMenu();
172
173   JMenuItem editSequence = new JMenuItem();
174
175   JMenu groupLinksMenu;
176
177   JMenuItem hideInsertions = new JMenuItem();
178
179   /**
180    * Creates a new PopupMenu object.
181    * 
182    * @param ap
183    *          DOCUMENT ME!
184    * @param seq
185    *          DOCUMENT ME!
186    */
187   public PopupMenu(final AlignmentPanel ap, Sequence seq,
188           List<String> links)
189   {
190     this(ap, seq, links, null);
191   }
192
193   /**
194    * 
195    * @param ap
196    * @param seq
197    * @param links
198    * @param groupLinks
199    */
200   public PopupMenu(final AlignmentPanel ap, final SequenceI seq,
201           List<String> links, List<String> groupLinks)
202   {
203     // /////////////////////////////////////////////////////////
204     // If this is activated from the sequence panel, the user may want to
205     // edit or annotate a particular residue. Therefore display the residue menu
206     //
207     // If from the IDPanel, we must display the sequence menu
208     // ////////////////////////////////////////////////////////
209     this.ap = ap;
210     sequence = seq;
211
212     for (String ff : FileFormats.getInstance().getWritableFormats(true))
213     {
214       JMenuItem item = new JMenuItem(ff);
215
216       item.addActionListener(new ActionListener()
217       {
218         @Override
219         public void actionPerformed(ActionEvent e)
220         {
221           outputText_actionPerformed(e);
222         }
223       });
224
225       outputMenu.add(item);
226     }
227
228     /*
229      * Build menus for annotation types that may be shown or hidden, and for
230      * 'reference annotations' that may be added to the alignment. First for the
231      * currently selected sequence (if there is one):
232      */
233     final List<SequenceI> selectedSequence = (seq == null
234             ? Collections.<SequenceI> emptyList() : Arrays.asList(seq));
235     buildAnnotationTypesMenus(seqShowAnnotationsMenu,
236             seqHideAnnotationsMenu, selectedSequence);
237     configureReferenceAnnotationsMenu(seqAddReferenceAnnotations,
238             selectedSequence);
239
240     /*
241      * And repeat for the current selection group (if there is one):
242      */
243     final List<SequenceI> selectedGroup = (ap.av.getSelectionGroup() == null
244             ? Collections.<SequenceI> emptyList()
245             : ap.av.getSelectionGroup().getSequences());
246     buildAnnotationTypesMenus(groupShowAnnotationsMenu,
247             groupHideAnnotationsMenu, selectedGroup);
248     configureReferenceAnnotationsMenu(groupAddReferenceAnnotations,
249             selectedGroup);
250
251     try
252     {
253       jbInit();
254     } catch (Exception e)
255     {
256       e.printStackTrace();
257     }
258
259     JMenuItem menuItem;
260     if (seq != null)
261     {
262       sequenceMenu.setText(sequence.getName());
263       if (seq == ap.av.getAlignment().getSeqrep())
264       {
265         makeReferenceSeq.setText(
266                 MessageManager.getString("action.unmark_as_reference"));
267       }
268       else
269       {
270         makeReferenceSeq.setText(
271                 MessageManager.getString("action.set_as_reference"));
272       }
273
274       if (!ap.av.getAlignment().isNucleotide())
275       {
276         remove(rnaStructureMenu);
277       }
278       else
279       {
280         int origCount = rnaStructureMenu.getItemCount();
281         /*
282          * add menu items to 2D-render any alignment or sequence secondary
283          * structure annotation
284          */
285         AlignmentAnnotation[] aas = ap.av.getAlignment()
286                 .getAlignmentAnnotation();
287         if (aas != null)
288         {
289           for (final AlignmentAnnotation aa : aas)
290           {
291             if (aa.isValidStruc() && aa.sequenceRef == null)
292             {
293               /*
294                * valid alignment RNA secondary structure annotation
295                */
296               menuItem = new JMenuItem();
297               menuItem.setText(MessageManager.formatMessage(
298                       "label.2d_rna_structure_line",
299                       new Object[]
300               { aa.label }));
301               menuItem.addActionListener(new ActionListener()
302               {
303                 @Override
304                 public void actionPerformed(ActionEvent e)
305                 {
306                   new AppVarna(seq, aa, ap);
307                 }
308               });
309               rnaStructureMenu.add(menuItem);
310             }
311           }
312         }
313
314         if (seq.getAnnotation() != null)
315         {
316           AlignmentAnnotation seqAnns[] = seq.getAnnotation();
317           for (final AlignmentAnnotation aa : seqAnns)
318           {
319             if (aa.isValidStruc())
320             {
321               /*
322                * valid sequence RNA secondary structure annotation
323                */
324               // TODO: make rnastrucF a bit more nice
325               menuItem = new JMenuItem();
326               menuItem.setText(MessageManager.formatMessage(
327                       "label.2d_rna_sequence_name",
328                       new Object[]
329               { seq.getName() }));
330               menuItem.addActionListener(new ActionListener()
331               {
332                 @Override
333                 public void actionPerformed(ActionEvent e)
334                 {
335                   // TODO: VARNA does'nt print gaps in the sequence
336                   new AppVarna(seq, aa, ap);
337                 }
338               });
339               rnaStructureMenu.add(menuItem);
340             }
341           }
342         }
343         if (rnaStructureMenu.getItemCount() == origCount)
344         {
345           remove(rnaStructureMenu);
346         }
347       }
348
349       menuItem = new JMenuItem(
350               MessageManager.getString("action.hide_sequences"));
351       menuItem.addActionListener(new ActionListener()
352       {
353         @Override
354         public void actionPerformed(ActionEvent e)
355         {
356           hideSequences(false);
357         }
358       });
359       add(menuItem);
360
361       if (sequence.isHMMConsensusSequence())
362       {
363         JMenuItem selectHMM = new JCheckBoxMenuItem();
364         selectHMM.setText(MessageManager.getString("label.select_hmm"));
365         selectHMM.addActionListener(new ActionListener()
366         {
367
368           @Override
369           public void actionPerformed(ActionEvent e)
370           {
371             selectHMM_actionPerformed(e);
372           }
373         });
374         add(selectHMM);
375
376         JMenuItem hmmAlign = new JCheckBoxMenuItem();
377         hmmAlign.setText(MessageManager.getString("label.hmmalign"));
378         hmmAlign.addActionListener(new ActionListener()
379         {
380
381           @Override
382           public void actionPerformed(ActionEvent e)
383           {
384             hmmAlign_actionPerformed(e);
385           }
386         });
387         add(hmmAlign);
388       }
389
390       if (ap.av.getSelectionGroup() != null
391               && ap.av.getSelectionGroup().getSize() > 1)
392       {
393         menuItem = new JMenuItem(
394                 MessageManager.formatMessage("label.represent_group_with",
395                         new Object[]
396                 { seq.getName() }));
397         menuItem.addActionListener(new ActionListener()
398         {
399           @Override
400           public void actionPerformed(ActionEvent e)
401           {
402             hideSequences(true);
403           }
404         });
405         sequenceMenu.add(menuItem);
406       }
407
408       if (ap.av.hasHiddenRows())
409       {
410         final int index = ap.av.getAlignment().findIndex(seq);
411
412         if (ap.av.adjustForHiddenSeqs(index)
413                 - ap.av.adjustForHiddenSeqs(index - 1) > 1)
414         {
415           menuItem = new JMenuItem(
416                   MessageManager.getString("action.reveal_sequences"));
417           menuItem.addActionListener(new ActionListener()
418           {
419             @Override
420             public void actionPerformed(ActionEvent e)
421             {
422               ap.av.showSequence(index);
423               if (ap.overviewPanel != null)
424               {
425                 ap.overviewPanel.updateOverviewImage();
426               }
427             }
428           });
429           add(menuItem);
430         }
431       }
432     }
433     // for the case when no sequences are even visible
434     if (ap.av.hasHiddenRows())
435     {
436       {
437         menuItem = new JMenuItem(
438                 MessageManager.getString("action.reveal_all"));
439         menuItem.addActionListener(new ActionListener()
440         {
441           @Override
442           public void actionPerformed(ActionEvent e)
443           {
444             ap.av.showAllHiddenSeqs();
445             if (ap.overviewPanel != null)
446             {
447               ap.overviewPanel.updateOverviewImage();
448             }
449           }
450         });
451
452         add(menuItem);
453       }
454     }
455
456     SequenceGroup sg = ap.av.getSelectionGroup();
457     boolean isDefinedGroup = (sg != null)
458             ? ap.av.getAlignment().getGroups().contains(sg) : false;
459
460     if (sg != null && sg.getSize() > 0)
461     {
462       groupName.setText(MessageManager
463               .getString("label.edit_name_and_description_current_group"));
464
465       ColourMenuHelper.setColourSelected(colourMenu, sg.getColourScheme());
466
467       conservationMenuItem.setEnabled(!sg.isNucleotide());
468
469       if (sg.cs != null)
470       {
471         if (sg.cs.conservationApplied())
472         {
473           conservationMenuItem.setSelected(true);
474         }
475         if (sg.cs.getThreshold() > 0)
476         {
477           abovePIDColour.setSelected(true);
478         }
479       }
480       modifyConservation.setEnabled(conservationMenuItem.isSelected());
481       modifyPID.setEnabled(abovePIDColour.isSelected());
482       displayNonconserved.setSelected(sg.getShowNonconserved());
483       showText.setSelected(sg.getDisplayText());
484       showColourText.setSelected(sg.getColourText());
485       showBoxes.setSelected(sg.getDisplayBoxes());
486       // add any groupURLs to the groupURL submenu and make it visible
487       if (groupLinks != null && groupLinks.size() > 0)
488       {
489         buildGroupURLMenu(sg, groupLinks);
490       }
491       // Add a 'show all structures' for the current selection
492       Hashtable<String, PDBEntry> pdbe = new Hashtable<>(),
493               reppdb = new Hashtable<>();
494       SequenceI sqass = null;
495       for (SequenceI sq : ap.av.getSequenceSelection())
496       {
497         Vector<PDBEntry> pes = sq.getDatasetSequence().getAllPDBEntries();
498         if (pes != null && pes.size() > 0)
499         {
500           reppdb.put(pes.get(0).getId(), pes.get(0));
501           for (PDBEntry pe : pes)
502           {
503             pdbe.put(pe.getId(), pe);
504             if (sqass == null)
505             {
506               sqass = sq;
507             }
508           }
509         }
510       }
511       if (pdbe.size() > 0)
512       {
513         final PDBEntry[] pe = pdbe.values()
514                 .toArray(new PDBEntry[pdbe.size()]),
515                 pr = reppdb.values().toArray(new PDBEntry[reppdb.size()]);
516         final JMenuItem gpdbview, rpdbview;
517       }
518     }
519     else
520     {
521       groupMenu.setVisible(false);
522       editMenu.setVisible(false);
523     }
524
525     if (!isDefinedGroup)
526     {
527       createGroupMenuItem.setVisible(true);
528       unGroupMenuItem.setVisible(false);
529       jMenu1.setText(MessageManager.getString("action.edit_new_group"));
530     }
531     else
532     {
533       createGroupMenuItem.setVisible(false);
534       unGroupMenuItem.setVisible(true);
535       jMenu1.setText(MessageManager.getString("action.edit_group"));
536     }
537
538     if (seq == null)
539     {
540       sequenceMenu.setVisible(false);
541       pdbStructureDialog.setVisible(false);
542       rnaStructureMenu.setVisible(false);
543     }
544
545     if (links != null && links.size() > 0)
546     {
547       addFeatureLinks(seq, links);
548     }
549   }
550
551   /**
552    * Adds a 'Link' menu item with a sub-menu item for each hyperlink provided.
553    * 
554    * @param seq
555    * @param links
556    */
557   void addFeatureLinks(final SequenceI seq, List<String> links)
558   {
559     JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
560     Map<String, List<String>> linkset = new LinkedHashMap<>();
561
562     for (String link : links)
563     {
564       UrlLink urlLink = null;
565       try
566       {
567         urlLink = new UrlLink(link);
568       } catch (Exception foo)
569       {
570         Cache.log.error("Exception for URLLink '" + link + "'", foo);
571         continue;
572       }
573
574       if (!urlLink.isValid())
575       {
576         Cache.log.error(urlLink.getInvalidMessage());
577         continue;
578       }
579
580       urlLink.createLinksFromSeq(seq, linkset);
581     }
582
583     addshowLinks(linkMenu, linkset.values());
584
585     // disable link menu if there are no valid entries
586     if (linkMenu.getItemCount() > 0)
587     {
588       linkMenu.setEnabled(true);
589     }
590     else
591     {
592       linkMenu.setEnabled(false);
593     }
594
595     if (sequence != null)
596     {
597       sequenceMenu.add(linkMenu);
598     }
599     else
600     {
601       add(linkMenu);
602     }
603
604   }
605
606   /**
607    * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
608    * "All" is added first, followed by a separator. Then add any annotation
609    * types associated with the current selection. Separate menus are built for
610    * the selected sequence group (if any), and the selected sequence.
611    * <p>
612    * Some annotation rows are always rendered together - these can be identified
613    * by a common graphGroup property > -1. Only one of each group will be marked
614    * as visible (to avoid duplication of the display). For such groups we add a
615    * composite type name, e.g.
616    * <p>
617    * IUPredWS (Long), IUPredWS (Short)
618    * 
619    * @param seq
620    */
621   protected void buildAnnotationTypesMenus(JMenu showMenu, JMenu hideMenu,
622           List<SequenceI> forSequences)
623   {
624     showMenu.removeAll();
625     hideMenu.removeAll();
626
627     final List<String> all = Arrays
628             .asList(new String[]
629     { MessageManager.getString("label.all") });
630     addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true,
631             true);
632     addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
633             false);
634     showMenu.addSeparator();
635     hideMenu.addSeparator();
636
637     final AlignmentAnnotation[] annotations = ap.getAlignment()
638             .getAlignmentAnnotation();
639
640     /*
641      * Find shown/hidden annotations types, distinguished by source (calcId),
642      * and grouped by graphGroup. Using LinkedHashMap means we will retrieve in
643      * the insertion order, which is the order of the annotations on the
644      * alignment.
645      */
646     Map<String, List<List<String>>> shownTypes = new LinkedHashMap<>();
647     Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<>();
648     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
649             AlignmentAnnotationUtils.asList(annotations), forSequences);
650
651     for (String calcId : hiddenTypes.keySet())
652     {
653       for (List<String> type : hiddenTypes.get(calcId))
654       {
655         addAnnotationTypeToShowHide(showMenu, forSequences, calcId, type,
656                 false, true);
657       }
658     }
659     // grey out 'show annotations' if none are hidden
660     showMenu.setEnabled(!hiddenTypes.isEmpty());
661
662     for (String calcId : shownTypes.keySet())
663     {
664       for (List<String> type : shownTypes.get(calcId))
665       {
666         addAnnotationTypeToShowHide(hideMenu, forSequences, calcId, type,
667                 false, false);
668       }
669     }
670     // grey out 'hide annotations' if none are shown
671     hideMenu.setEnabled(!shownTypes.isEmpty());
672   }
673
674   /**
675    * Returns a list of sequences - either the current selection group (if there
676    * is one), else the specified single sequence.
677    * 
678    * @param seq
679    * @return
680    */
681   protected List<SequenceI> getSequenceScope(SequenceI seq)
682   {
683     List<SequenceI> forSequences = null;
684     final SequenceGroup selectionGroup = ap.av.getSelectionGroup();
685     if (selectionGroup != null && selectionGroup.getSize() > 0)
686     {
687       forSequences = selectionGroup.getSequences();
688     }
689     else
690     {
691       forSequences = seq == null ? Collections.<SequenceI> emptyList()
692               : Arrays.asList(seq);
693     }
694     return forSequences;
695   }
696
697   /**
698    * Add one annotation type to the 'Show Annotations' or 'Hide Annotations'
699    * menus.
700    * 
701    * @param showOrHideMenu
702    *          the menu to add to
703    * @param forSequences
704    *          the sequences whose annotations may be shown or hidden
705    * @param calcId
706    * @param types
707    *          the label to add
708    * @param allTypes
709    *          if true this is a special label meaning 'All'
710    * @param actionIsShow
711    *          if true, the select menu item action is to show the annotation
712    *          type, else hide
713    */
714   protected void addAnnotationTypeToShowHide(JMenu showOrHideMenu,
715           final List<SequenceI> forSequences, String calcId,
716           final List<String> types, final boolean allTypes,
717           final boolean actionIsShow)
718   {
719     String label = types.toString(); // [a, b, c]
720     label = label.substring(1, label.length() - 1); // a, b, c
721     final JMenuItem item = new JMenuItem(label);
722     item.setToolTipText(calcId);
723     item.addActionListener(new ActionListener()
724     {
725       @Override
726       public void actionPerformed(ActionEvent e)
727       {
728         AlignmentUtils.showOrHideSequenceAnnotations(ap.getAlignment(),
729                 types, forSequences, allTypes, actionIsShow);
730         refresh();
731       }
732     });
733     showOrHideMenu.add(item);
734   }
735
736   private void buildGroupURLMenu(SequenceGroup sg, List<String> groupLinks)
737   {
738
739     // TODO: usability: thread off the generation of group url content so root
740     // menu appears asap
741     // sequence only URLs
742     // ID/regex match URLs
743     groupLinksMenu = new JMenu(
744             MessageManager.getString("action.group_link"));
745     // three types of url that might be created.
746     JMenu[] linkMenus = new JMenu[] { null,
747         new JMenu(MessageManager.getString("action.ids")),
748         new JMenu(MessageManager.getString("action.sequences")),
749         new JMenu(MessageManager.getString("action.ids_sequences")) };
750
751     SequenceI[] seqs = ap.av.getSelectionAsNewSequence();
752     String[][] idandseqs = GroupUrlLink.formStrings(seqs);
753     Hashtable<String, Object[]> commonDbrefs = new Hashtable<>();
754     for (int sq = 0; sq < seqs.length; sq++)
755     {
756
757       int start = seqs[sq].findPosition(sg.getStartRes()),
758               end = seqs[sq].findPosition(sg.getEndRes());
759       // just collect ids from dataset sequence
760       // TODO: check if IDs collected from selecton group intersects with the
761       // current selection, too
762       SequenceI sqi = seqs[sq];
763       while (sqi.getDatasetSequence() != null)
764       {
765         sqi = sqi.getDatasetSequence();
766       }
767       DBRefEntry[] dbr = sqi.getDBRefs();
768       if (dbr != null && dbr.length > 0)
769       {
770         for (int d = 0; d < dbr.length; d++)
771         {
772           String src = dbr[d].getSource(); // jalview.util.DBRefUtils.getCanonicalName(dbr[d].getSource()).toUpperCase();
773           Object[] sarray = commonDbrefs.get(src);
774           if (sarray == null)
775           {
776             sarray = new Object[2];
777             sarray[0] = new int[] { 0 };
778             sarray[1] = new String[seqs.length];
779
780             commonDbrefs.put(src, sarray);
781           }
782
783           if (((String[]) sarray[1])[sq] == null)
784           {
785             if (!dbr[d].hasMap() || (dbr[d].getMap()
786                     .locateMappedRange(start, end) != null))
787             {
788               ((String[]) sarray[1])[sq] = dbr[d].getAccessionId();
789               ((int[]) sarray[0])[0]++;
790             }
791           }
792         }
793       }
794     }
795     // now create group links for all distinct ID/sequence sets.
796     boolean addMenu = false; // indicates if there are any group links to give
797                              // to user
798     for (String link : groupLinks)
799     {
800       GroupUrlLink urlLink = null;
801       try
802       {
803         urlLink = new GroupUrlLink(link);
804       } catch (Exception foo)
805       {
806         Cache.log.error("Exception for GroupURLLink '" + link + "'", foo);
807         continue;
808       }
809       ;
810       if (!urlLink.isValid())
811       {
812         Cache.log.error(urlLink.getInvalidMessage());
813         continue;
814       }
815       final String label = urlLink.getLabel();
816       boolean usingNames = false;
817       // Now see which parts of the group apply for this URL
818       String ltarget = urlLink.getTarget(); // jalview.util.DBRefUtils.getCanonicalName(urlLink.getTarget());
819       Object[] idset = commonDbrefs.get(ltarget.toUpperCase());
820       String[] seqstr, ids; // input to makeUrl
821       if (idset != null)
822       {
823         int numinput = ((int[]) idset[0])[0];
824         String[] allids = ((String[]) idset[1]);
825         seqstr = new String[numinput];
826         ids = new String[numinput];
827         for (int sq = 0, idcount = 0; sq < seqs.length; sq++)
828         {
829           if (allids[sq] != null)
830           {
831             ids[idcount] = allids[sq];
832             seqstr[idcount++] = idandseqs[1][sq];
833           }
834         }
835       }
836       else
837       {
838         // just use the id/seq set
839         seqstr = idandseqs[1];
840         ids = idandseqs[0];
841         usingNames = true;
842       }
843       // and try and make the groupURL!
844
845       Object[] urlset = null;
846       try
847       {
848         urlset = urlLink.makeUrlStubs(ids, seqstr,
849                 "FromJalview" + System.currentTimeMillis(), false);
850       } catch (UrlStringTooLongException e)
851       {
852       }
853       if (urlset != null)
854       {
855         int type = urlLink.getGroupURLType() & 3;
856         // first two bits ofurlLink type bitfield are sequenceids and sequences
857         // TODO: FUTURE: ensure the groupURL menu structure can be generalised
858         addshowLink(linkMenus[type], label + (((type & 1) == 1)
859                 ? ("(" + (usingNames ? "Names" : ltarget) + ")") : ""),
860                 urlLink, urlset);
861         addMenu = true;
862       }
863     }
864     if (addMenu)
865     {
866       groupLinksMenu = new JMenu(
867               MessageManager.getString("action.group_link"));
868       for (int m = 0; m < linkMenus.length; m++)
869       {
870         if (linkMenus[m] != null
871                 && linkMenus[m].getMenuComponentCount() > 0)
872         {
873           groupLinksMenu.add(linkMenus[m]);
874         }
875       }
876
877       groupMenu.add(groupLinksMenu);
878     }
879   }
880
881   private void addshowLinks(JMenu linkMenu,
882           Collection<List<String>> linkset)
883   {
884     for (List<String> linkstrset : linkset)
885     {
886       // split linkstr into label and url
887       addshowLink(linkMenu, linkstrset.get(1), linkstrset.get(3));
888     }
889   }
890
891   /**
892    * add a show URL menu item to the given linkMenu
893    * 
894    * @param linkMenu
895    * @param label
896    *          - menu label string
897    * @param url
898    *          - url to open
899    */
900   private void addshowLink(JMenu linkMenu, String label, final String url)
901   {
902     JMenuItem item = new JMenuItem(label);
903     item.setToolTipText(MessageManager.formatMessage("label.open_url_param",
904             new Object[]
905     { url }));
906     item.addActionListener(new ActionListener()
907     {
908       @Override
909       public void actionPerformed(ActionEvent e)
910       {
911         new Thread(new Runnable()
912         {
913
914           @Override
915           public void run()
916           {
917             showLink(url);
918           }
919
920         }).start();
921       }
922     });
923
924     linkMenu.add(item);
925   }
926
927   /**
928    * add a late bound groupURL item to the given linkMenu
929    * 
930    * @param linkMenu
931    * @param label
932    *          - menu label string
933    * @param urlgenerator
934    *          GroupURLLink used to generate URL
935    * @param urlstub
936    *          Object array returned from the makeUrlStubs function.
937    */
938   private void addshowLink(JMenu linkMenu, String label,
939           final GroupUrlLink urlgenerator, final Object[] urlstub)
940   {
941     JMenuItem item = new JMenuItem(label);
942     item.setToolTipText(
943             MessageManager.formatMessage("label.open_url_seqs_param",
944                     new Object[]
945             { urlgenerator.getUrl_prefix(),
946         urlgenerator.getNumberInvolved(urlstub) }));
947     // TODO: put in info about what is being sent.
948     item.addActionListener(new ActionListener()
949     {
950       @Override
951       public void actionPerformed(ActionEvent e)
952       {
953         new Thread(new Runnable()
954         {
955
956           @Override
957           public void run()
958           {
959             try
960             {
961               showLink(urlgenerator.constructFrom(urlstub));
962             } catch (UrlStringTooLongException e2)
963             {
964             }
965           }
966
967         }).start();
968       }
969     });
970
971     linkMenu.add(item);
972   }
973
974   /**
975    * DOCUMENT ME!
976    * 
977    * @throws Exception
978    *           DOCUMENT ME!
979    */
980   private void jbInit() throws Exception
981   {
982     groupMenu.setText(MessageManager.getString("label.selection"));
983     groupName.setText(MessageManager.getString("label.name"));
984     groupName.addActionListener(new ActionListener()
985     {
986       @Override
987       public void actionPerformed(ActionEvent e)
988       {
989         groupName_actionPerformed();
990       }
991     });
992     sequenceMenu.setText(MessageManager.getString("label.sequence"));
993     sequenceName.setText(
994             MessageManager.getString("label.edit_name_description"));
995     sequenceName.addActionListener(new ActionListener()
996     {
997       @Override
998       public void actionPerformed(ActionEvent e)
999       {
1000         sequenceName_actionPerformed();
1001       }
1002     });
1003     chooseAnnotations
1004             .setText(MessageManager.getString("action.choose_annotations"));
1005     chooseAnnotations.addActionListener(new ActionListener()
1006     {
1007       @Override
1008       public void actionPerformed(ActionEvent e)
1009       {
1010         chooseAnnotations_actionPerformed(e);
1011       }
1012     });
1013     sequenceDetails
1014             .setText(MessageManager.getString("label.sequence_details"));
1015     sequenceDetails.addActionListener(new ActionListener()
1016     {
1017       @Override
1018       public void actionPerformed(ActionEvent e)
1019       {
1020         sequenceDetails_actionPerformed();
1021       }
1022     });
1023     sequenceSelDetails
1024             .setText(MessageManager.getString("label.sequence_details"));
1025     sequenceSelDetails.addActionListener(new ActionListener()
1026     {
1027       @Override
1028       public void actionPerformed(ActionEvent e)
1029       {
1030         sequenceSelectionDetails_actionPerformed();
1031       }
1032     });
1033
1034     unGroupMenuItem
1035             .setText(MessageManager.getString("action.remove_group"));
1036     unGroupMenuItem.addActionListener(new ActionListener()
1037     {
1038       @Override
1039       public void actionPerformed(ActionEvent e)
1040       {
1041         unGroupMenuItem_actionPerformed();
1042       }
1043     });
1044     createGroupMenuItem
1045             .setText(MessageManager.getString("action.create_group"));
1046     createGroupMenuItem.addActionListener(new ActionListener()
1047     {
1048       @Override
1049       public void actionPerformed(ActionEvent e)
1050       {
1051         createGroupMenuItem_actionPerformed();
1052       }
1053     });
1054
1055     outline.setText(MessageManager.getString("action.border_colour"));
1056     outline.addActionListener(new ActionListener()
1057     {
1058       @Override
1059       public void actionPerformed(ActionEvent e)
1060       {
1061         outline_actionPerformed();
1062       }
1063     });
1064     showBoxes.setText(MessageManager.getString("action.boxes"));
1065     showBoxes.setState(true);
1066     showBoxes.addActionListener(new ActionListener()
1067     {
1068       @Override
1069       public void actionPerformed(ActionEvent e)
1070       {
1071         showBoxes_actionPerformed();
1072       }
1073     });
1074     showText.setText(MessageManager.getString("action.text"));
1075     showText.setState(true);
1076     showText.addActionListener(new ActionListener()
1077     {
1078       @Override
1079       public void actionPerformed(ActionEvent e)
1080       {
1081         showText_actionPerformed();
1082       }
1083     });
1084     showColourText.setText(MessageManager.getString("label.colour_text"));
1085     showColourText.addActionListener(new ActionListener()
1086     {
1087       @Override
1088       public void actionPerformed(ActionEvent e)
1089       {
1090         showColourText_actionPerformed();
1091       }
1092     });
1093     displayNonconserved
1094             .setText(MessageManager.getString("label.show_non_conserved"));
1095     displayNonconserved.setState(true);
1096     displayNonconserved.addActionListener(new ActionListener()
1097     {
1098       @Override
1099       public void actionPerformed(ActionEvent e)
1100       {
1101         showNonconserved_actionPerformed();
1102       }
1103     });
1104     editMenu.setText(MessageManager.getString("action.edit"));
1105     cut.setText(MessageManager.getString("action.cut"));
1106     cut.addActionListener(new ActionListener()
1107     {
1108       @Override
1109       public void actionPerformed(ActionEvent e)
1110       {
1111         cut_actionPerformed();
1112       }
1113     });
1114     upperCase.setText(MessageManager.getString("label.to_upper_case"));
1115     upperCase.addActionListener(new ActionListener()
1116     {
1117       @Override
1118       public void actionPerformed(ActionEvent e)
1119       {
1120         changeCase(e);
1121       }
1122     });
1123     copy.setText(MessageManager.getString("action.copy"));
1124     copy.addActionListener(new ActionListener()
1125     {
1126       @Override
1127       public void actionPerformed(ActionEvent e)
1128       {
1129         copy_actionPerformed();
1130       }
1131     });
1132     lowerCase.setText(MessageManager.getString("label.to_lower_case"));
1133     lowerCase.addActionListener(new ActionListener()
1134     {
1135       @Override
1136       public void actionPerformed(ActionEvent e)
1137       {
1138         changeCase(e);
1139       }
1140     });
1141     toggle.setText(MessageManager.getString("label.toggle_case"));
1142     toggle.addActionListener(new ActionListener()
1143     {
1144       @Override
1145       public void actionPerformed(ActionEvent e)
1146       {
1147         changeCase(e);
1148       }
1149     });
1150     outputMenu.setText(
1151             MessageManager.getString("label.out_to_textbox") + "...");
1152     seqShowAnnotationsMenu
1153             .setText(MessageManager.getString("label.show_annotations"));
1154     seqHideAnnotationsMenu
1155             .setText(MessageManager.getString("label.hide_annotations"));
1156     groupShowAnnotationsMenu
1157             .setText(MessageManager.getString("label.show_annotations"));
1158     groupHideAnnotationsMenu
1159             .setText(MessageManager.getString("label.hide_annotations"));
1160     sequenceFeature.setText(
1161             MessageManager.getString("label.create_sequence_feature"));
1162     sequenceFeature.addActionListener(new ActionListener()
1163     {
1164       @Override
1165       public void actionPerformed(ActionEvent e)
1166       {
1167         sequenceFeature_actionPerformed();
1168       }
1169     });
1170     hmmBuildGroup.setText(MessageManager.getString("label.group_hmmbuild"));
1171     hmmBuildGroup.addActionListener(new ActionListener()
1172     {
1173       @Override
1174       public void actionPerformed(ActionEvent e)
1175       {
1176         hmmBuildGroup_actionPerformed();
1177       }
1178     });
1179     jMenu1.setText(MessageManager.getString("label.group"));
1180     pdbStructureDialog.setText(
1181             MessageManager.getString("label.show_pdbstruct_dialog"));
1182     pdbStructureDialog.addActionListener(new ActionListener()
1183     {
1184       @Override
1185       public void actionPerformed(ActionEvent actionEvent)
1186       {
1187         SequenceI[] selectedSeqs = new SequenceI[] { sequence };
1188         if (ap.av.getSelectionGroup() != null)
1189         {
1190           selectedSeqs = ap.av.getSequenceSelection();
1191         }
1192         new StructureChooser(selectedSeqs, sequence, ap);
1193       }
1194     });
1195
1196     rnaStructureMenu
1197             .setText(MessageManager.getString("label.view_rna_structure"));
1198
1199     // colStructureMenu.setText("Colour By Structure");
1200     editSequence.setText(
1201             MessageManager.getString("label.edit_sequence") + "...");
1202     editSequence.addActionListener(new ActionListener()
1203     {
1204       @Override
1205       public void actionPerformed(ActionEvent actionEvent)
1206       {
1207         editSequence_actionPerformed(actionEvent);
1208       }
1209     });
1210     makeReferenceSeq.setText(
1211             MessageManager.getString("label.mark_as_representative"));
1212     makeReferenceSeq.addActionListener(new ActionListener()
1213     {
1214
1215       @Override
1216       public void actionPerformed(ActionEvent actionEvent)
1217       {
1218         makeReferenceSeq_actionPerformed(actionEvent);
1219
1220       }
1221     });
1222     hideInsertions
1223             .setText(MessageManager.getString("label.hide_insertions"));
1224     hideInsertions.addActionListener(new ActionListener()
1225     {
1226
1227       @Override
1228       public void actionPerformed(ActionEvent e)
1229       {
1230         hideInsertions_actionPerformed(e);
1231       }
1232     });
1233
1234     groupMenu.add(sequenceSelDetails);
1235     add(groupMenu);
1236     add(sequenceMenu);
1237     add(rnaStructureMenu);
1238     add(pdbStructureDialog);
1239     if (sequence != null)
1240     {
1241       add(hideInsertions);
1242     }
1243     // annotations configuration panel suppressed for now
1244     // groupMenu.add(chooseAnnotations);
1245
1246     /*
1247      * Add show/hide annotations to the Sequence menu, and to the Selection menu
1248      * (if a selection group is in force).
1249      */
1250     sequenceMenu.add(seqShowAnnotationsMenu);
1251     sequenceMenu.add(seqHideAnnotationsMenu);
1252     sequenceMenu.add(seqAddReferenceAnnotations);
1253     groupMenu.add(groupShowAnnotationsMenu);
1254     groupMenu.add(groupHideAnnotationsMenu);
1255     groupMenu.add(groupAddReferenceAnnotations);
1256     groupMenu.add(editMenu);
1257     groupMenu.add(outputMenu);
1258     groupMenu.add(sequenceFeature);
1259     groupMenu.add(createGroupMenuItem);
1260     groupMenu.add(unGroupMenuItem);
1261     groupMenu.add(jMenu1);
1262     sequenceMenu.add(sequenceName);
1263     sequenceMenu.add(sequenceDetails);
1264     sequenceMenu.add(makeReferenceSeq);
1265
1266     initColourMenu();
1267     buildColourMenu();
1268
1269     editMenu.add(copy);
1270     editMenu.add(cut);
1271     editMenu.add(editSequence);
1272     editMenu.add(upperCase);
1273     editMenu.add(lowerCase);
1274     editMenu.add(toggle);
1275     // JBPNote: These shouldn't be added here - should appear in a generic
1276     // 'apply web service to this sequence menu'
1277     // pdbMenu.add(RNAFold);
1278     // pdbMenu.add(ContraFold);
1279     jMenu1.add(groupName);
1280     jMenu1.add(colourMenu);
1281     jMenu1.add(showBoxes);
1282     jMenu1.add(showText);
1283     jMenu1.add(showColourText);
1284     jMenu1.add(outline);
1285     jMenu1.add(displayNonconserved);
1286   }
1287
1288   protected void hmmBuildGroup_actionPerformed()
1289   {
1290     new Thread(new HMMBuildThread(ap.alignFrame)).start();
1291     ap.repaint();
1292
1293   }
1294
1295   protected void selectHMM_actionPerformed(ActionEvent e)
1296   {
1297     HiddenMarkovModel hmm = ap.av.getSequenceSelection()[0].getHMM();
1298     ap.av.setSelectedHMM(hmm);
1299   }
1300
1301   protected void hmmAlign_actionPerformed(ActionEvent e)
1302   {
1303     ap.av.setSelectedHMM(this.sequence.getHMM());
1304     new Thread(new HMMAlignThread(ap.alignFrame, true)).start();
1305     ap.repaint();
1306   }
1307
1308   /**
1309    * Constructs the entries for the colour menu
1310    */
1311   protected void initColourMenu()
1312   {
1313     colourMenu.setText(MessageManager.getString("label.group_colour"));
1314     textColour.setText(MessageManager.getString("label.text_colour"));
1315     textColour.addActionListener(new ActionListener()
1316     {
1317       @Override
1318       public void actionPerformed(ActionEvent e)
1319       {
1320         textColour_actionPerformed();
1321       }
1322     });
1323
1324     abovePIDColour.setText(
1325             MessageManager.getString("label.above_identity_threshold"));
1326     abovePIDColour.addActionListener(new ActionListener()
1327     {
1328       @Override
1329       public void actionPerformed(ActionEvent e)
1330       {
1331         abovePIDColour_actionPerformed(abovePIDColour.isSelected());
1332       }
1333     });
1334
1335     modifyPID.setText(
1336             MessageManager.getString("label.modify_identity_threshold"));
1337     modifyPID.addActionListener(new ActionListener()
1338     {
1339       @Override
1340       public void actionPerformed(ActionEvent e)
1341       {
1342         modifyPID_actionPerformed();
1343       }
1344     });
1345
1346     conservationMenuItem
1347             .setText(MessageManager.getString("action.by_conservation"));
1348     conservationMenuItem.addActionListener(new ActionListener()
1349     {
1350       @Override
1351       public void actionPerformed(ActionEvent e)
1352       {
1353         conservationMenuItem_actionPerformed(
1354                 conservationMenuItem.isSelected());
1355       }
1356     });
1357
1358     modifyConservation.setText(MessageManager
1359             .getString("label.modify_conservation_threshold"));
1360     modifyConservation.addActionListener(new ActionListener()
1361     {
1362       @Override
1363       public void actionPerformed(ActionEvent e)
1364       {
1365         modifyConservation_actionPerformed();
1366       }
1367     });
1368   }
1369
1370   /**
1371    * Builds the group colour sub-menu, including any user-defined colours which
1372    * were loaded at startup or during the Jalview session
1373    */
1374   protected void buildColourMenu()
1375   {
1376     SequenceGroup sg = ap.av.getSelectionGroup();
1377     if (sg == null)
1378     {
1379       /*
1380        * popup menu with no sequence group scope
1381        */
1382       return;
1383     }
1384     colourMenu.removeAll();
1385     colourMenu.add(textColour);
1386     colourMenu.addSeparator();
1387
1388     ColourMenuHelper.addMenuItems(colourMenu, this, sg, false);
1389
1390     colourMenu.addSeparator();
1391     colourMenu.add(conservationMenuItem);
1392     colourMenu.add(modifyConservation);
1393     colourMenu.add(abovePIDColour);
1394     colourMenu.add(modifyPID);
1395   }
1396
1397   protected void modifyConservation_actionPerformed()
1398   {
1399     SequenceGroup sg = getGroup();
1400     if (sg.cs != null)
1401     {
1402       SliderPanel.setConservationSlider(ap, sg.cs, sg.getName());
1403       SliderPanel.showConservationSlider();
1404     }
1405   }
1406
1407   protected void modifyPID_actionPerformed()
1408   {
1409     SequenceGroup sg = getGroup();
1410     if (sg.cs != null)
1411     {
1412       // int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup()
1413       // .getName());
1414       // sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus());
1415       SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup().getName());
1416       SliderPanel.showPIDSlider();
1417     }
1418   }
1419
1420   /**
1421    * Check for any annotations on the underlying dataset sequences (for the
1422    * current selection group) which are not 'on the alignment'.If any are found,
1423    * enable the option to add them to the alignment. The criteria for 'on the
1424    * alignment' is finding an alignment annotation on the alignment, matched on
1425    * calcId, label and sequenceRef.
1426    * 
1427    * A tooltip is also constructed that displays the source (calcId) and type
1428    * (label) of the annotations that can be added.
1429    * 
1430    * @param menuItem
1431    * @param forSequences
1432    */
1433   protected void configureReferenceAnnotationsMenu(JMenuItem menuItem,
1434           List<SequenceI> forSequences)
1435   {
1436     menuItem.setEnabled(false);
1437
1438     /*
1439      * Temporary store to hold distinct calcId / type pairs for the tooltip.
1440      * Using TreeMap means calcIds are shown in alphabetical order.
1441      */
1442     SortedMap<String, String> tipEntries = new TreeMap<>();
1443     final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<>();
1444     AlignmentI al = this.ap.av.getAlignment();
1445     AlignmentUtils.findAddableReferenceAnnotations(forSequences, tipEntries,
1446             candidates, al);
1447     if (!candidates.isEmpty())
1448     {
1449       StringBuilder tooltip = new StringBuilder(64);
1450       tooltip.append(MessageManager.getString("label.add_annotations_for"));
1451
1452       /*
1453        * Found annotations that could be added. Enable the menu item, and
1454        * configure its tooltip and action.
1455        */
1456       menuItem.setEnabled(true);
1457       for (String calcId : tipEntries.keySet())
1458       {
1459         tooltip.append("<br/>" + calcId + "/" + tipEntries.get(calcId));
1460       }
1461       String tooltipText = JvSwingUtils.wrapTooltip(true,
1462               tooltip.toString());
1463       menuItem.setToolTipText(tooltipText);
1464
1465       menuItem.addActionListener(new ActionListener()
1466       {
1467         @Override
1468         public void actionPerformed(ActionEvent e)
1469         {
1470           addReferenceAnnotations_actionPerformed(candidates);
1471         }
1472       });
1473     }
1474   }
1475
1476   /**
1477    * Add annotations to the sequences and to the alignment.
1478    * 
1479    * @param candidates
1480    *          a map whose keys are sequences on the alignment, and values a list
1481    *          of annotations to add to each sequence
1482    */
1483   protected void addReferenceAnnotations_actionPerformed(
1484           Map<SequenceI, List<AlignmentAnnotation>> candidates)
1485   {
1486     final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
1487     final AlignmentI alignment = this.ap.getAlignment();
1488     AlignmentUtils.addReferenceAnnotations(candidates, alignment,
1489             selectionGroup);
1490     refresh();
1491   }
1492
1493   protected void makeReferenceSeq_actionPerformed(ActionEvent actionEvent)
1494   {
1495     if (!ap.av.getAlignment().hasSeqrep())
1496     {
1497       // initialise the display flags so the user sees something happen
1498       ap.av.setDisplayReferenceSeq(true);
1499       ap.av.setColourByReferenceSeq(true);
1500       ap.av.getAlignment().setSeqrep(sequence);
1501     }
1502     else
1503     {
1504       if (ap.av.getAlignment().getSeqrep() == sequence)
1505       {
1506         ap.av.getAlignment().setSeqrep(null);
1507       }
1508       else
1509       {
1510         ap.av.getAlignment().setSeqrep(sequence);
1511       }
1512     }
1513     refresh();
1514   }
1515
1516   protected void hideInsertions_actionPerformed(ActionEvent actionEvent)
1517   {
1518     if (sequence != null)
1519     {
1520       /* ColumnSelection cs = ap.av.getColumnSelection();
1521        if (cs == null)
1522        {
1523          cs = new ColumnSelection();
1524        }
1525        cs.hideInsertionsFor(sequence);
1526        ap.av.setColumnSelection(cs);*/
1527
1528       HiddenColumns hidden = ap.av.getAlignment().getHiddenColumns();
1529       if (hidden == null)
1530       {
1531         hidden = new HiddenColumns();
1532       }
1533       hidden.hideInsertionsFor(sequence);
1534       ap.av.getAlignment().setHiddenColumns(hidden);
1535     }
1536     refresh();
1537   }
1538
1539   protected void sequenceSelectionDetails_actionPerformed()
1540   {
1541     createSequenceDetailsReport(ap.av.getSequenceSelection());
1542   }
1543
1544   protected void sequenceDetails_actionPerformed()
1545   {
1546     createSequenceDetailsReport(new SequenceI[] { sequence });
1547   }
1548
1549   public void createSequenceDetailsReport(SequenceI[] sequences)
1550   {
1551     CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
1552     StringBuilder contents = new StringBuilder(128);
1553     for (SequenceI seq : sequences)
1554     {
1555       contents.append("<p><h2>"
1556               + MessageManager.formatMessage(
1557                       "label.create_sequence_details_report_annotation_for",
1558                       new Object[]
1559                       { seq.getDisplayId(true) })
1560               + "</h2></p><p>");
1561       new SequenceAnnotationReport(null)
1562               .createSequenceAnnotationReport(contents, seq, true, true,
1563                       (ap.getSeqPanel().seqCanvas.fr != null)
1564                               ? ap.getSeqPanel().seqCanvas.fr.getMinMax()
1565                               : null);
1566       contents.append("</p>");
1567     }
1568     cap.setText("<html>" + contents.toString() + "</html>");
1569
1570     Desktop.addInternalFrame(cap, MessageManager.formatMessage(
1571             "label.sequence_details_for",
1572             (sequences.length == 1
1573                     ? new Object[]
1574                     { sequences[0].getDisplayId(true) }
1575                     : new Object[]
1576             { MessageManager.getString("label.selection") })), 500, 400);
1577
1578   }
1579
1580   protected void showNonconserved_actionPerformed()
1581   {
1582     getGroup().setShowNonconserved(displayNonconserved.isSelected());
1583     refresh();
1584   }
1585
1586   /**
1587    * call to refresh view after settings change
1588    */
1589   void refresh()
1590   {
1591     ap.updateAnnotation();
1592     ap.paintAlignment(true);
1593
1594     PaintRefresher.Refresh(this, ap.av.getSequenceSetId());
1595   }
1596
1597   /*
1598    * protected void covariationColour_actionPerformed() { getGroup().cs = new
1599    * CovariationColourScheme(sequence.getAnnotation()[0]); refresh(); }
1600    */
1601   /**
1602    * DOCUMENT ME!
1603    * 
1604    * @param selected
1605    * 
1606    * @param e
1607    *          DOCUMENT ME!
1608    */
1609   public void abovePIDColour_actionPerformed(boolean selected)
1610   {
1611     SequenceGroup sg = getGroup();
1612     if (sg.cs == null)
1613     {
1614       return;
1615     }
1616
1617     if (selected)
1618     {
1619       sg.cs.setConsensus(AAFrequency.calculate(
1620               sg.getSequences(ap.av.getHiddenRepSequences()),
1621               sg.getStartRes(), sg.getEndRes() + 1));
1622
1623       int threshold = SliderPanel.setPIDSliderSource(ap,
1624               sg.getGroupColourScheme(), getGroup().getName());
1625
1626       sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus());
1627
1628       SliderPanel.showPIDSlider();
1629     }
1630     else
1631     // remove PIDColouring
1632     {
1633       sg.cs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
1634       SliderPanel.hidePIDSlider();
1635     }
1636     modifyPID.setEnabled(selected);
1637
1638     refresh();
1639   }
1640
1641   /**
1642    * Open a panel where the user can choose which types of sequence annotation
1643    * to show or hide.
1644    * 
1645    * @param e
1646    */
1647   protected void chooseAnnotations_actionPerformed(ActionEvent e)
1648   {
1649     // todo correct way to guard against opening a duplicate panel?
1650     new AnnotationChooser(ap);
1651   }
1652
1653   /**
1654    * DOCUMENT ME!
1655    * 
1656    * @param e
1657    *          DOCUMENT ME!
1658    */
1659   public void conservationMenuItem_actionPerformed(boolean selected)
1660   {
1661     SequenceGroup sg = getGroup();
1662     if (sg.cs == null)
1663     {
1664       return;
1665     }
1666
1667     if (selected)
1668     {
1669       // JBPNote: Conservation name shouldn't be i18n translated
1670       Conservation c = new Conservation("Group",
1671               sg.getSequences(ap.av.getHiddenRepSequences()),
1672               sg.getStartRes(), sg.getEndRes() + 1);
1673
1674       c.calculate();
1675       c.verdict(false, ap.av.getConsPercGaps());
1676       sg.cs.setConservation(c);
1677
1678       SliderPanel.setConservationSlider(ap, sg.getGroupColourScheme(),
1679               sg.getName());
1680       SliderPanel.showConservationSlider();
1681     }
1682     else
1683     // remove ConservationColouring
1684     {
1685       sg.cs.setConservation(null);
1686       SliderPanel.hideConservationSlider();
1687     }
1688     modifyConservation.setEnabled(selected);
1689
1690     refresh();
1691   }
1692
1693   /**
1694    * DOCUMENT ME!
1695    * 
1696    * @param e
1697    *          DOCUMENT ME!
1698    */
1699   protected void groupName_actionPerformed()
1700   {
1701
1702     SequenceGroup sg = getGroup();
1703     EditNameDialog dialog = new EditNameDialog(sg.getName(),
1704             sg.getDescription(),
1705             "       " + MessageManager.getString("label.group_name") + " ",
1706             MessageManager.getString("label.group_description") + " ",
1707             MessageManager.getString("label.edit_group_name_description"),
1708             ap.alignFrame);
1709
1710     if (!dialog.accept)
1711     {
1712       return;
1713     }
1714
1715     sg.setName(dialog.getName());
1716     sg.setDescription(dialog.getDescription());
1717     refresh();
1718   }
1719
1720   /**
1721    * Get selection group - adding it to the alignment if necessary.
1722    * 
1723    * @return sequence group to operate on
1724    */
1725   SequenceGroup getGroup()
1726   {
1727     SequenceGroup sg = ap.av.getSelectionGroup();
1728     // this method won't add a new group if it already exists
1729     if (sg != null)
1730     {
1731       ap.av.getAlignment().addGroup(sg);
1732     }
1733
1734     return sg;
1735   }
1736
1737   /**
1738    * DOCUMENT ME!
1739    * 
1740    * @param e
1741    *          DOCUMENT ME!
1742    */
1743   void sequenceName_actionPerformed()
1744   {
1745     EditNameDialog dialog = new EditNameDialog(sequence.getName(),
1746             sequence.getDescription(),
1747             "       " + MessageManager.getString("label.sequence_name")
1748                     + " ",
1749             MessageManager.getString("label.sequence_description") + " ",
1750             MessageManager
1751                     .getString("label.edit_sequence_name_description"),
1752             ap.alignFrame);
1753
1754     if (!dialog.accept)
1755     {
1756       return;
1757     }
1758
1759     if (dialog.getName() != null)
1760     {
1761       if (dialog.getName().indexOf(" ") > -1)
1762       {
1763         JvOptionPane.showMessageDialog(ap,
1764                 MessageManager
1765                         .getString("label.spaces_converted_to_backslashes"),
1766                 MessageManager
1767                         .getString("label.no_spaces_allowed_sequence_name"),
1768                 JvOptionPane.WARNING_MESSAGE);
1769       }
1770
1771       sequence.setName(dialog.getName().replace(' ', '_'));
1772       ap.paintAlignment(false);
1773     }
1774
1775     sequence.setDescription(dialog.getDescription());
1776
1777     ap.av.firePropertyChange("alignment", null,
1778             ap.av.getAlignment().getSequences());
1779
1780   }
1781
1782   /**
1783    * DOCUMENT ME!
1784    * 
1785    * @param e
1786    *          DOCUMENT ME!
1787    */
1788   void unGroupMenuItem_actionPerformed()
1789   {
1790     SequenceGroup sg = ap.av.getSelectionGroup();
1791     ap.av.getAlignment().deleteGroup(sg);
1792     ap.av.setSelectionGroup(null);
1793     refresh();
1794   }
1795
1796   void createGroupMenuItem_actionPerformed()
1797   {
1798     getGroup(); // implicitly creates group - note - should apply defaults / use
1799                 // standard alignment window logic for this
1800     refresh();
1801   }
1802
1803   /**
1804    * DOCUMENT ME!
1805    * 
1806    * @param e
1807    *          DOCUMENT ME!
1808    */
1809   protected void outline_actionPerformed()
1810   {
1811     SequenceGroup sg = getGroup();
1812     Color col = JColorChooser.showDialog(this,
1813             MessageManager.getString("label.select_outline_colour"),
1814             Color.BLUE);
1815
1816     if (col != null)
1817     {
1818       sg.setOutlineColour(col);
1819     }
1820
1821     refresh();
1822   }
1823
1824   /**
1825    * DOCUMENT ME!
1826    * 
1827    * @param e
1828    *          DOCUMENT ME!
1829    */
1830   public void showBoxes_actionPerformed()
1831   {
1832     getGroup().setDisplayBoxes(showBoxes.isSelected());
1833     refresh();
1834   }
1835
1836   /**
1837    * DOCUMENT ME!
1838    * 
1839    * @param e
1840    *          DOCUMENT ME!
1841    */
1842   public void showText_actionPerformed()
1843   {
1844     getGroup().setDisplayText(showText.isSelected());
1845     refresh();
1846   }
1847
1848   /**
1849    * DOCUMENT ME!
1850    * 
1851    * @param e
1852    *          DOCUMENT ME!
1853    */
1854   public void showColourText_actionPerformed()
1855   {
1856     getGroup().setColourText(showColourText.isSelected());
1857     refresh();
1858   }
1859
1860   public void showLink(String url)
1861   {
1862     try
1863     {
1864       jalview.util.BrowserLauncher.openURL(url);
1865     } catch (Exception ex)
1866     {
1867       JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1868               MessageManager.getString("label.web_browser_not_found_unix"),
1869               MessageManager.getString("label.web_browser_not_found"),
1870               JvOptionPane.WARNING_MESSAGE);
1871
1872       ex.printStackTrace();
1873     }
1874   }
1875
1876   void hideSequences(boolean representGroup)
1877   {
1878     ap.av.hideSequences(sequence, representGroup);
1879   }
1880
1881   public void copy_actionPerformed()
1882   {
1883     ap.alignFrame.copy_actionPerformed(null);
1884   }
1885
1886   public void cut_actionPerformed()
1887   {
1888     ap.alignFrame.cut_actionPerformed(null);
1889   }
1890
1891   void changeCase(ActionEvent e)
1892   {
1893     Object source = e.getSource();
1894     SequenceGroup sg = ap.av.getSelectionGroup();
1895
1896     if (sg != null)
1897     {
1898       List<int[]> startEnd = ap.av.getVisibleRegionBoundaries(
1899               sg.getStartRes(), sg.getEndRes() + 1);
1900
1901       String description;
1902       int caseChange;
1903
1904       if (source == toggle)
1905       {
1906         description = MessageManager.getString("label.toggle_case");
1907         caseChange = ChangeCaseCommand.TOGGLE_CASE;
1908       }
1909       else if (source == upperCase)
1910       {
1911         description = MessageManager.getString("label.to_upper_case");
1912         caseChange = ChangeCaseCommand.TO_UPPER;
1913       }
1914       else
1915       {
1916         description = MessageManager.getString("label.to_lower_case");
1917         caseChange = ChangeCaseCommand.TO_LOWER;
1918       }
1919
1920       ChangeCaseCommand caseCommand = new ChangeCaseCommand(description,
1921               sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
1922               startEnd, caseChange);
1923
1924       ap.alignFrame.addHistoryItem(caseCommand);
1925
1926       ap.av.firePropertyChange("alignment", null,
1927               ap.av.getAlignment().getSequences());
1928
1929     }
1930   }
1931
1932   public void outputText_actionPerformed(ActionEvent e)
1933   {
1934     CutAndPasteTransfer cap = new CutAndPasteTransfer();
1935     cap.setForInput(null);
1936     Desktop.addInternalFrame(cap,
1937             MessageManager.formatMessage("label.alignment_output_command",
1938                     new Object[]
1939             { e.getActionCommand() }), 600, 500);
1940
1941     String[] omitHidden = null;
1942
1943     System.out.println("PROMPT USER HERE"); // TODO: decide if a prompt happens
1944     // or we simply trust the user wants
1945     // wysiwig behaviour
1946
1947     FileFormatI fileFormat = FileFormats.getInstance()
1948             .forName(e.getActionCommand());
1949     cap.setText(
1950             new FormatAdapter(ap).formatSequences(fileFormat, ap, true));
1951   }
1952
1953   public void sequenceFeature_actionPerformed()
1954   {
1955     SequenceGroup sg = ap.av.getSelectionGroup();
1956     if (sg == null)
1957     {
1958       return;
1959     }
1960
1961     List<SequenceI> seqs = new ArrayList<>();
1962     List<SequenceFeature> features = new ArrayList<>();
1963
1964     /*
1965      * assemble dataset sequences, and template new sequence features,
1966      * for the amend features dialog
1967      */
1968     int gSize = sg.getSize();
1969     for (int i = 0; i < gSize; i++)
1970     {
1971       int start = sg.getSequenceAt(i).findPosition(sg.getStartRes());
1972       int end = sg.findEndRes(sg.getSequenceAt(i));
1973       if (start <= end)
1974       {
1975         seqs.add(sg.getSequenceAt(i).getDatasetSequence());
1976         features.add(
1977                 new SequenceFeature(null, null, null, start, end, null));
1978       }
1979     }
1980
1981     if (ap.getSeqPanel().seqCanvas.getFeatureRenderer().amendFeatures(seqs,
1982             features, true, ap))
1983     {
1984       ap.alignFrame.setShowSeqFeatures(true);
1985       ap.highlightSearchResults(null);
1986     }
1987   }
1988
1989   public void textColour_actionPerformed()
1990   {
1991     SequenceGroup sg = getGroup();
1992     if (sg != null)
1993     {
1994       new TextColourChooser().chooseColour(ap, sg);
1995     }
1996   }
1997
1998   public void colourByStructure(String pdbid)
1999   {
2000     Annotation[] anots = ap.av.getStructureSelectionManager()
2001             .colourSequenceFromStructure(sequence, pdbid);
2002
2003     AlignmentAnnotation an = new AlignmentAnnotation("Structure",
2004             "Coloured by " + pdbid, anots);
2005
2006     ap.av.getAlignment().addAnnotation(an);
2007     an.createSequenceMapping(sequence, 0, true);
2008     // an.adjustForAlignment();
2009     ap.av.getAlignment().setAnnotationIndex(an, 0);
2010
2011     ap.adjustAnnotationHeight();
2012
2013     sequence.addAlignmentAnnotation(an);
2014
2015   }
2016
2017   public void editSequence_actionPerformed(ActionEvent actionEvent)
2018   {
2019     SequenceGroup sg = ap.av.getSelectionGroup();
2020
2021     if (sg != null)
2022     {
2023       if (sequence == null)
2024       {
2025         sequence = sg.getSequenceAt(0);
2026       }
2027
2028       EditNameDialog dialog = new EditNameDialog(
2029               sequence.getSequenceAsString(sg.getStartRes(),
2030                       sg.getEndRes() + 1),
2031               null, MessageManager.getString("label.edit_sequence"), null,
2032               MessageManager.getString("label.edit_sequence"),
2033               ap.alignFrame);
2034
2035       if (dialog.accept)
2036       {
2037         EditCommand editCommand = new EditCommand(
2038                 MessageManager.getString("label.edit_sequences"),
2039                 Action.REPLACE,
2040                 dialog.getName().replace(' ', ap.av.getGapCharacter()),
2041                 sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
2042                 sg.getStartRes(), sg.getEndRes() + 1, ap.av.getAlignment());
2043
2044         ap.alignFrame.addHistoryItem(editCommand);
2045
2046         ap.av.firePropertyChange("alignment", null,
2047                 ap.av.getAlignment().getSequences());
2048       }
2049     }
2050   }
2051
2052   /**
2053    * Action on user selecting an item from the colour menu (that does not have
2054    * its bespoke action handler)
2055    * 
2056    * @return
2057    */
2058   @Override
2059   public void changeColour_actionPerformed(String colourSchemeName)
2060   {
2061     SequenceGroup sg = getGroup();
2062     /*
2063      * switch to the chosen colour scheme (or null for None)
2064      */
2065     ColourSchemeI colourScheme = ColourSchemes.getInstance()
2066             .getColourScheme(colourSchemeName, sg,
2067                     ap.av.getHiddenRepSequences());
2068     sg.setColourScheme(colourScheme);
2069     if (colourScheme instanceof Blosum62ColourScheme
2070             || colourScheme instanceof PIDColourScheme)
2071     {
2072       sg.cs.setConsensus(AAFrequency.calculate(
2073               sg.getSequences(ap.av.getHiddenRepSequences()),
2074               sg.getStartRes(), sg.getEndRes() + 1));
2075     }
2076
2077     refresh();
2078   }
2079
2080 }