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