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