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