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