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