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