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