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