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