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