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