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