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