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