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