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