JAL-1619 first draft of 'linked protein and cDNA'
[jalview.git] / src / jalview / gui / AlignFrame.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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.AlignmentSorter;
25 import jalview.analysis.AlignmentUtils;
26 import jalview.analysis.Conservation;
27 import jalview.analysis.CrossRef;
28 import jalview.analysis.NJTree;
29 import jalview.analysis.ParseProperties;
30 import jalview.analysis.SequenceIdMatcher;
31 import jalview.api.AlignViewControllerGuiI;
32 import jalview.api.AlignViewControllerI;
33 import jalview.api.AlignmentViewPanel;
34 import jalview.api.analysis.ScoreModelI;
35 import jalview.bin.Cache;
36 import jalview.commands.CommandI;
37 import jalview.commands.EditCommand;
38 import jalview.commands.EditCommand.Action;
39 import jalview.commands.OrderCommand;
40 import jalview.commands.RemoveGapColCommand;
41 import jalview.commands.RemoveGapsCommand;
42 import jalview.commands.SlideSequencesCommand;
43 import jalview.commands.TrimRegionCommand;
44 import jalview.datamodel.AlignedCodonFrame;
45 import jalview.datamodel.Alignment;
46 import jalview.datamodel.AlignmentAnnotation;
47 import jalview.datamodel.AlignmentI;
48 import jalview.datamodel.AlignmentOrder;
49 import jalview.datamodel.AlignmentView;
50 import jalview.datamodel.ColumnSelection;
51 import jalview.datamodel.PDBEntry;
52 import jalview.datamodel.SeqCigar;
53 import jalview.datamodel.Sequence;
54 import jalview.datamodel.SequenceGroup;
55 import jalview.datamodel.SequenceI;
56 import jalview.io.AlignmentProperties;
57 import jalview.io.AnnotationFile;
58 import jalview.io.FeaturesFile;
59 import jalview.io.FileLoader;
60 import jalview.io.FormatAdapter;
61 import jalview.io.HTMLOutput;
62 import jalview.io.IdentifyFile;
63 import jalview.io.JalviewFileChooser;
64 import jalview.io.JalviewFileView;
65 import jalview.io.JnetAnnotationMaker;
66 import jalview.io.NewickFile;
67 import jalview.io.TCoffeeScoreFile;
68 import jalview.jbgui.GAlignFrame;
69 import jalview.schemes.Blosum62ColourScheme;
70 import jalview.schemes.BuriedColourScheme;
71 import jalview.schemes.ClustalxColourScheme;
72 import jalview.schemes.ColourSchemeI;
73 import jalview.schemes.ColourSchemeProperty;
74 import jalview.schemes.HelixColourScheme;
75 import jalview.schemes.HydrophobicColourScheme;
76 import jalview.schemes.NucleotideColourScheme;
77 import jalview.schemes.PIDColourScheme;
78 import jalview.schemes.PurinePyrimidineColourScheme;
79 import jalview.schemes.RNAHelicesColourChooser;
80 import jalview.schemes.ResidueProperties;
81 import jalview.schemes.StrandColourScheme;
82 import jalview.schemes.TCoffeeColourScheme;
83 import jalview.schemes.TaylorColourScheme;
84 import jalview.schemes.TurnColourScheme;
85 import jalview.schemes.UserColourScheme;
86 import jalview.schemes.ZappoColourScheme;
87 import jalview.structure.StructureSelectionManager;
88 import jalview.util.MapList;
89 import jalview.util.MessageManager;
90 import jalview.ws.jws1.Discoverer;
91 import jalview.ws.jws2.Jws2Discoverer;
92 import jalview.ws.jws2.jabaws2.Jws2Instance;
93 import jalview.ws.seqfetcher.DbSourceProxy;
94
95 import java.awt.BorderLayout;
96 import java.awt.Component;
97 import java.awt.GridLayout;
98 import java.awt.Rectangle;
99 import java.awt.Toolkit;
100 import java.awt.datatransfer.Clipboard;
101 import java.awt.datatransfer.DataFlavor;
102 import java.awt.datatransfer.StringSelection;
103 import java.awt.datatransfer.Transferable;
104 import java.awt.dnd.DnDConstants;
105 import java.awt.dnd.DropTargetDragEvent;
106 import java.awt.dnd.DropTargetDropEvent;
107 import java.awt.dnd.DropTargetEvent;
108 import java.awt.dnd.DropTargetListener;
109 import java.awt.event.ActionEvent;
110 import java.awt.event.ActionListener;
111 import java.awt.event.KeyAdapter;
112 import java.awt.event.KeyEvent;
113 import java.awt.event.MouseAdapter;
114 import java.awt.event.MouseEvent;
115 import java.awt.print.PageFormat;
116 import java.awt.print.PrinterJob;
117 import java.beans.PropertyChangeEvent;
118 import java.io.File;
119 import java.net.URL;
120 import java.util.ArrayList;
121 import java.util.Arrays;
122 import java.util.Enumeration;
123 import java.util.Hashtable;
124 import java.util.Iterator;
125 import java.util.List;
126 import java.util.Vector;
127
128 import javax.swing.JButton;
129 import javax.swing.JCheckBoxMenuItem;
130 import javax.swing.JEditorPane;
131 import javax.swing.JInternalFrame;
132 import javax.swing.JLabel;
133 import javax.swing.JLayeredPane;
134 import javax.swing.JMenu;
135 import javax.swing.JMenuItem;
136 import javax.swing.JOptionPane;
137 import javax.swing.JPanel;
138 import javax.swing.JProgressBar;
139 import javax.swing.JRadioButtonMenuItem;
140 import javax.swing.JScrollPane;
141 import javax.swing.SwingUtilities;
142
143 /**
144  * DOCUMENT ME!
145  * 
146  * @author $author$
147  * @version $Revision$
148  */
149 public class AlignFrame extends GAlignFrame implements DropTargetListener,
150         IProgressIndicator, AlignViewControllerGuiI
151 {
152
153   /** DOCUMENT ME!! */
154   public static final int DEFAULT_WIDTH = 700;
155
156   /** DOCUMENT ME!! */
157   public static final int DEFAULT_HEIGHT = 500;
158
159   public AlignmentPanel alignPanel;
160
161   AlignViewport viewport;
162
163   public AlignViewControllerI avc;
164
165   Vector alignPanels = new Vector();
166
167   /**
168    * Last format used to load or save alignments in this window
169    */
170   String currentFileFormat = null;
171
172   /**
173    * Current filename for this alignment
174    */
175   String fileName = null;
176
177   /**
178    * Creates a new AlignFrame object with specific width and height.
179    * 
180    * @param al
181    * @param width
182    * @param height
183    */
184   public AlignFrame(AlignmentI al, int width, int height)
185   {
186     this(al, null, width, height);
187   }
188
189   /**
190    * Creates a new AlignFrame object with specific width, height and
191    * sequenceSetId
192    * 
193    * @param al
194    * @param width
195    * @param height
196    * @param sequenceSetId
197    */
198   public AlignFrame(AlignmentI al, int width, int height,
199           String sequenceSetId)
200   {
201     this(al, null, width, height, sequenceSetId);
202   }
203
204   /**
205    * Creates a new AlignFrame object with specific width, height and
206    * sequenceSetId
207    * 
208    * @param al
209    * @param width
210    * @param height
211    * @param sequenceSetId
212    * @param viewId
213    */
214   public AlignFrame(AlignmentI al, int width, int height,
215           String sequenceSetId, String viewId)
216   {
217     this(al, null, width, height, sequenceSetId, viewId);
218   }
219
220   /**
221    * new alignment window with hidden columns
222    * 
223    * @param al
224    *          AlignmentI
225    * @param hiddenColumns
226    *          ColumnSelection or null
227    * @param width
228    *          Width of alignment frame
229    * @param height
230    *          height of frame.
231    */
232   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
233           int width, int height)
234   {
235     this(al, hiddenColumns, width, height, null);
236   }
237
238   /**
239    * Create alignment frame for al with hiddenColumns, a specific width and
240    * height, and specific sequenceId
241    * 
242    * @param al
243    * @param hiddenColumns
244    * @param width
245    * @param height
246    * @param sequenceSetId
247    *          (may be null)
248    */
249   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
250           int width, int height, String sequenceSetId)
251   {
252     this(al, hiddenColumns, width, height, sequenceSetId, null);
253   }
254
255   /**
256    * Create alignment frame for al with hiddenColumns, a specific width and
257    * height, and specific sequenceId
258    * 
259    * @param al
260    * @param hiddenColumns
261    * @param width
262    * @param height
263    * @param sequenceSetId
264    *          (may be null)
265    * @param viewId
266    *          (may be null)
267    */
268   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
269           int width, int height, String sequenceSetId, String viewId)
270   {
271     setSize(width, height);
272
273     if (al.getDataset() == null)
274     {
275       al.setDataset(null);
276     }
277
278     viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
279
280     alignPanel = new AlignmentPanel(this, viewport);
281
282
283     addAlignmentPanel(alignPanel, true);
284     init();
285   }
286
287   /**
288    * Make a new AlignFrame from existing alignmentPanels
289    * 
290    * @param ap
291    *          AlignmentPanel
292    * @param av
293    *          AlignViewport
294    */
295   public AlignFrame(AlignmentPanel ap)
296   {
297     viewport = ap.av;
298     alignPanel = ap;
299     addAlignmentPanel(ap, false);
300     init();
301   }
302
303   /**
304    * initalise the alignframe from the underlying viewport data and the
305    * configurations
306    */
307   void init()
308   {
309     avc = new jalview.controller.AlignViewController(this, viewport,
310             alignPanel);
311     if (viewport.getAlignmentConservationAnnotation() == null)
312     {
313       BLOSUM62Colour.setEnabled(false);
314       conservationMenuItem.setEnabled(false);
315       modifyConservation.setEnabled(false);
316       // PIDColour.setEnabled(false);
317       // abovePIDThreshold.setEnabled(false);
318       // modifyPID.setEnabled(false);
319     }
320
321     String sortby = jalview.bin.Cache.getDefault("SORT_ALIGNMENT",
322             "No sort");
323
324     if (sortby.equals("Id"))
325     {
326       sortIDMenuItem_actionPerformed(null);
327     }
328     else if (sortby.equals("Pairwise Identity"))
329     {
330       sortPairwiseMenuItem_actionPerformed(null);
331     }
332
333     if (Desktop.desktop != null)
334     {
335       this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
336       addServiceListeners();
337       setGUINucleotide(viewport.getAlignment().isNucleotide());
338     }
339
340     setMenusFromViewport(viewport);
341     buildSortByAnnotationScoresMenu();
342     buildTreeMenu();
343     
344     if (viewport.wrapAlignment)
345     {
346       wrapMenuItem_actionPerformed(null);
347     }
348
349     if (jalview.bin.Cache.getDefault("SHOW_OVERVIEW", false))
350     {
351       this.overviewMenuItem_actionPerformed(null);
352     }
353
354     addKeyListener();
355
356   }
357
358   /**
359    * Change the filename and format for the alignment, and enable the 'reload'
360    * button functionality.
361    * 
362    * @param file
363    *          valid filename
364    * @param format
365    *          format of file
366    */
367   public void setFileName(String file, String format)
368   {
369     fileName = file;
370     currentFileFormat = format;
371     reload.setEnabled(true);
372   }
373
374   void addKeyListener()
375   {
376     addKeyListener(new KeyAdapter()
377     {
378       @Override
379       public void keyPressed(KeyEvent evt)
380       {
381         if (viewport.cursorMode
382                 && ((evt.getKeyCode() >= KeyEvent.VK_0 && evt.getKeyCode() <= KeyEvent.VK_9) || (evt
383                         .getKeyCode() >= KeyEvent.VK_NUMPAD0 && evt
384                         .getKeyCode() <= KeyEvent.VK_NUMPAD9))
385                 && Character.isDigit(evt.getKeyChar()))
386         {
387           alignPanel.seqPanel.numberPressed(evt.getKeyChar());
388         }
389
390         switch (evt.getKeyCode())
391         {
392
393         case 27: // escape key
394           deselectAllSequenceMenuItem_actionPerformed(null);
395
396           break;
397
398         case KeyEvent.VK_DOWN:
399           if (evt.isAltDown() || !viewport.cursorMode)
400           {
401             moveSelectedSequences(false);
402           }
403           if (viewport.cursorMode)
404           {
405             alignPanel.seqPanel.moveCursor(0, 1);
406           }
407           break;
408
409         case KeyEvent.VK_UP:
410           if (evt.isAltDown() || !viewport.cursorMode)
411           {
412             moveSelectedSequences(true);
413           }
414           if (viewport.cursorMode)
415           {
416             alignPanel.seqPanel.moveCursor(0, -1);
417           }
418
419           break;
420
421         case KeyEvent.VK_LEFT:
422           if (evt.isAltDown() || !viewport.cursorMode)
423           {
424             slideSequences(false, alignPanel.seqPanel.getKeyboardNo1());
425           }
426           else
427           {
428             alignPanel.seqPanel.moveCursor(-1, 0);
429           }
430
431           break;
432
433         case KeyEvent.VK_RIGHT:
434           if (evt.isAltDown() || !viewport.cursorMode)
435           {
436             slideSequences(true, alignPanel.seqPanel.getKeyboardNo1());
437           }
438           else
439           {
440             alignPanel.seqPanel.moveCursor(1, 0);
441           }
442           break;
443
444         case KeyEvent.VK_SPACE:
445           if (viewport.cursorMode)
446           {
447             alignPanel.seqPanel.insertGapAtCursor(evt.isControlDown()
448                     || evt.isShiftDown() || evt.isAltDown());
449           }
450           break;
451
452         // case KeyEvent.VK_A:
453         // if (viewport.cursorMode)
454         // {
455         // alignPanel.seqPanel.insertNucAtCursor(false,"A");
456         // //System.out.println("A");
457         // }
458         // break;
459         /*
460          * case KeyEvent.VK_CLOSE_BRACKET: if (viewport.cursorMode) {
461          * System.out.println("closing bracket"); } break;
462          */
463         case KeyEvent.VK_DELETE:
464         case KeyEvent.VK_BACK_SPACE:
465           if (!viewport.cursorMode)
466           {
467             cut_actionPerformed(null);
468           }
469           else
470           {
471             alignPanel.seqPanel.deleteGapAtCursor(evt.isControlDown()
472                     || evt.isShiftDown() || evt.isAltDown());
473           }
474
475           break;
476
477         case KeyEvent.VK_S:
478           if (viewport.cursorMode)
479           {
480             alignPanel.seqPanel.setCursorRow();
481           }
482           break;
483         case KeyEvent.VK_C:
484           if (viewport.cursorMode && !evt.isControlDown())
485           {
486             alignPanel.seqPanel.setCursorColumn();
487           }
488           break;
489         case KeyEvent.VK_P:
490           if (viewport.cursorMode)
491           {
492             alignPanel.seqPanel.setCursorPosition();
493           }
494           break;
495
496         case KeyEvent.VK_ENTER:
497         case KeyEvent.VK_COMMA:
498           if (viewport.cursorMode)
499           {
500             alignPanel.seqPanel.setCursorRowAndColumn();
501           }
502           break;
503
504         case KeyEvent.VK_Q:
505           if (viewport.cursorMode)
506           {
507             alignPanel.seqPanel.setSelectionAreaAtCursor(true);
508           }
509           break;
510         case KeyEvent.VK_M:
511           if (viewport.cursorMode)
512           {
513             alignPanel.seqPanel.setSelectionAreaAtCursor(false);
514           }
515           break;
516
517         case KeyEvent.VK_F2:
518           viewport.cursorMode = !viewport.cursorMode;
519           statusBar.setText(MessageManager.formatMessage(
520                   "label.keyboard_editing_mode", new String[]
521                   { (viewport.cursorMode ? "on" : "off") }));
522           if (viewport.cursorMode)
523           {
524             alignPanel.seqPanel.seqCanvas.cursorX = viewport.startRes;
525             alignPanel.seqPanel.seqCanvas.cursorY = viewport.startSeq;
526           }
527           alignPanel.seqPanel.seqCanvas.repaint();
528           break;
529
530         case KeyEvent.VK_F1:
531           try
532           {
533             Help.showHelpWindow();
534           } catch (Exception ex)
535           {
536             ex.printStackTrace();
537           }
538           break;
539         case KeyEvent.VK_H:
540         {
541           boolean toggleSeqs = !evt.isControlDown();
542           boolean toggleCols = !evt.isShiftDown();
543           toggleHiddenRegions(toggleSeqs, toggleCols);
544           break;
545         }
546         case KeyEvent.VK_PAGE_UP:
547           if (viewport.wrapAlignment)
548           {
549             alignPanel.scrollUp(true);
550           }
551           else
552           {
553             alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
554                     - viewport.endSeq + viewport.startSeq);
555           }
556           break;
557         case KeyEvent.VK_PAGE_DOWN:
558           if (viewport.wrapAlignment)
559           {
560             alignPanel.scrollUp(false);
561           }
562           else
563           {
564             alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
565                     + viewport.endSeq - viewport.startSeq);
566           }
567           break;
568         }
569       }
570
571       @Override
572       public void keyReleased(KeyEvent evt)
573       {
574         switch (evt.getKeyCode())
575         {
576         case KeyEvent.VK_LEFT:
577           if (evt.isAltDown() || !viewport.cursorMode)
578           {
579             viewport.firePropertyChange("alignment", null, viewport
580                     .getAlignment().getSequences());
581           }
582           break;
583
584         case KeyEvent.VK_RIGHT:
585           if (evt.isAltDown() || !viewport.cursorMode)
586           {
587             viewport.firePropertyChange("alignment", null, viewport
588                     .getAlignment().getSequences());
589           }
590           break;
591         }
592       }
593     });
594   }
595
596   public void addAlignmentPanel(final AlignmentPanel ap, boolean newPanel)
597   {
598     ap.alignFrame = this;
599     avc = new jalview.controller.AlignViewController(this, viewport,
600             alignPanel);
601
602     alignPanels.addElement(ap);
603
604     PaintRefresher.Register(ap, ap.av.getSequenceSetId());
605
606     int aSize = alignPanels.size();
607
608     tabbedPane.setVisible(aSize > 1 || ap.av.viewName != null);
609
610     if (aSize == 1 && ap.av.viewName == null)
611     {
612       this.getContentPane().add(ap, BorderLayout.CENTER);
613     }
614     else
615     {
616       if (aSize == 2)
617       {
618         setInitialTabVisible();
619       }
620
621       expandViews.setEnabled(true);
622       gatherViews.setEnabled(true);
623       tabbedPane.addTab(ap.av.viewName, ap);
624
625       ap.setVisible(false);
626     }
627
628     if (newPanel)
629     {
630       if (ap.av.isPadGaps())
631       {
632         ap.av.getAlignment().padGaps();
633       }
634       ap.av.updateConservation(ap);
635       ap.av.updateConsensus(ap);
636       ap.av.updateStrucConsensus(ap);
637     }
638   }
639
640   public void setInitialTabVisible()
641   {
642     expandViews.setEnabled(true);
643     gatherViews.setEnabled(true);
644     tabbedPane.setVisible(true);
645     AlignmentPanel first = (AlignmentPanel) alignPanels.firstElement();
646     tabbedPane.addTab(first.av.viewName, first);
647     this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
648   }
649
650   public AlignViewport getViewport()
651   {
652     return viewport;
653   }
654
655   /* Set up intrinsic listeners for dynamically generated GUI bits. */
656   private void addServiceListeners()
657   {
658     final java.beans.PropertyChangeListener thisListener;
659     Desktop.instance.addJalviewPropertyChangeListener("services",
660             thisListener = new java.beans.PropertyChangeListener()
661             {
662               @Override
663               public void propertyChange(PropertyChangeEvent evt)
664               {
665                 // // System.out.println("Discoverer property change.");
666                 // if (evt.getPropertyName().equals("services"))
667                 {
668                   SwingUtilities.invokeLater(new Runnable()
669                   {
670
671                     @Override
672                     public void run()
673                     {
674                       System.err
675                               .println("Rebuild WS Menu for service change");
676                       BuildWebServiceMenu();
677                     }
678
679                   });
680                 }
681               }
682             });
683     addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
684     {
685       @Override
686       public void internalFrameClosed(
687               javax.swing.event.InternalFrameEvent evt)
688       {
689         System.out.println("deregistering discoverer listener");
690         Desktop.instance.removeJalviewPropertyChangeListener("services",
691                 thisListener);
692         closeMenuItem_actionPerformed(true);
693       };
694     });
695     // Finally, build the menu once to get current service state
696     new Thread(new Runnable()
697     {
698       @Override
699       public void run()
700       {
701         BuildWebServiceMenu();
702       }
703     }).start();
704   }
705
706   public void setGUINucleotide(boolean nucleotide)
707   {
708     showTranslation.setVisible(nucleotide);
709     cdna.setVisible(!nucleotide);
710     configureCdnaMenu();
711     conservationMenuItem.setEnabled(!nucleotide);
712     modifyConservation.setEnabled(!nucleotide);
713     showGroupConservation.setEnabled(!nucleotide);
714     rnahelicesColour.setEnabled(nucleotide);
715     purinePyrimidineColour.setEnabled(nucleotide);
716   }
717
718   /**
719    * Add any suitable options to the 'cDNA' sub-menu. Options may be to
720    * associate a cDNA alignment, or to align an associated alignment. To be
721    * suitable for association, an AlignFrame has to be nucleotide, and have the
722    * right number of sequences of corresponding length to this one.
723    */
724   protected void configureCdnaMenu()
725   {
726     cdna.removeAll();
727
728     /*
729      * Identify candidates for 'associate cDNA', add to menu.
730      */
731     List<AlignFrame> candidates = getCdnaCandidates();
732     for (final AlignFrame candidate : candidates)
733     {
734       final String text = MessageManager.getString("label.associate") + " "
735               + candidate.getTitle();
736       JMenuItem option = new JMenuItem(text);
737       option.addActionListener(new ActionListener()
738       {
739         @Override
740         public void actionPerformed(ActionEvent e)
741         {
742           associateCdna(candidate);
743         }
744       });
745       cdna.add(option);
746     }
747
748     /*
749      * Identify candidates for 'align cDNA', add to menu.
750      */
751     final AlignFrame[] alignframes = Desktop.getAlignframes();
752     if (alignframes != null)
753     {
754       for (final AlignFrame af : alignframes)
755       {
756         if (af != this)
757         {
758           if (this.viewport.getStructureSelectionManager()
759                   .hasCommandListener(af.viewport))
760           {
761             final String text = MessageManager.getString("label.align")
762                     + " " + af.getTitle();
763             JMenuItem option = new JMenuItem(text);
764             option.addActionListener(new ActionListener()
765             {
766               @Override
767               public void actionPerformed(ActionEvent e)
768               {
769                 af.alignPanel.getAlignment().alignAs(
770                         AlignFrame.this.alignPanel.getAlignment());
771                 af.viewport.alignmentChanged(af.alignPanel);
772               }
773             });
774             cdna.add(option);
775           }
776         }
777       }
778     }
779
780     cdna.setEnabled(cdna.getMenuComponentCount() > 0);
781   }
782
783   /**
784    * Returns a list of AlignFrame which are valid candidates for being the cDNA
785    * to map to this (protein) alignment. Valid means a nucleotide alignment with
786    * matching number of sequences and sequence lengths (excluding gaps).
787    * 
788    * @return
789    */
790   protected List<AlignFrame> getCdnaCandidates()
791   {
792     List<AlignFrame> result = new ArrayList<AlignFrame>();
793     if (this.alignPanel != null)
794     {
795       AlignmentI thisAlignment = this.alignPanel.getAlignment();
796       if (thisAlignment == null || thisAlignment.isNucleotide())
797       {
798         return result;
799       }
800       final AlignFrame[] alignframes = Desktop.getAlignframes();
801       if (alignframes != null)
802       {
803         for (AlignFrame af : alignframes)
804         {
805           if (af.alignPanel != null)
806           {
807             final AlignmentI thatAlignment = af.alignPanel.getAlignment();
808             if (thatAlignment.isNucleotide()
809                     && thisAlignment.isMappableTo(thatAlignment))
810             {
811               // TODO exclude an AlignFrame which is already mapped to this one
812               // simple version: exclude if already a CommandListener (should
813               // cover most cases but not all)
814               if (!this.viewport.getStructureSelectionManager()
815                       .hasCommandListener(af.viewport))
816               {
817                 result.add(af);
818               }
819             }
820           }
821         }
822       }
823     }
824     return result;
825   }
826
827   /**
828    * Build the codon mappings between the given (nucleotide) alignment and this
829    * (protein) alignment. Also make the cDNA alignment a CommandListener for the
830    * protein alignment so that edits are mirrored. The alignments must have the
831    * same number, and equivalent lengths, of (unaligned) sequence.
832    * 
833    * @param cdna
834    * @throws IllegalStateException
835    *           if sequence counts or lengths are incompatible
836    */
837   protected void associateCdna(AlignFrame cdna)
838   {
839     /*
840      * Warn that cDNA may be realigned to match protein
841      */
842     // int confirm = JOptionPane.showConfirmDialog(
843     // this,
844     // JvSwingUtils.wrapTooltip(true,
845     // MessageManager.getString("warn.cdna_realign")),
846     // MessageManager.getString("label.cdna_realign"),
847     // JOptionPane.OK_CANCEL_OPTION);
848     // if (confirm == JOptionPane.CANCEL_OPTION
849     // || confirm == JOptionPane.CLOSED_OPTION)
850     // {
851     // return;
852     // }
853
854     final AlignmentI aaAlignment = this.alignPanel.getAlignment();
855     Iterator<SequenceI> thisSeqs = aaAlignment
856             .getSequences().iterator();
857     Iterator<SequenceI> cdnaSeqs = cdna.alignPanel.getAlignment()
858             .getSequences().iterator();
859     AlignedCodonFrame acf = new AlignedCodonFrame(aaAlignment.getWidth());
860     while (thisSeqs.hasNext())
861     {
862       if (!cdnaSeqs.hasNext())
863       {
864         throw new IllegalStateException("Too few sequences to map");
865       }
866       final SequenceI aaSeq = thisSeqs.next();
867       String aaSeqString = aaSeq.getDatasetSequence()
868               .getSequenceAsString();
869       final SequenceI cdnaSeq = cdnaSeqs.next();
870       String cdnaSeqString = cdnaSeq.getDatasetSequence()
871               .getSequenceAsString();
872       final int aaLength = aaSeqString.length();
873       final int cdnaLength = cdnaSeqString.length();
874       if (cdnaLength != 3 * aaLength)
875       {
876         throw new IllegalStateException(
877                 "Protein/cDNA lengths don't match: " + aaLength + "/"
878                         + cdnaLength);
879       }
880
881       /*
882        * Warn if mapping includes non-standard translations
883        */
884       if (!doTranslationWarningCheck(aaSeq.getName(), aaSeqString,
885               cdnaSeqString))
886       {
887         return;
888       }
889
890       MapList map = new MapList(new int[]
891       { 1, cdnaLength }, new int[]
892       { 1, aaLength }, 3, 1);
893       acf.addMap(cdnaSeq, aaSeq, map);
894
895       aaAlignment.addCodonFrame(acf);
896
897       final StructureSelectionManager ssm = StructureSelectionManager
898               .getStructureSelectionManager(Desktop.instance);
899       ssm.addMappings(aaAlignment.getCodonFrames());
900       ssm.addCommandListener(cdna.getViewport());
901
902       /*
903        * Rebuild 'associate cDna' menu so it now excludes the one just
904        * associated.
905        */
906       configureCdnaMenu();
907     }
908   }
909
910   /**
911    * Show a warning if any non-standard cDNA to protein would result from
912    * mapping the sequences.
913    * 
914    * @param aaSeqName
915    * @param aaSeqString
916    * @param aaSeqString
917    * @return true if no warning, or it is accepted, false if user chooses not to
918    *         proceed.
919    */
920   protected boolean doTranslationWarningCheck(String aaSeqName,
921           String aaSeqString, String cdnaSeqString)
922   {
923     final int aaLength = aaSeqString.length();
924     boolean warning = false;
925     String msg = aaSeqName;
926     for (int i = 0; i < aaLength; i++)
927     {
928       String codon = cdnaSeqString.substring(i * 3, i * 3 + 3);
929       String aa = ResidueProperties.codonTranslate(codon);
930       if (!(aa.charAt(0) == aaSeqString.charAt(i)))
931       {
932         warning = true;
933         msg += ":" + (i + 1) + ":" + aaSeqString.charAt(i) + "/" + codon
934                 + ":" + aa;
935         break;
936       }
937     }
938     if (warning)
939     {
940       final String txt = JvSwingUtils.wrapTooltip(true, MessageManager
941               .formatMessage("warn.nonstandard_translation", msg));
942       int confirm = JOptionPane.showConfirmDialog(this, txt,
943               MessageManager.getString("label.nonstandard_translation"),
944               JOptionPane.OK_CANCEL_OPTION);
945       if (confirm == JOptionPane.CANCEL_OPTION
946               || confirm == JOptionPane.CLOSED_OPTION)
947       {
948         return false;
949       }
950     }
951     return true;
952   }
953
954   /**
955    * set up menus for the current viewport. This may be called after any
956    * operation that affects the data in the current view (selection changed,
957    * etc) to update the menus to reflect the new state.
958    */
959   public void setMenusForViewport()
960   {
961     setMenusFromViewport(viewport);
962   }
963
964   /**
965    * Need to call this method when tabs are selected for multiple views, or when
966    * loading from Jalview2XML.java
967    * 
968    * @param av
969    *          AlignViewport
970    */
971   void setMenusFromViewport(AlignViewport av)
972   {
973     padGapsMenuitem.setSelected(av.isPadGaps());
974     colourTextMenuItem.setSelected(av.showColourText);
975     abovePIDThreshold.setSelected(av.getAbovePIDThreshold());
976     conservationMenuItem.setSelected(av.getConservationSelected());
977     seqLimits.setSelected(av.getShowJVSuffix());
978     idRightAlign.setSelected(av.rightAlignIds);
979     centreColumnLabelsMenuItem.setState(av.centreColumnLabels);
980     renderGapsMenuItem.setSelected(av.renderGaps);
981     wrapMenuItem.setSelected(av.wrapAlignment);
982     scaleAbove.setVisible(av.wrapAlignment);
983     scaleLeft.setVisible(av.wrapAlignment);
984     scaleRight.setVisible(av.wrapAlignment);
985     annotationPanelMenuItem.setState(av.showAnnotation);
986     /*
987      * Show/hide annotations only enabled if annotation panel is shown
988      */
989     showAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
990     hideAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
991     showAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
992     hideAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
993     viewBoxesMenuItem.setSelected(av.showBoxes);
994     viewTextMenuItem.setSelected(av.showText);
995     showNonconservedMenuItem.setSelected(av.getShowUnconserved());
996     showGroupConsensus.setSelected(av.isShowGroupConsensus());
997     showGroupConservation.setSelected(av.isShowGroupConservation());
998     showConsensusHistogram.setSelected(av.isShowConsensusHistogram());
999     showSequenceLogo.setSelected(av.isShowSequenceLogo());
1000     normaliseSequenceLogo.setSelected(av.isNormaliseSequenceLogo());
1001
1002     setColourSelected(ColourSchemeProperty.getColourName(av
1003             .getGlobalColourScheme()));
1004
1005     showSeqFeatures.setSelected(av.showSequenceFeatures);
1006     hiddenMarkers.setState(av.showHiddenMarkers);
1007     applyToAllGroups.setState(av.getColourAppliesToAllGroups());
1008     showNpFeatsMenuitem.setSelected(av.isShowNpFeats());
1009     showDbRefsMenuitem.setSelected(av.isShowDbRefs());
1010     autoCalculate.setSelected(av.autoCalculateConsensus);
1011     sortByTree.setSelected(av.sortByTree);
1012     listenToViewSelections.setSelected(av.followSelection);
1013     rnahelicesColour.setEnabled(av.getAlignment().hasRNAStructure());
1014     rnahelicesColour
1015             .setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
1016     setShowProductsEnabled();
1017     updateEditMenuBar();
1018   }
1019
1020   // methods for implementing IProgressIndicator
1021   // need to refactor to a reusable stub class
1022   Hashtable progressBars, progressBarHandlers;
1023
1024   /*
1025    * (non-Javadoc)
1026    * 
1027    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
1028    */
1029   @Override
1030   public void setProgressBar(String message, long id)
1031   {
1032     if (progressBars == null)
1033     {
1034       progressBars = new Hashtable();
1035       progressBarHandlers = new Hashtable();
1036     }
1037
1038     JPanel progressPanel;
1039     Long lId = new Long(id);
1040     GridLayout layout = (GridLayout) statusPanel.getLayout();
1041     if (progressBars.get(lId) != null)
1042     {
1043       progressPanel = (JPanel) progressBars.get(new Long(id));
1044       statusPanel.remove(progressPanel);
1045       progressBars.remove(lId);
1046       progressPanel = null;
1047       if (message != null)
1048       {
1049         statusBar.setText(message);
1050       }
1051       if (progressBarHandlers.contains(lId))
1052       {
1053         progressBarHandlers.remove(lId);
1054       }
1055       layout.setRows(layout.getRows() - 1);
1056     }
1057     else
1058     {
1059       progressPanel = new JPanel(new BorderLayout(10, 5));
1060
1061       JProgressBar progressBar = new JProgressBar();
1062       progressBar.setIndeterminate(true);
1063
1064       progressPanel.add(new JLabel(message), BorderLayout.WEST);
1065       progressPanel.add(progressBar, BorderLayout.CENTER);
1066
1067       layout.setRows(layout.getRows() + 1);
1068       statusPanel.add(progressPanel);
1069
1070       progressBars.put(lId, progressPanel);
1071     }
1072     // update GUI
1073     // setMenusForViewport();
1074     validate();
1075   }
1076
1077   @Override
1078   public void registerHandler(final long id,
1079           final IProgressIndicatorHandler handler)
1080   {
1081     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
1082     {
1083       throw new Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
1084     }
1085     progressBarHandlers.put(new Long(id), handler);
1086     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
1087     if (handler.canCancel())
1088     {
1089       JButton cancel = new JButton(
1090               MessageManager.getString("action.cancel"));
1091       final IProgressIndicator us = this;
1092       cancel.addActionListener(new ActionListener()
1093       {
1094
1095         @Override
1096         public void actionPerformed(ActionEvent e)
1097         {
1098           handler.cancelActivity(id);
1099           us.setProgressBar(MessageManager.formatMessage("label.cancelled_params", new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
1100         }
1101       });
1102       progressPanel.add(cancel, BorderLayout.EAST);
1103     }
1104   }
1105
1106   /**
1107    * 
1108    * @return true if any progress bars are still active
1109    */
1110   @Override
1111   public boolean operationInProgress()
1112   {
1113     if (progressBars != null && progressBars.size() > 0)
1114     {
1115       return true;
1116     }
1117     return false;
1118   }
1119
1120   @Override
1121   public void setStatus(String text)
1122   {
1123     statusBar.setText(text);
1124   };
1125
1126   /*
1127    * Added so Castor Mapping file can obtain Jalview Version
1128    */
1129   public String getVersion()
1130   {
1131     return jalview.bin.Cache.getProperty("VERSION");
1132   }
1133
1134   public FeatureRenderer getFeatureRenderer()
1135   {
1136     return alignPanel.seqPanel.seqCanvas.getFeatureRenderer();
1137   }
1138
1139   @Override
1140   public void fetchSequence_actionPerformed(ActionEvent e)
1141   {
1142     new SequenceFetcher(this);
1143   }
1144
1145   @Override
1146   public void addFromFile_actionPerformed(ActionEvent e)
1147   {
1148     Desktop.instance.inputLocalFileMenuItem_actionPerformed(viewport);
1149   }
1150
1151   @Override
1152   public void reload_actionPerformed(ActionEvent e)
1153   {
1154     if (fileName != null)
1155     {
1156       // TODO: JAL-1108 - ensure all associated frames are closed regardless of
1157       // originating file's format
1158       // TODO: work out how to recover feature settings for correct view(s) when
1159       // file is reloaded.
1160       if (currentFileFormat.equals("Jalview"))
1161       {
1162         JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1163         for (int i = 0; i < frames.length; i++)
1164         {
1165           if (frames[i] instanceof AlignFrame && frames[i] != this
1166                   && ((AlignFrame) frames[i]).fileName != null
1167                   && ((AlignFrame) frames[i]).fileName.equals(fileName))
1168           {
1169             try
1170             {
1171               frames[i].setSelected(true);
1172               Desktop.instance.closeAssociatedWindows();
1173             } catch (java.beans.PropertyVetoException ex)
1174             {
1175             }
1176           }
1177
1178         }
1179         Desktop.instance.closeAssociatedWindows();
1180
1181         FileLoader loader = new FileLoader();
1182         String protocol = fileName.startsWith("http:") ? "URL" : "File";
1183         loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
1184       }
1185       else
1186       {
1187         Rectangle bounds = this.getBounds();
1188
1189         FileLoader loader = new FileLoader();
1190         String protocol = fileName.startsWith("http:") ? "URL" : "File";
1191         AlignFrame newframe = loader.LoadFileWaitTillLoaded(fileName,
1192                 protocol, currentFileFormat);
1193
1194         newframe.setBounds(bounds);
1195         if (featureSettings != null && featureSettings.isShowing())
1196         {
1197           final Rectangle fspos = featureSettings.frame.getBounds();
1198           // TODO: need a 'show feature settings' function that takes bounds -
1199           // need to refactor Desktop.addFrame
1200           newframe.featureSettings_actionPerformed(null);
1201           final FeatureSettings nfs = newframe.featureSettings;
1202           SwingUtilities.invokeLater(new Runnable()
1203           {
1204             @Override
1205             public void run()
1206             {
1207               nfs.frame.setBounds(fspos);
1208             }
1209           });
1210           this.featureSettings.close();
1211           this.featureSettings = null;
1212         }
1213         this.closeMenuItem_actionPerformed(true);
1214       }
1215     }
1216   }
1217
1218   @Override
1219   public void addFromText_actionPerformed(ActionEvent e)
1220   {
1221     Desktop.instance.inputTextboxMenuItem_actionPerformed(viewport);
1222   }
1223
1224   @Override
1225   public void addFromURL_actionPerformed(ActionEvent e)
1226   {
1227     Desktop.instance.inputURLMenuItem_actionPerformed(viewport);
1228   }
1229
1230   @Override
1231   public void save_actionPerformed(ActionEvent e)
1232   {
1233     if (fileName == null
1234             || (currentFileFormat == null || !jalview.io.FormatAdapter
1235                     .isValidIOFormat(currentFileFormat, true))
1236             || fileName.startsWith("http"))
1237     {
1238       saveAs_actionPerformed(null);
1239     }
1240     else
1241     {
1242       saveAlignment(fileName, currentFileFormat);
1243     }
1244   }
1245
1246   /**
1247    * DOCUMENT ME!
1248    * 
1249    * @param e
1250    *          DOCUMENT ME!
1251    */
1252   @Override
1253   public void saveAs_actionPerformed(ActionEvent e)
1254   {
1255     JalviewFileChooser chooser = new JalviewFileChooser(
1256             jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
1257             jalview.io.AppletFormatAdapter.WRITABLE_EXTENSIONS,
1258             jalview.io.AppletFormatAdapter.WRITABLE_FNAMES,
1259             currentFileFormat, false);
1260
1261     chooser.setFileView(new JalviewFileView());
1262     chooser.setDialogTitle(MessageManager.getString("label.save_alignment_to_file"));
1263     chooser.setToolTipText(MessageManager.getString("action.save"));
1264
1265     int value = chooser.showSaveDialog(this);
1266
1267     if (value == JalviewFileChooser.APPROVE_OPTION)
1268     {
1269       currentFileFormat = chooser.getSelectedFormat();
1270       if (currentFileFormat == null)
1271       {
1272         JOptionPane
1273                 .showInternalMessageDialog(
1274                         Desktop.desktop,
1275                         MessageManager
1276                                 .getString("label.select_file_format_before_saving"),
1277                         MessageManager
1278                                 .getString("label.file_format_not_specified"),
1279                         JOptionPane.WARNING_MESSAGE);
1280         value = chooser.showSaveDialog(this);
1281         return;
1282       }
1283
1284       fileName = chooser.getSelectedFile().getPath();
1285
1286       jalview.bin.Cache.setProperty("DEFAULT_FILE_FORMAT",
1287               currentFileFormat);
1288
1289       jalview.bin.Cache.setProperty("LAST_DIRECTORY", fileName);
1290       if (currentFileFormat.indexOf(" ") > -1)
1291       {
1292         currentFileFormat = currentFileFormat.substring(0,
1293                 currentFileFormat.indexOf(" "));
1294       }
1295       saveAlignment(fileName, currentFileFormat);
1296     }
1297   }
1298
1299   public boolean saveAlignment(String file, String format)
1300   {
1301     boolean success = true;
1302
1303     if (format.equalsIgnoreCase("Jalview"))
1304     {
1305       String shortName = title;
1306
1307       if (shortName.indexOf(java.io.File.separatorChar) > -1)
1308       {
1309         shortName = shortName.substring(shortName
1310                 .lastIndexOf(java.io.File.separatorChar) + 1);
1311       }
1312
1313       success = new Jalview2XML().SaveAlignment(this, file, shortName);
1314
1315       statusBar.setText(MessageManager.formatMessage(
1316               "label.successfully_saved_to_file_in_format", new String[]
1317               { fileName, format }));
1318
1319     }
1320     else
1321     {
1322       if (!jalview.io.AppletFormatAdapter.isValidFormat(format, true))
1323       {
1324         warningMessage("Cannot save file " + fileName + " using format "
1325                 + format, "Alignment output format not supported");
1326         saveAs_actionPerformed(null);
1327         // JBPNote need to have a raise_gui flag here
1328         return false;
1329       }
1330
1331       String[] omitHidden = null;
1332
1333       if (viewport.hasHiddenColumns())
1334       {
1335         int reply = JOptionPane
1336                 .showInternalConfirmDialog(
1337                         Desktop.desktop,
1338                         MessageManager
1339                                 .getString("label.alignment_contains_hidden_columns"),
1340                         MessageManager
1341                                 .getString("action.save_omit_hidden_columns"),
1342                         JOptionPane.YES_NO_OPTION,
1343                         JOptionPane.QUESTION_MESSAGE);
1344
1345         if (reply == JOptionPane.YES_OPTION)
1346         {
1347           omitHidden = viewport.getViewAsString(false);
1348         }
1349       }
1350       FormatAdapter f = new FormatAdapter();
1351       String output = f.formatSequences(format,
1352               viewport.getAlignment(), // class cast exceptions will
1353               // occur in the distant future
1354               omitHidden, f.getCacheSuffixDefault(format),
1355               viewport.getColumnSelection());
1356
1357       if (output == null)
1358       {
1359         success = false;
1360       }
1361       else
1362       {
1363         try
1364         {
1365           java.io.PrintWriter out = new java.io.PrintWriter(
1366                   new java.io.FileWriter(file));
1367
1368           out.print(output);
1369           out.close();
1370           this.setTitle(file);
1371           statusBar.setText(MessageManager.formatMessage(
1372                   "label.successfully_saved_to_file_in_format",
1373                   new String[]
1374                   { fileName, format }));
1375         } catch (Exception ex)
1376         {
1377           success = false;
1378           ex.printStackTrace();
1379         }
1380       }
1381     }
1382
1383     if (!success)
1384     {
1385       JOptionPane.showInternalMessageDialog(this, MessageManager
1386               .formatMessage("label.couldnt_save_file", new String[]
1387               { fileName }), MessageManager
1388               .getString("label.error_saving_file"),
1389               JOptionPane.WARNING_MESSAGE);
1390     }
1391
1392     return success;
1393   }
1394
1395   private void warningMessage(String warning, String title)
1396   {
1397     if (new jalview.util.Platform().isHeadless())
1398     {
1399       System.err.println("Warning: " + title + "\nWarning: " + warning);
1400
1401     }
1402     else
1403     {
1404       JOptionPane.showInternalMessageDialog(this, warning, title,
1405               JOptionPane.WARNING_MESSAGE);
1406     }
1407     return;
1408   }
1409
1410   /**
1411    * DOCUMENT ME!
1412    * 
1413    * @param e
1414    *          DOCUMENT ME!
1415    */
1416   @Override
1417   protected void outputText_actionPerformed(ActionEvent e)
1418   {
1419     String[] omitHidden = null;
1420
1421     if (viewport.hasHiddenColumns())
1422     {
1423       int reply = JOptionPane
1424               .showInternalConfirmDialog(
1425                       Desktop.desktop,
1426                       MessageManager
1427                               .getString("label.alignment_contains_hidden_columns"),
1428                       MessageManager
1429                               .getString("action.save_omit_hidden_columns"),
1430                       JOptionPane.YES_NO_OPTION,
1431                       JOptionPane.QUESTION_MESSAGE);
1432
1433       if (reply == JOptionPane.YES_OPTION)
1434       {
1435         omitHidden = viewport.getViewAsString(false);
1436       }
1437     }
1438
1439     CutAndPasteTransfer cap = new CutAndPasteTransfer();
1440     cap.setForInput(null);
1441
1442     try
1443     {
1444       cap.setText(new FormatAdapter().formatSequences(e.getActionCommand(),
1445               viewport.getAlignment(), omitHidden,
1446               viewport.getColumnSelection()));
1447       Desktop.addInternalFrame(cap, MessageManager.formatMessage(
1448               "label.alignment_output_command", new String[]
1449               { e.getActionCommand() }), 600, 500);
1450     } catch (OutOfMemoryError oom)
1451     {
1452       new OOMWarning("Outputting alignment as " + e.getActionCommand(), oom);
1453       cap.dispose();
1454     }
1455
1456   }
1457
1458   /**
1459    * DOCUMENT ME!
1460    * 
1461    * @param e
1462    *          DOCUMENT ME!
1463    */
1464   @Override
1465   protected void htmlMenuItem_actionPerformed(ActionEvent e)
1466   {
1467     new HTMLOutput(alignPanel,
1468             alignPanel.seqPanel.seqCanvas.getSequenceRenderer(),
1469             alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
1470   }
1471
1472   public void createImageMap(File file, String image)
1473   {
1474     alignPanel.makePNGImageMap(file, image);
1475   }
1476
1477   /**
1478    * DOCUMENT ME!
1479    * 
1480    * @param e
1481    *          DOCUMENT ME!
1482    */
1483   @Override
1484   public void createPNG(File f)
1485   {
1486     alignPanel.makePNG(f);
1487   }
1488
1489   /**
1490    * DOCUMENT ME!
1491    * 
1492    * @param e
1493    *          DOCUMENT ME!
1494    */
1495   @Override
1496   public void createEPS(File f)
1497   {
1498     alignPanel.makeEPS(f);
1499   }
1500
1501   public void createSVG(File f)
1502   {
1503     alignPanel.makeSVG(f);
1504   }
1505   @Override
1506   public void pageSetup_actionPerformed(ActionEvent e)
1507   {
1508     PrinterJob printJob = PrinterJob.getPrinterJob();
1509     PrintThread.pf = printJob.pageDialog(printJob.defaultPage());
1510   }
1511
1512   /**
1513    * DOCUMENT ME!
1514    * 
1515    * @param e
1516    *          DOCUMENT ME!
1517    */
1518   @Override
1519   public void printMenuItem_actionPerformed(ActionEvent e)
1520   {
1521     // Putting in a thread avoids Swing painting problems
1522     PrintThread thread = new PrintThread(alignPanel);
1523     thread.start();
1524   }
1525
1526   @Override
1527   public void exportFeatures_actionPerformed(ActionEvent e)
1528   {
1529     new AnnotationExporter().exportFeatures(alignPanel);
1530   }
1531
1532   @Override
1533   public void exportAnnotations_actionPerformed(ActionEvent e)
1534   {
1535     new AnnotationExporter().exportAnnotations(alignPanel,
1536             viewport.showAnnotation ? viewport.getAlignment()
1537                     .getAlignmentAnnotation() : null, viewport
1538                     .getAlignment().getGroups(), ((Alignment) viewport
1539                     .getAlignment()).alignmentProperties);
1540   }
1541
1542   @Override
1543   public void associatedData_actionPerformed(ActionEvent e)
1544   {
1545     // Pick the tree file
1546     JalviewFileChooser chooser = new JalviewFileChooser(
1547             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1548     chooser.setFileView(new JalviewFileView());
1549     chooser.setDialogTitle(MessageManager
1550             .getString("label.load_jalview_annotations"));
1551     chooser.setToolTipText(MessageManager
1552             .getString("label.load_jalview_annotations"));
1553
1554     int value = chooser.showOpenDialog(null);
1555
1556     if (value == JalviewFileChooser.APPROVE_OPTION)
1557     {
1558       String choice = chooser.getSelectedFile().getPath();
1559       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
1560       loadJalviewDataFile(choice, null, null, null);
1561     }
1562
1563   }
1564
1565   /**
1566    * Close the current view or all views in the alignment frame. If the frame
1567    * only contains one view then the alignment will be removed from memory.
1568    * 
1569    * @param closeAllTabs
1570    */
1571   @Override
1572   public void closeMenuItem_actionPerformed(boolean closeAllTabs)
1573   {
1574     if (alignPanels != null && alignPanels.size() < 2)
1575     {
1576       closeAllTabs = true;
1577     }
1578
1579     try
1580     {
1581       if (alignPanels != null)
1582       {
1583         if (closeAllTabs)
1584         {
1585           if (this.isClosed())
1586           {
1587             // really close all the windows - otherwise wait till
1588             // setClosed(true) is called
1589             for (int i = 0; i < alignPanels.size(); i++)
1590             {
1591               AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i);
1592               ap.closePanel();
1593             }
1594           }
1595         }
1596         else
1597         {
1598           closeView(alignPanel);
1599         }
1600       }
1601
1602       if (closeAllTabs)
1603       {
1604         this.setClosed(true);
1605       }
1606     } catch (Exception ex)
1607     {
1608       ex.printStackTrace();
1609     }
1610   }
1611
1612   /**
1613    * close alignPanel2 and shuffle tabs appropriately.
1614    * 
1615    * @param alignPanel2
1616    */
1617   public void closeView(AlignmentPanel alignPanel2)
1618   {
1619     int index = tabbedPane.getSelectedIndex();
1620     int closedindex = tabbedPane.indexOfComponent(alignPanel2);
1621     alignPanels.removeElement(alignPanel2);
1622     // Unnecessary
1623     // if (viewport == alignPanel2.av)
1624     // {
1625     // viewport = null;
1626     // }
1627     alignPanel2.closePanel();
1628     alignPanel2 = null;
1629
1630     tabbedPane.removeTabAt(closedindex);
1631     tabbedPane.validate();
1632
1633     if (index > closedindex || index == tabbedPane.getTabCount())
1634     {
1635       // modify currently selected tab index if necessary.
1636       index--;
1637     }
1638
1639     this.tabSelectionChanged(index);
1640   }
1641
1642   /**
1643    * DOCUMENT ME!
1644    */
1645   void updateEditMenuBar()
1646   {
1647
1648     if (viewport.getHistoryList().size() > 0)
1649     {
1650       undoMenuItem.setEnabled(true);
1651       CommandI command = viewport.getHistoryList().peek();
1652       undoMenuItem.setText(MessageManager.formatMessage(
1653               "label.undo_command", new String[]
1654               { command.getDescription() }));
1655     }
1656     else
1657     {
1658       undoMenuItem.setEnabled(false);
1659       undoMenuItem.setText(MessageManager.getString("action.undo"));
1660     }
1661
1662     if (viewport.getRedoList().size() > 0)
1663     {
1664       redoMenuItem.setEnabled(true);
1665
1666       CommandI command = viewport.getRedoList().peek();
1667       redoMenuItem.setText(MessageManager.formatMessage(
1668               "label.redo_command", new String[]
1669               { command.getDescription() }));
1670     }
1671     else
1672     {
1673       redoMenuItem.setEnabled(false);
1674       redoMenuItem.setText(MessageManager.getString("action.redo"));
1675     }
1676   }
1677
1678   public void addHistoryItem(CommandI command)
1679   {
1680     if (command.getSize() > 0)
1681     {
1682       viewport.addToHistoryList(command);
1683       viewport.clearRedoList();
1684       updateEditMenuBar();
1685       viewport.updateHiddenColumns();
1686       // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
1687       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1688       // viewport.getColumnSelection()
1689       // .getHiddenColumns().size() > 0);
1690     }
1691   }
1692
1693   /**
1694    * 
1695    * @return alignment objects for all views
1696    */
1697   AlignmentI[] getViewAlignments()
1698   {
1699     if (alignPanels != null)
1700     {
1701       Enumeration e = alignPanels.elements();
1702       AlignmentI[] als = new AlignmentI[alignPanels.size()];
1703       for (int i = 0; e.hasMoreElements(); i++)
1704       {
1705         als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
1706       }
1707       return als;
1708     }
1709     if (viewport != null)
1710     {
1711       return new AlignmentI[]
1712       { viewport.getAlignment() };
1713     }
1714     return null;
1715   }
1716
1717   /**
1718    * DOCUMENT ME!
1719    * 
1720    * @param e
1721    *          DOCUMENT ME!
1722    */
1723   @Override
1724   protected void undoMenuItem_actionPerformed(ActionEvent e)
1725   {
1726     if (viewport.getHistoryList().isEmpty())
1727     {
1728       return;
1729     }
1730     CommandI command = viewport.getHistoryList().pop();
1731     viewport.addToRedoList(command);
1732     // TODO: execute command before adding to redo list / broadcasting?
1733     command.undoCommand(getViewAlignments());
1734
1735     AlignViewport originalSource = getOriginatingSource(command);
1736     updateEditMenuBar();
1737
1738     if (originalSource != null)
1739     {
1740       if (originalSource != viewport)
1741       {
1742         Cache.log
1743                 .warn("Implementation worry: mismatch of viewport origin for undo");
1744       }
1745       originalSource.updateHiddenColumns();
1746       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1747       // null
1748       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1749       // viewport.getColumnSelection()
1750       // .getHiddenColumns().size() > 0);
1751       originalSource.firePropertyChange("alignment", null, originalSource
1752               .getAlignment().getSequences());
1753     }
1754   }
1755
1756   /**
1757    * DOCUMENT ME!
1758    * 
1759    * @param e
1760    *          DOCUMENT ME!
1761    */
1762   @Override
1763   protected void redoMenuItem_actionPerformed(ActionEvent e)
1764   {
1765     if (viewport.getRedoList().size() < 1)
1766     {
1767       return;
1768     }
1769
1770     CommandI command = viewport.getRedoList().pop();
1771     viewport.addToHistoryList(command);
1772     command.doCommand(getViewAlignments());
1773
1774     AlignViewport originalSource = getOriginatingSource(command);
1775     updateEditMenuBar();
1776
1777     if (originalSource != null)
1778     {
1779
1780       if (originalSource != viewport)
1781       {
1782         Cache.log
1783                 .warn("Implementation worry: mismatch of viewport origin for redo");
1784       }
1785       originalSource.updateHiddenColumns();
1786       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1787       // null
1788       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1789       // viewport.getColumnSelection()
1790       // .getHiddenColumns().size() > 0);
1791       originalSource.firePropertyChange("alignment", null, originalSource
1792               .getAlignment().getSequences());
1793     }
1794   }
1795
1796   AlignViewport getOriginatingSource(CommandI command)
1797   {
1798     AlignViewport originalSource = null;
1799     // For sequence removal and addition, we need to fire
1800     // the property change event FROM the viewport where the
1801     // original alignment was altered
1802     AlignmentI al = null;
1803     if (command instanceof EditCommand)
1804     {
1805       EditCommand editCommand = (EditCommand) command;
1806       al = editCommand.getAlignment();
1807       Vector comps = (Vector) PaintRefresher.components.get(viewport
1808               .getSequenceSetId());
1809
1810       for (int i = 0; i < comps.size(); i++)
1811       {
1812         if (comps.elementAt(i) instanceof AlignmentPanel)
1813         {
1814           if (al == ((AlignmentPanel) comps.elementAt(i)).av.getAlignment())
1815           {
1816             originalSource = ((AlignmentPanel) comps.elementAt(i)).av;
1817             break;
1818           }
1819         }
1820       }
1821     }
1822
1823     if (originalSource == null)
1824     {
1825       // The original view is closed, we must validate
1826       // the current view against the closed view first
1827       if (al != null)
1828       {
1829         PaintRefresher.validateSequences(al, viewport.getAlignment());
1830       }
1831
1832       originalSource = viewport;
1833     }
1834
1835     return originalSource;
1836   }
1837
1838   /**
1839    * DOCUMENT ME!
1840    * 
1841    * @param up
1842    *          DOCUMENT ME!
1843    */
1844   public void moveSelectedSequences(boolean up)
1845   {
1846     SequenceGroup sg = viewport.getSelectionGroup();
1847
1848     if (sg == null)
1849     {
1850       return;
1851     }
1852     viewport.getAlignment().moveSelectedSequencesByOne(sg,
1853             viewport.getHiddenRepSequences(), up);
1854     alignPanel.paintAlignment(true);
1855   }
1856
1857   synchronized void slideSequences(boolean right, int size)
1858   {
1859     List<SequenceI> sg = new Vector();
1860     if (viewport.cursorMode)
1861     {
1862       sg.add(viewport.getAlignment().getSequenceAt(
1863               alignPanel.seqPanel.seqCanvas.cursorY));
1864     }
1865     else if (viewport.getSelectionGroup() != null
1866             && viewport.getSelectionGroup().getSize() != viewport
1867                     .getAlignment().getHeight())
1868     {
1869       sg = viewport.getSelectionGroup().getSequences(
1870               viewport.getHiddenRepSequences());
1871     }
1872
1873     if (sg.size() < 1)
1874     {
1875       return;
1876     }
1877
1878     Vector invertGroup = new Vector();
1879
1880     for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
1881     {
1882       if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
1883       {
1884         invertGroup.add(viewport.getAlignment().getSequenceAt(i));
1885       }
1886     }
1887
1888     SequenceI[] seqs1 = sg.toArray(new SequenceI[0]);
1889
1890     SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
1891     for (int i = 0; i < invertGroup.size(); i++)
1892     {
1893       seqs2[i] = (SequenceI) invertGroup.elementAt(i);
1894     }
1895
1896     SlideSequencesCommand ssc;
1897     if (right)
1898     {
1899       ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
1900               size, viewport.getGapCharacter());
1901     }
1902     else
1903     {
1904       ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
1905               size, viewport.getGapCharacter());
1906     }
1907
1908     int groupAdjustment = 0;
1909     if (ssc.getGapsInsertedBegin() && right)
1910     {
1911       if (viewport.cursorMode)
1912       {
1913         alignPanel.seqPanel.moveCursor(size, 0);
1914       }
1915       else
1916       {
1917         groupAdjustment = size;
1918       }
1919     }
1920     else if (!ssc.getGapsInsertedBegin() && !right)
1921     {
1922       if (viewport.cursorMode)
1923       {
1924         alignPanel.seqPanel.moveCursor(-size, 0);
1925       }
1926       else
1927       {
1928         groupAdjustment = -size;
1929       }
1930     }
1931
1932     if (groupAdjustment != 0)
1933     {
1934       viewport.getSelectionGroup().setStartRes(
1935               viewport.getSelectionGroup().getStartRes() + groupAdjustment);
1936       viewport.getSelectionGroup().setEndRes(
1937               viewport.getSelectionGroup().getEndRes() + groupAdjustment);
1938     }
1939
1940     boolean appendHistoryItem = false;
1941     if (viewport.getHistoryList() != null
1942             && viewport.getHistoryList().size() > 0
1943             && viewport.getHistoryList().peek() instanceof SlideSequencesCommand)
1944     {
1945       appendHistoryItem = ssc
1946               .appendSlideCommand((SlideSequencesCommand) viewport
1947                       .getHistoryList()
1948                       .peek());
1949     }
1950
1951     if (!appendHistoryItem)
1952     {
1953       addHistoryItem(ssc);
1954     }
1955
1956     repaint();
1957   }
1958
1959   /**
1960    * DOCUMENT ME!
1961    * 
1962    * @param e
1963    *          DOCUMENT ME!
1964    */
1965   @Override
1966   protected void copy_actionPerformed(ActionEvent e)
1967   {
1968     System.gc();
1969     if (viewport.getSelectionGroup() == null)
1970     {
1971       return;
1972     }
1973     // TODO: preserve the ordering of displayed alignment annotation in any
1974     // internal paste (particularly sequence associated annotation)
1975     SequenceI[] seqs = viewport.getSelectionAsNewSequence();
1976     String[] omitHidden = null;
1977
1978     if (viewport.hasHiddenColumns())
1979     {
1980       omitHidden = viewport.getViewAsString(true);
1981     }
1982
1983     String output = new FormatAdapter().formatSequences("Fasta", seqs,
1984             omitHidden);
1985
1986     StringSelection ss = new StringSelection(output);
1987
1988     try
1989     {
1990       jalview.gui.Desktop.internalCopy = true;
1991       // Its really worth setting the clipboard contents
1992       // to empty before setting the large StringSelection!!
1993       Toolkit.getDefaultToolkit().getSystemClipboard()
1994               .setContents(new StringSelection(""), null);
1995
1996       Toolkit.getDefaultToolkit().getSystemClipboard()
1997               .setContents(ss, Desktop.instance);
1998     } catch (OutOfMemoryError er)
1999     {
2000       new OOMWarning("copying region", er);
2001       return;
2002     }
2003
2004     Vector hiddenColumns = null;
2005     if (viewport.hasHiddenColumns())
2006     {
2007       hiddenColumns = new Vector();
2008       int hiddenOffset = viewport.getSelectionGroup().getStartRes(), hiddenCutoff = viewport
2009               .getSelectionGroup().getEndRes();
2010       for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns()
2011               .size(); i++)
2012       {
2013         int[] region = (int[]) viewport.getColumnSelection()
2014                 .getHiddenColumns().elementAt(i);
2015         if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff)
2016         {
2017           hiddenColumns.addElement(new int[]
2018           { region[0] - hiddenOffset, region[1] - hiddenOffset });
2019         }
2020       }
2021     }
2022
2023     Desktop.jalviewClipboard = new Object[]
2024     { seqs, viewport.getAlignment().getDataset(), hiddenColumns };
2025     statusBar.setText(MessageManager.formatMessage(
2026             "label.copied_sequences_to_clipboard", new String[]
2027             { Integer.valueOf(seqs.length).toString() }));
2028   }
2029
2030   /**
2031    * DOCUMENT ME!
2032    * 
2033    * @param e
2034    *          DOCUMENT ME!
2035    */
2036   @Override
2037   protected void pasteNew_actionPerformed(ActionEvent e)
2038   {
2039     paste(true);
2040   }
2041
2042   /**
2043    * DOCUMENT ME!
2044    * 
2045    * @param e
2046    *          DOCUMENT ME!
2047    */
2048   @Override
2049   protected void pasteThis_actionPerformed(ActionEvent e)
2050   {
2051     paste(false);
2052   }
2053
2054   /**
2055    * Paste contents of Jalview clipboard
2056    * 
2057    * @param newAlignment
2058    *          true to paste to a new alignment, otherwise add to this.
2059    */
2060   void paste(boolean newAlignment)
2061   {
2062     boolean externalPaste = true;
2063     try
2064     {
2065       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
2066       Transferable contents = c.getContents(this);
2067
2068       if (contents == null)
2069       {
2070         return;
2071       }
2072
2073       String str, format;
2074       try
2075       {
2076         str = (String) contents.getTransferData(DataFlavor.stringFlavor);
2077         if (str.length() < 1)
2078         {
2079           return;
2080         }
2081
2082         format = new IdentifyFile().Identify(str, "Paste");
2083
2084       } catch (OutOfMemoryError er)
2085       {
2086         new OOMWarning("Out of memory pasting sequences!!", er);
2087         return;
2088       }
2089
2090       SequenceI[] sequences;
2091       boolean annotationAdded = false;
2092       AlignmentI alignment = null;
2093
2094       if (Desktop.jalviewClipboard != null)
2095       {
2096         // The clipboard was filled from within Jalview, we must use the
2097         // sequences
2098         // And dataset from the copied alignment
2099         SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
2100         // be doubly sure that we create *new* sequence objects.
2101         sequences = new SequenceI[newseq.length];
2102         for (int i = 0; i < newseq.length; i++)
2103         {
2104           sequences[i] = new Sequence(newseq[i]);
2105         }
2106         alignment = new Alignment(sequences);
2107         externalPaste = false;
2108       }
2109       else
2110       {
2111         // parse the clipboard as an alignment.
2112         alignment = new FormatAdapter().readFile(str, "Paste", format);
2113         sequences = alignment.getSequencesArray();
2114       }
2115
2116       int alwidth = 0;
2117       ArrayList<Integer> newGraphGroups = new ArrayList<Integer>();
2118       int fgroup = -1;
2119
2120       if (newAlignment)
2121       {
2122
2123         if (Desktop.jalviewClipboard != null)
2124         {
2125           // dataset is inherited
2126           alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
2127         }
2128         else
2129         {
2130           // new dataset is constructed
2131           alignment.setDataset(null);
2132         }
2133         alwidth = alignment.getWidth() + 1;
2134       }
2135       else
2136       {
2137         AlignmentI pastedal = alignment; // preserve pasted alignment object
2138         // Add pasted sequences and dataset into existing alignment.
2139         alignment = viewport.getAlignment();
2140         alwidth = alignment.getWidth() + 1;
2141         // decide if we need to import sequences from an existing dataset
2142         boolean importDs = Desktop.jalviewClipboard != null
2143                 && Desktop.jalviewClipboard[1] != alignment.getDataset();
2144         // importDs==true instructs us to copy over new dataset sequences from
2145         // an existing alignment
2146         Vector newDs = (importDs) ? new Vector() : null; // used to create
2147         // minimum dataset set
2148
2149         for (int i = 0; i < sequences.length; i++)
2150         {
2151           if (importDs)
2152           {
2153             newDs.addElement(null);
2154           }
2155           SequenceI ds = sequences[i].getDatasetSequence(); // null for a simple
2156           // paste
2157           if (importDs && ds != null)
2158           {
2159             if (!newDs.contains(ds))
2160             {
2161               newDs.setElementAt(ds, i);
2162               ds = new Sequence(ds);
2163               // update with new dataset sequence
2164               sequences[i].setDatasetSequence(ds);
2165             }
2166             else
2167             {
2168               ds = sequences[newDs.indexOf(ds)].getDatasetSequence();
2169             }
2170           }
2171           else
2172           {
2173             // copy and derive new dataset sequence
2174             sequences[i] = sequences[i].deriveSequence();
2175             alignment.getDataset().addSequence(
2176                     sequences[i].getDatasetSequence());
2177             // TODO: avoid creation of duplicate dataset sequences with a
2178             // 'contains' method using SequenceI.equals()/SequenceI.contains()
2179           }
2180           alignment.addSequence(sequences[i]); // merges dataset
2181         }
2182         if (newDs != null)
2183         {
2184           newDs.clear(); // tidy up
2185         }
2186         if (alignment.getAlignmentAnnotation() != null)
2187         {
2188           for (AlignmentAnnotation alan : alignment
2189                   .getAlignmentAnnotation())
2190           {
2191             if (alan.graphGroup > fgroup)
2192             {
2193               fgroup = alan.graphGroup;
2194             }
2195           }
2196         }
2197         if (pastedal.getAlignmentAnnotation() != null)
2198         {
2199           // Add any annotation attached to alignment.
2200           AlignmentAnnotation[] alann = pastedal.getAlignmentAnnotation();
2201           for (int i = 0; i < alann.length; i++)
2202           {
2203             annotationAdded = true;
2204             if (alann[i].sequenceRef == null && !alann[i].autoCalculated)
2205             {
2206               AlignmentAnnotation newann = new AlignmentAnnotation(alann[i]);
2207               if (newann.graphGroup > -1)
2208               {
2209                 if (newGraphGroups.size() <= newann.graphGroup
2210                         || newGraphGroups.get(newann.graphGroup) == null)
2211                 {
2212                   for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
2213                   {
2214                     newGraphGroups.add(q, null);
2215                   }
2216                   newGraphGroups.set(newann.graphGroup, new Integer(
2217                           ++fgroup));
2218                 }
2219                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
2220                         .intValue();
2221               }
2222
2223               newann.padAnnotation(alwidth);
2224               alignment.addAnnotation(newann);
2225             }
2226           }
2227         }
2228       }
2229       if (!newAlignment)
2230       {
2231         // /////
2232         // ADD HISTORY ITEM
2233         //
2234         addHistoryItem(new EditCommand(
2235                 MessageManager.getString("label.add_sequences"),
2236                 Action.PASTE,
2237                 sequences, 0, alignment.getWidth(), alignment));
2238       }
2239       // Add any annotations attached to sequences
2240       for (int i = 0; i < sequences.length; i++)
2241       {
2242         if (sequences[i].getAnnotation() != null)
2243         {
2244           AlignmentAnnotation newann;
2245           for (int a = 0; a < sequences[i].getAnnotation().length; a++)
2246           {
2247             annotationAdded = true;
2248             newann = sequences[i].getAnnotation()[a];
2249             newann.adjustForAlignment();
2250             newann.padAnnotation(alwidth);
2251             if (newann.graphGroup > -1)
2252             {
2253               if (newann.graphGroup > -1)
2254               {
2255                 if (newGraphGroups.size() <= newann.graphGroup
2256                         || newGraphGroups.get(newann.graphGroup) == null)
2257                 {
2258                   for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
2259                   {
2260                     newGraphGroups.add(q, null);
2261                   }
2262                   newGraphGroups.set(newann.graphGroup, new Integer(
2263                           ++fgroup));
2264                 }
2265                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
2266                         .intValue();
2267               }
2268             }
2269             alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation
2270             // was
2271             // duplicated
2272             // earlier
2273             alignment
2274                     .setAnnotationIndex(sequences[i].getAnnotation()[a], a);
2275           }
2276         }
2277       }
2278       if (!newAlignment)
2279       {
2280
2281         // propagate alignment changed.
2282         viewport.setEndSeq(alignment.getHeight());
2283         if (annotationAdded)
2284         {
2285           // Duplicate sequence annotation in all views.
2286           AlignmentI[] alview = this.getViewAlignments();
2287           for (int i = 0; i < sequences.length; i++)
2288           {
2289             AlignmentAnnotation sann[] = sequences[i].getAnnotation();
2290             if (sann == null)
2291             {
2292               continue;
2293             }
2294             for (int avnum = 0; avnum < alview.length; avnum++)
2295             {
2296               if (alview[avnum] != alignment)
2297               {
2298                 // duplicate in a view other than the one with input focus
2299                 int avwidth = alview[avnum].getWidth() + 1;
2300                 // this relies on sann being preserved after we
2301                 // modify the sequence's annotation array for each duplication
2302                 for (int a = 0; a < sann.length; a++)
2303                 {
2304                   AlignmentAnnotation newann = new AlignmentAnnotation(
2305                           sann[a]);
2306                   sequences[i].addAlignmentAnnotation(newann);
2307                   newann.padAnnotation(avwidth);
2308                   alview[avnum].addAnnotation(newann); // annotation was
2309                   // duplicated earlier
2310                   // TODO JAL-1145 graphGroups are not updated for sequence
2311                   // annotation added to several views. This may cause
2312                   // strangeness
2313                   alview[avnum].setAnnotationIndex(newann, a);
2314                 }
2315               }
2316             }
2317           }
2318           buildSortByAnnotationScoresMenu();
2319         }
2320         viewport.firePropertyChange("alignment", null,
2321                 alignment.getSequences());
2322         if (alignPanels != null)
2323         {
2324           for (AlignmentPanel ap : ((Vector<AlignmentPanel>) alignPanels))
2325           {
2326             ap.validateAnnotationDimensions(false);
2327           }
2328         }
2329         else
2330         {
2331           alignPanel.validateAnnotationDimensions(false);
2332         }
2333
2334       }
2335       else
2336       {
2337         AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2338                 DEFAULT_HEIGHT);
2339         String newtitle = new String("Copied sequences");
2340
2341         if (Desktop.jalviewClipboard != null
2342                 && Desktop.jalviewClipboard[2] != null)
2343         {
2344           Vector hc = (Vector) Desktop.jalviewClipboard[2];
2345           for (int i = 0; i < hc.size(); i++)
2346           {
2347             int[] region = (int[]) hc.elementAt(i);
2348             af.viewport.hideColumns(region[0], region[1]);
2349           }
2350         }
2351
2352         // >>>This is a fix for the moment, until a better solution is
2353         // found!!<<<
2354         af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
2355                 .transferSettings(
2356                         alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
2357
2358         // TODO: maintain provenance of an alignment, rather than just make the
2359         // title a concatenation of operations.
2360         if (!externalPaste)
2361         {
2362           if (title.startsWith("Copied sequences"))
2363           {
2364             newtitle = title;
2365           }
2366           else
2367           {
2368             newtitle = newtitle.concat("- from " + title);
2369           }
2370         }
2371         else
2372         {
2373           newtitle = new String("Pasted sequences");
2374         }
2375
2376         Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
2377                 DEFAULT_HEIGHT);
2378
2379       }
2380
2381     } catch (Exception ex)
2382     {
2383       ex.printStackTrace();
2384       System.out.println("Exception whilst pasting: " + ex);
2385       // could be anything being pasted in here
2386     }
2387
2388   }
2389
2390   @Override
2391   protected void expand_newalign(ActionEvent e)
2392   {
2393     try
2394     {
2395       AlignmentI alignment = AlignmentUtils.expandContext(getViewport()
2396               .getAlignment(), -1);
2397       AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2398               DEFAULT_HEIGHT);
2399       String newtitle = new String("Flanking alignment");
2400
2401       if (Desktop.jalviewClipboard != null
2402               && Desktop.jalviewClipboard[2] != null)
2403       {
2404         Vector hc = (Vector) Desktop.jalviewClipboard[2];
2405         for (int i = 0; i < hc.size(); i++)
2406         {
2407           int[] region = (int[]) hc.elementAt(i);
2408           af.viewport.hideColumns(region[0], region[1]);
2409         }
2410       }
2411
2412       // >>>This is a fix for the moment, until a better solution is
2413       // found!!<<<
2414       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
2415               .transferSettings(
2416                       alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
2417
2418       // TODO: maintain provenance of an alignment, rather than just make the
2419       // title a concatenation of operations.
2420       {
2421         if (title.startsWith("Copied sequences"))
2422         {
2423           newtitle = title;
2424         }
2425         else
2426         {
2427           newtitle = newtitle.concat("- from " + title);
2428         }
2429       }
2430
2431       Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
2432
2433     } catch (Exception ex)
2434     {
2435       ex.printStackTrace();
2436       System.out.println("Exception whilst pasting: " + ex);
2437       // could be anything being pasted in here
2438     } catch (OutOfMemoryError oom)
2439     {
2440       new OOMWarning("Viewing flanking region of alignment", oom);
2441     }
2442   }
2443
2444   /**
2445    * DOCUMENT ME!
2446    * 
2447    * @param e
2448    *          DOCUMENT ME!
2449    */
2450   @Override
2451   protected void cut_actionPerformed(ActionEvent e)
2452   {
2453     copy_actionPerformed(null);
2454     delete_actionPerformed(null);
2455   }
2456
2457   /**
2458    * DOCUMENT ME!
2459    * 
2460    * @param e
2461    *          DOCUMENT ME!
2462    */
2463   @Override
2464   protected void delete_actionPerformed(ActionEvent evt)
2465   {
2466
2467     SequenceGroup sg = viewport.getSelectionGroup();
2468     if (sg == null)
2469     {
2470       return;
2471     }
2472
2473     List<SequenceI> seqs = new ArrayList<SequenceI>(sg.getSize());
2474     SequenceI seq;
2475     for (int i = 0; i < sg.getSize(); i++)
2476     {
2477       seq = sg.getSequenceAt(i);
2478       seqs.add(seq);
2479     }
2480
2481     // If the cut affects all sequences, warn, remove highlighted columns
2482     if (sg.getSize() == viewport.getAlignment().getHeight())
2483     {
2484       int confirm = JOptionPane.showConfirmDialog(this,
2485               MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
2486               MessageManager.getString("label.delete_all"), // $NON-NLS-1$
2487               JOptionPane.OK_CANCEL_OPTION);
2488
2489       if (confirm == JOptionPane.CANCEL_OPTION
2490               || confirm == JOptionPane.CLOSED_OPTION)
2491       {
2492         return;
2493       }
2494       viewport.getColumnSelection().removeElements(sg.getStartRes(),
2495               sg.getEndRes() + 1);
2496     }
2497
2498     SequenceI[] cut = new SequenceI[seqs.size()];
2499     for (int i = 0; i < seqs.size(); i++)
2500     {
2501       cut[i] = seqs.get(i);
2502     }
2503
2504     /*
2505      * //ADD HISTORY ITEM
2506      */
2507     addHistoryItem(new EditCommand(
2508             MessageManager.getString("label.cut_sequences"), Action.CUT,
2509             cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
2510             viewport.getAlignment()));
2511
2512     viewport.setSelectionGroup(null);
2513     viewport.sendSelection();
2514     viewport.getAlignment().deleteGroup(sg);
2515
2516     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2517             .getSequences());
2518     if (viewport.getAlignment().getHeight() < 1)
2519     {
2520       try
2521       {
2522         this.setClosed(true);
2523       } catch (Exception ex)
2524       {
2525       }
2526     }
2527   }
2528
2529   /**
2530    * DOCUMENT ME!
2531    * 
2532    * @param e
2533    *          DOCUMENT ME!
2534    */
2535   @Override
2536   protected void deleteGroups_actionPerformed(ActionEvent e)
2537   {
2538     if (avc.deleteGroups())
2539     {
2540       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
2541       alignPanel.updateAnnotation();
2542       alignPanel.paintAlignment(true);
2543     }
2544   }
2545
2546   /**
2547    * DOCUMENT ME!
2548    * 
2549    * @param e
2550    *          DOCUMENT ME!
2551    */
2552   @Override
2553   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
2554   {
2555     SequenceGroup sg = new SequenceGroup();
2556
2557     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
2558     {
2559       sg.addSequence(viewport.getAlignment().getSequenceAt(i), false);
2560     }
2561
2562     sg.setEndRes(viewport.getAlignment().getWidth() - 1);
2563     viewport.setSelectionGroup(sg);
2564     viewport.sendSelection();
2565     alignPanel.paintAlignment(true);
2566     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2567   }
2568
2569   /**
2570    * DOCUMENT ME!
2571    * 
2572    * @param e
2573    *          DOCUMENT ME!
2574    */
2575   @Override
2576   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
2577   {
2578     if (viewport.cursorMode)
2579     {
2580       alignPanel.seqPanel.keyboardNo1 = null;
2581       alignPanel.seqPanel.keyboardNo2 = null;
2582     }
2583     viewport.setSelectionGroup(null);
2584     viewport.getColumnSelection().clear();
2585     viewport.setSelectionGroup(null);
2586     alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
2587     alignPanel.idPanel.idCanvas.searchResults = null;
2588     alignPanel.paintAlignment(true);
2589     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2590     viewport.sendSelection();
2591   }
2592
2593   /**
2594    * DOCUMENT ME!
2595    * 
2596    * @param e
2597    *          DOCUMENT ME!
2598    */
2599   @Override
2600   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
2601   {
2602     SequenceGroup sg = viewport.getSelectionGroup();
2603
2604     if (sg == null)
2605     {
2606       selectAllSequenceMenuItem_actionPerformed(null);
2607
2608       return;
2609     }
2610
2611     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
2612     {
2613       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
2614     }
2615
2616     alignPanel.paintAlignment(true);
2617     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2618     viewport.sendSelection();
2619   }
2620
2621   @Override
2622   public void invertColSel_actionPerformed(ActionEvent e)
2623   {
2624     viewport.invertColumnSelection();
2625     alignPanel.paintAlignment(true);
2626     viewport.sendSelection();
2627   }
2628
2629   /**
2630    * DOCUMENT ME!
2631    * 
2632    * @param e
2633    *          DOCUMENT ME!
2634    */
2635   @Override
2636   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
2637   {
2638     trimAlignment(true);
2639   }
2640
2641   /**
2642    * DOCUMENT ME!
2643    * 
2644    * @param e
2645    *          DOCUMENT ME!
2646    */
2647   @Override
2648   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
2649   {
2650     trimAlignment(false);
2651   }
2652
2653   void trimAlignment(boolean trimLeft)
2654   {
2655     ColumnSelection colSel = viewport.getColumnSelection();
2656     int column;
2657
2658     if (colSel.size() > 0)
2659     {
2660       if (trimLeft)
2661       {
2662         column = colSel.getMin();
2663       }
2664       else
2665       {
2666         column = colSel.getMax();
2667       }
2668
2669       SequenceI[] seqs;
2670       if (viewport.getSelectionGroup() != null)
2671       {
2672         seqs = viewport.getSelectionGroup().getSequencesAsArray(
2673                 viewport.getHiddenRepSequences());
2674       }
2675       else
2676       {
2677         seqs = viewport.getAlignment().getSequencesArray();
2678       }
2679
2680       TrimRegionCommand trimRegion;
2681       if (trimLeft)
2682       {
2683         trimRegion = new TrimRegionCommand("Remove Left",
2684                 TrimRegionCommand.TRIM_LEFT, seqs, column,
2685                 viewport.getAlignment(), viewport.getColumnSelection(),
2686                 viewport.getSelectionGroup());
2687         viewport.setStartRes(0);
2688       }
2689       else
2690       {
2691         trimRegion = new TrimRegionCommand("Remove Right",
2692                 TrimRegionCommand.TRIM_RIGHT, seqs, column,
2693                 viewport.getAlignment(), viewport.getColumnSelection(),
2694                 viewport.getSelectionGroup());
2695       }
2696
2697       statusBar.setText(MessageManager.formatMessage(
2698               "label.removed_columns", new String[]
2699               { Integer.valueOf(trimRegion.getSize()).toString() }));
2700
2701       addHistoryItem(trimRegion);
2702
2703       for (SequenceGroup sg : viewport.getAlignment().getGroups())
2704       {
2705         if ((trimLeft && !sg.adjustForRemoveLeft(column))
2706                 || (!trimLeft && !sg.adjustForRemoveRight(column)))
2707         {
2708           viewport.getAlignment().deleteGroup(sg);
2709         }
2710       }
2711
2712       viewport.firePropertyChange("alignment", null, viewport
2713               .getAlignment().getSequences());
2714     }
2715   }
2716
2717   /**
2718    * DOCUMENT ME!
2719    * 
2720    * @param e
2721    *          DOCUMENT ME!
2722    */
2723   @Override
2724   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
2725   {
2726     int start = 0, end = viewport.getAlignment().getWidth() - 1;
2727
2728     SequenceI[] seqs;
2729     if (viewport.getSelectionGroup() != null)
2730     {
2731       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2732               viewport.getHiddenRepSequences());
2733       start = viewport.getSelectionGroup().getStartRes();
2734       end = viewport.getSelectionGroup().getEndRes();
2735     }
2736     else
2737     {
2738       seqs = viewport.getAlignment().getSequencesArray();
2739     }
2740
2741     RemoveGapColCommand removeGapCols = new RemoveGapColCommand(
2742             "Remove Gapped Columns", seqs, start, end,
2743             viewport.getAlignment());
2744
2745     addHistoryItem(removeGapCols);
2746
2747     statusBar.setText(MessageManager.formatMessage(
2748             "label.removed_empty_columns", new String[]
2749             { Integer.valueOf(removeGapCols.getSize()).toString() }));
2750
2751     // This is to maintain viewport position on first residue
2752     // of first sequence
2753     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
2754     int startRes = seq.findPosition(viewport.startRes);
2755     // ShiftList shifts;
2756     // viewport.getAlignment().removeGaps(shifts=new ShiftList());
2757     // edit.alColumnChanges=shifts.getInverse();
2758     // if (viewport.hasHiddenColumns)
2759     // viewport.getColumnSelection().compensateForEdits(shifts);
2760     viewport.setStartRes(seq.findIndex(startRes) - 1);
2761     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2762             .getSequences());
2763
2764   }
2765
2766   /**
2767    * DOCUMENT ME!
2768    * 
2769    * @param e
2770    *          DOCUMENT ME!
2771    */
2772   @Override
2773   public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
2774   {
2775     int start = 0, end = viewport.getAlignment().getWidth() - 1;
2776
2777     SequenceI[] seqs;
2778     if (viewport.getSelectionGroup() != null)
2779     {
2780       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2781               viewport.getHiddenRepSequences());
2782       start = viewport.getSelectionGroup().getStartRes();
2783       end = viewport.getSelectionGroup().getEndRes();
2784     }
2785     else
2786     {
2787       seqs = viewport.getAlignment().getSequencesArray();
2788     }
2789
2790     // This is to maintain viewport position on first residue
2791     // of first sequence
2792     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
2793     int startRes = seq.findPosition(viewport.startRes);
2794
2795     addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
2796             viewport.getAlignment()));
2797
2798     viewport.setStartRes(seq.findIndex(startRes) - 1);
2799
2800     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2801             .getSequences());
2802
2803   }
2804
2805   /**
2806    * DOCUMENT ME!
2807    * 
2808    * @param e
2809    *          DOCUMENT ME!
2810    */
2811   @Override
2812   public void padGapsMenuitem_actionPerformed(ActionEvent e)
2813   {
2814     viewport.setPadGaps(padGapsMenuitem.isSelected());
2815     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2816             .getSequences());
2817   }
2818
2819   // else
2820   {
2821     // if (justifySeqs>0)
2822     {
2823       // alignment.justify(justifySeqs!=RIGHT_JUSTIFY);
2824     }
2825   }
2826
2827   // }
2828
2829   /**
2830    * DOCUMENT ME!
2831    * 
2832    * @param e
2833    *          DOCUMENT ME!
2834    */
2835   @Override
2836   public void findMenuItem_actionPerformed(ActionEvent e)
2837   {
2838     new Finder();
2839   }
2840
2841   /**
2842    * Create a new view of the current alignment.
2843    */
2844   @Override
2845   public void newView_actionPerformed(ActionEvent e)
2846   {
2847     newView(true);
2848   }
2849
2850   /**
2851    * 
2852    * @param copyAnnotation
2853    *          if true then duplicate all annnotation, groups and settings
2854    * @return new alignment panel, already displayed.
2855    */
2856   public AlignmentPanel newView(boolean copyAnnotation)
2857   {
2858     return newView(null, copyAnnotation);
2859   }
2860
2861   /**
2862    * 
2863    * @param viewTitle
2864    *          title of newly created view
2865    * @return new alignment panel, already displayed.
2866    */
2867   public AlignmentPanel newView(String viewTitle)
2868   {
2869     return newView(viewTitle, true);
2870   }
2871
2872   /**
2873    * 
2874    * @param viewTitle
2875    *          title of newly created view
2876    * @param copyAnnotation
2877    *          if true then duplicate all annnotation, groups and settings
2878    * @return new alignment panel, already displayed.
2879    */
2880   public AlignmentPanel newView(String viewTitle, boolean copyAnnotation)
2881   {
2882     AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
2883             true);
2884     if (!copyAnnotation)
2885     {
2886       // just remove all the current annotation except for the automatic stuff
2887       newap.av.getAlignment().deleteAllGroups();
2888       for (AlignmentAnnotation alan : newap.av.getAlignment()
2889               .getAlignmentAnnotation())
2890       {
2891         if (!alan.autoCalculated)
2892         {
2893           newap.av.getAlignment().deleteAnnotation(alan);
2894         }
2895         ;
2896       }
2897     }
2898
2899     newap.av.gatherViewsHere = false;
2900
2901     if (viewport.viewName == null)
2902     {
2903       viewport.viewName = "Original";
2904     }
2905
2906     newap.av.setHistoryList(viewport.getHistoryList());
2907     newap.av.setRedoList(viewport.getRedoList());
2908
2909     int index = Desktop.getViewCount(viewport.getSequenceSetId());
2910     // make sure the new view has a unique name - this is essential for Jalview
2911     // 2 archives
2912     boolean addFirstIndex = false;
2913     if (viewTitle == null || viewTitle.trim().length() == 0)
2914     {
2915       viewTitle = MessageManager.getString("action.view");
2916       addFirstIndex = true;
2917     }
2918     else
2919     {
2920       index = 1;// we count from 1 if given a specific name
2921     }
2922     String newViewName = viewTitle + ((addFirstIndex) ? " " + index : "");
2923     Vector comps = (Vector) PaintRefresher.components.get(viewport
2924             .getSequenceSetId());
2925     Vector existingNames = new Vector();
2926     for (int i = 0; i < comps.size(); i++)
2927     {
2928       if (comps.elementAt(i) instanceof AlignmentPanel)
2929       {
2930         AlignmentPanel ap = (AlignmentPanel) comps.elementAt(i);
2931         if (!existingNames.contains(ap.av.viewName))
2932         {
2933           existingNames.addElement(ap.av.viewName);
2934         }
2935       }
2936     }
2937
2938     while (existingNames.contains(newViewName))
2939     {
2940       newViewName = viewTitle + " " + (++index);
2941     }
2942
2943     newap.av.viewName = newViewName;
2944
2945     addAlignmentPanel(newap, true);
2946     newap.alignmentChanged();
2947
2948     if (alignPanels.size() == 2)
2949     {
2950       viewport.gatherViewsHere = true;
2951     }
2952     tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
2953     return newap;
2954   }
2955
2956   @Override
2957   public void expandViews_actionPerformed(ActionEvent e)
2958   {
2959     Desktop.instance.explodeViews(this);
2960   }
2961
2962   @Override
2963   public void gatherViews_actionPerformed(ActionEvent e)
2964   {
2965     Desktop.instance.gatherViews(this);
2966   }
2967
2968   /**
2969    * DOCUMENT ME!
2970    * 
2971    * @param e
2972    *          DOCUMENT ME!
2973    */
2974   @Override
2975   public void font_actionPerformed(ActionEvent e)
2976   {
2977     new FontChooser(alignPanel);
2978   }
2979
2980   /**
2981    * DOCUMENT ME!
2982    * 
2983    * @param e
2984    *          DOCUMENT ME!
2985    */
2986   @Override
2987   protected void seqLimit_actionPerformed(ActionEvent e)
2988   {
2989     viewport.setShowJVSuffix(seqLimits.isSelected());
2990
2991     alignPanel.idPanel.idCanvas.setPreferredSize(alignPanel
2992             .calculateIdWidth());
2993     alignPanel.paintAlignment(true);
2994   }
2995
2996   @Override
2997   public void idRightAlign_actionPerformed(ActionEvent e)
2998   {
2999     viewport.rightAlignIds = idRightAlign.isSelected();
3000     alignPanel.paintAlignment(true);
3001   }
3002
3003   @Override
3004   public void centreColumnLabels_actionPerformed(ActionEvent e)
3005   {
3006     viewport.centreColumnLabels = centreColumnLabelsMenuItem.getState();
3007     alignPanel.paintAlignment(true);
3008   }
3009
3010   /*
3011    * (non-Javadoc)
3012    * 
3013    * @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed()
3014    */
3015   @Override
3016   protected void followHighlight_actionPerformed()
3017   {
3018     if (viewport.followHighlight = this.followHighlightMenuItem.getState())
3019     {
3020       alignPanel.scrollToPosition(
3021               alignPanel.seqPanel.seqCanvas.searchResults, false);
3022     }
3023   }
3024
3025   /**
3026    * DOCUMENT ME!
3027    * 
3028    * @param e
3029    *          DOCUMENT ME!
3030    */
3031   @Override
3032   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
3033   {
3034     viewport.setColourText(colourTextMenuItem.isSelected());
3035     alignPanel.paintAlignment(true);
3036   }
3037
3038   /**
3039    * DOCUMENT ME!
3040    * 
3041    * @param e
3042    *          DOCUMENT ME!
3043    */
3044   @Override
3045   public void wrapMenuItem_actionPerformed(ActionEvent e)
3046   {
3047     scaleAbove.setVisible(wrapMenuItem.isSelected());
3048     scaleLeft.setVisible(wrapMenuItem.isSelected());
3049     scaleRight.setVisible(wrapMenuItem.isSelected());
3050     viewport.setWrapAlignment(wrapMenuItem.isSelected());
3051     alignPanel.setWrapAlignment(wrapMenuItem.isSelected());
3052   }
3053
3054   @Override
3055   public void showAllSeqs_actionPerformed(ActionEvent e)
3056   {
3057     viewport.showAllHiddenSeqs();
3058   }
3059
3060   @Override
3061   public void showAllColumns_actionPerformed(ActionEvent e)
3062   {
3063     viewport.showAllHiddenColumns();
3064     repaint();
3065   }
3066
3067   @Override
3068   public void hideSelSequences_actionPerformed(ActionEvent e)
3069   {
3070     viewport.hideAllSelectedSeqs();
3071     alignPanel.paintAlignment(true);
3072   }
3073
3074   /**
3075    * called by key handler and the hide all/show all menu items
3076    * 
3077    * @param toggleSeqs
3078    * @param toggleCols
3079    */
3080   private void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols)
3081   {
3082
3083     boolean hide = false;
3084     SequenceGroup sg = viewport.getSelectionGroup();
3085     if (!toggleSeqs && !toggleCols)
3086     {
3087       // Hide everything by the current selection - this is a hack - we do the
3088       // invert and then hide
3089       // first check that there will be visible columns after the invert.
3090       if ((viewport.getColumnSelection() != null
3091               && viewport.getColumnSelection().getSelected() != null && viewport
3092               .getColumnSelection().getSelected().size() > 0)
3093               || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg
3094                       .getEndRes()))
3095       {
3096         // now invert the sequence set, if required - empty selection implies
3097         // that no hiding is required.
3098         if (sg != null)
3099         {
3100           invertSequenceMenuItem_actionPerformed(null);
3101           sg = viewport.getSelectionGroup();
3102           toggleSeqs = true;
3103
3104         }
3105         viewport.expandColSelection(sg, true);
3106         // finally invert the column selection and get the new sequence
3107         // selection.
3108         invertColSel_actionPerformed(null);
3109         toggleCols = true;
3110       }
3111     }
3112
3113     if (toggleSeqs)
3114     {
3115       if (sg != null && sg.getSize() != viewport.getAlignment().getHeight())
3116       {
3117         hideSelSequences_actionPerformed(null);
3118         hide = true;
3119       }
3120       else if (!(toggleCols && viewport.getColumnSelection().getSelected()
3121               .size() > 0))
3122       {
3123         showAllSeqs_actionPerformed(null);
3124       }
3125     }
3126
3127     if (toggleCols)
3128     {
3129       if (viewport.getColumnSelection().getSelected().size() > 0)
3130       {
3131         hideSelColumns_actionPerformed(null);
3132         if (!toggleSeqs)
3133         {
3134           viewport.setSelectionGroup(sg);
3135         }
3136       }
3137       else if (!hide)
3138       {
3139         showAllColumns_actionPerformed(null);
3140       }
3141     }
3142   }
3143
3144   /*
3145    * (non-Javadoc)
3146    * 
3147    * @see
3148    * jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt.
3149    * event.ActionEvent)
3150    */
3151   @Override
3152   public void hideAllButSelection_actionPerformed(ActionEvent e)
3153   {
3154     toggleHiddenRegions(false, false);
3155   }
3156
3157   /*
3158    * (non-Javadoc)
3159    * 
3160    * @see
3161    * jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event
3162    * .ActionEvent)
3163    */
3164   @Override
3165   public void hideAllSelection_actionPerformed(ActionEvent e)
3166   {
3167     SequenceGroup sg = viewport.getSelectionGroup();
3168     viewport.expandColSelection(sg, false);
3169     viewport.hideAllSelectedSeqs();
3170     viewport.hideSelectedColumns();
3171     alignPanel.paintAlignment(true);
3172   }
3173
3174   /*
3175    * (non-Javadoc)
3176    * 
3177    * @see
3178    * jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event.
3179    * ActionEvent)
3180    */
3181   @Override
3182   public void showAllhidden_actionPerformed(ActionEvent e)
3183   {
3184     viewport.showAllHiddenColumns();
3185     viewport.showAllHiddenSeqs();
3186     alignPanel.paintAlignment(true);
3187   }
3188
3189   @Override
3190   public void hideSelColumns_actionPerformed(ActionEvent e)
3191   {
3192     viewport.hideSelectedColumns();
3193     alignPanel.paintAlignment(true);
3194   }
3195
3196   @Override
3197   public void hiddenMarkers_actionPerformed(ActionEvent e)
3198   {
3199     viewport.setShowHiddenMarkers(hiddenMarkers.isSelected());
3200     repaint();
3201   }
3202
3203   /**
3204    * DOCUMENT ME!
3205    * 
3206    * @param e
3207    *          DOCUMENT ME!
3208    */
3209   @Override
3210   protected void scaleAbove_actionPerformed(ActionEvent e)
3211   {
3212     viewport.setScaleAboveWrapped(scaleAbove.isSelected());
3213     alignPanel.paintAlignment(true);
3214   }
3215
3216   /**
3217    * DOCUMENT ME!
3218    * 
3219    * @param e
3220    *          DOCUMENT ME!
3221    */
3222   @Override
3223   protected void scaleLeft_actionPerformed(ActionEvent e)
3224   {
3225     viewport.setScaleLeftWrapped(scaleLeft.isSelected());
3226     alignPanel.paintAlignment(true);
3227   }
3228
3229   /**
3230    * DOCUMENT ME!
3231    * 
3232    * @param e
3233    *          DOCUMENT ME!
3234    */
3235   @Override
3236   protected void scaleRight_actionPerformed(ActionEvent e)
3237   {
3238     viewport.setScaleRightWrapped(scaleRight.isSelected());
3239     alignPanel.paintAlignment(true);
3240   }
3241
3242   /**
3243    * DOCUMENT ME!
3244    * 
3245    * @param e
3246    *          DOCUMENT ME!
3247    */
3248   @Override
3249   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
3250   {
3251     viewport.setShowBoxes(viewBoxesMenuItem.isSelected());
3252     alignPanel.paintAlignment(true);
3253   }
3254
3255   /**
3256    * DOCUMENT ME!
3257    * 
3258    * @param e
3259    *          DOCUMENT ME!
3260    */
3261   @Override
3262   public void viewTextMenuItem_actionPerformed(ActionEvent e)
3263   {
3264     viewport.setShowText(viewTextMenuItem.isSelected());
3265     alignPanel.paintAlignment(true);
3266   }
3267
3268   /**
3269    * DOCUMENT ME!
3270    * 
3271    * @param e
3272    *          DOCUMENT ME!
3273    */
3274   @Override
3275   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
3276   {
3277     viewport.setRenderGaps(renderGapsMenuItem.isSelected());
3278     alignPanel.paintAlignment(true);
3279   }
3280
3281   public FeatureSettings featureSettings;
3282
3283   @Override
3284   public void featureSettings_actionPerformed(ActionEvent e)
3285   {
3286     if (featureSettings != null)
3287     {
3288       featureSettings.close();
3289       featureSettings = null;
3290     }
3291     if (!showSeqFeatures.isSelected())
3292     {
3293       // make sure features are actually displayed
3294       showSeqFeatures.setSelected(true);
3295       showSeqFeatures_actionPerformed(null);
3296     }
3297     featureSettings = new FeatureSettings(this);
3298   }
3299
3300   /**
3301    * Set or clear 'Show Sequence Features'
3302    * 
3303    * @param evt
3304    *          DOCUMENT ME!
3305    */
3306   @Override
3307   public void showSeqFeatures_actionPerformed(ActionEvent evt)
3308   {
3309     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
3310     alignPanel.paintAlignment(true);
3311     if (alignPanel.getOverviewPanel() != null)
3312     {
3313       alignPanel.getOverviewPanel().updateOverviewImage();
3314     }
3315   }
3316
3317   /**
3318    * Set or clear 'Show Sequence Features'
3319    * 
3320    * @param evt
3321    *          DOCUMENT ME!
3322    */
3323   @Override
3324   public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
3325   {
3326     viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
3327             .isSelected());
3328     if (viewport.getShowSequenceFeaturesHeight())
3329     {
3330       // ensure we're actually displaying features
3331       viewport.setShowSequenceFeatures(true);
3332       showSeqFeatures.setSelected(true);
3333     }
3334     alignPanel.paintAlignment(true);
3335     if (alignPanel.getOverviewPanel() != null)
3336     {
3337       alignPanel.getOverviewPanel().updateOverviewImage();
3338     }
3339   }
3340
3341   /**
3342    * Action on toggle of the 'Show annotations' menu item. This shows or hides
3343    * the annotations panel as a whole.
3344    * 
3345    * The options to show/hide all annotations should be enabled when the panel
3346    * is shown, and disabled when the panel is hidden.
3347    * 
3348    * @param e
3349    */
3350   @Override
3351   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
3352   {
3353     final boolean setVisible = annotationPanelMenuItem.isSelected();
3354     viewport.setShowAnnotation(setVisible);
3355     alignPanel.setAnnotationVisible(setVisible);
3356     this.showAllSeqAnnotations.setEnabled(setVisible);
3357     this.hideAllSeqAnnotations.setEnabled(setVisible);
3358     this.showAllAlAnnotations.setEnabled(setVisible);
3359     this.hideAllAlAnnotations.setEnabled(setVisible);
3360   }
3361
3362   @Override
3363   public void alignmentProperties()
3364   {
3365     JEditorPane editPane = new JEditorPane("text/html", "");
3366     editPane.setEditable(false);
3367     StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
3368             .formatAsHtml();
3369     editPane.setText(MessageManager.formatMessage("label.html_content",
3370             new String[]
3371             { contents.toString() }));
3372     JInternalFrame frame = new JInternalFrame();
3373     frame.getContentPane().add(new JScrollPane(editPane));
3374
3375     Desktop.instance.addInternalFrame(frame, MessageManager.formatMessage(
3376             "label.alignment_properties", new String[]
3377             { getTitle() }), 500, 400);
3378   }
3379
3380   /**
3381    * DOCUMENT ME!
3382    * 
3383    * @param e
3384    *          DOCUMENT ME!
3385    */
3386   @Override
3387   public void overviewMenuItem_actionPerformed(ActionEvent e)
3388   {
3389     if (alignPanel.overviewPanel != null)
3390     {
3391       return;
3392     }
3393
3394     JInternalFrame frame = new JInternalFrame();
3395     OverviewPanel overview = new OverviewPanel(alignPanel);
3396     frame.setContentPane(overview);
3397     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
3398             "label.overview_params", new String[]
3399             { this.getTitle() }), frame.getWidth(), frame.getHeight());
3400     frame.pack();
3401     frame.setLayer(JLayeredPane.PALETTE_LAYER);
3402     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
3403     {
3404       @Override
3405       public void internalFrameClosed(
3406               javax.swing.event.InternalFrameEvent evt)
3407       {
3408         alignPanel.setOverviewPanel(null);
3409       };
3410     });
3411
3412     alignPanel.setOverviewPanel(overview);
3413   }
3414
3415   @Override
3416   public void textColour_actionPerformed(ActionEvent e)
3417   {
3418     new TextColourChooser().chooseColour(alignPanel, null);
3419   }
3420
3421   /**
3422    * DOCUMENT ME!
3423    * 
3424    * @param e
3425    *          DOCUMENT ME!
3426    */
3427   @Override
3428   protected void noColourmenuItem_actionPerformed(ActionEvent e)
3429   {
3430     changeColour(null);
3431   }
3432
3433   /**
3434    * DOCUMENT ME!
3435    * 
3436    * @param e
3437    *          DOCUMENT ME!
3438    */
3439   @Override
3440   public void clustalColour_actionPerformed(ActionEvent e)
3441   {
3442     changeColour(new ClustalxColourScheme(viewport.getAlignment(),
3443             viewport.getHiddenRepSequences()));
3444   }
3445
3446   /**
3447    * DOCUMENT ME!
3448    * 
3449    * @param e
3450    *          DOCUMENT ME!
3451    */
3452   @Override
3453   public void zappoColour_actionPerformed(ActionEvent e)
3454   {
3455     changeColour(new ZappoColourScheme());
3456   }
3457
3458   /**
3459    * DOCUMENT ME!
3460    * 
3461    * @param e
3462    *          DOCUMENT ME!
3463    */
3464   @Override
3465   public void taylorColour_actionPerformed(ActionEvent e)
3466   {
3467     changeColour(new TaylorColourScheme());
3468   }
3469
3470   /**
3471    * DOCUMENT ME!
3472    * 
3473    * @param e
3474    *          DOCUMENT ME!
3475    */
3476   @Override
3477   public void hydrophobicityColour_actionPerformed(ActionEvent e)
3478   {
3479     changeColour(new HydrophobicColourScheme());
3480   }
3481
3482   /**
3483    * DOCUMENT ME!
3484    * 
3485    * @param e
3486    *          DOCUMENT ME!
3487    */
3488   @Override
3489   public void helixColour_actionPerformed(ActionEvent e)
3490   {
3491     changeColour(new HelixColourScheme());
3492   }
3493
3494   /**
3495    * DOCUMENT ME!
3496    * 
3497    * @param e
3498    *          DOCUMENT ME!
3499    */
3500   @Override
3501   public void strandColour_actionPerformed(ActionEvent e)
3502   {
3503     changeColour(new StrandColourScheme());
3504   }
3505
3506   /**
3507    * DOCUMENT ME!
3508    * 
3509    * @param e
3510    *          DOCUMENT ME!
3511    */
3512   @Override
3513   public void turnColour_actionPerformed(ActionEvent e)
3514   {
3515     changeColour(new TurnColourScheme());
3516   }
3517
3518   /**
3519    * DOCUMENT ME!
3520    * 
3521    * @param e
3522    *          DOCUMENT ME!
3523    */
3524   @Override
3525   public void buriedColour_actionPerformed(ActionEvent e)
3526   {
3527     changeColour(new BuriedColourScheme());
3528   }
3529
3530   /**
3531    * DOCUMENT ME!
3532    * 
3533    * @param e
3534    *          DOCUMENT ME!
3535    */
3536   @Override
3537   public void nucleotideColour_actionPerformed(ActionEvent e)
3538   {
3539     changeColour(new NucleotideColourScheme());
3540   }
3541
3542   @Override
3543   public void purinePyrimidineColour_actionPerformed(ActionEvent e)
3544   {
3545     changeColour(new PurinePyrimidineColourScheme());
3546   }
3547
3548   /*
3549    * public void covariationColour_actionPerformed(ActionEvent e) {
3550    * changeColour(new
3551    * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
3552    * ()[0])); }
3553    */
3554   @Override
3555   public void annotationColour_actionPerformed(ActionEvent e)
3556   {
3557     new AnnotationColourChooser(viewport, alignPanel);
3558   }
3559
3560   @Override
3561   public void rnahelicesColour_actionPerformed(ActionEvent e)
3562   {
3563     new RNAHelicesColourChooser(viewport, alignPanel);
3564   }
3565
3566   /**
3567    * DOCUMENT ME!
3568    * 
3569    * @param e
3570    *          DOCUMENT ME!
3571    */
3572   @Override
3573   protected void applyToAllGroups_actionPerformed(ActionEvent e)
3574   {
3575     viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
3576   }
3577
3578   /**
3579    * DOCUMENT ME!
3580    * 
3581    * @param cs
3582    *          DOCUMENT ME!
3583    */
3584   public void changeColour(ColourSchemeI cs)
3585   {
3586     // TODO: compare with applet and pull up to model method
3587     int threshold = 0;
3588
3589     if (cs != null)
3590     {
3591       if (viewport.getAbovePIDThreshold())
3592       {
3593         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
3594                 "Background");
3595         cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
3596       }
3597       else
3598       {
3599         cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
3600       }
3601
3602       if (viewport.getConservationSelected())
3603       {
3604
3605         Alignment al = (Alignment) viewport.getAlignment();
3606         Conservation c = new Conservation("All",
3607                 ResidueProperties.propHash, 3, al.getSequences(), 0,
3608                 al.getWidth() - 1);
3609
3610         c.calculate();
3611         c.verdict(false, viewport.getConsPercGaps());
3612
3613         cs.setConservation(c);
3614
3615         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
3616                 cs, "Background"));
3617       }
3618       else
3619       {
3620         cs.setConservation(null);
3621       }
3622
3623       cs.setConsensus(viewport.getSequenceConsensusHash());
3624     }
3625
3626     viewport.setGlobalColourScheme(cs);
3627
3628     if (viewport.getColourAppliesToAllGroups())
3629     {
3630
3631       for (SequenceGroup sg : viewport.getAlignment().getGroups())
3632       {
3633         if (cs == null)
3634         {
3635           sg.cs = null;
3636           continue;
3637         }
3638
3639         if (cs instanceof ClustalxColourScheme)
3640         {
3641           sg.cs = new ClustalxColourScheme(sg,
3642                   viewport.getHiddenRepSequences());
3643         }
3644         else if (cs instanceof UserColourScheme)
3645         {
3646           sg.cs = new UserColourScheme(((UserColourScheme) cs).getColours());
3647         }
3648         else
3649         {
3650           try
3651           {
3652             sg.cs = cs.getClass().newInstance();
3653           } catch (Exception ex)
3654           {
3655           }
3656         }
3657
3658         if (viewport.getAbovePIDThreshold()
3659                 || cs instanceof PIDColourScheme
3660                 || cs instanceof Blosum62ColourScheme)
3661         {
3662           sg.cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
3663
3664           sg.cs.setConsensus(AAFrequency.calculate(
3665                   sg.getSequences(viewport.getHiddenRepSequences()),
3666                   sg.getStartRes(), sg.getEndRes() + 1));
3667         }
3668         else
3669         {
3670           sg.cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
3671         }
3672
3673         if (viewport.getConservationSelected())
3674         {
3675           Conservation c = new Conservation("Group",
3676                   ResidueProperties.propHash, 3, sg.getSequences(viewport
3677                           .getHiddenRepSequences()), sg.getStartRes(),
3678                   sg.getEndRes() + 1);
3679           c.calculate();
3680           c.verdict(false, viewport.getConsPercGaps());
3681           sg.cs.setConservation(c);
3682         }
3683         else
3684         {
3685           sg.cs.setConservation(null);
3686         }
3687       }
3688     }
3689
3690     if (alignPanel.getOverviewPanel() != null)
3691     {
3692       alignPanel.getOverviewPanel().updateOverviewImage();
3693     }
3694
3695     alignPanel.paintAlignment(true);
3696   }
3697
3698   /**
3699    * DOCUMENT ME!
3700    * 
3701    * @param e
3702    *          DOCUMENT ME!
3703    */
3704   @Override
3705   protected void modifyPID_actionPerformed(ActionEvent e)
3706   {
3707     if (viewport.getAbovePIDThreshold()
3708             && viewport.getGlobalColourScheme() != null)
3709     {
3710       SliderPanel.setPIDSliderSource(alignPanel,
3711               viewport.getGlobalColourScheme(), "Background");
3712       SliderPanel.showPIDSlider();
3713     }
3714   }
3715
3716   /**
3717    * DOCUMENT ME!
3718    * 
3719    * @param e
3720    *          DOCUMENT ME!
3721    */
3722   @Override
3723   protected void modifyConservation_actionPerformed(ActionEvent e)
3724   {
3725     if (viewport.getConservationSelected()
3726             && viewport.getGlobalColourScheme() != null)
3727     {
3728       SliderPanel.setConservationSlider(alignPanel,
3729               viewport.getGlobalColourScheme(), "Background");
3730       SliderPanel.showConservationSlider();
3731     }
3732   }
3733
3734   /**
3735    * DOCUMENT ME!
3736    * 
3737    * @param e
3738    *          DOCUMENT ME!
3739    */
3740   @Override
3741   protected void conservationMenuItem_actionPerformed(ActionEvent e)
3742   {
3743     viewport.setConservationSelected(conservationMenuItem.isSelected());
3744
3745     viewport.setAbovePIDThreshold(false);
3746     abovePIDThreshold.setSelected(false);
3747
3748     changeColour(viewport.getGlobalColourScheme());
3749
3750     modifyConservation_actionPerformed(null);
3751   }
3752
3753   /**
3754    * DOCUMENT ME!
3755    * 
3756    * @param e
3757    *          DOCUMENT ME!
3758    */
3759   @Override
3760   public void abovePIDThreshold_actionPerformed(ActionEvent e)
3761   {
3762     viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
3763
3764     conservationMenuItem.setSelected(false);
3765     viewport.setConservationSelected(false);
3766
3767     changeColour(viewport.getGlobalColourScheme());
3768
3769     modifyPID_actionPerformed(null);
3770   }
3771
3772   /**
3773    * DOCUMENT ME!
3774    * 
3775    * @param e
3776    *          DOCUMENT ME!
3777    */
3778   @Override
3779   public void userDefinedColour_actionPerformed(ActionEvent e)
3780   {
3781     if (e.getActionCommand().equals(
3782             MessageManager.getString("action.user_defined")))
3783     {
3784       new UserDefinedColours(alignPanel, null);
3785     }
3786     else
3787     {
3788       UserColourScheme udc = (UserColourScheme) UserDefinedColours
3789               .getUserColourSchemes().get(e.getActionCommand());
3790
3791       changeColour(udc);
3792     }
3793   }
3794
3795   public void updateUserColourMenu()
3796   {
3797
3798     Component[] menuItems = colourMenu.getMenuComponents();
3799     int i, iSize = menuItems.length;
3800     for (i = 0; i < iSize; i++)
3801     {
3802       if (menuItems[i].getName() != null
3803               && menuItems[i].getName().equals("USER_DEFINED"))
3804       {
3805         colourMenu.remove(menuItems[i]);
3806         iSize--;
3807       }
3808     }
3809     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
3810     {
3811       java.util.Enumeration userColours = jalview.gui.UserDefinedColours
3812               .getUserColourSchemes().keys();
3813
3814       while (userColours.hasMoreElements())
3815       {
3816         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
3817                 userColours.nextElement().toString());
3818         radioItem.setName("USER_DEFINED");
3819         radioItem.addMouseListener(new MouseAdapter()
3820         {
3821           @Override
3822           public void mousePressed(MouseEvent evt)
3823           {
3824             if (evt.isControlDown()
3825                     || SwingUtilities.isRightMouseButton(evt))
3826             {
3827               radioItem.removeActionListener(radioItem.getActionListeners()[0]);
3828
3829               int option = JOptionPane.showInternalConfirmDialog(
3830                       jalview.gui.Desktop.desktop,
3831                       MessageManager
3832                               .getString("label.remove_from_default_list"),
3833                       MessageManager
3834                               .getString("label.remove_user_defined_colour"),
3835                       JOptionPane.YES_NO_OPTION);
3836               if (option == JOptionPane.YES_OPTION)
3837               {
3838                 jalview.gui.UserDefinedColours
3839                         .removeColourFromDefaults(radioItem.getText());
3840                 colourMenu.remove(radioItem);
3841               }
3842               else
3843               {
3844                 radioItem.addActionListener(new ActionListener()
3845                 {
3846                   @Override
3847                   public void actionPerformed(ActionEvent evt)
3848                   {
3849                     userDefinedColour_actionPerformed(evt);
3850                   }
3851                 });
3852               }
3853             }
3854           }
3855         });
3856         radioItem.addActionListener(new ActionListener()
3857         {
3858           @Override
3859           public void actionPerformed(ActionEvent evt)
3860           {
3861             userDefinedColour_actionPerformed(evt);
3862           }
3863         });
3864
3865         colourMenu.insert(radioItem, 15);
3866         colours.add(radioItem);
3867       }
3868     }
3869   }
3870
3871   /**
3872    * DOCUMENT ME!
3873    * 
3874    * @param e
3875    *          DOCUMENT ME!
3876    */
3877   @Override
3878   public void PIDColour_actionPerformed(ActionEvent e)
3879   {
3880     changeColour(new PIDColourScheme());
3881   }
3882
3883   /**
3884    * DOCUMENT ME!
3885    * 
3886    * @param e
3887    *          DOCUMENT ME!
3888    */
3889   @Override
3890   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
3891   {
3892     changeColour(new Blosum62ColourScheme());
3893   }
3894
3895   /**
3896    * DOCUMENT ME!
3897    * 
3898    * @param e
3899    *          DOCUMENT ME!
3900    */
3901   @Override
3902   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
3903   {
3904     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3905     AlignmentSorter.sortByPID(viewport.getAlignment(), viewport
3906             .getAlignment().getSequenceAt(0), null);
3907     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
3908             viewport.getAlignment()));
3909     alignPanel.paintAlignment(true);
3910   }
3911
3912   /**
3913    * DOCUMENT ME!
3914    * 
3915    * @param e
3916    *          DOCUMENT ME!
3917    */
3918   @Override
3919   public void sortIDMenuItem_actionPerformed(ActionEvent e)
3920   {
3921     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3922     AlignmentSorter.sortByID(viewport.getAlignment());
3923     addHistoryItem(new OrderCommand("ID Sort", oldOrder,
3924             viewport.getAlignment()));
3925     alignPanel.paintAlignment(true);
3926   }
3927
3928   /**
3929    * DOCUMENT ME!
3930    * 
3931    * @param e
3932    *          DOCUMENT ME!
3933    */
3934   @Override
3935   public void sortLengthMenuItem_actionPerformed(ActionEvent e)
3936   {
3937     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3938     AlignmentSorter.sortByLength(viewport.getAlignment());
3939     addHistoryItem(new OrderCommand("Length Sort", oldOrder,
3940             viewport.getAlignment()));
3941     alignPanel.paintAlignment(true);
3942   }
3943
3944   /**
3945    * DOCUMENT ME!
3946    * 
3947    * @param e
3948    *          DOCUMENT ME!
3949    */
3950   @Override
3951   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
3952   {
3953     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3954     AlignmentSorter.sortByGroup(viewport.getAlignment());
3955     addHistoryItem(new OrderCommand("Group Sort", oldOrder,
3956             viewport.getAlignment()));
3957
3958     alignPanel.paintAlignment(true);
3959   }
3960
3961   /**
3962    * DOCUMENT ME!
3963    * 
3964    * @param e
3965    *          DOCUMENT ME!
3966    */
3967   @Override
3968   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
3969   {
3970     new RedundancyPanel(alignPanel, this);
3971   }
3972
3973   /**
3974    * DOCUMENT ME!
3975    * 
3976    * @param e
3977    *          DOCUMENT ME!
3978    */
3979   @Override
3980   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
3981   {
3982     if ((viewport.getSelectionGroup() == null)
3983             || (viewport.getSelectionGroup().getSize() < 2))
3984     {
3985       JOptionPane.showInternalMessageDialog(this, MessageManager
3986               .getString("label.you_must_select_least_two_sequences"),
3987               MessageManager.getString("label.invalid_selection"),
3988               JOptionPane.WARNING_MESSAGE);
3989     }
3990     else
3991     {
3992       JInternalFrame frame = new JInternalFrame();
3993       frame.setContentPane(new PairwiseAlignPanel(viewport));
3994       Desktop.addInternalFrame(frame,
3995               MessageManager.getString("action.pairwise_alignment"), 600,
3996               500);
3997     }
3998   }
3999
4000   /**
4001    * DOCUMENT ME!
4002    * 
4003    * @param e
4004    *          DOCUMENT ME!
4005    */
4006   @Override
4007   public void PCAMenuItem_actionPerformed(ActionEvent e)
4008   {
4009     if (((viewport.getSelectionGroup() != null)
4010             && (viewport.getSelectionGroup().getSize() < 4) && (viewport
4011             .getSelectionGroup().getSize() > 0))
4012             || (viewport.getAlignment().getHeight() < 4))
4013     {
4014       JOptionPane
4015               .showInternalMessageDialog(
4016                       this,
4017                       MessageManager
4018                               .getString("label.principal_component_analysis_must_take_least_four_input_sequences"),
4019                       MessageManager
4020                               .getString("label.sequence_selection_insufficient"),
4021                       JOptionPane.WARNING_MESSAGE);
4022
4023       return;
4024     }
4025
4026     new PCAPanel(alignPanel);
4027   }
4028
4029   @Override
4030   public void autoCalculate_actionPerformed(ActionEvent e)
4031   {
4032     viewport.autoCalculateConsensus = autoCalculate.isSelected();
4033     if (viewport.autoCalculateConsensus)
4034     {
4035       viewport.firePropertyChange("alignment", null, viewport
4036               .getAlignment().getSequences());
4037     }
4038   }
4039
4040   @Override
4041   public void sortByTreeOption_actionPerformed(ActionEvent e)
4042   {
4043     viewport.sortByTree = sortByTree.isSelected();
4044   }
4045
4046   @Override
4047   protected void listenToViewSelections_actionPerformed(ActionEvent e)
4048   {
4049     viewport.followSelection = listenToViewSelections.isSelected();
4050   }
4051
4052   /**
4053    * DOCUMENT ME!
4054    * 
4055    * @param e
4056    *          DOCUMENT ME!
4057    */
4058   @Override
4059   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
4060   {
4061     NewTreePanel("AV", "PID", "Average distance tree using PID");
4062   }
4063
4064   /**
4065    * DOCUMENT ME!
4066    * 
4067    * @param e
4068    *          DOCUMENT ME!
4069    */
4070   @Override
4071   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
4072   {
4073     NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
4074   }
4075
4076   /**
4077    * DOCUMENT ME!
4078    * 
4079    * @param e
4080    *          DOCUMENT ME!
4081    */
4082   @Override
4083   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
4084   {
4085     NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
4086   }
4087
4088   /**
4089    * DOCUMENT ME!
4090    * 
4091    * @param e
4092    *          DOCUMENT ME!
4093    */
4094   @Override
4095   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
4096   {
4097     NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
4098   }
4099
4100   /**
4101    * DOCUMENT ME!
4102    * 
4103    * @param type
4104    *          DOCUMENT ME!
4105    * @param pwType
4106    *          DOCUMENT ME!
4107    * @param title
4108    *          DOCUMENT ME!
4109    */
4110   void NewTreePanel(String type, String pwType, String title)
4111   {
4112     TreePanel tp;
4113
4114     if (viewport.getSelectionGroup() != null
4115             && viewport.getSelectionGroup().getSize() > 0)
4116     {
4117       if (viewport.getSelectionGroup().getSize() < 3)
4118       {
4119         JOptionPane
4120                 .showMessageDialog(
4121                         Desktop.desktop,
4122                         MessageManager
4123                                 .getString("label.you_need_more_two_sequences_selected_build_tree"),
4124                         MessageManager
4125                                 .getString("label.not_enough_sequences"),
4126                         JOptionPane.WARNING_MESSAGE);
4127         return;
4128       }
4129
4130       SequenceGroup sg = viewport.getSelectionGroup();
4131
4132       /* Decide if the selection is a column region */
4133       for (SequenceI _s : sg.getSequences())
4134       {
4135         if (_s.getLength() < sg.getEndRes())
4136         {
4137           JOptionPane
4138                   .showMessageDialog(
4139                           Desktop.desktop,
4140                           MessageManager
4141                                   .getString("label.selected_region_to_tree_may_only_contain_residues_or_gaps"),
4142                           MessageManager
4143                                   .getString("label.sequences_selection_not_aligned"),
4144                           JOptionPane.WARNING_MESSAGE);
4145
4146           return;
4147         }
4148       }
4149
4150       title = title + " on region";
4151       tp = new TreePanel(alignPanel, type, pwType);
4152     }
4153     else
4154     {
4155       // are the visible sequences aligned?
4156       if (!viewport.getAlignment().isAligned(false))
4157       {
4158         JOptionPane
4159                 .showMessageDialog(
4160                         Desktop.desktop,
4161                         MessageManager
4162                                 .getString("label.sequences_must_be_aligned_before_creating_tree"),
4163                         MessageManager
4164                                 .getString("label.sequences_not_aligned"),
4165                         JOptionPane.WARNING_MESSAGE);
4166
4167         return;
4168       }
4169
4170       if (viewport.getAlignment().getHeight() < 2)
4171       {
4172         return;
4173       }
4174
4175       tp = new TreePanel(alignPanel, type, pwType);
4176     }
4177
4178     title += " from ";
4179
4180     if (viewport.viewName != null)
4181     {
4182       title += viewport.viewName + " of ";
4183     }
4184
4185     title += this.title;
4186
4187     Desktop.addInternalFrame(tp, title, 600, 500);
4188   }
4189
4190   /**
4191    * DOCUMENT ME!
4192    * 
4193    * @param title
4194    *          DOCUMENT ME!
4195    * @param order
4196    *          DOCUMENT ME!
4197    */
4198   public void addSortByOrderMenuItem(String title,
4199           final AlignmentOrder order)
4200   {
4201     final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new String[]{title}));
4202     sort.add(item);
4203     item.addActionListener(new java.awt.event.ActionListener()
4204     {
4205       @Override
4206       public void actionPerformed(ActionEvent e)
4207       {
4208         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4209
4210         // TODO: JBPNote - have to map order entries to curent SequenceI
4211         // pointers
4212         AlignmentSorter.sortBy(viewport.getAlignment(), order);
4213
4214         addHistoryItem(new OrderCommand(order.getName(), oldOrder, viewport
4215                 .getAlignment()));
4216
4217         alignPanel.paintAlignment(true);
4218       }
4219     });
4220   }
4221
4222   /**
4223    * Add a new sort by annotation score menu item
4224    * 
4225    * @param sort
4226    *          the menu to add the option to
4227    * @param scoreLabel
4228    *          the label used to retrieve scores for each sequence on the
4229    *          alignment
4230    */
4231   public void addSortByAnnotScoreMenuItem(JMenu sort,
4232           final String scoreLabel)
4233   {
4234     final JMenuItem item = new JMenuItem(scoreLabel);
4235     sort.add(item);
4236     item.addActionListener(new java.awt.event.ActionListener()
4237     {
4238       @Override
4239       public void actionPerformed(ActionEvent e)
4240       {
4241         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4242         AlignmentSorter.sortByAnnotationScore(scoreLabel,
4243                 viewport.getAlignment());// ,viewport.getSelectionGroup());
4244         addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
4245                 viewport.getAlignment()));
4246         alignPanel.paintAlignment(true);
4247       }
4248     });
4249   }
4250
4251   /**
4252    * last hash for alignment's annotation array - used to minimise cost of
4253    * rebuild.
4254    */
4255   protected int _annotationScoreVectorHash;
4256
4257   /**
4258    * search the alignment and rebuild the sort by annotation score submenu the
4259    * last alignment annotation vector hash is stored to minimize cost of
4260    * rebuilding in subsequence calls.
4261    * 
4262    */
4263   @Override
4264   public void buildSortByAnnotationScoresMenu()
4265   {
4266     if (viewport.getAlignment().getAlignmentAnnotation() == null)
4267     {
4268       return;
4269     }
4270
4271     if (viewport.getAlignment().getAlignmentAnnotation().hashCode() != _annotationScoreVectorHash)
4272     {
4273       sortByAnnotScore.removeAll();
4274       // almost certainly a quicker way to do this - but we keep it simple
4275       Hashtable scoreSorts = new Hashtable();
4276       AlignmentAnnotation aann[];
4277       for (SequenceI sqa : viewport.getAlignment().getSequences())
4278       {
4279         aann = sqa.getAnnotation();
4280         for (int i = 0; aann != null && i < aann.length; i++)
4281         {
4282           if (aann[i].hasScore() && aann[i].sequenceRef != null)
4283           {
4284             scoreSorts.put(aann[i].label, aann[i].label);
4285           }
4286         }
4287       }
4288       Enumeration labels = scoreSorts.keys();
4289       while (labels.hasMoreElements())
4290       {
4291         addSortByAnnotScoreMenuItem(sortByAnnotScore,
4292                 (String) labels.nextElement());
4293       }
4294       sortByAnnotScore.setVisible(scoreSorts.size() > 0);
4295       scoreSorts.clear();
4296
4297       _annotationScoreVectorHash = viewport.getAlignment()
4298               .getAlignmentAnnotation().hashCode();
4299     }
4300   }
4301
4302   /**
4303    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
4304    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
4305    * call. Listeners are added to remove the menu item when the treePanel is
4306    * closed, and adjust the tree leaf to sequence mapping when the alignment is
4307    * modified.
4308    * 
4309    * @param treePanel
4310    *          Displayed tree window.
4311    * @param title
4312    *          SortBy menu item title.
4313    */
4314   @Override
4315   public void buildTreeMenu()
4316   {
4317     calculateTree.removeAll();
4318     // build the calculate menu
4319
4320     for (final String type : new String[]
4321     { "NJ", "AV" })
4322     {
4323       String treecalcnm = MessageManager.getString("label.tree_calc_"
4324               + type.toLowerCase());
4325       for (final Object pwtype : ResidueProperties.scoreMatrices.keySet())
4326       {
4327         JMenuItem tm = new JMenuItem();
4328         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
4329         if (sm.isProtein() == !viewport.getAlignment().isNucleotide())
4330         {
4331           String smn = MessageManager.getStringOrReturn(
4332                   "label.score_model_", sm.getName());
4333           final String title = MessageManager.formatMessage(
4334                   "label.treecalc_title", treecalcnm, smn);
4335           tm.setText(title);//
4336           tm.addActionListener(new java.awt.event.ActionListener()
4337           {
4338             @Override
4339             public void actionPerformed(ActionEvent e)
4340             {
4341               NewTreePanel(type, (String) pwtype, title);
4342             }
4343           });
4344           calculateTree.add(tm);
4345         }
4346
4347       }
4348     }
4349     sortByTreeMenu.removeAll();
4350
4351     Vector comps = (Vector) PaintRefresher.components.get(viewport
4352             .getSequenceSetId());
4353     Vector treePanels = new Vector();
4354     int i, iSize = comps.size();
4355     for (i = 0; i < iSize; i++)
4356     {
4357       if (comps.elementAt(i) instanceof TreePanel)
4358       {
4359         treePanels.add(comps.elementAt(i));
4360       }
4361     }
4362
4363     iSize = treePanels.size();
4364
4365     if (iSize < 1)
4366     {
4367       sortByTreeMenu.setVisible(false);
4368       return;
4369     }
4370
4371     sortByTreeMenu.setVisible(true);
4372
4373     for (i = 0; i < treePanels.size(); i++)
4374     {
4375       final TreePanel tp = (TreePanel) treePanels.elementAt(i);
4376       final JMenuItem item = new JMenuItem(tp.getTitle());
4377       final NJTree tree = ((TreePanel) treePanels.elementAt(i)).getTree();
4378       item.addActionListener(new java.awt.event.ActionListener()
4379       {
4380         @Override
4381         public void actionPerformed(ActionEvent e)
4382         {
4383           tp.sortByTree_actionPerformed(null);
4384           addHistoryItem(tp.sortAlignmentIn(alignPanel));
4385
4386         }
4387       });
4388
4389       sortByTreeMenu.add(item);
4390     }
4391   }
4392
4393   public boolean sortBy(AlignmentOrder alorder, String undoname)
4394   {
4395     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4396     AlignmentSorter.sortBy(viewport.getAlignment(), alorder);
4397     if (undoname != null)
4398     {
4399       addHistoryItem(new OrderCommand(undoname, oldOrder,
4400               viewport.getAlignment()));
4401     }
4402     alignPanel.paintAlignment(true);
4403     return true;
4404   }
4405
4406   /**
4407    * Work out whether the whole set of sequences or just the selected set will
4408    * be submitted for multiple alignment.
4409    * 
4410    */
4411   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
4412   {
4413     // Now, check we have enough sequences
4414     AlignmentView msa = null;
4415
4416     if ((viewport.getSelectionGroup() != null)
4417             && (viewport.getSelectionGroup().getSize() > 1))
4418     {
4419       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to
4420       // some common interface!
4421       /*
4422        * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
4423        * SequenceI[sz = seqs.getSize(false)];
4424        * 
4425        * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI)
4426        * seqs.getSequenceAt(i); }
4427        */
4428       msa = viewport.getAlignmentView(true);
4429     }
4430     else if (viewport.getSelectionGroup() != null
4431             && viewport.getSelectionGroup().getSize() == 1)
4432     {
4433       int option = JOptionPane.showConfirmDialog(this,
4434               MessageManager.getString("warn.oneseq_msainput_selection"),
4435               MessageManager.getString("label.invalid_selection"),
4436               JOptionPane.OK_CANCEL_OPTION);
4437       if (option == JOptionPane.OK_OPTION)
4438       {
4439         msa = viewport.getAlignmentView(false);
4440       }
4441     }
4442     else
4443     {
4444       msa = viewport.getAlignmentView(false);
4445     }
4446     return msa;
4447   }
4448
4449   /**
4450    * Decides what is submitted to a secondary structure prediction service: the
4451    * first sequence in the alignment, or in the current selection, or, if the
4452    * alignment is 'aligned' (ie padded with gaps), then the currently selected
4453    * region or the whole alignment. (where the first sequence in the set is the
4454    * one that the prediction will be for).
4455    */
4456   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
4457   {
4458     AlignmentView seqs = null;
4459
4460     if ((viewport.getSelectionGroup() != null)
4461             && (viewport.getSelectionGroup().getSize() > 0))
4462     {
4463       seqs = viewport.getAlignmentView(true);
4464     }
4465     else
4466     {
4467       seqs = viewport.getAlignmentView(false);
4468     }
4469     // limit sequences - JBPNote in future - could spawn multiple prediction
4470     // jobs
4471     // TODO: viewport.getAlignment().isAligned is a global state - the local
4472     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
4473     if (!viewport.getAlignment().isAligned(false))
4474     {
4475       seqs.setSequences(new SeqCigar[]
4476       { seqs.getSequences()[0] });
4477       // TODO: if seqs.getSequences().length>1 then should really have warned
4478       // user!
4479
4480     }
4481     return seqs;
4482   }
4483
4484   /**
4485    * DOCUMENT ME!
4486    * 
4487    * @param e
4488    *          DOCUMENT ME!
4489    */
4490   @Override
4491   protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
4492   {
4493     // Pick the tree file
4494     JalviewFileChooser chooser = new JalviewFileChooser(
4495             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
4496     chooser.setFileView(new JalviewFileView());
4497     chooser.setDialogTitle(MessageManager
4498             .getString("label.select_newick_like_tree_file"));
4499     chooser.setToolTipText(MessageManager.getString("label.load_tree_file"));
4500
4501     int value = chooser.showOpenDialog(null);
4502
4503     if (value == JalviewFileChooser.APPROVE_OPTION)
4504     {
4505       String choice = chooser.getSelectedFile().getPath();
4506       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
4507       jalview.io.NewickFile fin = null;
4508       try
4509       {
4510         fin = new jalview.io.NewickFile(choice, "File");
4511         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
4512       } catch (Exception ex)
4513       {
4514         JOptionPane
4515                 .showMessageDialog(
4516                         Desktop.desktop,
4517                         ex.getMessage(),
4518                         MessageManager
4519                                 .getString("label.problem_reading_tree_file"),
4520                         JOptionPane.WARNING_MESSAGE);
4521         ex.printStackTrace();
4522       }
4523       if (fin != null && fin.hasWarningMessage())
4524       {
4525         JOptionPane.showMessageDialog(Desktop.desktop, fin
4526                 .getWarningMessage(), MessageManager
4527                 .getString("label.possible_problem_with_tree_file"),
4528                 JOptionPane.WARNING_MESSAGE);
4529       }
4530     }
4531   }
4532
4533   @Override
4534   protected void tcoffeeColorScheme_actionPerformed(ActionEvent e)
4535   {
4536     changeColour(new TCoffeeColourScheme(alignPanel.getAlignment()));
4537   }
4538
4539   public TreePanel ShowNewickTree(NewickFile nf, String title)
4540   {
4541     return ShowNewickTree(nf, title, 600, 500, 4, 5);
4542   }
4543
4544   public TreePanel ShowNewickTree(NewickFile nf, String title,
4545           AlignmentView input)
4546   {
4547     return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
4548   }
4549
4550   public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
4551           int h, int x, int y)
4552   {
4553     return ShowNewickTree(nf, title, null, w, h, x, y);
4554   }
4555
4556   /**
4557    * Add a treeviewer for the tree extracted from a newick file object to the
4558    * current alignment view
4559    * 
4560    * @param nf
4561    *          the tree
4562    * @param title
4563    *          tree viewer title
4564    * @param input
4565    *          Associated alignment input data (or null)
4566    * @param w
4567    *          width
4568    * @param h
4569    *          height
4570    * @param x
4571    *          position
4572    * @param y
4573    *          position
4574    * @return TreePanel handle
4575    */
4576   public TreePanel ShowNewickTree(NewickFile nf, String title,
4577           AlignmentView input, int w, int h, int x, int y)
4578   {
4579     TreePanel tp = null;
4580
4581     try
4582     {
4583       nf.parse();
4584
4585       if (nf.getTree() != null)
4586       {
4587         tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
4588
4589         tp.setSize(w, h);
4590
4591         if (x > 0 && y > 0)
4592         {
4593           tp.setLocation(x, y);
4594         }
4595
4596         Desktop.addInternalFrame(tp, title, w, h);
4597       }
4598     } catch (Exception ex)
4599     {
4600       ex.printStackTrace();
4601     }
4602
4603     return tp;
4604   }
4605
4606   private boolean buildingMenu = false;
4607
4608   /**
4609    * Generates menu items and listener event actions for web service clients
4610    * 
4611    */
4612   public void BuildWebServiceMenu()
4613   {
4614     while (buildingMenu)
4615     {
4616       try
4617       {
4618         System.err.println("Waiting for building menu to finish.");
4619         Thread.sleep(10);
4620       } catch (Exception e)
4621       {
4622       }
4623       ;
4624     }
4625     final AlignFrame me = this;
4626     buildingMenu = true;
4627     new Thread(new Runnable()
4628     {
4629       @Override
4630       public void run()
4631       {
4632         final List<JMenuItem> legacyItems = new ArrayList<JMenuItem>();
4633         try
4634         {
4635           System.err.println("Building ws menu again "
4636                   + Thread.currentThread());
4637           // TODO: add support for context dependent disabling of services based
4638           // on
4639           // alignment and current selection
4640           // TODO: add additional serviceHandle parameter to specify abstract
4641           // handler
4642           // class independently of AbstractName
4643           // TODO: add in rediscovery GUI function to restart discoverer
4644           // TODO: group services by location as well as function and/or
4645           // introduce
4646           // object broker mechanism.
4647           final Vector<JMenu> wsmenu = new Vector<JMenu>();
4648           final IProgressIndicator af = me;
4649           final JMenu msawsmenu = new JMenu("Alignment");
4650           final JMenu secstrmenu = new JMenu(
4651                   "Secondary Structure Prediction");
4652           final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
4653           final JMenu analymenu = new JMenu("Analysis");
4654           final JMenu dismenu = new JMenu("Protein Disorder");
4655           // final JMenu msawsmenu = new
4656           // JMenu(MessageManager.getString("label.alignment"));
4657           // final JMenu secstrmenu = new
4658           // JMenu(MessageManager.getString("label.secondary_structure_prediction"));
4659           // final JMenu seqsrchmenu = new
4660           // JMenu(MessageManager.getString("label.sequence_database_search"));
4661           // final JMenu analymenu = new
4662           // JMenu(MessageManager.getString("label.analysis"));
4663           // final JMenu dismenu = new
4664           // JMenu(MessageManager.getString("label.protein_disorder"));
4665           // JAL-940 - only show secondary structure prediction services from
4666           // the legacy server
4667           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
4668               // &&
4669           Discoverer.services != null && (Discoverer.services.size() > 0))
4670           {
4671             // TODO: refactor to allow list of AbstractName/Handler bindings to
4672             // be
4673             // stored or retrieved from elsewhere
4674             // No MSAWS used any more:
4675             // Vector msaws = null; // (Vector)
4676             // Discoverer.services.get("MsaWS");
4677             Vector secstrpr = (Vector) Discoverer.services
4678                     .get("SecStrPred");
4679             if (secstrpr != null)
4680             {
4681               // Add any secondary structure prediction services
4682               for (int i = 0, j = secstrpr.size(); i < j; i++)
4683               {
4684                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
4685                         .get(i);
4686                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4687                         .getServiceClient(sh);
4688                 int p = secstrmenu.getItemCount();
4689                 impl.attachWSMenuEntry(secstrmenu, me);
4690                 int q = secstrmenu.getItemCount();
4691                 for (int litm = p; litm < q; litm++)
4692                 {
4693                   legacyItems.add(secstrmenu.getItem(litm));
4694                 }
4695               }
4696             }
4697           }
4698
4699           // Add all submenus in the order they should appear on the web
4700           // services menu
4701           wsmenu.add(msawsmenu);
4702           wsmenu.add(secstrmenu);
4703           wsmenu.add(dismenu);
4704           wsmenu.add(analymenu);
4705           // No search services yet
4706           // wsmenu.add(seqsrchmenu);
4707
4708           javax.swing.SwingUtilities.invokeLater(new Runnable()
4709           {
4710             @Override
4711             public void run()
4712             {
4713               try
4714               {
4715                 webService.removeAll();
4716                 // first, add discovered services onto the webservices menu
4717                 if (wsmenu.size() > 0)
4718                 {
4719                   for (int i = 0, j = wsmenu.size(); i < j; i++)
4720                   {
4721                     webService.add(wsmenu.get(i));
4722                   }
4723                 }
4724                 else
4725                 {
4726                   webService.add(me.webServiceNoServices);
4727                 }
4728                 // TODO: move into separate menu builder class.
4729                 boolean new_sspred = false;
4730                 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
4731                 {
4732                   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4733                   if (jws2servs != null)
4734                   {
4735                     if (jws2servs.hasServices())
4736                     {
4737                       jws2servs.attachWSMenuEntry(webService, me);
4738                       for (Jws2Instance sv : jws2servs.getServices())
4739                       {
4740                         if (sv.description.toLowerCase().contains("jpred"))
4741                         {
4742                           for (JMenuItem jmi : legacyItems)
4743                           {
4744                             jmi.setVisible(false);
4745                           }
4746                         }
4747                       }
4748
4749                     }
4750                     if (jws2servs.isRunning())
4751                     {
4752                       JMenuItem tm = new JMenuItem(
4753                               "Still discovering JABA Services");
4754                       tm.setEnabled(false);
4755                       webService.add(tm);
4756                     }
4757                   }
4758                 }
4759                 build_urlServiceMenu(me.webService);
4760                 build_fetchdbmenu(webService);
4761                 for (JMenu item : wsmenu)
4762                 {
4763                   if (item.getItemCount() == 0)
4764                   {
4765                     item.setEnabled(false);
4766                   }
4767                   else
4768                   {
4769                     item.setEnabled(true);
4770                   }
4771                 }
4772               } catch (Exception e)
4773               {
4774                 Cache.log
4775                         .debug("Exception during web service menu building process.",
4776                                 e);
4777               }
4778               ;
4779             }
4780           });
4781         } catch (Exception e)
4782         {
4783         }
4784         ;
4785
4786         buildingMenu = false;
4787       }
4788     }).start();
4789
4790   }
4791
4792   /**
4793    * construct any groupURL type service menu entries.
4794    * 
4795    * @param webService
4796    */
4797   private void build_urlServiceMenu(JMenu webService)
4798   {
4799     // TODO: remove this code when 2.7 is released
4800     // DEBUG - alignmentView
4801     /*
4802      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
4803      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
4804      * 
4805      * @Override public void actionPerformed(ActionEvent e) {
4806      * jalview.datamodel.AlignmentView
4807      * .testSelectionViews(af.viewport.getAlignment(),
4808      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
4809      * 
4810      * }); webService.add(testAlView);
4811      */
4812     // TODO: refactor to RestClient discoverer and merge menu entries for
4813     // rest-style services with other types of analysis/calculation service
4814     // SHmmr test client - still being implemented.
4815     // DEBUG - alignmentView
4816
4817     for (jalview.ws.rest.RestClient client : jalview.ws.rest.RestClient
4818             .getRestClients())
4819     {
4820       client.attachWSMenuEntry(
4821               JvSwingUtils.findOrCreateMenu(webService, client.getAction()),
4822               this);
4823     }
4824   }
4825
4826   /*
4827    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
4828    * chooser = new JalviewFileChooser(jalview.bin.Cache.
4829    * getProperty("LAST_DIRECTORY"));
4830    * 
4831    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
4832    * to Vamsas file"); chooser.setToolTipText("Export");
4833    * 
4834    * int value = chooser.showSaveDialog(this);
4835    * 
4836    * if (value == JalviewFileChooser.APPROVE_OPTION) {
4837    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
4838    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
4839    * chooser.getSelectedFile().getAbsolutePath(), this); } }
4840    */
4841   /**
4842    * prototype of an automatically enabled/disabled analysis function
4843    * 
4844    */
4845   protected void setShowProductsEnabled()
4846   {
4847     SequenceI[] selection = viewport.getSequenceSelection();
4848     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
4849             viewport.getAlignment().getDataset()))
4850     {
4851       showProducts.setEnabled(true);
4852
4853     }
4854     else
4855     {
4856       showProducts.setEnabled(false);
4857     }
4858   }
4859
4860   /**
4861    * search selection for sequence xRef products and build the show products
4862    * menu.
4863    * 
4864    * @param selection
4865    * @param dataset
4866    * @return true if showProducts menu should be enabled.
4867    */
4868   public boolean canShowProducts(SequenceI[] selection,
4869           boolean isRegionSelection, Alignment dataset)
4870   {
4871     boolean showp = false;
4872     try
4873     {
4874       showProducts.removeAll();
4875       final boolean dna = viewport.getAlignment().isNucleotide();
4876       final Alignment ds = dataset;
4877       String[] ptypes = (selection == null || selection.length == 0) ? null
4878               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
4879       // Object[] prods =
4880       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
4881       // selection, dataset, true);
4882       final SequenceI[] sel = selection;
4883       for (int t = 0; ptypes != null && t < ptypes.length; t++)
4884       {
4885         showp = true;
4886         final boolean isRegSel = isRegionSelection;
4887         final AlignFrame af = this;
4888         final String source = ptypes[t];
4889         JMenuItem xtype = new JMenuItem(ptypes[t]);
4890         xtype.addActionListener(new ActionListener()
4891         {
4892
4893           @Override
4894           public void actionPerformed(ActionEvent e)
4895           {
4896             // TODO: new thread for this call with vis-delay
4897             af.showProductsFor(af.viewport.getSequenceSelection(), ds,
4898                     isRegSel, dna, source);
4899           }
4900
4901         });
4902         showProducts.add(xtype);
4903       }
4904       showProducts.setVisible(showp);
4905       showProducts.setEnabled(showp);
4906     } catch (Exception e)
4907     {
4908       jalview.bin.Cache.log
4909               .warn("canTranslate threw an exception - please report to help@jalview.org",
4910                       e);
4911       return false;
4912     }
4913     return showp;
4914   }
4915
4916   protected void showProductsFor(SequenceI[] sel, Alignment ds,
4917           boolean isRegSel, boolean dna, String source)
4918   {
4919     final boolean fisRegSel = isRegSel;
4920     final boolean fdna = dna;
4921     final String fsrc = source;
4922     final AlignFrame ths = this;
4923     final SequenceI[] fsel = sel;
4924     Runnable foo = new Runnable()
4925     {
4926
4927       @Override
4928       public void run()
4929       {
4930         final long sttime = System.currentTimeMillis();
4931         ths.setProgressBar(MessageManager.formatMessage("status.searching_for_sequences_from", new String[]{fsrc}), sttime);
4932         try
4933         {
4934           Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
4935           // our local
4936           // dataset
4937           // reference
4938           Alignment prods = CrossRef
4939                   .findXrefSequences(fsel, fdna, fsrc, ds);
4940           if (prods != null)
4941           {
4942             SequenceI[] sprods = new SequenceI[prods.getHeight()];
4943             for (int s = 0; s < sprods.length; s++)
4944             {
4945               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
4946               if (ds.getSequences() == null
4947                       || !ds.getSequences().contains(
4948                               sprods[s].getDatasetSequence()))
4949               {
4950                 ds.addSequence(sprods[s].getDatasetSequence());
4951               }
4952               sprods[s].updatePDBIds();
4953             }
4954             Alignment al = new Alignment(sprods);
4955             AlignedCodonFrame[] cf = prods.getCodonFrames();
4956             al.setDataset(ds);
4957             for (int s = 0; cf != null && s < cf.length; s++)
4958             {
4959               al.addCodonFrame(cf[s]);
4960               cf[s] = null;
4961             }
4962             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
4963                     DEFAULT_HEIGHT);
4964             String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
4965                     + " for " + ((fisRegSel) ? "selected region of " : "")
4966                     + getTitle();
4967             Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
4968                     DEFAULT_HEIGHT);
4969           }
4970           else
4971           {
4972             System.err.println("No Sequences generated for xRef type "
4973                     + fsrc);
4974           }
4975         } catch (Exception e)
4976         {
4977           jalview.bin.Cache.log.error(
4978                   "Exception when finding crossreferences", e);
4979         } catch (OutOfMemoryError e)
4980         {
4981           new OOMWarning("whilst fetching crossreferences", e);
4982         } catch (Error e)
4983         {
4984           jalview.bin.Cache.log.error("Error when finding crossreferences",
4985                   e);
4986         }
4987         ths.setProgressBar(MessageManager.formatMessage("status.finished_searching_for_sequences_from", new String[]{fsrc}),
4988                 sttime);
4989       }
4990
4991     };
4992     Thread frunner = new Thread(foo);
4993     frunner.start();
4994   }
4995
4996   public boolean canShowTranslationProducts(SequenceI[] selection,
4997           AlignmentI alignment)
4998   {
4999     // old way
5000     try
5001     {
5002       return (jalview.analysis.Dna.canTranslate(selection,
5003               viewport.getViewAsVisibleContigs(true)));
5004     } catch (Exception e)
5005     {
5006       jalview.bin.Cache.log
5007               .warn("canTranslate threw an exception - please report to help@jalview.org",
5008                       e);
5009       return false;
5010     }
5011   }
5012
5013   @Override
5014   public void showProducts_actionPerformed(ActionEvent e)
5015   {
5016     // /////////////////////////////
5017     // Collect Data to be translated/transferred
5018
5019     SequenceI[] selection = viewport.getSequenceSelection();
5020     AlignmentI al = null;
5021     try
5022     {
5023       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
5024               .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
5025               viewport.getAlignment().getDataset());
5026     } catch (Exception ex)
5027     {
5028       al = null;
5029       jalview.bin.Cache.log.debug("Exception during translation.", ex);
5030     }
5031     if (al == null)
5032     {
5033       JOptionPane
5034               .showMessageDialog(
5035                       Desktop.desktop,
5036                       MessageManager
5037                               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
5038                       MessageManager.getString("label.translation_failed"),
5039                       JOptionPane.WARNING_MESSAGE);
5040     }
5041     else
5042     {
5043       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
5044       Desktop.addInternalFrame(af, MessageManager.formatMessage(
5045               "label.translation_of_params", new String[]
5046               { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
5047     }
5048   }
5049
5050   /**
5051    * Construct and display a new frame containing the translation of this
5052    * frame's cDNA sequences to their protein (amino acid) equivalents.
5053    */
5054   @Override
5055   public void showTranslation_actionPerformed(ActionEvent e)
5056   {
5057     // /////////////////////////////
5058     // Collect Data to be translated/transferred
5059
5060     SequenceI[] selection = viewport.getSequenceSelection();
5061     String[] seqstring = viewport.getViewAsString(true);
5062     AlignmentI al = null;
5063     try
5064     {
5065       al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
5066               viewport.getViewAsVisibleContigs(true), viewport
5067                       .getGapCharacter(), viewport.getAlignment()
5068                       .getAlignmentAnnotation(), viewport.getAlignment()
5069                       .getWidth(), viewport.getAlignment().getDataset());
5070     } catch (Exception ex)
5071     {
5072       al = null;
5073       jalview.bin.Cache.log.error(
5074               "Exception during translation. Please report this !", ex);
5075       JOptionPane
5076               .showMessageDialog(
5077                       Desktop.desktop,
5078                       MessageManager
5079                               .getString("label.error_when_translating_sequences_submit_bug_report"),
5080                       MessageManager
5081                               .getString("label.implementation_error")
5082                               + MessageManager
5083                                       .getString("translation_failed"),
5084                       JOptionPane.ERROR_MESSAGE);
5085       return;
5086     }
5087     if (al == null)
5088     {
5089       JOptionPane
5090               .showMessageDialog(
5091                       Desktop.desktop,
5092                       MessageManager
5093                               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
5094                       MessageManager.getString("label.translation_failed"),
5095                       JOptionPane.WARNING_MESSAGE);
5096     }
5097     else
5098     {
5099       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
5100       Desktop.addInternalFrame(af, MessageManager.formatMessage(
5101               "label.translation_of_params", new String[]
5102               { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
5103       viewport.getStructureSelectionManager().addCommandListener(viewport);
5104     }
5105   }
5106
5107   /**
5108    * Try to load a features file onto the alignment.
5109    * 
5110    * @param file
5111    *          contents or path to retrieve file
5112    * @param type
5113    *          access mode of file (see jalview.io.AlignFile)
5114    * @return true if features file was parsed corectly.
5115    */
5116   public boolean parseFeaturesFile(String file, String type)
5117   {
5118     boolean featuresFile = false;
5119     try
5120     {
5121       featuresFile = new FeaturesFile(file, type).parse(viewport
5122               .getAlignment().getDataset(), alignPanel.seqPanel.seqCanvas
5123               .getFeatureRenderer().featureColours, false,
5124               jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
5125     } catch (Exception ex)
5126     {
5127       ex.printStackTrace();
5128     }
5129
5130     if (featuresFile)
5131     {
5132       viewport.showSequenceFeatures = true;
5133       showSeqFeatures.setSelected(true);
5134       if (alignPanel.seqPanel.seqCanvas.fr != null)
5135       {
5136         // update the min/max ranges where necessary
5137         alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
5138       }
5139       if (featureSettings != null)
5140       {
5141         featureSettings.setTableData();
5142       }
5143       alignPanel.paintAlignment(true);
5144     }
5145
5146     return featuresFile;
5147   }
5148
5149   @Override
5150   public void dragEnter(DropTargetDragEvent evt)
5151   {
5152   }
5153
5154   @Override
5155   public void dragExit(DropTargetEvent evt)
5156   {
5157   }
5158
5159   @Override
5160   public void dragOver(DropTargetDragEvent evt)
5161   {
5162   }
5163
5164   @Override
5165   public void dropActionChanged(DropTargetDragEvent evt)
5166   {
5167   }
5168
5169   @Override
5170   public void drop(DropTargetDropEvent evt)
5171   {
5172     Transferable t = evt.getTransferable();
5173     java.util.List files = null;
5174
5175     try
5176     {
5177       DataFlavor uriListFlavor = new DataFlavor(
5178               "text/uri-list;class=java.lang.String");
5179       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
5180       {
5181         // Works on Windows and MacOSX
5182         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
5183         files = (java.util.List) t
5184                 .getTransferData(DataFlavor.javaFileListFlavor);
5185       }
5186       else if (t.isDataFlavorSupported(uriListFlavor))
5187       {
5188         // This is used by Unix drag system
5189         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
5190         String data = (String) t.getTransferData(uriListFlavor);
5191         files = new java.util.ArrayList(1);
5192         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
5193                 data, "\r\n"); st.hasMoreTokens();)
5194         {
5195           String s = st.nextToken();
5196           if (s.startsWith("#"))
5197           {
5198             // the line is a comment (as per the RFC 2483)
5199             continue;
5200           }
5201
5202           java.net.URI uri = new java.net.URI(s);
5203           // check to see if we can handle this kind of URI
5204           if (uri.getScheme().toLowerCase().startsWith("http"))
5205           {
5206             files.add(uri.toString());
5207           }
5208           else
5209           {
5210             // otherwise preserve old behaviour: catch all for file objects
5211             java.io.File file = new java.io.File(uri);
5212             files.add(file.toString());
5213           }
5214         }
5215       }
5216     } catch (Exception e)
5217     {
5218       e.printStackTrace();
5219     }
5220     if (files != null)
5221     {
5222       try
5223       {
5224         // check to see if any of these files have names matching sequences in
5225         // the alignment
5226         SequenceIdMatcher idm = new SequenceIdMatcher(viewport
5227                 .getAlignment().getSequencesArray());
5228         /**
5229          * Object[] { String,SequenceI}
5230          */
5231         ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
5232         ArrayList<String> filesnotmatched = new ArrayList<String>();
5233         for (int i = 0; i < files.size(); i++)
5234         {
5235           String file = files.get(i).toString();
5236           String pdbfn = "";
5237           String protocol = FormatAdapter.checkProtocol(file);
5238           if (protocol == jalview.io.FormatAdapter.FILE)
5239           {
5240             File fl = new File(file);
5241             pdbfn = fl.getName();
5242           }
5243           else if (protocol == jalview.io.FormatAdapter.URL)
5244           {
5245             URL url = new URL(file);
5246             pdbfn = url.getFile();
5247           }
5248           if (pdbfn.length() > 0)
5249           {
5250             // attempt to find a match in the alignment
5251             SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
5252             int l = 0, c = pdbfn.indexOf(".");
5253             while (mtch == null && c != -1)
5254             {
5255               do
5256               {
5257                 l = c;
5258               } while ((c = pdbfn.indexOf(".", l)) > l);
5259               if (l > -1)
5260               {
5261                 pdbfn = pdbfn.substring(0, l);
5262               }
5263               mtch = idm.findAllIdMatches(pdbfn);
5264             }
5265             if (mtch != null)
5266             {
5267               String type = null;
5268               try
5269               {
5270                 type = new IdentifyFile().Identify(file, protocol);
5271               } catch (Exception ex)
5272               {
5273                 type = null;
5274               }
5275               if (type != null)
5276               {
5277                 if (type.equalsIgnoreCase("PDB"))
5278                 {
5279                   filesmatched.add(new Object[]
5280                   { file, protocol, mtch });
5281                   continue;
5282                 }
5283               }
5284             }
5285             // File wasn't named like one of the sequences or wasn't a PDB file.
5286             filesnotmatched.add(file);
5287           }
5288         }
5289         int assocfiles = 0;
5290         if (filesmatched.size() > 0)
5291         {
5292           if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
5293                   || JOptionPane
5294                           .showConfirmDialog(
5295                                   this,
5296                                   MessageManager
5297                                           .formatMessage(
5298                                                   "label.automatically_associate_pdb_files_with_sequences_same_name",
5299                                                   new String[]
5300                                                   { Integer.valueOf(
5301                                                           filesmatched
5302                                                                   .size())
5303                                                           .toString() }),
5304                                   MessageManager
5305                                           .getString("label.automatically_associate_pdb_files_by_name"),
5306                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
5307
5308           {
5309             for (Object[] fm : filesmatched)
5310             {
5311               // try and associate
5312               // TODO: may want to set a standard ID naming formalism for
5313               // associating PDB files which have no IDs.
5314               for (SequenceI toassoc : (SequenceI[]) fm[2])
5315               {
5316                 PDBEntry pe = new AssociatePdbFileWithSeq()
5317                         .associatePdbWithSeq((String) fm[0],
5318                                 (String) fm[1], toassoc, false,
5319                                 Desktop.instance);
5320                 if (pe != null)
5321                 {
5322                   System.err.println("Associated file : "
5323                           + ((String) fm[0]) + " with "
5324                           + toassoc.getDisplayId(true));
5325                   assocfiles++;
5326                 }
5327               }
5328               alignPanel.paintAlignment(true);
5329             }
5330           }
5331         }
5332         if (filesnotmatched.size() > 0)
5333         {
5334           if (assocfiles > 0
5335                   && (Cache.getDefault(
5336                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
5337                           .showConfirmDialog(
5338                                   this,
5339                                   "<html>"+MessageManager
5340                                           .formatMessage(
5341                                                   "label.ignore_unmatched_dropped_files_info",
5342                                                   new String[]
5343                                                   { Integer.valueOf(
5344                                                           filesnotmatched
5345                                                                   .size())
5346                                                           .toString() })+"</html>",
5347                                   MessageManager
5348                                           .getString("label.ignore_unmatched_dropped_files"),
5349                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
5350           {
5351             return;
5352           }
5353           for (String fn : filesnotmatched)
5354           {
5355             loadJalviewDataFile(fn, null, null, null);
5356           }
5357
5358         }
5359       } catch (Exception ex)
5360       {
5361         ex.printStackTrace();
5362       }
5363     }
5364   }
5365
5366   /**
5367    * Attempt to load a "dropped" file or URL string: First by testing whether
5368    * it's and Annotation file, then a JNet file, and finally a features file. If
5369    * all are false then the user may have dropped an alignment file onto this
5370    * AlignFrame.
5371    * 
5372    * @param file
5373    *          either a filename or a URL string.
5374    */
5375   public void loadJalviewDataFile(String file, String protocol,
5376           String format, SequenceI assocSeq)
5377   {
5378     try
5379     {
5380       if (protocol == null)
5381       {
5382         protocol = jalview.io.FormatAdapter.checkProtocol(file);
5383       }
5384       // if the file isn't identified, or not positively identified as some
5385       // other filetype (PFAM is default unidentified alignment file type) then
5386       // try to parse as annotation.
5387       boolean isAnnotation = (format == null || format
5388               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
5389               .readAnnotationFile(viewport.getAlignment(), file, protocol)
5390               : false;
5391
5392       if (!isAnnotation)
5393       {
5394         // first see if its a T-COFFEE score file
5395         TCoffeeScoreFile tcf = null;
5396         try
5397         {
5398           tcf = new TCoffeeScoreFile(file, protocol);
5399           if (tcf.isValid())
5400           {
5401             if (tcf.annotateAlignment(viewport.getAlignment(), true))
5402             {
5403               tcoffeeColour.setEnabled(true);
5404               tcoffeeColour.setSelected(true);
5405               changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
5406               isAnnotation = true;
5407               statusBar
5408                       .setText(MessageManager
5409                               .getString("label.successfully_pasted_tcoffee_scores_to_alignment"));
5410             }
5411             else
5412             {
5413               // some problem - if no warning its probable that the ID matching
5414               // process didn't work
5415               JOptionPane
5416                       .showMessageDialog(
5417                               Desktop.desktop,
5418                               tcf.getWarningMessage() == null ? MessageManager
5419                                       .getString("label.check_file_matches_sequence_ids_alignment")
5420                                       : tcf.getWarningMessage(),
5421                               MessageManager
5422                                       .getString("label.problem_reading_tcoffee_score_file"),
5423                               JOptionPane.WARNING_MESSAGE);
5424             }
5425           }
5426           else
5427           {
5428             tcf = null;
5429           }
5430         } catch (Exception x)
5431         {
5432           Cache.log
5433                   .debug("Exception when processing data source as T-COFFEE score file",
5434                           x);
5435           tcf = null;
5436         }
5437         if (tcf == null)
5438         {
5439           // try to see if its a JNet 'concise' style annotation file *before*
5440           // we
5441           // try to parse it as a features file
5442           if (format == null)
5443           {
5444             format = new IdentifyFile().Identify(file, protocol);
5445           }
5446           if (format.equalsIgnoreCase("JnetFile"))
5447           {
5448             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
5449                     file, protocol);
5450             new JnetAnnotationMaker().add_annotation(predictions,
5451                     viewport.getAlignment(), 0, false);
5452             isAnnotation = true;
5453           }
5454           else
5455           {
5456             /*
5457              * if (format.equalsIgnoreCase("PDB")) {
5458              * 
5459              * String pdbfn = ""; // try to match up filename with sequence id
5460              * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
5461              * new File(file); pdbfn = fl.getName(); } else if (protocol ==
5462              * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
5463              * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
5464              * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
5465              * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
5466              * // attempt to find a match in the alignment SequenceI mtch =
5467              * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
5468              * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
5469              * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
5470              * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
5471              * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
5472              * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
5473              * { System.err.println("Associated file : " + file + " with " +
5474              * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
5475              * TODO: maybe need to load as normal otherwise return; } }
5476              */
5477             // try to parse it as a features file
5478             boolean isGroupsFile = parseFeaturesFile(file, protocol);
5479             // if it wasn't a features file then we just treat it as a general
5480             // alignment file to load into the current view.
5481             if (!isGroupsFile)
5482             {
5483               new FileLoader().LoadFile(viewport, file, protocol, format);
5484             }
5485             else
5486             {
5487               alignPanel.paintAlignment(true);
5488             }
5489           }
5490         }
5491       }
5492       if (isAnnotation)
5493       {
5494
5495         alignPanel.adjustAnnotationHeight();
5496         viewport.updateSequenceIdColours();
5497         buildSortByAnnotationScoresMenu();
5498         alignPanel.paintAlignment(true);
5499       }
5500     } catch (Exception ex)
5501     {
5502       ex.printStackTrace();
5503     } catch (OutOfMemoryError oom)
5504     {
5505       try
5506       {
5507         System.gc();
5508       } catch (Exception x)
5509       {
5510       }
5511       ;
5512       new OOMWarning(
5513               "loading data "
5514                       + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
5515                               : "using " + protocol + " from " + file)
5516                               : ".")
5517                       + (format != null ? "(parsing as '" + format
5518                               + "' file)" : ""), oom, Desktop.desktop);
5519     }
5520   }
5521
5522   @Override
5523   public void tabSelectionChanged(int index)
5524   {
5525     if (index > -1)
5526     {
5527       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
5528       viewport = alignPanel.av;
5529       avc.setViewportAndAlignmentPanel(viewport, alignPanel);
5530       setMenusFromViewport(viewport);
5531     }
5532   }
5533
5534   @Override
5535   public void tabbedPane_mousePressed(MouseEvent e)
5536   {
5537     if (SwingUtilities.isRightMouseButton(e))
5538     {
5539       String reply = JOptionPane.showInternalInputDialog(this,
5540               MessageManager.getString("label.enter_view_name"),
5541               MessageManager.getString("label.enter_view_name"),
5542               JOptionPane.QUESTION_MESSAGE);
5543
5544       if (reply != null)
5545       {
5546         viewport.viewName = reply;
5547         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
5548       }
5549     }
5550   }
5551
5552   public AlignViewport getCurrentView()
5553   {
5554     return viewport;
5555   }
5556
5557   /**
5558    * Open the dialog for regex description parsing.
5559    */
5560   @Override
5561   protected void extractScores_actionPerformed(ActionEvent e)
5562   {
5563     ParseProperties pp = new jalview.analysis.ParseProperties(
5564             viewport.getAlignment());
5565     // TODO: verify regex and introduce GUI dialog for version 2.5
5566     // if (pp.getScoresFromDescription("col", "score column ",
5567     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
5568     // true)>0)
5569     if (pp.getScoresFromDescription("description column",
5570             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
5571     {
5572       buildSortByAnnotationScoresMenu();
5573     }
5574   }
5575
5576   /*
5577    * (non-Javadoc)
5578    * 
5579    * @see
5580    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
5581    * )
5582    */
5583   @Override
5584   protected void showDbRefs_actionPerformed(ActionEvent e)
5585   {
5586     viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
5587   }
5588
5589   /*
5590    * (non-Javadoc)
5591    * 
5592    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
5593    * ActionEvent)
5594    */
5595   @Override
5596   protected void showNpFeats_actionPerformed(ActionEvent e)
5597   {
5598     viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
5599   }
5600
5601   /**
5602    * find the viewport amongst the tabs in this alignment frame and close that
5603    * tab
5604    * 
5605    * @param av
5606    */
5607   public boolean closeView(AlignViewport av)
5608   {
5609     if (viewport == av)
5610     {
5611       this.closeMenuItem_actionPerformed(false);
5612       return true;
5613     }
5614     Component[] comp = tabbedPane.getComponents();
5615     for (int i = 0; comp != null && i < comp.length; i++)
5616     {
5617       if (comp[i] instanceof AlignmentPanel)
5618       {
5619         if (((AlignmentPanel) comp[i]).av == av)
5620         {
5621           // close the view.
5622           closeView((AlignmentPanel) comp[i]);
5623           return true;
5624         }
5625       }
5626     }
5627     return false;
5628   }
5629
5630   protected void build_fetchdbmenu(JMenu webService)
5631   {
5632     // Temporary hack - DBRef Fetcher always top level ws entry.
5633     // TODO We probably want to store a sequence database checklist in
5634     // preferences and have checkboxes.. rather than individual sources selected
5635     // here
5636     final JMenu rfetch = new JMenu(
5637             MessageManager.getString("action.fetch_db_references"));
5638     rfetch.setToolTipText(MessageManager
5639             .getString("label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences"));
5640     webService.add(rfetch);
5641
5642     final JCheckBoxMenuItem trimrs = new JCheckBoxMenuItem(
5643             MessageManager.getString("option.trim_retrieved_seqs"));
5644     trimrs.setToolTipText(MessageManager
5645             .getString("label.trim_retrieved_sequences"));
5646     trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true));
5647     trimrs.addActionListener(new ActionListener()
5648     {
5649       @Override
5650       public void actionPerformed(ActionEvent e)
5651       {
5652         trimrs.setSelected(trimrs.isSelected());
5653         Cache.setProperty("TRIM_FETCHED_DATASET_SEQS",
5654                 Boolean.valueOf(trimrs.isSelected()).toString());
5655       };
5656     });
5657     rfetch.add(trimrs);
5658     JMenuItem fetchr = new JMenuItem(
5659             MessageManager.getString("label.standard_databases"));
5660     fetchr.setToolTipText(MessageManager
5661             .getString("label.fetch_embl_uniprot"));
5662     fetchr.addActionListener(new ActionListener()
5663     {
5664
5665       @Override
5666       public void actionPerformed(ActionEvent e)
5667       {
5668         new Thread(new Runnable()
5669         {
5670
5671           @Override
5672           public void run()
5673           {
5674             new jalview.ws.DBRefFetcher(alignPanel.av
5675                     .getSequenceSelection(), alignPanel.alignFrame)
5676                     .fetchDBRefs(false);
5677           }
5678         }).start();
5679
5680       }
5681
5682     });
5683     rfetch.add(fetchr);
5684     final AlignFrame me = this;
5685     new Thread(new Runnable()
5686     {
5687       @Override
5688       public void run()
5689       {
5690         final jalview.ws.SequenceFetcher sf = SequenceFetcher
5691                 .getSequenceFetcherSingleton(me);
5692         javax.swing.SwingUtilities.invokeLater(new Runnable()
5693         {
5694           @Override
5695           public void run()
5696           {
5697             String[] dbclasses = sf.getOrderedSupportedSources();
5698             // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
5699             // jalview.util.QuickSort.sort(otherdb, otherdb);
5700             List<DbSourceProxy> otherdb;
5701             JMenu dfetch = new JMenu();
5702             JMenu ifetch = new JMenu();
5703             JMenuItem fetchr = null;
5704             int comp = 0, icomp = 0, mcomp = 15;
5705             String mname = null;
5706             int dbi = 0;
5707             for (String dbclass : dbclasses)
5708             {
5709               otherdb = sf.getSourceProxy(dbclass);
5710               // add a single entry for this class, or submenu allowing 'fetch
5711               // all' or pick one
5712               if (otherdb == null || otherdb.size() < 1)
5713               {
5714                 continue;
5715               }
5716               // List<DbSourceProxy> dbs=otherdb;
5717               // otherdb=new ArrayList<DbSourceProxy>();
5718               // for (DbSourceProxy db:dbs)
5719               // {
5720               // if (!db.isA(DBRefSource.ALIGNMENTDB)
5721               // }
5722               if (mname == null)
5723               {
5724                 mname = "From " + dbclass;
5725               }
5726               if (otherdb.size() == 1)
5727               {
5728                 final DbSourceProxy[] dassource = otherdb
5729                         .toArray(new DbSourceProxy[0]);
5730                 DbSourceProxy src = otherdb.get(0);
5731                 fetchr = new JMenuItem(src.getDbSource());
5732                 fetchr.addActionListener(new ActionListener()
5733                 {
5734
5735                   @Override
5736                   public void actionPerformed(ActionEvent e)
5737                   {
5738                     new Thread(new Runnable()
5739                     {
5740
5741                       @Override
5742                       public void run()
5743                       {
5744                         new jalview.ws.DBRefFetcher(alignPanel.av
5745                                 .getSequenceSelection(),
5746                                 alignPanel.alignFrame, dassource)
5747                                 .fetchDBRefs(false);
5748                       }
5749                     }).start();
5750                   }
5751
5752                 });
5753                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{src.getDbName()})));
5754                 dfetch.add(fetchr);
5755                 comp++;
5756               }
5757               else
5758               {
5759                 final DbSourceProxy[] dassource = otherdb
5760                         .toArray(new DbSourceProxy[0]);
5761                 // fetch all entry
5762                 DbSourceProxy src = otherdb.get(0);
5763                 fetchr = new JMenuItem(MessageManager.formatMessage(
5764                         "label.fetch_all_param", new String[]
5765                         { src.getDbSource() }));
5766                 fetchr.addActionListener(new ActionListener()
5767                 {
5768                   @Override
5769                   public void actionPerformed(ActionEvent e)
5770                   {
5771                     new Thread(new Runnable()
5772                     {
5773
5774                       @Override
5775                       public void run()
5776                       {
5777                         new jalview.ws.DBRefFetcher(alignPanel.av
5778                                 .getSequenceSelection(),
5779                                 alignPanel.alignFrame, dassource)
5780                                 .fetchDBRefs(false);
5781                       }
5782                     }).start();
5783                   }
5784                 });
5785
5786                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new String[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
5787                 dfetch.add(fetchr);
5788                 comp++;
5789                 // and then build the rest of the individual menus
5790                 ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new String[]{src.getDbSource()}));
5791                 icomp = 0;
5792                 String imname = null;
5793                 int i = 0;
5794                 for (DbSourceProxy sproxy : otherdb)
5795                 {
5796                   String dbname = sproxy.getDbName();
5797                   String sname = dbname.length() > 5 ? dbname.substring(0,
5798                           5) + "..." : dbname;
5799                   String msname = dbname.length() > 10 ? dbname.substring(
5800                           0, 10) + "..." : dbname;
5801                   if (imname == null)
5802                   {
5803                     imname = MessageManager.formatMessage("label.from_msname", new String[]{sname});
5804                   }
5805                   fetchr = new JMenuItem(msname);
5806                   final DbSourceProxy[] dassrc =
5807                   { sproxy };
5808                   fetchr.addActionListener(new ActionListener()
5809                   {
5810
5811                     @Override
5812                     public void actionPerformed(ActionEvent e)
5813                     {
5814                       new Thread(new Runnable()
5815                       {
5816
5817                         @Override
5818                         public void run()
5819                         {
5820                           new jalview.ws.DBRefFetcher(alignPanel.av
5821                                   .getSequenceSelection(),
5822                                   alignPanel.alignFrame, dassrc)
5823                                   .fetchDBRefs(false);
5824                         }
5825                       }).start();
5826                     }
5827
5828                   });
5829                   fetchr.setToolTipText("<html>"
5830                           + MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{dbname}));
5831                   ifetch.add(fetchr);
5832                   ++i;
5833                   if (++icomp >= mcomp || i == (otherdb.size()))
5834                   {
5835                     ifetch.setText(MessageManager.formatMessage(
5836                             "label.source_to_target", imname, sname));
5837                     dfetch.add(ifetch);
5838                     ifetch = new JMenu();
5839                     imname = null;
5840                     icomp = 0;
5841                     comp++;
5842                   }
5843                 }
5844               }
5845               ++dbi;
5846               if (comp >= mcomp || dbi >= (dbclasses.length))
5847               {
5848                 dfetch.setText(MessageManager.formatMessage(
5849                         "label.source_to_target", mname, dbclass));
5850                 rfetch.add(dfetch);
5851                 dfetch = new JMenu();
5852                 mname = null;
5853                 comp = 0;
5854               }
5855             }
5856           }
5857         });
5858       }
5859     }).start();
5860
5861   }
5862
5863   /**
5864    * Left justify the whole alignment.
5865    */
5866   @Override
5867   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
5868   {
5869     AlignmentI al = viewport.getAlignment();
5870     al.justify(false);
5871     viewport.firePropertyChange("alignment", null, al);
5872   }
5873
5874   /**
5875    * Right justify the whole alignment.
5876    */
5877   @Override
5878   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
5879   {
5880     AlignmentI al = viewport.getAlignment();
5881     al.justify(true);
5882     viewport.firePropertyChange("alignment", null, al);
5883   }
5884
5885   public void setShowSeqFeatures(boolean b)
5886   {
5887     showSeqFeatures.setSelected(true);
5888     viewport.setShowSequenceFeatures(true);
5889   }
5890
5891   /*
5892    * (non-Javadoc)
5893    * 
5894    * @see
5895    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
5896    * awt.event.ActionEvent)
5897    */
5898   @Override
5899   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
5900   {
5901     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
5902     alignPanel.paintAlignment(true);
5903   }
5904
5905   /*
5906    * (non-Javadoc)
5907    * 
5908    * @see
5909    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
5910    * .ActionEvent)
5911    */
5912   @Override
5913   protected void showGroupConsensus_actionPerformed(ActionEvent e)
5914   {
5915     viewport.setShowGroupConsensus(showGroupConsensus.getState());
5916     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5917
5918   }
5919
5920   /*
5921    * (non-Javadoc)
5922    * 
5923    * @see
5924    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
5925    * .event.ActionEvent)
5926    */
5927   @Override
5928   protected void showGroupConservation_actionPerformed(ActionEvent e)
5929   {
5930     viewport.setShowGroupConservation(showGroupConservation.getState());
5931     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5932   }
5933
5934   /*
5935    * (non-Javadoc)
5936    * 
5937    * @see
5938    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
5939    * .event.ActionEvent)
5940    */
5941   @Override
5942   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
5943   {
5944     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
5945     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5946   }
5947
5948   /*
5949    * (non-Javadoc)
5950    * 
5951    * @see
5952    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
5953    * .event.ActionEvent)
5954    */
5955   @Override
5956   protected void showSequenceLogo_actionPerformed(ActionEvent e)
5957   {
5958     viewport.setShowSequenceLogo(showSequenceLogo.getState());
5959     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5960   }
5961
5962   @Override
5963   protected void normaliseSequenceLogo_actionPerformed(ActionEvent e)
5964   {
5965     showSequenceLogo.setState(true);
5966     viewport.setShowSequenceLogo(true);
5967     viewport.setNormaliseSequenceLogo(normaliseSequenceLogo.getState());
5968     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5969   }
5970
5971   @Override
5972   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
5973   {
5974     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5975   }
5976
5977   /*
5978    * (non-Javadoc)
5979    * 
5980    * @see
5981    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
5982    * .event.ActionEvent)
5983    */
5984   @Override
5985   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
5986   {
5987     if (avc.makeGroupsFromSelection())
5988     {
5989       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5990       alignPanel.updateAnnotation();
5991       alignPanel.paintAlignment(true);
5992     }
5993   }
5994
5995   @Override
5996   protected void createGroup_actionPerformed(ActionEvent e)
5997   {
5998     if (avc.createGroup())
5999     {
6000       alignPanel.alignmentChanged();
6001     }
6002   }
6003
6004   @Override
6005   protected void unGroup_actionPerformed(ActionEvent e)
6006   {
6007     if (avc.unGroup())
6008     {
6009       alignPanel.alignmentChanged();
6010     }
6011   }
6012
6013   /**
6014    * make the given alignmentPanel the currently selected tab
6015    * 
6016    * @param alignmentPanel
6017    */
6018   public void setDisplayedView(AlignmentPanel alignmentPanel)
6019   {
6020     if (!viewport.getSequenceSetId().equals(
6021             alignmentPanel.av.getSequenceSetId()))
6022     {
6023       throw new Error(MessageManager.getString("error.implementation_error_cannot_show_view_alignment_frame"));
6024     }
6025     if (tabbedPane != null
6026             & alignPanels.indexOf(alignmentPanel) != tabbedPane
6027                     .getSelectedIndex())
6028     {
6029       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
6030     }
6031   }
6032
6033   /**
6034    * Action on selection of menu options to Show or Hide annotations.
6035    * 
6036    * @param visible
6037    * @param forSequences
6038    *          update sequence-related annotations
6039    * @param forAlignment
6040    *          update non-sequence-related annotations
6041    */
6042   @Override
6043   protected void setAnnotationsVisibility(boolean visible,
6044           boolean forSequences, boolean forAlignment)
6045   {
6046     for (AlignmentAnnotation aa : alignPanel.getAlignment()
6047             .getAlignmentAnnotation())
6048     {
6049       boolean apply = (aa.sequenceRef == null && forAlignment)
6050               || (aa.sequenceRef != null && forSequences);
6051       if (apply)
6052       {
6053         aa.visible = visible;
6054       }
6055     }
6056     alignPanel.validateAnnotationDimensions(false);
6057     alignPanel.alignmentChanged();
6058   }
6059
6060   /**
6061    * Store selected annotation sort order for the view and repaint.
6062    */
6063   @Override
6064   protected void sortAnnotations_actionPerformed()
6065   {
6066     this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder());
6067     this.alignPanel.av
6068             .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
6069     alignPanel.paintAlignment(true);
6070   }
6071
6072   /**
6073    * 
6074    * @return alignment panels in this alignemnt frame
6075    */
6076   public List<AlignmentViewPanel> getAlignPanels()
6077   {
6078     return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
6079   }
6080 }
6081
6082 class PrintThread extends Thread
6083 {
6084   AlignmentPanel ap;
6085
6086   public PrintThread(AlignmentPanel ap)
6087   {
6088     this.ap = ap;
6089   }
6090
6091   static PageFormat pf;
6092
6093   @Override
6094   public void run()
6095   {
6096     PrinterJob printJob = PrinterJob.getPrinterJob();
6097
6098     if (pf != null)
6099     {
6100       printJob.setPrintable(ap, pf);
6101     }
6102     else
6103     {
6104       printJob.setPrintable(ap);
6105     }
6106
6107     if (printJob.printDialog())
6108     {
6109       try
6110       {
6111         printJob.print();
6112       } catch (Exception PrintException)
6113       {
6114         PrintException.printStackTrace();
6115       }
6116     }
6117   }
6118 }