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