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