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