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