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