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