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