9080cdd7abd669b637bb90b0873fc0f02622ac3f
[jalview.git] / src / jalview / appletgui / APopupMenu.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.appletgui;
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.JalviewLite;
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.PDBEntry;
34 import jalview.datamodel.SequenceFeature;
35 import jalview.datamodel.SequenceGroup;
36 import jalview.datamodel.SequenceI;
37 import jalview.io.AppletFormatAdapter;
38 import jalview.io.DataSourceType;
39 import jalview.io.FileFormat;
40 import jalview.io.SequenceAnnotationReport;
41 import jalview.schemes.Blosum62ColourScheme;
42 import jalview.schemes.BuriedColourScheme;
43 import jalview.schemes.ClustalxColourScheme;
44 import jalview.schemes.HelixColourScheme;
45 import jalview.schemes.HydrophobicColourScheme;
46 import jalview.schemes.NucleotideColourScheme;
47 import jalview.schemes.PIDColourScheme;
48 import jalview.schemes.StrandColourScheme;
49 import jalview.schemes.TaylorColourScheme;
50 import jalview.schemes.TurnColourScheme;
51 import jalview.schemes.ZappoColourScheme;
52 import jalview.util.MessageManager;
53 import jalview.util.UrlLink;
54
55 import java.awt.CheckboxMenuItem;
56 import java.awt.Frame;
57 import java.awt.Menu;
58 import java.awt.MenuItem;
59 import java.awt.event.ActionEvent;
60 import java.awt.event.ActionListener;
61 import java.awt.event.ItemEvent;
62 import java.awt.event.ItemListener;
63 import java.util.Arrays;
64 import java.util.Collection;
65 import java.util.Collections;
66 import java.util.LinkedHashMap;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.SortedMap;
70 import java.util.TreeMap;
71 import java.util.Vector;
72
73 public class APopupMenu extends java.awt.PopupMenu implements
74         ActionListener, ItemListener
75 {
76   Menu groupMenu = new Menu();
77
78   MenuItem editGroupName = new MenuItem();
79
80   protected MenuItem clustalColour = new MenuItem();
81
82   protected MenuItem zappoColour = new MenuItem();
83
84   protected MenuItem taylorColour = new MenuItem();
85
86   protected MenuItem hydrophobicityColour = new MenuItem();
87
88   protected MenuItem helixColour = new MenuItem();
89
90   protected MenuItem strandColour = new MenuItem();
91
92   protected MenuItem turnColour = new MenuItem();
93
94   protected MenuItem buriedColour = new MenuItem();
95
96   protected CheckboxMenuItem abovePIDColour = new CheckboxMenuItem();
97
98   protected MenuItem userDefinedColour = new MenuItem();
99
100   protected MenuItem PIDColour = new MenuItem();
101
102   protected MenuItem BLOSUM62Colour = new MenuItem();
103
104   MenuItem noColourmenuItem = new MenuItem();
105
106   protected CheckboxMenuItem conservationMenuItem = new CheckboxMenuItem();
107
108   final AlignmentPanel ap;
109
110   MenuItem unGroupMenuItem = new MenuItem();
111
112   MenuItem createGroupMenuItem = new MenuItem();
113
114   MenuItem nucleotideMenuItem = new MenuItem();
115
116   Menu colourMenu = new Menu();
117
118   CheckboxMenuItem showBoxes = new CheckboxMenuItem();
119
120   CheckboxMenuItem showText = new CheckboxMenuItem();
121
122   CheckboxMenuItem showColourText = new CheckboxMenuItem();
123
124   CheckboxMenuItem displayNonconserved = new CheckboxMenuItem();
125
126   Menu seqShowAnnotationsMenu = new Menu(
127           MessageManager.getString("label.show_annotations"));
128
129   Menu seqHideAnnotationsMenu = new Menu(
130           MessageManager.getString("label.hide_annotations"));
131
132   MenuItem seqAddReferenceAnnotations = new MenuItem(
133           MessageManager.getString("label.add_reference_annotations"));
134
135   Menu groupShowAnnotationsMenu = new Menu(
136           MessageManager.getString("label.show_annotations"));
137
138   Menu groupHideAnnotationsMenu = new Menu(
139           MessageManager.getString("label.hide_annotations"));
140
141   MenuItem groupAddReferenceAnnotations = new MenuItem(
142           MessageManager.getString("label.add_reference_annotations"));
143
144   Menu editMenu = new Menu(MessageManager.getString("action.edit"));
145
146   MenuItem copy = new MenuItem(MessageManager.getString("action.copy"));
147
148   MenuItem cut = new MenuItem(MessageManager.getString("action.cut"));
149
150   MenuItem toUpper = new MenuItem(
151           MessageManager.getString("label.to_upper_case"));
152
153   MenuItem toLower = new MenuItem(
154           MessageManager.getString("label.to_lower_case"));
155
156   MenuItem toggleCase = new MenuItem(
157           MessageManager.getString("label.toggle_case"));
158
159   Menu outputmenu = new Menu();
160
161   Menu seqMenu = new Menu();
162
163   MenuItem pdb = new MenuItem();
164
165   MenuItem hideSeqs = new MenuItem();
166
167   MenuItem repGroup = new MenuItem();
168
169   MenuItem sequenceName = new MenuItem(
170           MessageManager.getString("label.edit_name_description"));
171
172   MenuItem sequenceFeature = new MenuItem(
173           MessageManager.getString("label.create_sequence_feature"));
174
175   MenuItem editSequence = new MenuItem(
176           MessageManager.getString("label.edit_sequence"));
177
178   MenuItem sequenceDetails = new MenuItem(
179           MessageManager.getString("label.sequence_details"));
180
181   MenuItem selSeqDetails = new MenuItem(
182           MessageManager.getString("label.sequence_details"));
183
184   MenuItem makeReferenceSeq = new MenuItem();
185
186   SequenceI seq;
187
188   MenuItem revealAll = new MenuItem();
189
190   MenuItem revealSeq = new MenuItem();
191
192   /**
193    * index of sequence to be revealed
194    */
195   int revealSeq_index = -1;
196
197   Menu menu1 = new Menu();
198
199   public APopupMenu(AlignmentPanel apanel, final SequenceI seq,
200           Vector<String> links)
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
209     this.ap = apanel;
210     this.seq = seq;
211
212     try
213     {
214       jbInit();
215     } catch (Exception e)
216     {
217       e.printStackTrace();
218     }
219
220     for (String ff : FileFormat.getWritableFormats(true))
221     {
222       MenuItem item = new MenuItem(ff);
223
224       item.addActionListener(this);
225       outputmenu.add(item);
226     }
227
228     buildAnnotationSubmenus();
229
230     SequenceGroup sg = ap.av.getSelectionGroup();
231     if (sg != null && sg.getSize() > 0)
232     {
233       editGroupName.setLabel(MessageManager.formatMessage(
234               "label.name_param", new Object[] { sg.getName() }));
235       showText.setState(sg.getDisplayText());
236       showColourText.setState(sg.getColourText());
237       showBoxes.setState(sg.getDisplayBoxes());
238       displayNonconserved.setState(sg.getShowNonconserved());
239       if (!ap.av.getAlignment().getGroups().contains(sg))
240       {
241         menu1.setLabel(MessageManager.getString("action.edit_new_group"));
242         groupMenu.remove(unGroupMenuItem);
243       }
244       else
245       {
246         menu1.setLabel(MessageManager.getString("action.edit_group"));
247         groupMenu.remove(createGroupMenuItem);
248       }
249
250     }
251     else
252     {
253       remove(hideSeqs);
254       remove(groupMenu);
255     }
256
257     if (links != null && links.size() > 0)
258     {
259       addFeatureLinks(seq, links);
260     }
261
262     // TODO: add group link menu entry here
263     if (seq != null)
264     {
265       seqMenu.setLabel(seq.getName());
266       if (seq == ap.av.getAlignment().getSeqrep())
267       {
268         makeReferenceSeq.setLabel(MessageManager
269                 .getString("action.unmark_as_reference"));// Unmark
270                                                           // representative");
271       }
272       else
273       {
274         makeReferenceSeq.setLabel(MessageManager
275                 .getString("action.set_as_reference")); // );
276       }
277       repGroup.setLabel(MessageManager.formatMessage(
278               "label.represent_group_with", new Object[] { seq.getName() }));
279     }
280     else
281     {
282       remove(seqMenu);
283     }
284
285     if (!ap.av.hasHiddenRows())
286     {
287       remove(revealAll);
288       remove(revealSeq);
289     }
290     else
291     {
292       final int index = ap.av.getAlignment().findIndex(seq);
293
294       if (ap.av.adjustForHiddenSeqs(index)
295               - ap.av.adjustForHiddenSeqs(index - 1) > 1)
296       {
297         revealSeq_index = index;
298       }
299       else
300       {
301         remove(revealSeq);
302       }
303     }
304   }
305
306   /**
307    * Adds a 'Link' menu item with a sub-menu item for each hyperlink provided.
308    * 
309    * @param seq
310    * @param links
311    */
312   void addFeatureLinks(final SequenceI seq, List<String> links)
313   {
314     Menu linkMenu = new Menu(MessageManager.getString("action.link"));
315     Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
316
317     for (String link : links)
318     {
319       UrlLink urlLink = null;
320       try
321       {
322         urlLink = new UrlLink(link);
323       } catch (Exception foo)
324       {
325         System.err.println("Exception for URLLink '" + link + "': "
326                 + foo.getMessage());
327         continue;
328       }
329
330       if (!urlLink.isValid())
331       {
332         System.err.println(urlLink.getInvalidMessage());
333         continue;
334       }
335
336       urlLink.createLinksFromSeq(seq, linkset);
337     }
338
339     addshowLinks(linkMenu, linkset.values());
340
341     // disable link menu if there are no valid entries
342     if (linkMenu.getItemCount() > 0)
343     {
344       linkMenu.setEnabled(true);
345     }
346     else
347     {
348       linkMenu.setEnabled(false);
349     }
350
351     if (seq != null)
352     {
353       seqMenu.add(linkMenu);
354     }
355     else
356     {
357       add(linkMenu);
358     }
359
360   }
361
362   private void addshowLinks(Menu linkMenu, Collection<List<String>> linkset)
363   {
364     for (List<String> linkstrset : linkset)
365     {
366       // split linkstr into label and url
367       addshowLink(linkMenu, linkstrset.get(1), linkstrset.get(3));
368     }
369   }
370
371   /**
372    * Build menus for annotation types that may be shown or hidden, and for
373    * 'reference annotations' that may be added to the alignment.
374    */
375   private void buildAnnotationSubmenus()
376   {
377     /*
378      * First for the currently selected sequence (if there is one):
379      */
380     final List<SequenceI> selectedSequence = (seq == null ? Collections
381             .<SequenceI> emptyList() : Arrays.asList(seq));
382     buildAnnotationTypesMenus(seqShowAnnotationsMenu,
383             seqHideAnnotationsMenu, selectedSequence);
384     configureReferenceAnnotationsMenu(seqAddReferenceAnnotations,
385             selectedSequence);
386
387     /*
388      * and repeat for the current selection group (if there is one):
389      */
390     final List<SequenceI> selectedGroup = (ap.av.getSelectionGroup() == null ? Collections
391             .<SequenceI> emptyList() : ap.av.getSelectionGroup()
392             .getSequences());
393     buildAnnotationTypesMenus(groupShowAnnotationsMenu,
394             groupHideAnnotationsMenu, selectedGroup);
395     configureReferenceAnnotationsMenu(groupAddReferenceAnnotations,
396             selectedGroup);
397   }
398
399   /**
400    * Determine whether or not to enable 'add reference annotations' menu item.
401    * It is enable if there are any annotations, on any of the selected
402    * sequences, which are not yet on the alignment (visible or not).
403    * 
404    * @param menu
405    * @param forSequences
406    */
407   private void configureReferenceAnnotationsMenu(MenuItem menuItem,
408           List<SequenceI> forSequences)
409   {
410     menuItem.setEnabled(false);
411
412     /*
413      * Temporary store to hold distinct calcId / type pairs for the tooltip.
414      * Using TreeMap means calcIds are shown in alphabetical order.
415      */
416     SortedMap<String, String> tipEntries = new TreeMap<String, String>();
417     final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<SequenceI, List<AlignmentAnnotation>>();
418     AlignmentI al = this.ap.av.getAlignment();
419     AlignmentUtils.findAddableReferenceAnnotations(forSequences,
420             tipEntries, candidates, al);
421     if (!candidates.isEmpty())
422     {
423       StringBuilder tooltip = new StringBuilder(64);
424       tooltip.append(MessageManager.getString("label.add_annotations_for"));
425
426       /*
427        * Found annotations that could be added. Enable the menu item, and
428        * configure its action.
429        */
430       menuItem.setEnabled(true);
431
432       menuItem.addActionListener(new ActionListener()
433       {
434         @Override
435         public void actionPerformed(ActionEvent e)
436         {
437           addReferenceAnnotations_actionPerformed(candidates);
438         }
439       });
440     }
441   }
442
443   /**
444    * Add annotations to the sequences and to the alignment.
445    * 
446    * @param candidates
447    *          a map whose keys are sequences on the alignment, and values a list
448    *          of annotations to add to each sequence
449    */
450   protected void addReferenceAnnotations_actionPerformed(
451           Map<SequenceI, List<AlignmentAnnotation>> candidates)
452   {
453     final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
454     final AlignmentI alignment = this.ap.getAlignment();
455     AlignmentUtils.addReferenceAnnotations(candidates, alignment,
456             selectionGroup);
457     refresh();
458   }
459
460   /**
461    * add a show URL menu item to the given linkMenu
462    * 
463    * @param linkMenu
464    * @param target
465    *          - menu label string
466    * @param url
467    *          - url to open
468    */
469   private void addshowLink(Menu linkMenu, final String target,
470           final String url)
471   {
472     addshowLink(linkMenu, target, target, url);
473   }
474
475   /**
476    * add a show URL menu item to the given linkMenu
477    * 
478    * @param linkMenu
479    * @param target
480    *          - URL target window
481    * @param label
482    *          - menu label string
483    * @param url
484    *          - url to open
485    */
486   private void addshowLink(Menu linkMenu, final String target,
487           final String label, final String url)
488   {
489     MenuItem item = new MenuItem(label);
490     item.addActionListener(new java.awt.event.ActionListener()
491     {
492       @Override
493       public void actionPerformed(ActionEvent e)
494       {
495         ap.alignFrame.showURL(url, target);
496       }
497     });
498     linkMenu.add(item);
499   }
500
501   @Override
502   public void itemStateChanged(ItemEvent evt)
503   {
504     if (evt.getSource() == abovePIDColour)
505     {
506       abovePIDColour_itemStateChanged();
507     }
508     else if (evt.getSource() == showColourText)
509     {
510       showColourText_itemStateChanged();
511     }
512     else if (evt.getSource() == showText)
513     {
514       showText_itemStateChanged();
515     }
516     else if (evt.getSource() == showBoxes)
517     {
518       showBoxes_itemStateChanged();
519     }
520     else if (evt.getSource() == displayNonconserved)
521     {
522       this.showNonconserved_itemStateChanged();
523     }
524   }
525
526   @Override
527   public void actionPerformed(ActionEvent evt)
528   {
529     Object source = evt.getSource();
530     if (source == clustalColour)
531     {
532       clustalColour_actionPerformed();
533     }
534     else if (source == zappoColour)
535     {
536       zappoColour_actionPerformed();
537     }
538     else if (source == taylorColour)
539     {
540       taylorColour_actionPerformed();
541     }
542     else if (source == hydrophobicityColour)
543     {
544       hydrophobicityColour_actionPerformed();
545     }
546     else if (source == helixColour)
547     {
548       helixColour_actionPerformed();
549     }
550     else if (source == strandColour)
551     {
552       strandColour_actionPerformed();
553     }
554     else if (source == turnColour)
555     {
556       turnColour_actionPerformed();
557     }
558     else if (source == buriedColour)
559     {
560       buriedColour_actionPerformed();
561     }
562     else if (source == nucleotideMenuItem)
563     {
564       nucleotideMenuItem_actionPerformed();
565     }
566
567     else if (source == userDefinedColour)
568     {
569       userDefinedColour_actionPerformed();
570     }
571     else if (source == PIDColour)
572     {
573       PIDColour_actionPerformed();
574     }
575     else if (source == BLOSUM62Colour)
576     {
577       BLOSUM62Colour_actionPerformed();
578     }
579     else if (source == noColourmenuItem)
580     {
581       noColourmenuItem_actionPerformed();
582     }
583     else if (source == conservationMenuItem)
584     {
585       conservationMenuItem_itemStateChanged();
586     }
587     else if (source == unGroupMenuItem)
588     {
589       unGroupMenuItem_actionPerformed();
590     }
591
592     else if (source == createGroupMenuItem)
593     {
594       createGroupMenuItem_actionPerformed();
595     }
596
597     else if (source == sequenceName)
598     {
599       editName();
600     }
601     else if (source == makeReferenceSeq)
602     {
603       makeReferenceSeq_actionPerformed();
604     }
605     else if (source == sequenceDetails)
606     {
607       showSequenceDetails();
608     }
609     else if (source == selSeqDetails)
610     {
611       showSequenceSelectionDetails();
612     }
613     else if (source == pdb)
614     {
615       addPDB();
616     }
617     else if (source == hideSeqs)
618     {
619       hideSequences(false);
620     }
621     else if (source == repGroup)
622     {
623       hideSequences(true);
624     }
625     else if (source == revealSeq)
626     {
627       ap.av.showSequence(revealSeq_index);
628     }
629     else if (source == revealAll)
630     {
631       ap.av.showAllHiddenSeqs();
632     }
633
634     else if (source == editGroupName)
635     {
636       EditNameDialog dialog = new EditNameDialog(getGroup().getName(),
637               getGroup().getDescription(), "       Group Name",
638               "Group Description", ap.alignFrame,
639               "Edit Group Name / Description", 500, 100, true);
640
641       if (dialog.accept)
642       {
643         getGroup().setName(dialog.getName().replace(' ', '_'));
644         getGroup().setDescription(dialog.getDescription());
645       }
646
647     }
648     else if (source == copy)
649     {
650       ap.alignFrame.copy_actionPerformed();
651     }
652     else if (source == cut)
653     {
654       ap.alignFrame.cut_actionPerformed();
655     }
656     else if (source == editSequence)
657     {
658       SequenceGroup sg = ap.av.getSelectionGroup();
659
660       if (sg != null)
661       {
662         if (seq == null)
663         {
664           seq = sg.getSequenceAt(0);
665         }
666
667         EditNameDialog dialog = new EditNameDialog(seq.getSequenceAsString(
668                 sg.getStartRes(), sg.getEndRes() + 1), null,
669                 "Edit Sequence ", null,
670
671                 ap.alignFrame, "Edit Sequence", 500, 100, true);
672
673         if (dialog.accept)
674         {
675           EditCommand editCommand = new EditCommand(
676                   MessageManager.getString("label.edit_sequences"),
677                   Action.REPLACE, dialog.getName().replace(' ',
678                           ap.av.getGapCharacter()),
679                   sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
680                   sg.getStartRes(), sg.getEndRes() + 1,
681                   ap.av.getAlignment());
682
683           ap.alignFrame.addHistoryItem(editCommand);
684
685           ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
686                   .getSequences());
687         }
688       }
689     }
690     else if (source == toUpper || source == toLower || source == toggleCase)
691     {
692       SequenceGroup sg = ap.av.getSelectionGroup();
693       if (sg != null)
694       {
695         List<int[]> startEnd = ap.av.getVisibleRegionBoundaries(
696                 sg.getStartRes(), sg.getEndRes() + 1);
697
698         String description;
699         int caseChange;
700
701         if (source == toggleCase)
702         {
703           description = "Toggle Case";
704           caseChange = ChangeCaseCommand.TOGGLE_CASE;
705         }
706         else if (source == toUpper)
707         {
708           description = "To Upper Case";
709           caseChange = ChangeCaseCommand.TO_UPPER;
710         }
711         else
712         {
713           description = "To Lower Case";
714           caseChange = ChangeCaseCommand.TO_LOWER;
715         }
716
717         ChangeCaseCommand caseCommand = new ChangeCaseCommand(description,
718                 sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
719                 startEnd, caseChange);
720
721         ap.alignFrame.addHistoryItem(caseCommand);
722
723         ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
724                 .getSequences());
725
726       }
727     }
728     else if (source == sequenceFeature)
729     {
730       SequenceGroup sg = ap.av.getSelectionGroup();
731       if (sg == null)
732       {
733         return;
734       }
735
736       int rsize = 0, gSize = sg.getSize();
737       SequenceI[] rseqs, seqs = new SequenceI[gSize];
738       SequenceFeature[] tfeatures, features = new SequenceFeature[gSize];
739
740       for (int i = 0; i < gSize; i++)
741       {
742         int start = sg.getSequenceAt(i).findPosition(sg.getStartRes());
743         int end = sg.findEndRes(sg.getSequenceAt(i));
744         if (start <= end)
745         {
746           seqs[rsize] = sg.getSequenceAt(i);
747           features[rsize] = new SequenceFeature(null, null, null, start,
748                   end, "Jalview");
749           rsize++;
750         }
751       }
752       rseqs = new SequenceI[rsize];
753       tfeatures = new SequenceFeature[rsize];
754       System.arraycopy(seqs, 0, rseqs, 0, rsize);
755       System.arraycopy(features, 0, tfeatures, 0, rsize);
756       features = tfeatures;
757       seqs = rseqs;
758
759       if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs,
760               features, true, ap))
761       {
762         ap.alignFrame.sequenceFeatures.setState(true);
763         ap.av.setShowSequenceFeatures(true);
764         ;
765         ap.highlightSearchResults(null);
766       }
767     }
768     else
769     {
770       outputText(evt);
771     }
772
773   }
774
775   void outputText(ActionEvent e)
776   {
777     CutAndPasteTransfer cap = new CutAndPasteTransfer(true, ap.alignFrame);
778
779     Frame frame = new Frame();
780     frame.add(cap);
781     JalviewLite.addFrame(frame, MessageManager.formatMessage(
782             "label.selection_output_command",
783             new Object[] { e.getActionCommand() }), 600, 500);
784     // JBPNote: getSelectionAsNewSequence behaviour has changed - this method
785     // now returns a full copy of sequence data
786     // TODO consider using getSequenceSelection instead here
787
788     FileFormat fileFormat = FileFormat.valueOf(e.getActionCommand());
789     cap.setText(new AppletFormatAdapter().formatSequences(fileFormat,
790             ap.av.getShowJVSuffix(), ap, true));
791
792   }
793
794   protected void showSequenceSelectionDetails()
795   {
796     createSequenceDetailsReport(ap.av.getSequenceSelection());
797   }
798
799   protected void showSequenceDetails()
800   {
801     createSequenceDetailsReport(new SequenceI[] { seq });
802   }
803
804   public void createSequenceDetailsReport(SequenceI[] sequences)
805   {
806
807     CutAndPasteTransfer cap = new CutAndPasteTransfer(false, ap.alignFrame);
808
809     StringBuilder contents = new StringBuilder(128);
810     for (SequenceI seq : sequences)
811     {
812       contents.append(MessageManager.formatMessage(
813               "label.annotation_for_displayid",
814               new Object[] { seq.getDisplayId(true) }));
815       new SequenceAnnotationReport(null).createSequenceAnnotationReport(
816               contents,
817               seq,
818               true,
819               true,
820               (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr
821                       .getMinMax() : null);
822       contents.append("</p>");
823     }
824     Frame frame = new Frame();
825     frame.add(cap);
826     jalview.bin.JalviewLite.addFrame(frame, "Sequence Details for "
827             + (sequences.length == 1 ? sequences[0].getDisplayId(true)
828                     : "Selection"), 600, 500);
829     cap.setText(MessageManager.formatMessage("label.html_content",
830             new Object[] { contents.toString() }));
831   }
832
833   void editName()
834   {
835     EditNameDialog dialog = new EditNameDialog(seq.getName(),
836             seq.getDescription(), "       Sequence Name",
837             "Sequence Description", ap.alignFrame,
838             "Edit Sequence Name / Description", 500, 100, true);
839
840     if (dialog.accept)
841     {
842       seq.setName(dialog.getName());
843       seq.setDescription(dialog.getDescription());
844       ap.paintAlignment(false);
845     }
846   }
847
848   void addPDB()
849   {
850     Vector<PDBEntry> pdbs = seq.getAllPDBEntries();
851     if (pdbs != null&& !pdbs.isEmpty())
852     {
853       PDBEntry entry = pdbs.firstElement();
854
855       if (ap.av.applet.jmolAvailable)
856       {
857         new jalview.appletgui.AppletJmol(entry, new SequenceI[] { seq },
858                 null, ap, DataSourceType.URL);
859       }
860       else
861       {
862         new MCview.AppletPDBViewer(entry, new SequenceI[] { seq }, null,
863                 ap, DataSourceType.URL);
864       }
865
866     }
867     else
868     {
869       CutAndPasteTransfer cap = new CutAndPasteTransfer(true, ap.alignFrame);
870       cap.setText(MessageManager.getString("label.paste_pdb_file"));
871       cap.setPDBImport(seq);
872       Frame frame = new Frame();
873       frame.add(cap);
874       jalview.bin.JalviewLite.addFrame(frame, MessageManager.formatMessage(
875               "label.paste_pdb_file_for_sequence",
876               new Object[] { seq.getName() }), 400, 300);
877     }
878   }
879
880   private void jbInit() throws Exception
881   {
882     groupMenu.setLabel(MessageManager.getString("label.selection"));
883     sequenceFeature.addActionListener(this);
884
885     editGroupName.addActionListener(this);
886     unGroupMenuItem.setLabel(MessageManager
887             .getString("action.remove_group"));
888     unGroupMenuItem.addActionListener(this);
889
890     createGroupMenuItem.setLabel(MessageManager
891             .getString("action.create_group"));
892     createGroupMenuItem.addActionListener(this);
893
894     nucleotideMenuItem.setLabel(MessageManager
895             .getString("label.nucleotide"));
896     nucleotideMenuItem.addActionListener(this);
897     conservationMenuItem.addItemListener(this);
898     abovePIDColour.addItemListener(this);
899     colourMenu.setLabel(MessageManager.getString("label.group_colour"));
900     showBoxes.setLabel(MessageManager.getString("action.boxes"));
901     showBoxes.setState(true);
902     showBoxes.addItemListener(this);
903     sequenceName.addActionListener(this);
904     sequenceDetails.addActionListener(this);
905     selSeqDetails.addActionListener(this);
906     displayNonconserved.setLabel(MessageManager
907             .getString("label.show_non_conversed"));
908     displayNonconserved.setState(false);
909     displayNonconserved.addItemListener(this);
910     showText.setLabel(MessageManager.getString("action.text"));
911     showText.addItemListener(this);
912     showColourText.setLabel(MessageManager.getString("label.colour_text"));
913     showColourText.addItemListener(this);
914     outputmenu.setLabel(MessageManager.getString("label.out_to_textbox"));
915     seqMenu.setLabel(MessageManager.getString("label.sequence"));
916     pdb.setLabel(MessageManager.getString("label.view_pdb_structure"));
917     hideSeqs.setLabel(MessageManager.getString("action.hide_sequences"));
918     repGroup.setLabel(MessageManager.formatMessage(
919             "label.represent_group_with", new Object[] { "" }));
920     revealAll.setLabel(MessageManager.getString("action.reveal_all"));
921     revealSeq.setLabel(MessageManager.getString("action.reveal_sequences"));
922     menu1.setLabel(MessageManager.getString("label.group:"));
923     add(groupMenu);
924     this.add(seqMenu);
925     this.add(hideSeqs);
926     this.add(revealSeq);
927     this.add(revealAll);
928     // groupMenu.add(selSeqDetails);
929     groupMenu.add(groupShowAnnotationsMenu);
930     groupMenu.add(groupHideAnnotationsMenu);
931     groupMenu.add(groupAddReferenceAnnotations);
932     groupMenu.add(editMenu);
933     groupMenu.add(outputmenu);
934     groupMenu.add(sequenceFeature);
935     groupMenu.add(createGroupMenuItem);
936     groupMenu.add(unGroupMenuItem);
937     groupMenu.add(menu1);
938
939     colourMenu.add(noColourmenuItem);
940     colourMenu.add(clustalColour);
941     colourMenu.add(BLOSUM62Colour);
942     colourMenu.add(PIDColour);
943     colourMenu.add(zappoColour);
944     colourMenu.add(taylorColour);
945     colourMenu.add(hydrophobicityColour);
946     colourMenu.add(helixColour);
947     colourMenu.add(strandColour);
948     colourMenu.add(turnColour);
949     colourMenu.add(buriedColour);
950     colourMenu.add(nucleotideMenuItem);
951     colourMenu.add(userDefinedColour);
952     colourMenu.addSeparator();
953     colourMenu.add(abovePIDColour);
954     colourMenu.add(conservationMenuItem);
955
956     noColourmenuItem.setLabel(MessageManager.getString("label.none"));
957     noColourmenuItem.addActionListener(this);
958
959     clustalColour.setLabel(MessageManager.getString("label.clustalx"));
960     clustalColour.addActionListener(this);
961     zappoColour.setLabel(MessageManager.getString("label.zappo"));
962     zappoColour.addActionListener(this);
963     taylorColour.setLabel(MessageManager.getString("label.taylor"));
964     taylorColour.addActionListener(this);
965     hydrophobicityColour.setLabel(MessageManager
966             .getString("label.hydrophobicity"));
967     hydrophobicityColour.addActionListener(this);
968     helixColour
969             .setLabel(MessageManager.getString("label.helix_propensity"));
970     helixColour.addActionListener(this);
971     strandColour.setLabel(MessageManager
972             .getString("label.strand_propensity"));
973     strandColour.addActionListener(this);
974     turnColour.setLabel(MessageManager.getString("label.turn_propensity"));
975     turnColour.addActionListener(this);
976     buriedColour.setLabel(MessageManager.getString("label.buried_index"));
977     buriedColour.addActionListener(this);
978     abovePIDColour.setLabel(MessageManager
979             .getString("label.above_identity_percentage"));
980
981     userDefinedColour.setLabel(MessageManager
982             .getString("action.user_defined"));
983     userDefinedColour.addActionListener(this);
984     PIDColour.setLabel(MessageManager
985             .getString("label.percentage_identity"));
986     PIDColour.addActionListener(this);
987     BLOSUM62Colour.setLabel("BLOSUM62");
988     BLOSUM62Colour.addActionListener(this);
989     conservationMenuItem.setLabel(MessageManager
990             .getString("label.conservation"));
991
992     editMenu.add(copy);
993     copy.addActionListener(this);
994     editMenu.add(cut);
995     cut.addActionListener(this);
996
997     editMenu.add(editSequence);
998     editSequence.addActionListener(this);
999
1000     editMenu.add(toUpper);
1001     toUpper.addActionListener(this);
1002     editMenu.add(toLower);
1003     toLower.addActionListener(this);
1004     editMenu.add(toggleCase);
1005     seqMenu.add(seqShowAnnotationsMenu);
1006     seqMenu.add(seqHideAnnotationsMenu);
1007     seqMenu.add(seqAddReferenceAnnotations);
1008     seqMenu.add(sequenceName);
1009     seqMenu.add(makeReferenceSeq);
1010     // seqMenu.add(sequenceDetails);
1011
1012     if (!ap.av.applet.useXtrnalSviewer)
1013     {
1014       seqMenu.add(pdb);
1015     }
1016     seqMenu.add(repGroup);
1017     menu1.add(editGroupName);
1018     menu1.add(colourMenu);
1019     menu1.add(showBoxes);
1020     menu1.add(showText);
1021     menu1.add(showColourText);
1022     menu1.add(displayNonconserved);
1023     toggleCase.addActionListener(this);
1024     pdb.addActionListener(this);
1025     hideSeqs.addActionListener(this);
1026     repGroup.addActionListener(this);
1027     revealAll.addActionListener(this);
1028     revealSeq.addActionListener(this);
1029     makeReferenceSeq.addActionListener(this);
1030   }
1031
1032   void refresh()
1033   {
1034     ap.paintAlignment(true);
1035   }
1036
1037   protected void clustalColour_actionPerformed()
1038   {
1039     SequenceGroup sg = getGroup();
1040     sg.cs = new ClustalxColourScheme(sg, ap.av.getHiddenRepSequences());
1041     refresh();
1042   }
1043
1044   protected void zappoColour_actionPerformed()
1045   {
1046     getGroup().cs = new ZappoColourScheme();
1047     refresh();
1048   }
1049
1050   protected void taylorColour_actionPerformed()
1051   {
1052     getGroup().cs = new TaylorColourScheme();
1053     refresh();
1054   }
1055
1056   protected void hydrophobicityColour_actionPerformed()
1057   {
1058     getGroup().cs = new HydrophobicColourScheme();
1059     refresh();
1060   }
1061
1062   protected void helixColour_actionPerformed()
1063   {
1064     getGroup().cs = new HelixColourScheme();
1065     refresh();
1066   }
1067
1068   protected void strandColour_actionPerformed()
1069   {
1070     getGroup().cs = new StrandColourScheme();
1071     refresh();
1072   }
1073
1074   protected void turnColour_actionPerformed()
1075   {
1076     getGroup().cs = new TurnColourScheme();
1077     refresh();
1078   }
1079
1080   protected void buriedColour_actionPerformed()
1081   {
1082     getGroup().cs = new BuriedColourScheme();
1083     refresh();
1084   }
1085
1086   public void nucleotideMenuItem_actionPerformed()
1087   {
1088     getGroup().cs = new NucleotideColourScheme();
1089     refresh();
1090   }
1091
1092   protected void abovePIDColour_itemStateChanged()
1093   {
1094     SequenceGroup sg = getGroup();
1095     if (sg.cs == null)
1096     {
1097       return;
1098     }
1099
1100     if (abovePIDColour.getState())
1101     {
1102       sg.cs.setConsensus(AAFrequency.calculate(sg.getSequences(ap.av
1103               .getHiddenRepSequences()), 0, ap.av.getAlignment().getWidth()));
1104       int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup()
1105               .getName());
1106
1107       sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus());
1108
1109       SliderPanel.showPIDSlider();
1110
1111     }
1112     else
1113     // remove PIDColouring
1114     {
1115       sg.cs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
1116     }
1117
1118     refresh();
1119
1120   }
1121
1122   protected void userDefinedColour_actionPerformed()
1123   {
1124     new UserDefinedColours(ap, getGroup());
1125   }
1126
1127   protected void PIDColour_actionPerformed()
1128   {
1129     SequenceGroup sg = getGroup();
1130     sg.cs = new PIDColourScheme();
1131     sg.cs.setConsensus(AAFrequency.calculate(sg.getSequences(ap.av
1132             .getHiddenRepSequences()), 0, ap.av.getAlignment().getWidth()));
1133     refresh();
1134   }
1135
1136   protected void BLOSUM62Colour_actionPerformed()
1137   {
1138     SequenceGroup sg = getGroup();
1139
1140     sg.cs = new Blosum62ColourScheme();
1141
1142     sg.cs.setConsensus(AAFrequency.calculate(sg.getSequences(ap.av
1143             .getHiddenRepSequences()), 0, ap.av.getAlignment().getWidth()));
1144
1145     refresh();
1146   }
1147
1148   protected void noColourmenuItem_actionPerformed()
1149   {
1150     getGroup().cs = null;
1151     refresh();
1152   }
1153
1154   protected void conservationMenuItem_itemStateChanged()
1155   {
1156     SequenceGroup sg = getGroup();
1157     if (sg.cs == null)
1158     {
1159       return;
1160     }
1161
1162     if (conservationMenuItem.getState())
1163     {
1164       sg.cs.setConservation(Conservation.calculateConservation("Group", sg
1165               .getSequences(ap.av.getHiddenRepSequences()), 0, ap.av
1166               .getAlignment().getWidth(), false, ap.av.getConsPercGaps(),
1167               false));
1168       SliderPanel.setConservationSlider(ap, sg.cs, sg.getName());
1169       SliderPanel.showConservationSlider();
1170     }
1171     else
1172     // remove ConservationColouring
1173     {
1174       sg.cs.setConservation(null);
1175     }
1176
1177     refresh();
1178   }
1179
1180   SequenceGroup getGroup()
1181   {
1182     SequenceGroup sg = ap.av.getSelectionGroup();
1183
1184     // this method won't add a new group if it already exists
1185     if (sg != null)
1186     {
1187       ap.av.getAlignment().addGroup(sg);
1188     }
1189
1190     return sg;
1191   }
1192
1193   void unGroupMenuItem_actionPerformed()
1194   {
1195     SequenceGroup sg = ap.av.getSelectionGroup();
1196     ap.av.getAlignment().deleteGroup(sg);
1197     ap.av.setSelectionGroup(null);
1198     ap.paintAlignment(true);
1199   }
1200
1201   void createGroupMenuItem_actionPerformed()
1202   {
1203     getGroup(); // implicitly create group
1204     refresh();
1205   }
1206
1207   public void showColourText_itemStateChanged()
1208   {
1209     getGroup().setColourText(showColourText.getState());
1210     refresh();
1211   }
1212
1213   public void showText_itemStateChanged()
1214   {
1215     getGroup().setDisplayText(showText.getState());
1216     refresh();
1217   }
1218
1219   public void makeReferenceSeq_actionPerformed()
1220   {
1221     if (!ap.av.getAlignment().hasSeqrep())
1222     {
1223       // initialise the display flags so the user sees something happen
1224       ap.av.setDisplayReferenceSeq(true);
1225       ap.av.setColourByReferenceSeq(true);
1226       ap.av.getAlignment().setSeqrep(seq);
1227     }
1228     else
1229     {
1230       if (ap.av.getAlignment().getSeqrep() == seq)
1231       {
1232         ap.av.getAlignment().setSeqrep(null);
1233       }
1234       else
1235       {
1236         ap.av.getAlignment().setSeqrep(seq);
1237       }
1238     }
1239     refresh();
1240   }
1241
1242   public void showNonconserved_itemStateChanged()
1243   {
1244     getGroup().setShowNonconserved(this.displayNonconserved.getState());
1245     refresh();
1246   }
1247
1248   public void showBoxes_itemStateChanged()
1249   {
1250     getGroup().setDisplayBoxes(showBoxes.getState());
1251     refresh();
1252   }
1253
1254   void hideSequences(boolean representGroup)
1255   {
1256     ap.av.hideSequences(seq, representGroup);
1257   }
1258
1259   /**
1260    * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
1261    * "All" is added first, followed by a separator. Then add any annotation
1262    * types associated with the current selection. Separate menus are built for
1263    * the selected sequence group (if any), and the selected sequence.
1264    * <p>
1265    * Some annotation rows are always rendered together - these can be identified
1266    * by a common graphGroup property > -1. Only one of each group will be marked
1267    * as visible (to avoid duplication of the display). For such groups we add a
1268    * composite type name, e.g.
1269    * <p>
1270    * IUPredWS (Long), IUPredWS (Short)
1271    * 
1272    * @param seq
1273    */
1274   protected void buildAnnotationTypesMenus(Menu showMenu, Menu hideMenu,
1275           List<SequenceI> forSequences)
1276   {
1277     showMenu.removeAll();
1278     hideMenu.removeAll();
1279
1280     final List<String> all = Arrays.asList(new String[] { MessageManager
1281             .getString("label.all") });
1282     addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, true);
1283     addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
1284             false);
1285     showMenu.addSeparator();
1286     hideMenu.addSeparator();
1287
1288     final AlignmentAnnotation[] annotations = ap.getAlignment()
1289             .getAlignmentAnnotation();
1290
1291     /*
1292      * Find shown/hidden annotations types, distinguished by source (calcId),
1293      * and grouped by graphGroup. Using LinkedHashMap means we will retrieve in
1294      * the insertion order, which is the order of the annotations on the
1295      * alignment.
1296      */
1297     Map<String, List<List<String>>> shownTypes = new LinkedHashMap<String, List<List<String>>>();
1298     Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<String, List<List<String>>>();
1299     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
1300             AlignmentAnnotationUtils.asList(annotations), forSequences);
1301
1302     for (String calcId : hiddenTypes.keySet())
1303     {
1304       for (List<String> type : hiddenTypes.get(calcId))
1305       {
1306         addAnnotationTypeToShowHide(showMenu, forSequences, calcId, type,
1307                 false, true);
1308       }
1309     }
1310     // grey out 'show annotations' if none are hidden
1311     showMenu.setEnabled(!hiddenTypes.isEmpty());
1312
1313     for (String calcId : shownTypes.keySet())
1314     {
1315       for (List<String> type : shownTypes.get(calcId))
1316       {
1317         addAnnotationTypeToShowHide(hideMenu, forSequences, calcId, type,
1318                 false, false);
1319       }
1320     }
1321     // grey out 'hide annotations' if none are shown
1322     hideMenu.setEnabled(!shownTypes.isEmpty());
1323   }
1324
1325   /**
1326    * Add one annotation type to the 'Show Annotations' or 'Hide Annotations'
1327    * menus.
1328    * 
1329    * @param showOrHideMenu
1330    *          the menu to add to
1331    * @param forSequences
1332    *          the sequences whose annotations may be shown or hidden
1333    * @param calcId
1334    * @param types
1335    *          the label to add
1336    * @param allTypes
1337    *          if true this is a special label meaning 'All'
1338    * @param actionIsShow
1339    *          if true, the select menu item action is to show the annotation
1340    *          type, else hide
1341    */
1342   protected void addAnnotationTypeToShowHide(Menu showOrHideMenu,
1343           final List<SequenceI> forSequences, String calcId,
1344           final List<String> types, final boolean allTypes,
1345           final boolean actionIsShow)
1346   {
1347     String label = types.toString(); // [a, b, c]
1348     label = label.substring(1, label.length() - 1);
1349     final MenuItem item = new MenuItem(label);
1350     item.addActionListener(new java.awt.event.ActionListener()
1351     {
1352       @Override
1353       public void actionPerformed(ActionEvent e)
1354       {
1355         AlignmentUtils.showOrHideSequenceAnnotations(ap.getAlignment(),
1356                 types, forSequences, allTypes, actionIsShow);
1357         refresh();
1358       }
1359     });
1360     showOrHideMenu.add(item);
1361   }
1362
1363 }