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