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