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