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